@audienti/cli 0.2.0-exora.1 → 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 (85) hide show
  1. package/README.md +30 -6
  2. package/cli/account-reply-operations.mjs +26 -1
  3. package/cli/agents.mjs +67 -14
  4. package/cli/assignment-operations.mjs +12 -12
  5. package/cli/assignment.mjs +6 -6
  6. package/cli/attachment-operations.mjs +9 -0
  7. package/cli/attachments.mjs +28 -0
  8. package/cli/audience-operations.mjs +5 -0
  9. package/cli/audiences.mjs +7 -4
  10. package/cli/audienti.mjs +114 -21
  11. package/cli/audit-operations.mjs +17 -0
  12. package/cli/audit.mjs +39 -0
  13. package/cli/budget-operations.mjs +8 -0
  14. package/cli/budgets.mjs +34 -0
  15. package/cli/campaign-find.mjs +155 -0
  16. package/cli/campaign-operations.mjs +30 -0
  17. package/cli/campaigns.mjs +61 -0
  18. package/cli/cohort-operations.mjs +22 -0
  19. package/cli/cohorts.mjs +45 -0
  20. package/cli/connection-registry-operations.mjs +2 -1
  21. package/cli/discovery-operations.mjs +4 -3
  22. package/cli/discovery.mjs +3 -2
  23. package/cli/file-operations.mjs +29 -0
  24. package/cli/files.mjs +14 -0
  25. package/cli/guardrails-operations.mjs +12 -0
  26. package/cli/guardrails.mjs +37 -0
  27. package/cli/json-file.mjs +1 -1
  28. package/cli/kv-operations.mjs +17 -0
  29. package/cli/kv.mjs +58 -0
  30. package/cli/mcp.mjs +3 -3
  31. package/cli/notification-operations.mjs +8 -0
  32. package/cli/offer-operations.mjs +1 -1
  33. package/cli/outbound-operations.mjs +16 -0
  34. package/cli/outbound.mjs +54 -0
  35. package/cli/outcome-operations.mjs +26 -0
  36. package/cli/outcomes.mjs +16 -0
  37. package/cli/owner-account-client.mjs +3 -3
  38. package/cli/playbook-help.mjs +79 -0
  39. package/cli/playbook-operations.mjs +36 -0
  40. package/cli/progress.mjs +21 -7
  41. package/cli/question-operations.mjs +12 -0
  42. package/cli/questions.mjs +32 -0
  43. package/cli/queue-operations.mjs +12 -0
  44. package/cli/queue.mjs +25 -0
  45. package/cli/recipient-operations.mjs +1 -1
  46. package/cli/recipients.mjs +6 -2
  47. package/cli/rooms-operations.mjs +48 -0
  48. package/cli/rooms.mjs +160 -0
  49. package/cli/search-operations.mjs +9 -0
  50. package/cli/secret-operations.mjs +7 -0
  51. package/cli/secrets.mjs +30 -0
  52. package/cli/sender-account-operations.mjs +6 -2
  53. package/cli/sender-accounts.mjs +18 -8
  54. package/cli/skills-mcp.mjs +13 -6
  55. package/cli/skills.mjs +73 -16
  56. package/cli/tools-operations.mjs +18 -10
  57. package/cli/tools.mjs +48 -20
  58. package/cli/usage-operations.mjs +8 -0
  59. package/cli/usage.mjs +41 -0
  60. package/cli/work-execution-operations.mjs +7 -0
  61. package/cli/work-execution.mjs +3 -3
  62. package/cli/work-operations.mjs +7 -5
  63. package/cli/work.mjs +11 -6
  64. package/cli/workspaces.mjs +121 -24
  65. package/package.json +2 -2
  66. package/skills/agent-rooms/SKILL.md +140 -0
  67. package/skills/agent-rooms/references/commands.md +78 -0
  68. package/skills/audienti/SKILL.md +112 -6
  69. package/skills/audienti/references/helpers.md +15 -2
  70. package/skills/audienti/references/profile-workflows.md +6 -4
  71. package/skills/audienti/scripts/contracts.mjs +1 -1
  72. package/skills/getting-started/SKILL.md +227 -0
  73. package/skills/getting-started/references/audience.md +143 -0
  74. package/skills/getting-started/references/campaign-draft.md +80 -0
  75. package/skills/getting-started/references/capabilities.md +237 -0
  76. package/skills/getting-started/references/mailbox-check.md +64 -0
  77. package/skills/getting-started/references/outcome-checks.md +114 -0
  78. package/skills/getting-started/references/replies.md +186 -0
  79. package/skills/getting-started/references/reply-checks.md +54 -0
  80. package/skills/playbook-design/SKILL.md +136 -0
  81. package/skills/playbook-design/references/fields.md +130 -0
  82. package/source-manifest.json +83 -38
  83. package/cli/approach-operations.mjs +0 -12
  84. package/cli/experiment-operations.mjs +0 -25
  85. package/cli/experiments.mjs +0 -26
