@audienti/cli 0.1.145 → 0.2.0-exora.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +47 -895
  2. package/bin/audienti-mcp.mjs +2 -0
  3. package/cli/account-intake-operations.mjs +28 -0
  4. package/cli/account-intake.mjs +33 -0
  5. package/cli/account-replies.mjs +35 -0
  6. package/cli/account-reply-operations.mjs +55 -0
  7. package/cli/agents.mjs +160 -0
  8. package/cli/assignment-operations.mjs +218 -0
  9. package/cli/assignment.mjs +116 -0
  10. package/cli/attachment-operations.mjs +9 -0
  11. package/cli/attachments.mjs +28 -0
  12. package/cli/audience-operations.mjs +21 -0
  13. package/cli/audiences.mjs +29 -0
  14. package/cli/audienti.mjs +217 -0
  15. package/cli/audit-operations.mjs +17 -0
  16. package/cli/audit.mjs +39 -0
  17. package/cli/budget-operations.mjs +8 -0
  18. package/cli/budgets.mjs +34 -0
  19. package/cli/campaign-find.mjs +155 -0
  20. package/cli/campaign-operations.mjs +30 -0
  21. package/cli/campaigns.mjs +61 -0
  22. package/cli/cohort-operations.mjs +22 -0
  23. package/cli/cohorts.mjs +45 -0
  24. package/cli/connection-registry-operations.mjs +8 -0
  25. package/cli/content.mjs +29 -0
  26. package/cli/discovery-operations.mjs +28 -0
  27. package/cli/discovery.mjs +15 -0
  28. package/cli/file-operations.mjs +29 -0
  29. package/cli/files.mjs +14 -0
  30. package/cli/guardrails-operations.mjs +12 -0
  31. package/cli/guardrails.mjs +37 -0
  32. package/cli/json-file.mjs +13 -0
  33. package/cli/kv-operations.mjs +17 -0
  34. package/cli/kv.mjs +58 -0
  35. package/cli/mcp.mjs +47 -0
  36. package/cli/notification-operations.mjs +8 -0
  37. package/cli/offer-operations.mjs +12 -0
  38. package/cli/offers.mjs +24 -0
  39. package/cli/outbound-operations.mjs +16 -0
  40. package/cli/outbound.mjs +54 -0
  41. package/cli/outcome-operations.mjs +26 -0
  42. package/cli/outcomes.mjs +16 -0
  43. package/cli/owner-account-client.mjs +18 -0
  44. package/cli/playbook-help.mjs +79 -0
  45. package/cli/playbook-operations.mjs +36 -0
  46. package/cli/profiles.mjs +29 -0
  47. package/cli/progress.mjs +26 -0
  48. package/cli/prospecting-settings-operations.mjs +16 -0
  49. package/cli/prospecting-settings.mjs +15 -0
  50. package/cli/question-operations.mjs +12 -0
  51. package/cli/questions.mjs +32 -0
  52. package/cli/queue-operations.mjs +12 -0
  53. package/cli/queue.mjs +25 -0
  54. package/cli/recipient-operations.mjs +13 -0
  55. package/cli/recipients.mjs +34 -0
  56. package/cli/rooms-operations.mjs +48 -0
  57. package/cli/rooms.mjs +160 -0
  58. package/cli/search-operations.mjs +9 -0
  59. package/cli/secret-operations.mjs +7 -0
  60. package/cli/secrets.mjs +30 -0
  61. package/cli/sender-account-operations.mjs +28 -0
  62. package/cli/sender-accounts.mjs +33 -0
  63. package/cli/skills-mcp.mjs +31 -0
  64. package/cli/skills.mjs +113 -0
  65. package/cli/suppression-operations.mjs +8 -0
  66. package/cli/suppression.mjs +12 -0
  67. package/cli/tools-operations.mjs +78 -0
  68. package/cli/tools.mjs +100 -0
  69. package/cli/usage-operations.mjs +8 -0
  70. package/cli/usage.mjs +41 -0
  71. package/cli/work-execution-operations.mjs +42 -0
  72. package/cli/work-execution.mjs +29 -0
  73. package/cli/work-feedback-operations.mjs +17 -0
  74. package/cli/work-feedback.mjs +18 -0
  75. package/cli/work-operations.mjs +30 -0
  76. package/cli/work.mjs +32 -0
  77. package/cli/workspaces.mjs +255 -0
  78. package/package.json +15 -26
  79. package/skills/agent-rooms/SKILL.md +140 -0
  80. package/skills/agent-rooms/references/commands.md +78 -0
  81. package/skills/audienti/SKILL.md +161 -317
  82. package/skills/audienti/references/helpers.md +162 -0
  83. package/skills/audienti/references/profile-workflows.md +214 -0
  84. package/skills/audienti/references/workflow-receipt.md +49 -0
  85. package/skills/audienti/scripts/contracts.mjs +72 -0
  86. package/skills/audienti/scripts/profile-helpers.mjs +138 -0
  87. package/skills/audienti/scripts/run.mjs +40 -0
  88. package/skills/getting-started/SKILL.md +227 -0
  89. package/skills/getting-started/references/audience.md +143 -0
  90. package/skills/getting-started/references/campaign-draft.md +80 -0
  91. package/skills/getting-started/references/capabilities.md +237 -0
  92. package/skills/getting-started/references/mailbox-check.md +64 -0
  93. package/skills/getting-started/references/outcome-checks.md +114 -0
  94. package/skills/getting-started/references/replies.md +186 -0
  95. package/skills/getting-started/references/reply-checks.md +54 -0
  96. package/skills/playbook-design/SKILL.md +136 -0
  97. package/skills/playbook-design/references/fields.md +130 -0
  98. package/source-manifest.json +104 -0
  99. package/.mcp.json +0 -13
  100. package/CHANGELOG.md +0 -1018
  101. package/bin/audienti-mcp.js +0 -5
  102. package/bin/audienti.js +0 -6
  103. package/install +0 -83
  104. package/skills/audienti/references/methodology.md +0 -111
  105. package/skills/audienti-gift-research/SKILL.md +0 -47
  106. package/src/agent-skills.js +0 -60
  107. package/src/api-client.js +0 -1639
  108. package/src/cli.js +0 -16992
  109. package/src/config.js +0 -130
  110. package/src/mcp.js +0 -103
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ import { openSync, closeSync, readSync, realpathSync } from 'node:fs';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { MAX_INPUT_BYTES } from './contracts.mjs';
5
+ import { runHelper } from './profile-helpers.mjs';
6
+
7
+ export function readHelperInput(path) {
8
+ const fd = path === '-' ? 0 : openSync(path, 'r');
9
+ try {
10
+ const chunks = []; let size = 0;
11
+ while (size <= MAX_INPUT_BYTES) {
12
+ const chunk = Buffer.alloc(Math.min(65536, MAX_INPUT_BYTES + 1 - size));
13
+ const count = readSync(fd, chunk, 0, chunk.length, null);
14
+ if (!count) break;
15
+ size += count; chunks.push(chunk.subarray(0, count));
16
+ }
17
+ if (size > MAX_INPUT_BYTES) throw new Error('Helper input exceeds 1 MiB.');
18
+ try { return JSON.parse(Buffer.concat(chunks).toString('utf8')); }
19
+ catch { throw new Error('Helper input must be valid JSON.'); }
20
+ } finally { if (fd !== 0) closeSync(fd); }
21
+ }
22
+
23
+ export function executeHelperFile(name, path) {
24
+ // Neither a filename nor a helper name is ever imported or executed.
25
+ return runHelper(name, readHelperInput(path));
26
+ }
27
+
28
+ let isEntry = false;
29
+ try { isEntry = Boolean(process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href); }
30
+ catch { /* Imported from a synthetic runner without a file entry point. */ }
31
+ if (isEntry) {
32
+ try {
33
+ const [name, flag, path, ...rest] = process.argv.slice(2);
34
+ if (!name || flag !== '--file' || !path || rest.length) throw new Error('Usage: node scripts/run.mjs HELPER --file INPUT.json|-');
35
+ process.stdout.write(`${JSON.stringify(executeHelperFile(name, path))}\n`);
36
+ } catch {
37
+ process.stderr.write('Skill helper failed. Use a bundled helper name and valid JSON input of at most 1 MiB.\n');
38
+ process.exitCode = 1;
39
+ }
40
+ }
@@ -0,0 +1,227 @@
1
+ ---
2
+ name: getting-started
3
+ description: Run once, right after your Audienti connection is approved. Report the tools and connectors you already have, introduce yourself to the user, and propose a first campaign test (offer, audience, playbook, length) drafted from what you can already read, so the user only corrects it. Use again when your tools change or the user asks for a new test.
4
+ ---
5
+
6
+ # Getting started with Audienti
7
+
8
+ You have just been connected to a person's Audienti account. Audienti is an agentic CRM
9
+ and the system of record for this work: audiences, offers, campaigns, people, companies
10
+ and the work you do for them. The person who approved you is the user. The user wants
11
+ to learn which playbook books meetings: turn their ideas into campaign tests quickly, do
12
+ as much of the work as you can, and ask only for what you cannot find yourself. Write
13
+ plainly and briefly, address the user as "you", no emoji, no marketing tone.
14
+
15
+ Start every run with the work queue: `audienti queue next --json`. The reply opens with
16
+ `orientation`: what Audienti is, who you are and how to work. Follow it. One read
17
+ covers every workspace your user approved you in (`workspaces` lists them), so one
18
+ schedule and one call are enough; `--workspace ID` narrows it to one. `next` is the task
19
+ to take now; tasks you already hold are in `holding`, finish those first. Each task names
20
+ its `workspace`: run every `audienti` command for it with `--workspace` and that ID, as
21
+ `workspaceStep` says. Claim a task before you start it (`audienti queue claim TASK`), report it
22
+ when you finish (`audienti queue done TASK --note "what you did"`) or release it
23
+ (`audienti queue release TASK --note "why"`). Over MCP, use `agent_queue_list`,
24
+ `agent_queue_claim`, `agent_queue_complete` and `agent_queue_release`. On a long task,
25
+ claim it again every 20 to 30 minutes with a note that says where you are: that extends
26
+ your lease and shows the user your progress; if your lease lapses, another agent may
27
+ take the task and continue from your last note. A refused claim says why: `task_taken`
28
+ (another agent holds it; pick another task), `task_closed` (done, skipped or no longer
29
+ due; read the queue again) or `task_not_for_you` (the message says which: assigned to
30
+ another agent; or it needs a capability you have not reported as connected, so report
31
+ your tools again if you have one; or its requirements are being refreshed, so read the
32
+ queue again and claim it from there). Your first task is usually
33
+ **Report your tools and connectors**, part 1 of three: triage your tools and report
34
+ them, asking nothing; introduce yourself and propose a first campaign as one question in
35
+ Operator (`audienti ask`), not in chat; save the campaign as a draft once the user
36
+ answers, and show five fits.
37
+
38
+ ## Rules that always apply
39
+
40
+ - **Report, never connect.** List only the tools and connectors your runtime already
41
+ has. Do not install connectors, start OAuth or ask the user to sign in to anything
42
+ during this skill. If something is missing, say it is missing.
43
+ - **Never invent.** Draft only from what you read. Do not make up customers, results,
44
+ numbers or claims. Mark anything you inferred as inferred.
45
+ - **Draft, never send.** Everything you create is a draft. Nothing goes to a prospect.
46
+ The user activates a campaign in Audienti.
47
+ - **Never include secrets.** No passwords, API keys, tokens or session cookies in any
48
+ report, message or field.
49
+ - **Read-only does not stop you.** Run this skill right after connecting, without
50
+ waiting to be told. If the user left **Allow workspace changes** unticked, you can
51
+ still read, work the queue, report your tools and ask in Operator (`audienti ask`).
52
+ When a step has to save a record, ask the user there to turn it on in **Settings →
53
+ Agents**, and carry on with the rest.
54
+ - **Audienti is the record.** Save what you and the user decide in Audienti records
55
+ (offer text, audience filters, campaign premise and notes), not only in chat or memory.
56
+
57
+ ## 1. Triage your tools
58
+
59
+ First, keep yourself awake. Audienti never calls you, so set a durable schedule in your
60
+ own host now that starts you and runs `audienti queue next` (no `--workspace`, so it
61
+ covers all your user's workspaces), then does the work it returns: every 5 minutes during your user's working hours, every 15 minutes outside them
62
+ (`queue next` names the hours in `orientation`; `pollAfterSeconds` gives the next gap). A
63
+ timer inside your current session, such as a Claude Code `/loop` or a session cron, stops
64
+ when the session ends and does not count. Use a schedule your host keeps: a Paseo schedule
65
+ (`paseo schedule create`, then check it with `paseo schedule ls`), a Claude Code routine or
66
+ cloud schedule, a Codex automation, or cron. Tell your user the schedule is set, and name
67
+ it, when you introduce yourself. If your host cannot wake you, say plainly that it cannot
68
+ wake you, so you only check when they message you.
69
+
70
+ 1. Confirm your connection: `audienti workspace list --profile AGENT_PROFILE`, then select
71
+ the workspace the user approved (`audienti workspace use ID --profile AGENT_PROFILE`;
72
+ over MCP, `workspace_select`).
73
+ 2. List every tool, connector, plugin and integration you can call now: your runtime's
74
+ own listing (tool list, connector settings, MCP servers, installed plugins) and every
75
+ integration hub you use. Check aggregators first: if you reach tools through Composio,
76
+ Treg or another hub, list each app connected there. Then list your native connectors.
77
+ Leave out Audienti's own hosted tools unless you reach them through another connector.
78
+ 3. Turn that list into capability rows (capability id, provider, `via`, the user's
79
+ `account` for anything you send as the user, access, status) as
80
+ [the capability report format](references/capabilities.md) describes; it has the
81
+ full catalog. Never write `via: audienti`.
82
+ 4. Report it: `audienti agent capabilities report --file CAPABILITIES.json --profile
83
+ AGENT_PROFILE` (over MCP, `agent_capabilities_report` with the same JSON; over HTTP,
84
+ `PUT /api/v1/agent-capabilities`). The report replaces the previous one, and the
85
+ user sees it in **Settings → Agents**. It counts as stale after seven days: report
86
+ again whenever your tools change, and at least weekly.
87
+ 5. Check what Audienti counts for you: `audienti agent capabilities --profile
88
+ AGENT_PROFILE` (over MCP, `agent_capabilities_list`): the rows that work now and the
89
+ ones that work with Audienti's hosted tools in this workspace.
90
+ 6. Install the skills you will use: `audienti skills list --compact`, then `audienti
91
+ skills read ID SKILL.md` and only the references the task needs (over MCP,
92
+ `skills_list` with `compact: true` and `skills_read`). The `audienti` skill covers
93
+ prospect finding and profile research; planning, writing and cadence skills are in
94
+ the `audienti/plugins` marketplace, installed with your runtime's plugin installer.
95
+
96
+ Email goes out from your user's own mailbox through your own email connector (Gmail or
97
+ Outlook, through Composio, a Claude connector or similar). If you have none, tell your user
98
+ exactly: “I need a Gmail or Outlook connector to send from your mailbox.” Never send your
99
+ user to Settings to connect a mailbox. If you have one, report the mailbox in your
100
+ capability report and ask your user to approve it in **Settings → Sending**; then
101
+ Audienti gives you a queue task to send one test email from the mailbox to itself, read it
102
+ back and report its code; the mailbox counts once you do
103
+ ([the mailbox test email](references/mailbox-check.md)). Never ask your user for the code.
104
+ A campaign can run without a mailbox: its people with an email step wait at that step, and their
105
+ LinkedIn steps go on. Nothing is skipped. They go on once a mailbox is confirmed, so the fix is the
106
+ email connector.
107
+
108
+ ## 2. Introduce yourself and propose a first campaign
109
+
110
+ First check what already exists: `audienti campaign list`, `audienti offer list`,
111
+ `audienti audience list`. If the workspace already has an active campaign, introduce
112
+ yourself, list your tools and ask what the user wants to test next; skip the rest of
113
+ this section.
114
+
115
+ Otherwise draft a complete first campaign before you ask anything. Start from the
116
+ user's own answers: `audienti workspace show` returns `startingAudience` (their website or
117
+ `noWebsite`, what they sell, `about` the business, the first `offer`, and their target buyer:
118
+ industries, company `sizes`, job functions, job `titles` and regions, with `providerId`s you
119
+ can reuse; ask only for what they left out) and
120
+ `approximateSize`, how many people Audienti can reach for that buyer (10000 means more
121
+ than 10,000); both are `null` if they did not answer on the setup page. Treat them as the
122
+ user's first audience, not a finished one. Under 1,000 people is too few to target: one
123
+ sending account reaches about 500 a month, so the campaign runs dry within a month. If
124
+ `approximateSize` is below 1,000, propose a wider buyer (more industries, job functions or
125
+ regions) in your draft. Then read what your tools reach: the user's
126
+ website, documents, recent sent email, CRM records and LinkedIn profile. Fill every
127
+ field of [the campaign draft](references/campaign-draft.md) in the user's own words,
128
+ offer first; if you cannot find an offer, your one question is what they sell and to
129
+ whom. Build one buyer lane as structured filters, every value resolved with `audienti
130
+ audience options --kind KIND --q TEXT`, always with the person function that owns the
131
+ problem, plus at most one smart rule (`textCriteria`) a reviewer can answer true or
132
+ false ([audience definition](references/audience.md)). The audience says who they are;
133
+ anything about why now (hiring, funding, a new leader, an intent to buy) is a campaign
134
+ signal, not a smart rule. Give the campaign at least one signal: prospect finding starts
135
+ from signals, which find the companies that need the offer today ([the through line](references/campaign-draft.md#signals-the-through-line)).
136
+
137
+ Ask the user in Audienti, not in chat: `audienti ask "QUESTION" --context "PROPOSAL"
138
+ --option "Go" --option "Change it" --topic first_campaign`. It shows as a card in
139
+ Operator, and the answer comes back to you as a task. The topic keeps one open card per
140
+ subject (`first_campaign`, `campaign:ID` or `tools`): a new question with the same topic
141
+ replaces the old one. Keep the question itself to one short sentence: the decision you
142
+ need. Put the rest in the context, one short line per fact, in this order: who you are
143
+ and what you will do; your tools in one line and what is missing, as a fact; the
144
+ proposed campaign as a short card, marking what you inferred; the first message as you
145
+ would send it, so the user can check the voice; every rule that cannot say exactly what
146
+ the user asked for, with what you used instead (company size bands cannot express 200 to
147
+ 2,000 employees); a missing sending account for the playbook's first channel; at most two
148
+ questions the user can answer on the same card; and that nothing is sent without their
149
+ approval. Finding the five fits in section 3 costs credits, so ask for that OK on the
150
+ same card: `--option "Approve, find 5 people for up to 20 credits" --option "Change it"`.
151
+ A note the user types on the card comes with the option they pick. Give every cost in
152
+ credits, never dollars. Name
153
+ things the way the user sees them; never put IDs (campaign, signal, revision) in the
154
+ question or the context.
155
+ Whenever you propose something or ask the user to check your work, give the yes/no choice
156
+ as options (`--option "Approve" --option "Change it"`): each option is a button that sends
157
+ at once. Leave out error codes, status numbers and tool or provider names; say in plain
158
+ words what happened and what you need. Link people and records with their URLs.
159
+ [The campaign draft](references/campaign-draft.md) ends with an example of the shape.
160
+
161
+ ## 3. Save the drafts and show five fits
162
+
163
+ When the user answers (a **Read your user's answer** task in your queue):
164
+
165
+ 1. Apply their changes. Save their words as they wrote them.
166
+ 2. Create the records with fresh request IDs, reusing each ID if you retry:
167
+ `audienti offer create`, `audienti audience create`, then `audienti campaign create`
168
+ with the premise, playbook, channels and `schedule.endAt` (over MCP, `offer_create`,
169
+ `audience_create`, `campaign_create`). The campaign starts in `Draft`; nothing runs
170
+ until the user starts it. Then save the pace: `audienti campaign pace ID --per-day N`
171
+ with the user's number, or `--per-day derived` when they kept it (over MCP,
172
+ `campaign_pace_set`). Only `campaign pace` takes `--per-day`; `campaign create` and
173
+ `update` refuse it. When the user approved a spend for finding, such as "find 5 people
174
+ for up to 20 credits", save the cap too: `audienti campaign pace ID --max-credits 20`.
175
+ Each day's find task carries it; pass it to `audienti campaign find` and never raise it.
176
+ 3. Run `audienti audience summary --audience ID` and name five people or companies
177
+ that fit, with one line on why each fits and a link to the evidence. If you have no
178
+ people-data tool, say so and show the audience filters instead.
179
+ 4. Ask the user with `audienti ask --campaign ID` to review it on the campaign page
180
+ and start it. Quote the campaign's own Pace line from `audienti campaign pace ID`.
181
+ Name the controls the page has: the status menu next to the campaign name offers
182
+ **Find prospects only**, which moves it to `Preparing`, where signal tracking and
183
+ prospect finding run, and **Activate campaign**, which moves it to `Active`, where
184
+ engagement runs too. There is no Start button.
185
+ 5. Then offer to draft first messages for the five fits once the campaign is active.
186
+
187
+ If a create call fails, say what happened in one sentence and what you will do next; do
188
+ not retry with a new request ID. For a later idea, draft a new campaign or a successor
189
+ (`audienti campaign successor ID`) that changes one thing (playbook, offer or audience)
190
+ and name what changed, so the two tests compare; save a new playbook as a template with
191
+ the playbook-design skill, then pin it to the successor.
192
+
193
+ ## After getting started: work the queue
194
+
195
+ After that, Audienti puts the daily work in your queue: get a draft campaign ready to
196
+ start, find prospects at the campaign's pace, plan each prospect's next step, handle
197
+ replies and send what the user approved. Every task carries its steps and names its
198
+ skill; outcome checks follow [the check procedure](references/outcome-checks.md),
199
+ replies [the reply procedure](references/replies.md), a mailbox's test email [the mailbox test email](references/mailbox-check.md), and the
200
+ reply check before each email follow-up [the mailbox checks](references/reply-checks.md). When the user skips a touch, do
201
+ not propose it again unless they ask for a rewrite; when they take a person over, leave
202
+ them alone. A **Rewrite** (`work.revise`) comes to the agent that wrote the draft; its
203
+ steps carry the commands. Answer the request with `audienti work changes respond`:
204
+ saving the revision alone does not answer it, and the answer finishes the task. A new
205
+ note from the user replaces the task, and the task closes when the user approves,
206
+ skips or withdraws the touch, even while you hold it: then stop and read the queue
207
+ again. Every first email ends with one plain sentence that lets the prospect opt out.
208
+ Do not do work another agent holds (`fleet`); hand off or ask for help in a room
209
+ (`audienti rooms send`). `needsOtherTools` lists tasks no agent can take: tell the
210
+ user which connector would unlock them. A task there with `waitingOn: credits` needs an
211
+ Audienti hosted tool and the workspace has no credits left: tell the user once that it
212
+ waits on credits and runs when they add credits in Billing, and do not ask again. When
213
+ credits are added or run out, or the user adds or removes one of their own provider keys
214
+ in the Vault, every agent gets a notice task: run `audienti agent capabilities` again,
215
+ then do what its steps say. If you reported an `owner.notify` capability, Audienti puts one
216
+ **Tell your user about an alert** task in your queue for each alert it records. Its steps
217
+ give the message and a link: tell your user that message on whatever channel you use with
218
+ them (chat, push, Slack, SMS or email), include the link, and mark the task done once it
219
+ is delivered. Done means delivered: a done task is not created again for the same alert.
220
+ An agent that did not report `owner.notify` gets no such task. When the queue is empty, your timer runs it
221
+ again after `pollAfterSeconds`; when `ask` or `queue next` returns an `announce` line,
222
+ say it in your chat and point to Operator. Keep machine state in Audienti, not in your
223
+ own memory: `audienti kv set KEY VALUE` stores cursors, change tokens, hashes, ids and
224
+ timestamps for every agent of your user in this workspace, `audienti kv get KEY` reads
225
+ them back on the next run, and `--if-version N` protects a key two agents may touch;
226
+ credentials never go there: the Vault holds them and you store the Vault key name. Never
227
+ send anything to a prospect that the user has not approved.
@@ -0,0 +1,143 @@
1
+ # Audience definition
2
+
3
+ An audience is a set of structured filters plus one smart rule. Discovery and fit checks
4
+ read the structured filters first, so put everything you can into them. Free text is
5
+ only for the one test the filters cannot express.
6
+
7
+ An audience says **who** the buyer is: facts about the company and the person that stay
8
+ true for months. It never says **why now**. Hiring, funding, a new leader, a tool
9
+ change, a post or an intent to buy are events, and events are campaign signals (see
10
+ section 5). An audience with an event in it matches almost no one.
11
+
12
+ ## 1. Pick one buyer lane
13
+
14
+ One audience describes one kind of buyer: one function at one seniority band, in one
15
+ kind of company. If the offer sells to two different buyers (for example finance and
16
+ operations), draft two audiences and two campaigns. Do not mix lanes in one audience.
17
+
18
+ Every audience sets `person.functions`. Seniority alone is not a lane: "all VPs" reaches
19
+ the VP of Finance and the VP of Engineering as well as the buyer. Pick the function that
20
+ owns the problem the offer solves and signs for it. For example, an outbound or SDR
21
+ agency sells to whoever owns pipeline: Sales, and Marketing when marketing owns
22
+ pipeline. Add `currentTitles` such as "VP of Sales" or "Chief Revenue Officer" when the
23
+ function is still too wide.
24
+
25
+ ## 2. Resolve every value with `audience options`
26
+
27
+ Never type a value from memory. Look each one up and copy the `providerId` and `name`
28
+ the command returns:
29
+
30
+ ```
31
+ audienti audience options --kind industries --q logistics
32
+ audienti audience options --kind sizes
33
+ audienti audience options --kind locations --q "United Kingdom"
34
+ audienti audience options --kind seniorities
35
+ audienti audience options --kind functions --q finance
36
+ audienti audience options --kind titles --q "head of finance"
37
+ ```
38
+
39
+ Kinds: `industries`, `sizes`, `types`, `locations`, `seniorities`, `functions`, `titles`.
40
+ Over MCP, use `audience_options` with the same `kind`. Name industries one by one: new
41
+ audiences cannot add industry groups, because a group's industries do not match its name.
42
+
43
+ - Search ignores case. Several terms in one query ("freight and logistics", or a
44
+ comma-separated list) return the matches for each term.
45
+ - An industry search matches the industry's own name, not the group it sits in:
46
+ "logistics" returns the logistics industries, not airlines or rail.
47
+ - Company size comes in LinkedIn's bands. A range in `--q` ("100 to 1000", "500+",
48
+ "under 50") returns every band it overlaps. Add all of them to `company.sizes`.
49
+ - `functions` is LinkedIn's fixed list of 26 job functions. It needs no Sales Navigator
50
+ account.
51
+ - A title that is not in the list can be saved as typed, without a `providerId`.
52
+
53
+ ## 3. Fill the template
54
+
55
+ ```json
56
+ {
57
+ "name": "Heads of finance, UK logistics, 51-500 staff",
58
+ "company": {
59
+ "industries": [{ "providerId": "ID", "name": "Logistics and Supply Chain" }, { "providerId": "ID", "name": "Package/Freight Delivery" }],
60
+ "sizes": [{ "providerId": "ID", "name": "51-200" }, { "providerId": "ID", "name": "201-500" }],
61
+ "locations": [{ "providerId": "ID", "name": "United Kingdom" }]
62
+ },
63
+ "person": {
64
+ "functions": [{ "providerId": "ID", "name": "Finance" }],
65
+ "seniorities": [{ "providerId": "ID", "name": "Director" }],
66
+ "currentTitles": [{ "providerId": "ID", "name": "Head of Finance" }],
67
+ "locations": [{ "providerId": "ID", "name": "United Kingdom" }]
68
+ },
69
+ "seniorityMatchMode": "at_least",
70
+ "textCriteria": "The company runs its own truck or freight fleet.",
71
+ "notes": "Region and size inferred from the user's last 20 sent emails; user confirmed in Operator."
72
+ }
73
+ ```
74
+
75
+ - `seniorityMatchMode: "at_least"` matches the chosen seniority and higher.
76
+ - After saving, `audienti audience summary --audience ID` returns `sizes`: about how many
77
+ people the included industries, functions and regions reach, against the 1,000-person
78
+ minimum. Under 1,000, widen the industries, functions or regions.
79
+ - Use `negative: true` on a value to exclude it (for example a competitor's industry).
80
+ - `currentTitles` narrows the lane; keep it to the titles that buy, at most a few.
81
+
82
+ ## 4. Write the smart rule (`textCriteria`)
83
+
84
+ The smart rule is one criterion that a reviewer can answer true or false from a
85
+ person's profile or company page. It covers only what the filters cannot express.
86
+
87
+ Good:
88
+
89
+ - "The company runs its own truck or freight fleet."
90
+ - "The person manages accounts receivable or credit control."
91
+ - "The company sells to hospitals."
92
+
93
+ Not allowed in the smart rule:
94
+
95
+ - Provenance: "inferred from…", "based on the website", "found in sent email".
96
+ - Hedges or open questions: "user to confirm", "probably", "maybe", "TBD".
97
+ - Values the filters already hold (industry, size, region, seniority, function, title).
98
+ - More than one test joined with "and" or "or". Pick the one that matters most.
99
+ - Events, timing or intent: "is hiring…", "recently raised…", "is considering…",
100
+ "is looking for…", "just changed…". These are campaign signals (section 5).
101
+
102
+ Put provenance in `notes`. Put open questions to the user in Operator with
103
+ `audienti ask`, not in the audience.
104
+
105
+ Saving refuses a smart rule that states the company's size or an event, and says where
106
+ it belongs: size in `company.sizes`, events in a campaign signal.
107
+
108
+ Check the rule before you save: `audienti audience improve --text "RULE"` returns a
109
+ suggested rewrite when the rule is vague.
110
+
111
+ ## 5. Audience or signal?
112
+
113
+ Ask of each condition: would it still be true of this company in six months?
114
+
115
+ | Condition | Where it goes |
116
+ | --- | --- |
117
+ | Sells B2B software; 51 to 500 staff; based in the US | Audience filters |
118
+ | Sells to hospitals; runs its own freight fleet | Audience smart rule |
119
+ | Owns pipeline or revenue (VP of Sales, CRO) | Audience `person.functions`, `seniorities`, `currentTitles` |
120
+ | Is hiring SDRs; is considering an outbound agency | Campaign signal |
121
+ | Raised a round this quarter; new VP of Sales in the last 90 days | Campaign signal |
122
+
123
+ A signal lives in the campaign's `signals` array. A `research` signal is a yes/no
124
+ question a researcher answers from public evidence such as job posts, news or the
125
+ company's site:
126
+
127
+ ```json
128
+ {
129
+ "signalMatch": "any",
130
+ "signals": [{
131
+ "id": "UUID",
132
+ "kind": "research",
133
+ "scope": "company",
134
+ "question": "Is the company hiring SDRs or BDRs, or looking for an outbound agency?",
135
+ "expected": true,
136
+ "details": "Open SDR or BDR job posts, or a post asking for agency recommendations, in the last 90 days."
137
+ }]
138
+ }
139
+ ```
140
+
141
+ `signalMatch: "any"` lets a company qualify on any one signal; `"all"` needs every one.
142
+ A signal narrows who is worth contacting now; it never replaces the audience, so the
143
+ company and person still have to fit the audience.
@@ -0,0 +1,80 @@
1
+ # Campaign draft
2
+
3
+ Fill this before you ask the user anything. Mark each value with where it came from:
4
+ `found` (quote or link), `inferred` (say from what) or `needed` (ask the user).
5
+
6
+ | Field | Where it goes | How to fill it |
7
+ | --- | --- | --- |
8
+ | Offer: what you sell | Offer | Website, pitch deck, proposals, email signature. User's words. |
9
+ | Offer: problem it solves | Offer | Website, sent email, call notes. User's words. |
10
+ | Offer: proof point | Offer | Only a result or customer the user already states publicly or confirms. |
11
+ | Company type, size, region | Audience filters | Current customers in the CRM or sent email; check values with `audience options`. |
12
+ | Buyer function, seniority and title | Audience filters | The function that owns the problem the offer solves, then who replies to the user or signs deals. Always set a function. |
13
+ | Who to avoid | Do-not-contact list | Current customers, partners, competitors (`dnc add` or `dnc import`). |
14
+ | Signals | Campaign `signals` | At least one. Events that make a company worth contacting now: hiring for a role, funding, a new leader, a tool change. Each one is a yes/no `research` question ([audience definition](audience.md) section 5). Never put these in the audience. |
15
+ | Premise | Campaign premise | One sentence: why these people, why now. It names the signal and why the signal means they need the offer. |
16
+ | Playbook | Campaign playbook pin | One playbook from `playbook list`, or a new one drafted with the playbook-design skill, with one line on why. When the list returns a `recommendation`, start from that starter playbook and name it with its `reason` in one line. |
17
+ | Channels | Campaign channels | `email` and `linkedin`, only for accounts you reported. |
18
+ | Pace | Campaign pace | New people a day. Take the number and its reason from the **Pace line** in your queue task for the channels you propose: 25 a day per confirmed LinkedIn account, or the campaign's own number (50 by default) for email only. Write it as `Pace: 25 a day · 1 LinkedIn account · about 100 people a week`. Once the campaign is saved, `audienti campaign pace ID` returns its own line: use that line, so the card and the campaign page show the same number. The user can change it in their answer. |
19
+ | Test length | Campaign `schedule.endAt`, `schedule.maxCompanies` | 30 days from activation or a company cap, whichever comes first. |
20
+ | Success measure | Playbook goal stage | Meetings booked. |
21
+ | Waits | Playbook step `wait` | What each step waits for (`accept`, `reply`, `meeting` or `timeout`) and for how many days; their sum fits the test length. A wait starts only when the agent reports a succeeded result for the plan action that carries the step's key as `playbookStepKey`, and any reply ends every wait (playbook-design `references/fields.md`). |
22
+
23
+ ## Signals: the through line
24
+
25
+ Prospect finding starts from signals: they find the companies that need the offer
26
+ today. A campaign with no signal, no target companies and no finding instructions cannot
27
+ start, so every signal campaign you draft has at least one signal. When the user already
28
+ has the people, or a source Audienti does not have, draft a custom campaign instead:
29
+ `method: "custom"`, finding instructions in the user's words and no signals. A signal is not "they might want to buy". It is an
30
+ observable event that shows a company has the problem the offer solves, now. Build the
31
+ line in this order:
32
+
33
+ 1. **Offer**: what it solves. "An outbound agency books meetings for B2B software teams."
34
+ 2. **Problem**: who has it. "The company needs pipeline and has no working outbound team."
35
+ 3. **Signal**: the event that shows the problem today. "The company is hiring SDRs or
36
+ BDRs": it has decided to invest in outbound but does not have the team yet.
37
+ 4. **Premise**: one sentence that joins them. "Companies hiring their first SDRs need
38
+ pipeline before the hires ramp; the agency books meetings in weeks."
39
+
40
+ If you cannot name the event that shows the problem, ask the user what tells them a
41
+ company needs the offer now. Do not leave the signals empty.
42
+
43
+ Ask for at most two `needed` values in your first message. Fill the rest after the user
44
+ replies.
45
+
46
+ Name the campaign after the buyer and the test, for example "Heads of finance,
47
+ logistics UK, problem-led email". When a later test changes one thing, say which in the
48
+ name or notes.
49
+
50
+ ## Mapping to records
51
+
52
+ | Draft part | Command | MCP tool |
53
+ | --- | --- | --- |
54
+ | Offer | `audienti offer create --file OFFER.json --request-id UUID` | `offer_create` |
55
+ | Audience | `audienti audience create --file AUDIENCE.json --request-id UUID` | `audience_create` |
56
+ | Campaign | `audienti campaign create --file CAMPAIGN.json --request-id UUID` | `campaign_create` |
57
+ | Pace | `audienti campaign pace ID --per-day derived`, or `--per-day N` with the user's number | `campaign_pace_set` |
58
+ | Next test | `audienti campaign successor ID --file CAMPAIGN.json --request-id UUID` | `campaign_successor` |
59
+
60
+ Read each command's request fields in the API schema at `/api/v1/openapi.json`
61
+ (search for the MCP tool name above). A campaign also needs the `audienceRevision`
62
+ and `offerRevision` returned when you saved the audience and offer.
63
+
64
+ ## Example proposal
65
+
66
+ The shape of a first proposal, not fixed copy:
67
+
68
+ > I'm Arthur, your prospecting agent in Audienti. I find people who fit, research them
69
+ > and draft first messages for you to review. I can use Gmail (read, draft), LinkedIn
70
+ > through Composio, and web search. No CRM is connected, so records stay in Audienti.
71
+ > Proposed first test, 30 days. Offer: late-invoice recovery for logistics firms (from
72
+ > your website). Audience: finance function, Director and above, logistics companies,
73
+ > 51 to 500 staff, UK (region inferred from your last 20 sent emails). Smart rule: the
74
+ > company runs its own truck or freight fleet. Signal: the company is hiring a credit
75
+ > controller or accounts receivable clerk. Premise: a company hiring for collections
76
+ > has late invoices piling up now, and recovery gets cash back faster than a new hire. Playbook: problem-led email, then LinkedIn
77
+ > follow-up. Pace: 25 a day · 1 LinkedIn account · about 100 people a week. Success:
78
+ > meetings booked. Two questions: is the UK the right first region, and is there a
79
+ > customer result I may cite? Answer "Go" to save it as a draft, or write your changes.
80
+ > Nothing is sent until you approve it.