@@ -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.
@@ -0,0 +1,237 @@
1
+ # Capability report format
2
+
3
+ Report what you can do for the user right now, as rows. One row per capability,
4
+ provider, source and account. The capability ids come from Audienti's catalog
5
+ ([ADR 015](https://github.com/omalab/exora/blob/main/docs/decisions/015-capability-taxonomy.md));
6
+ the full list is below.
7
+
8
+ ```json
9
+ {
10
+ "runtime": { "name": "Claude Code", "model": "claude-opus-5-5" },
11
+ "capabilities": [
12
+ { "capability": "web.search", "provider": "web", "via": "native", "access": "read", "status": "connected" },
13
+ { "capability": "web.page.read", "provider": "web", "via": "native", "access": "read", "status": "connected" },
14
+ { "capability": "profile.message.send", "provider": "email", "via": "composio:gmail", "account": "owner@example.com", "access": "send", "status": "connected" },
15
+ { "capability": "profile.inbox.read", "provider": "email", "via": "composio:gmail", "account": "owner@example.com", "access": "read", "status": "connected" },
16
+ { "capability": "profile.read", "provider": "linkedin", "via": "composio:linkedin", "access": "read", "status": "connected" },
17
+ { "capability": "profile.message.send", "provider": "linkedin", "via": "composio:linkedin", "account": "linkedin.com/in/owner", "access": "send", "status": "connected", "limits": { "daily": 20 } },
18
+ { "capability": "crm.records.search", "provider": "salesforce", "via": "mcp:salesforce", "access": "read", "status": "needs_sign_in" }
19
+ ]
20
+ }
21
+ ```
22
+
23
+ ```sh
24
+ audienti agent capabilities report --file CAPABILITIES.json --profile AGENT_PROFILE
25
+ audienti agent capabilities --profile AGENT_PROFILE
26
+ ```
27
+
28
+ The first command replaces your previous report. The second prints what Audienti now
29
+ counts for you: the rows that work now, and the rows that work with Audienti's hosted
30
+ tools in the selected workspace. Over MCP, use `agent_capabilities_report` and
31
+ `agent_capabilities_list`. Over HTTP, `PUT` and `GET /api/v1/agent-capabilities`.
32
+
33
+ ## Fields
34
+
35
+ - `runtime.name`: your product or runtime as the user knows it. `runtime.model` is
36
+ optional.
37
+ - `capability`: an id from the list below. An unknown id is refused.
38
+ - `provider`: which kind of profile the work touches. Required where the list names
39
+ providers; leave it out where it says "none". Where it says "any", leave it out or
40
+ name the one you use.
41
+ - `via`: where you reach it. `native` for a connector built into your runtime;
42
+ `composio:<app>` (for example `composio:gmail`), `treg:<tool>` or `mcp:<server>` for
43
+ a hub or MCP server. Check your hubs first: list each app connected there as its own
44
+ row.
45
+ - `account`: the user's own mailbox, LinkedIn account or handle you act through, such as
46
+ `owner@example.com` or `linkedin.com/in/owner`. Required for every verb that acts as
47
+ the user (marked "as the user" below).
48
+ - `access`: `read`, `write` or `send`, as listed for the capability. `send` reaches
49
+ another person; drafting is `write`.
50
+ - `status`: `connected` when you can call it now, `needs_sign_in` when it is installed
51
+ but signed out, `unavailable` when it exists but fails.
52
+ - `cost`: leave it out. Your own tools are `owner`.
53
+ - `limits`: optional `daily` and `hourly` caps you respect.
54
+ - `identity`: optional, on an email or LinkedIn row that names an `account`. The
55
+ account's own stable id, as `{ "namespace": "...", "accountId": "..." }`, so Audienti
56
+ can match the LinkedIn account or mailbox to the one the user set up. Use the same identity on
57
+ every row for that account. Examples: `{ "namespace": "linkedin", "accountId":
58
+ "ACoAAB1234" }` for a LinkedIn account, `{ "namespace": "gmail", "accountId":
59
+ "108234567890" }` for a Gmail mailbox.
60
+
61
+ At most 64 rows and 32 KiB.
62
+
63
+ ## LinkedIn accounts
64
+
65
+ For each LinkedIn account you send as, add an entry to `seats` with what you know about
66
+ it. Audienti shows it to the user on the LinkedIn account's page and plans around it.
67
+
68
+ ```json
69
+ {
70
+ "seats": [
71
+ {
72
+ "provider": "linkedin",
73
+ "account": "linkedin.com/in/owner",
74
+ "checkEverySeconds": 21600,
75
+ "plan": "premium_business",
76
+ "inmail": { "available": true, "quotaPerMonth": 15, "creditsLeft": 11 },
77
+ "limits": { "connectionRequestsPerDay": 20, "connectionRequestsPerWeek": 100, "messagesPerDay": 50, "inmailPerMonth": 15 }
78
+ }
79
+ ]
80
+ }
81
+ ```
82
+
83
+ - `account`: the same LinkedIn account you name in your LinkedIn rows.
84
+ - `checkEverySeconds`: how often you can check this LinkedIn account's sent invitations, at least
85
+ 3600 (one hour). Audienti schedules outcome checks at this cadence; every 6 hours
86
+ when you leave it out. The user never changes it.
87
+ - `plan`: the LinkedIn plan the account has, as the LinkedIn interface shows it: `free`,
88
+ `premium_career`, `premium_business`, `premium_all_in_one`, `sales_navigator_core`,
89
+ `sales_navigator_advanced` or `recruiter_lite`. Leave it out when you cannot tell; never guess.
90
+ The plan sets the InMail allowance. Audienti keeps connection notes to 200 characters for
91
+ now, whatever the plan.
92
+ - `inmail`: whether the LinkedIn account can send InMail, its monthly quota, and `creditsLeft`,
93
+ the balance the interface shows now (Sales Navigator shows it in the compose window and on
94
+ Settings). With a balance, Audienti plans an InMail only while `creditsLeft` is above 0.
95
+ Report `profile.inmail.send` as well; a LinkedIn account counts as InMail-capable only with both.
96
+ - `limits`: the safe limits for this LinkedIn account. Audienti never plans more LinkedIn sends on
97
+ it than its per-day and per-week limits. The user can lower a limit, never
98
+ raise it above the one you report.
99
+
100
+ Report `profile.invitations.read` on the LinkedIn account too, so outcome checks can be assigned to
101
+ you.
102
+
103
+ ## Rules
104
+
105
+ - **Report, never connect.** List only what you can call now. Do not install or sign in
106
+ to anything to fill the list.
107
+ - **Never invent.** Leave out anything you cannot call. A wrong row sends tasks to you
108
+ that you cannot do.
109
+ - **Never report `via: audienti`.** Audienti adds rows for its own hosted tools itself
110
+ when the workspace has credits; a report that sends `audienti` is refused. Leave out
111
+ Audienti's hosted tools unless you reach them through another connector.
112
+ - **Never include secrets** (tokens, cookies, keys) or personal data about the user's
113
+ contacts.
114
+ - Report again when your tools change, and at least every seven days. The user sees
115
+ older rows as stale.
116
+
117
+ ## The catalog
118
+
119
+ Launch rows can be required by queue tasks. Later rows are accepted and shown to the
120
+ user, but no task requires them yet.
121
+
122
+ ### People and companies
123
+
124
+ | Capability | Providers | Access | What it does | Launch |
125
+ | --- | --- | --- | --- | --- |
126
+ | `people.search` | any | read | Find people from audience filters or a question | yes |
127
+ | `people.discover` | any | read | Find look-alikes from a seed person or company | yes |
128
+ | `person.profiles.discover` | any | read | Expand one person into their other profiles | yes |
129
+ | `companies.search` | any | read | Find companies from filters | yes |
130
+ | `company.enrich` | any | read | Fill size, industry, location, site | yes |
131
+ | `company.posts.list` | linkedin, x, instagram, tiktok | read | What a company page has posted | yes |
132
+
133
+ ### Profile: research
134
+
135
+ | Capability | Providers | Access | What it does | Launch |
136
+ | --- | --- | --- | --- | --- |
137
+ | `profile.read` | linkedin, x, instagram, tiktok, github, web | read | Fetch one profile by URL or handle | yes |
138
+ | `profile.enrich` | linkedin, email | read | Fill title, company, location, history from one handle | yes |
139
+ | `profile.find` | email, phone | read | Find a person's handle on this provider | yes |
140
+ | `profile.verify` | email | read | Check the handle is live and deliverable | yes |
141
+ | `profile.posts.list` | linkedin, x, instagram, tiktok | read | What this profile has posted | yes |
142
+ | `profile.comments.list` | linkedin, x, instagram, tiktok | read | Comments this profile wrote on others' posts | yes |
143
+ | `profile.reactions.list` | linkedin, x, instagram, tiktok | read | Reactions this profile gave | yes |
144
+
145
+ ### Profile: as the user (needs `account`)
146
+
147
+ | Capability | Providers | Access | What it does | Launch |
148
+ | --- | --- | --- | --- | --- |
149
+ | `profile.message.send` | email, linkedin, whatsapp, phone, x, instagram | send | Send an approved message | email, linkedin |
150
+ | `profile.inbox.read` | email, linkedin, whatsapp | read | Watch the user's inbox for replies | email, linkedin |
151
+ | `profile.invitations.read` | linkedin | read | Read the user's sent invitations, to see which were accepted or closed | yes |
152
+ | `profile.invitations.received` | linkedin | read | Read the invitations people sent to the user (as the user) | linkedin |
153
+ | `profile.inmail.send` | linkedin | send | Send an approved InMail | yes |
154
+ | `profile.connection.request` | linkedin | send | Send an approved connection request | yes |
155
+ | `profile.connection.withdraw` | linkedin | send | Withdraw a pending request | yes |
156
+ | `profile.connection.remove` | linkedin | send | Remove a connection | later |
157
+ | `profile.follow` | linkedin, x, instagram, tiktok | send | Follow a person or company | linkedin |
158
+ | `profile.unfollow` | linkedin, x, instagram, tiktok | send | Unfollow | linkedin |
159
+ | `profile.mute` | x, instagram | send | Mute | later |
160
+ | `profile.block` | x, instagram, linkedin | send | Block | later |
161
+ | `profile.call` | phone | send | Call a prospect | later |
162
+
163
+ ### Posts and topics
164
+
165
+ | Capability | Providers | Access | What it does | Launch |
166
+ | --- | --- | --- | --- | --- |
167
+ | `topic.track` | any | read | Track a topic for signals | yes |
168
+ | `posts.search` | linkedin, x, instagram, tiktok, web | read | Find posts about a topic | yes |
169
+ | `post.read` | linkedin, x, instagram, tiktok | read | Fetch one post with its type | yes |
170
+ | `post.comments.list` | linkedin, x, instagram, tiktok | read | Who commented, and what they said | yes |
171
+ | `post.reactions.list` | linkedin, x, instagram, tiktok | read | Who reacted | yes |
172
+ | `post.comment` | linkedin, x, instagram, tiktok | send | Comment as the user (as the user) | linkedin |
173
+ | `post.react` | linkedin, x, instagram, tiktok | send | React as the user (as the user) | linkedin |
174
+ | `post.publish` | linkedin, x, instagram, tiktok | send | Publish a post as the user (as the user) | linkedin |
175
+ | `profile.visit` | linkedin | read | Visit a profile as a person would (as the user) | linkedin |
176
+ | `profile.views.read` | linkedin | read | Read who viewed the user's profile (as the user) | linkedin |
177
+
178
+ ### Web, calendar, records, owner, integration
179
+
180
+ | Capability | Providers | Access | What it does | Launch |
181
+ | --- | --- | --- | --- | --- |
182
+ | `web.search` | web | read | Search the web | yes |
183
+ | `web.page.read` | web | read | Read one page | yes |
184
+ | `calendar.read` | google, microsoft | read | Free and busy time | later |
185
+ | `calendar.freebusy.read` | google, microsoft | read | Read the user's free and busy times to offer slots in a reply | later |
186
+ | `calendar.event.create` | google, microsoft | write | Create an event and send the invite from the user's calendar | later |
187
+ | `meeting.book` | google, microsoft | write | Book or propose a meeting | later |
188
+ | `meeting.reschedule` | google, microsoft | write | Move a booked meeting | later |
189
+ | `meeting.cancel` | google, microsoft | write | Cancel a booked meeting | later |
190
+ | `crm.records.search` | hubspot, salesforce, pipedrive, other | read | Find records in the user's CRM | yes |
191
+ | `crm.record.read` | hubspot, salesforce, pipedrive, other | read | Read one CRM record | yes |
192
+ | `crm.record.write` | hubspot, salesforce, pipedrive, other | write | Write back to one CRM record | yes |
193
+ | `owner.notify` | push, slack, sms, email | send | Reach the user on their own channel | yes |
194
+ | `webhook.receive` | none | read | Take a push instead of polling | later |
195
+ | `webhook.send` | none | send | Post events to the user's URL | later |
196
+
197
+ `owner.notify` means you can reach your user outside Audienti: chat, push, Slack, SMS or email. Report it
198
+ and Audienti puts a **Tell your user about an alert** task in your queue for each alert it records in
199
+ the workspace; the task carries the message and a link, and you mark it done once you delivered it. An
200
+ agent that has not reported `owner.notify` gets no such task.
201
+
202
+ ### Tools you use for yourself
203
+
204
+ | Capability | Providers | Access | What it does | Launch |
205
+ | --- | --- | --- | --- | --- |
206
+ | `tool.media.transcribe` | none | read | Video or audio to text | later |
207
+ | `tool.image.read` | none | read | Read text and content out of an image | later |
208
+ | `tool.proxy.session.start` | none | write | Sticky residential session for your own browser, metered by bandwidth | yes |
209
+ | `tool.proxy.session.rotate` | none | write | New IP on the current session | yes |
210
+ | `tool.proxy.config.read` | none | read | Read region, stickiness and bandwidth cap | yes |
211
+ | `tool.proxy.config.write` | none | write | Set region, stickiness and bandwidth cap | yes |
212
+
213
+ Your runtime's documents, spreadsheets, files, code, memory, tasks and chat are not
214
+ capabilities. Leave them out.
215
+
216
+ ## Older reports
217
+
218
+ `audienti agent inventory report` (categories such as `web_search` or `crm`) and the
219
+ per-account `audienti agent capabilities report` with `provider` and `accountRef` are
220
+ still accepted for one release. Audienti translates both into rows. Send the format above
221
+ instead.
222
+
223
+ Translated inventory reports match fewer queue tasks than before:
224
+
225
+ - `company_data` alone matches none of the find, verify or enrich tasks.
226
+ - `linkedin` alone no longer matches find or enrich tasks.
227
+ - `web_search` alone no longer matches enrich tasks.
228
+ - `people_data` alone no longer matches verify tasks.
229
+ - `email` and `linkedin` match send tasks only for their own provider and only with
230
+ `send` access; `social` matches no send task.
231
+
232
+ A LinkedIn message can carry a file. Upload it first with `audienti attachments upload FILE` (an image up to 5 MB,
233
+ or a voice note up to 5 MB and 2 minutes; Audienti reads a WAV, M4A, OGG or MP3 voice note's length from the file; add `--duration SECONDS` only for a file it cannot read). Name the returned id in
234
+ `content.attachments` of the `linkedin.send` action, and tag that action `profile.message.send`. A connection
235
+ request carries no file. A voice note goes only to a connection. When you send from your own account, run
236
+ `audienti attachments get ID --out PATH` to fetch the file, then attach it in LinkedIn. A managed account sends it
237
+ for you.
@@ -0,0 +1,64 @@
1
+ # The mailbox test email (`mailbox.confirm`)
2
+
3
+ Audienti never signs in to your user's mailbox. You send from it through your own email
4
+ connector (Gmail or Outlook, through Composio, a Claude connector or similar), so you also
5
+ prove it works. A mailbox counts for campaigns only once you sent a test email from it to
6
+ itself, read it back and reported the code. Your user does nothing in this check: never ask
7
+ them for the code, and never send them to Settings to confirm a mailbox.
8
+
9
+ When your user connects a mailbox you reported, or asks for another test email in Settings →
10
+ Sending, you get one `mailbox.confirm` task for it. Its `detail.test` gives the mailbox as
11
+ you reported it (`mailbox`), its address as Audienti has it (`address`, or `null` when the
12
+ mailbox was reported under a name or an account id that is no address), the code (`code`) and
13
+ the subject to use (`subject`, `Audienti mailbox check` and the code).
14
+
15
+ 1. Claim the task: `audienti queue claim TASK_ID`.
16
+ 2. Send one email from that mailbox to its own address, and to nobody else. Use the address
17
+ your connector reports for the mailbox; `address` is only a hint, and if your connector
18
+ reports another, use that one. Use the subject the task gives and put the code in the body
19
+ too. Send nothing else.
20
+ 3. Read that mailbox's inbox with your connector until the email arrives: about once a
21
+ minute, for up to 15 minutes. Read only the test email. Note its message id and the time
22
+ it arrived.
23
+ 4. Report what you read, with the address you sent it to (`sentTo`):
24
+
25
+ ```json
26
+ { "results": [{ "kind": "mailbox.received", "code": "CODE", "sentTo": "ADDRESS_YOU_SENT_TO", "message": "MESSAGE_ID", "receivedAt": "2026-11-04T12:01:00Z" }] }
27
+ ```
28
+
29
+ ```sh
30
+ audienti outcomes report TASK_ID --file RESULTS.json
31
+ ```
32
+
33
+ 5. Delete both copies of the test email, the sent one and the received one, then report it:
34
+ `{ "results": [{ "kind": "mailbox.deleted", "message": "MESSAGE_ID" }] }`.
35
+ 6. Report the task done: `audienti queue done TASK_ID --note "test email confirmed"`.
36
+
37
+ Use the code exactly as you read it back from the email; the dash and case do not matter.
38
+ Only the task's own code confirms the mailbox. A code of an older test email is refused
39
+ (`stale_code`), any other code too (`wrong_code`), and so is an email that arrived before
40
+ the task asked for it (`received_before_asked`), and so is an email sent to another of your
41
+ user's mailboxes (`other_mailbox`). Reporting the same email again returns
42
+ `already_recorded`.
43
+
44
+ You can do this check even if you can only read the workspace: your own test email is the
45
+ one result a read-only agent may report.
46
+
47
+ ## When you cannot send or read
48
+
49
+ Report which step failed, with a short reason, then report the task done:
50
+
51
+ ```json
52
+ { "results": [{ "kind": "mailbox.failed", "step": "send", "reason": "No Gmail connector" }] }
53
+ ```
54
+
55
+ - `send`: you have no connector that can send from the mailbox, or the send failed. Tell
56
+ your user exactly: "I need a Gmail or Outlook connector to send from your mailbox."
57
+ - `read`: the email went out but you cannot read the mailbox's inbox, or it did not arrive
58
+ in 15 minutes. Tell your user exactly: "I need a Gmail or Outlook connector that can read
59
+ your mailbox's inbox."
60
+
61
+ Settings → Sending and Capabilities then show your user which step failed. No new task comes
62
+ until they fix your connector and choose Send another test email. The task cannot be
63
+ reported done until you reported `mailbox.received` or `mailbox.failed`
64
+ (`mailbox_not_reported`).
@@ -0,0 +1,114 @@
1
+ # Outcome checks
2
+
3
+ An outcome check is a queue task (`outcomes.check`) that asks you whether the people
4
+ you invited on one of the user's LinkedIn accounts have accepted. Audienti schedules it
5
+ for you at the cadence you reported for that LinkedIn account (`checkEverySeconds`, every 6 hours
6
+ when you report none), and only while someone on that LinkedIn account waits for an accept. Each
7
+ task lists at most 100 people, so a LinkedIn account with more people waiting gets several tasks
8
+ in the same interval, and every waiting person is on one of them. You never decide when to check, and you never check
9
+ anyone the task does not list.
10
+
11
+ ## What the task gives you
12
+
13
+ The task's `detail` holds:
14
+
15
+ - `seat`: the LinkedIn account to check (`account` is the LinkedIn account you reported, for example
16
+ `linkedin.com/in/owner`).
17
+ - `waiting`: the people to check, at most 100. The list stays as it is while you hold
18
+ the task, so you can report on everyone it lists; no other check lists them meanwhile,
19
+ even one for the next interval. Each has `person` (the Audienti person
20
+ id you report against), `name`, `profileUrl` and `invitedAt` (when the invitation was
21
+ sent).
22
+
23
+ The task requires `profile.invitations.read` on that LinkedIn account. If you hold it, the task is
24
+ assigned to you.
25
+
26
+ ## The procedure
27
+
28
+ 1. Claim the task: `audienti queue claim TASK_ID`.
29
+ 2. Read the LinkedIn account's own sent invitations with your tool, or read each listed person's
30
+ profile to see the connection degree. Look up only the people in `waiting`. Do not
31
+ list, search or open anyone else, and do not page through the user's whole network.
32
+ 3. Keep to the limits you reported for this LinkedIn account. Spread reads out if your tool asks
33
+ you to. If you cannot check now (signed out, rate limited), release the task with a
34
+ note: `audienti queue release TASK_ID --note "why"`.
35
+ 4. Write what you found to a file and report it:
36
+
37
+ ```json
38
+ {
39
+ "results": [
40
+ { "kind": "linkedin.accept", "person": "PERSON_ID", "at": "2026-11-01T09:30:00Z" },
41
+ { "kind": "linkedin.invite_closed", "person": "PERSON_ID", "reason": "withdrawn" },
42
+ { "kind": "linkedin.profile_degree", "person": "PERSON_ID", "degree": 2 }
43
+ ]
44
+ }
45
+ ```
46
+
47
+ ```sh
48
+ audienti outcomes report TASK_ID --file RESULTS.json
49
+ ```
50
+
51
+ 5. Report the task done: `audienti queue done TASK_ID --note "3 checked, 1 accepted"`.
52
+
53
+ ## Result kinds
54
+
55
+ - `linkedin.accept {person, at}`: the person accepted. `at` is when they accepted, as
56
+ close as your tool tells you. Audienti ends the person's wait and plans the next step
57
+ of the playbook, which appears in your queue.
58
+ - `linkedin.invite_closed {person, reason}`: the invitation is no longer pending.
59
+ `reason` is `withdrawn` (the user or you withdrew it) or `expired` (LinkedIn closed
60
+ it). Audienti records the fact; the wait still ends at its timeout.
61
+ - `linkedin.profile_degree {person, degree}`: optional evidence, the connection degree
62
+ you read on the profile (1, 2 or 3). A 1 means connected; report it as an accept as
63
+ well when you know the time.
64
+
65
+ A `steps.run` task (a comment, react, withdraw, follow or unfollow step of a playbook, for one person) takes these
66
+ results instead. The task names the step in `detail.step` and tells you how to find the post.
67
+
68
+ - `linkedin.comment {person, postUrl}`: you commented on the post. Comment only when you can add
69
+ material value; otherwise react.
70
+ - `linkedin.reaction {person, postUrl, reaction}`: you reacted. `reaction` is `like` (the default),
71
+ `celebrate`, `support`, `love`, `insightful` or `funny`.
72
+ - `linkedin.follow {person}` and `linkedin.unfollow {person}`: for a follow or unfollow step. Audienti records
73
+ that your account now follows the person, or no longer does. It skips a later Follow step for someone you
74
+ already follow, and an Unfollow step for someone you do not.
75
+ - `linkedin.step_skipped {person, reason}`: nothing to engage with, or your user declined.
76
+ - `linkedin.invite_closed {person, reason: withdrawn}`: for a withdraw step.
77
+
78
+ Audienti records the step as run, and the playbook goes on to its next step.
79
+
80
+ An `invitations.receive` task (every 6 hours, only on a seat whose connector can read received invitations)
81
+ asks you to read the connection requests people sent to your user. Read only the pending received list. Never
82
+ accept, decline or open anyone. Report each pending request:
83
+
84
+ - `linkedin.invitation_received {ref, profileUrl, name, sentAt}`: give `ref` (the request id your connector shows,
85
+ otherwise the person's profile address) or `profileUrl`; `name` and `sentAt` when you have them. Audienti records
86
+ each request once and tells your user once. Report the same request again and it returns `already_recorded`.
87
+
88
+ Report done with no results when no request is pending.
89
+
90
+ Leave out anyone still pending. They stay on the next check until they accept or the
91
+ wait times out.
92
+
93
+ ## What the report returns
94
+
95
+ `results` has one entry per result you sent, in the same order. Each person is recorded
96
+ on their own, so one person who cannot be recorded does not hold back the others.
97
+
98
+ - `status`: `recorded` (written now), `already_recorded` (an earlier report wrote it;
99
+ `eventId` is that earlier event) or `not_recorded` (nothing was written).
100
+ - `reason`: why, when there is one. `participation_ended` or
101
+ `participation_unavailable` means the fact was recorded but nobody moves, because
102
+ the person's campaign participation ended or its playbook cannot be read.
103
+ `not_found` with `not_recorded` means the person is no longer in the workspace.
104
+ - `next`: the person's next step after the report, when there is one.
105
+ - `failed`: the accept was recorded, but planning the next step failed. Audienti shows
106
+ the failure on the person's timeline; `reason` holds its code. Sending the same
107
+ accept again returns the same failure until it is resolved.
108
+
109
+ ## Errors
110
+
111
+ - `not_claimed`: claim the task first, or your lease expired. Read the queue again.
112
+ - `not_on_list`: you reported a person the task does not list. Report only the listed
113
+ people.
114
+ - `validation_error`: a result has a wrong kind, person id or time.