@audienti/cli 0.2.0-exora.4 → 0.2.0-exora.6
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.
- package/README.md +2 -2
- package/cli/account-reply-operations.mjs +24 -1
- package/cli/agents.mjs +42 -10
- package/cli/audience-operations.mjs +1 -1
- package/cli/audienti.mjs +33 -18
- package/cli/campaign-find.mjs +155 -0
- package/cli/campaign-operations.mjs +8 -4
- package/cli/campaigns.mjs +34 -3
- package/cli/cohort-operations.mjs +1 -1
- package/cli/discovery-operations.mjs +2 -1
- package/cli/discovery.mjs +2 -1
- package/cli/kv-operations.mjs +17 -0
- package/cli/kv.mjs +58 -0
- package/cli/mcp.mjs +2 -2
- package/cli/outcome-operations.mjs +19 -0
- package/cli/outcomes.mjs +16 -0
- package/cli/owner-account-client.mjs +1 -1
- package/cli/{approach-operations.mjs → playbook-operations.mjs} +15 -15
- package/cli/progress.mjs +7 -7
- package/cli/question-operations.mjs +4 -4
- package/cli/questions.mjs +4 -4
- package/cli/queue-operations.mjs +7 -6
- package/cli/queue.mjs +4 -3
- package/cli/recipient-operations.mjs +1 -1
- package/cli/recipients.mjs +6 -2
- package/cli/rooms-operations.mjs +4 -4
- package/cli/rooms.mjs +4 -4
- package/cli/secret-operations.mjs +7 -0
- package/cli/secrets.mjs +30 -0
- package/cli/sender-account-operations.mjs +6 -2
- package/cli/sender-accounts.mjs +13 -8
- package/cli/skills.mjs +5 -5
- package/cli/tools-operations.mjs +18 -10
- package/cli/tools.mjs +48 -20
- package/cli/work-execution-operations.mjs +6 -0
- package/cli/work-execution.mjs +3 -3
- package/cli/work-operations.mjs +4 -3
- package/cli/work.mjs +2 -2
- package/cli/workspaces.mjs +24 -14
- package/package.json +2 -2
- package/skills/agent-rooms/SKILL.md +13 -11
- package/skills/agent-rooms/references/commands.md +2 -2
- package/skills/audienti/SKILL.md +80 -0
- package/skills/getting-started/SKILL.md +186 -158
- package/skills/getting-started/references/audience.md +72 -7
- package/skills/getting-started/references/campaign-draft.md +53 -11
- package/skills/getting-started/references/capabilities.md +219 -0
- package/skills/getting-started/references/mailbox-check.md +64 -0
- package/skills/getting-started/references/outcome-checks.md +89 -0
- package/skills/getting-started/references/replies.md +152 -0
- package/skills/getting-started/references/reply-checks.md +54 -0
- package/skills/playbook-design/SKILL.md +132 -0
- package/skills/{approach-design → playbook-design}/references/fields.md +28 -22
- package/source-manifest.json +54 -43
- package/skills/approach-design/SKILL.md +0 -104
- package/skills/getting-started/references/inventory.md +0 -52
|
@@ -4,12 +4,24 @@ An audience is a set of structured filters plus one smart rule. Discovery and fi
|
|
|
4
4
|
read the structured filters first, so put everything you can into them. Free text is
|
|
5
5
|
only for the one test the filters cannot express.
|
|
6
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
|
+
|
|
7
12
|
## 1. Pick one buyer lane
|
|
8
13
|
|
|
9
14
|
One audience describes one kind of buyer: one function at one seniority band, in one
|
|
10
15
|
kind of company. If the offer sells to two different buyers (for example finance and
|
|
11
16
|
operations), draft two audiences and two campaigns. Do not mix lanes in one audience.
|
|
12
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
|
+
|
|
13
25
|
## 2. Resolve every value with `audience options`
|
|
14
26
|
|
|
15
27
|
Never type a value from memory. Look each one up and copy the `providerId` and `name`
|
|
@@ -24,8 +36,19 @@ audienti audience options --kind functions --q finance
|
|
|
24
36
|
audienti audience options --kind titles --q "head of finance"
|
|
25
37
|
```
|
|
26
38
|
|
|
27
|
-
Kinds: `industries`, `
|
|
28
|
-
|
|
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`.
|
|
29
52
|
|
|
30
53
|
## 3. Fill the template
|
|
31
54
|
|
|
@@ -33,7 +56,7 @@ Kinds: `industries`, `industry_groups`, `sizes`, `types`, `locations`, `seniorit
|
|
|
33
56
|
{
|
|
34
57
|
"name": "Heads of finance, UK logistics, 51-500 staff",
|
|
35
58
|
"company": {
|
|
36
|
-
"industries": [{ "providerId": "ID", "name": "
|
|
59
|
+
"industries": [{ "providerId": "ID", "name": "Logistics and Supply Chain" }, { "providerId": "ID", "name": "Package/Freight Delivery" }],
|
|
37
60
|
"sizes": [{ "providerId": "ID", "name": "51-200" }, { "providerId": "ID", "name": "201-500" }],
|
|
38
61
|
"locations": [{ "providerId": "ID", "name": "United Kingdom" }]
|
|
39
62
|
},
|
|
@@ -45,11 +68,14 @@ Kinds: `industries`, `industry_groups`, `sizes`, `types`, `locations`, `seniorit
|
|
|
45
68
|
},
|
|
46
69
|
"seniorityMatchMode": "at_least",
|
|
47
70
|
"textCriteria": "The company runs its own truck or freight fleet.",
|
|
48
|
-
"notes": "Region and size inferred from the
|
|
71
|
+
"notes": "Region and size inferred from the user's last 20 sent emails; user confirmed in Operator."
|
|
49
72
|
}
|
|
50
73
|
```
|
|
51
74
|
|
|
52
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.
|
|
53
79
|
- Use `negative: true` on a value to exclude it (for example a competitor's industry).
|
|
54
80
|
- `currentTitles` narrows the lane; keep it to the titles that buy, at most a few.
|
|
55
81
|
|
|
@@ -67,12 +93,51 @@ Good:
|
|
|
67
93
|
Not allowed in the smart rule:
|
|
68
94
|
|
|
69
95
|
- Provenance: "inferred from…", "based on the website", "found in sent email".
|
|
70
|
-
- Hedges or open questions: "
|
|
71
|
-
- Values the filters already hold (industry, size, region, seniority, title).
|
|
96
|
+
- Hedges or open questions: "user to confirm", "probably", "maybe", "TBD".
|
|
97
|
+
- Values the filters already hold (industry, size, region, seniority, function, title).
|
|
72
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).
|
|
73
101
|
|
|
74
|
-
Put provenance in `notes`. Put open questions to the
|
|
102
|
+
Put provenance in `notes`. Put open questions to the user in Operator with
|
|
75
103
|
`audienti ask`, not in the audience.
|
|
76
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
|
+
|
|
77
108
|
Check the rule before you save: `audienti audience improve --text "RULE"` returns a
|
|
78
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.
|
|
@@ -1,23 +1,46 @@
|
|
|
1
1
|
# Campaign draft
|
|
2
2
|
|
|
3
|
-
Fill this before you ask the
|
|
4
|
-
`found` (quote or link), `inferred` (say from what) or `needed` (ask the
|
|
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
5
|
|
|
6
6
|
| Field | Where it goes | How to fill it |
|
|
7
7
|
| --- | --- | --- |
|
|
8
|
-
| Offer: what you sell | Offer | Website, pitch deck, proposals, email signature.
|
|
9
|
-
| Offer: problem it solves | Offer | Website, sent email, call notes.
|
|
10
|
-
| Offer: proof point | Offer | Only a result or customer the
|
|
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
11
|
| Company type, size, region | Audience filters | Current customers in the CRM or sent email; check values with `audience options`. |
|
|
12
|
-
| Buyer
|
|
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
13
|
| Who to avoid | Do-not-contact list | Current customers, partners, competitors (`dnc add` or `dnc import`). |
|
|
14
|
-
|
|
|
15
|
-
|
|
|
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. |
|
|
16
17
|
| Channels | Campaign channels | `email` and `linkedin`, only for accounts you reported. |
|
|
17
|
-
|
|
|
18
|
-
|
|
|
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`). |
|
|
19
22
|
|
|
20
|
-
|
|
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
|
|
21
44
|
replies.
|
|
22
45
|
|
|
23
46
|
Name the campaign after the buyer and the test, for example "Heads of finance,
|
|
@@ -31,8 +54,27 @@ name or notes.
|
|
|
31
54
|
| Offer | `audienti offer create --file OFFER.json --request-id UUID` | `offer_create` |
|
|
32
55
|
| Audience | `audienti audience create --file AUDIENCE.json --request-id UUID` | `audience_create` |
|
|
33
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` |
|
|
34
58
|
| Next test | `audienti campaign successor ID --file CAMPAIGN.json --request-id UUID` | `campaign_successor` |
|
|
35
59
|
|
|
36
60
|
Read each command's request fields in the API schema at `/api/v1/openapi.json`
|
|
37
61
|
(search for the MCP tool name above). A campaign also needs the `audienceRevision`
|
|
38
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,219 @@
|
|
|
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
|
+
"inmail": { "available": true, "quotaPerMonth": 15 },
|
|
76
|
+
"limits": { "connectionRequestsPerDay": 20, "connectionRequestsPerWeek": 100, "messagesPerDay": 50, "inmailPerMonth": 15 }
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- `account`: the same LinkedIn account you name in your LinkedIn rows.
|
|
83
|
+
- `checkEverySeconds`: how often you can check this LinkedIn account's sent invitations, at least
|
|
84
|
+
3600 (one hour). Audienti schedules outcome checks at this cadence; every 6 hours
|
|
85
|
+
when you leave it out. The user never changes it.
|
|
86
|
+
- `inmail`: whether the LinkedIn account can send InMail, and its monthly quota. Report
|
|
87
|
+
`profile.inmail.send` as well; a LinkedIn account counts as InMail-capable only with both.
|
|
88
|
+
- `limits`: the safe limits for this LinkedIn account. Audienti never plans more LinkedIn sends on
|
|
89
|
+
it than its per-day and per-week limits. The user can lower a limit, never
|
|
90
|
+
raise it above the one you report.
|
|
91
|
+
|
|
92
|
+
Report `profile.invitations.read` on the LinkedIn account too, so outcome checks can be assigned to
|
|
93
|
+
you.
|
|
94
|
+
|
|
95
|
+
## Rules
|
|
96
|
+
|
|
97
|
+
- **Report, never connect.** List only what you can call now. Do not install or sign in
|
|
98
|
+
to anything to fill the list.
|
|
99
|
+
- **Never invent.** Leave out anything you cannot call. A wrong row sends tasks to you
|
|
100
|
+
that you cannot do.
|
|
101
|
+
- **Never report `via: audienti`.** Audienti adds rows for its own hosted tools itself
|
|
102
|
+
when the workspace has credits; a report that sends `audienti` is refused. Leave out
|
|
103
|
+
Audienti's hosted tools unless you reach them through another connector.
|
|
104
|
+
- **Never include secrets** (tokens, cookies, keys) or personal data about the user's
|
|
105
|
+
contacts.
|
|
106
|
+
- Report again when your tools change, and at least every seven days. The user sees
|
|
107
|
+
older rows as stale.
|
|
108
|
+
|
|
109
|
+
## The catalog
|
|
110
|
+
|
|
111
|
+
Launch rows can be required by queue tasks. Later rows are accepted and shown to the
|
|
112
|
+
user, but no task requires them yet.
|
|
113
|
+
|
|
114
|
+
### People and companies
|
|
115
|
+
|
|
116
|
+
| Capability | Providers | Access | What it does | Launch |
|
|
117
|
+
| --- | --- | --- | --- | --- |
|
|
118
|
+
| `people.search` | any | read | Find people from audience filters or a question | yes |
|
|
119
|
+
| `people.discover` | any | read | Find look-alikes from a seed person or company | yes |
|
|
120
|
+
| `person.profiles.discover` | any | read | Expand one person into their other profiles | yes |
|
|
121
|
+
| `companies.search` | any | read | Find companies from filters | yes |
|
|
122
|
+
| `company.enrich` | any | read | Fill size, industry, location, site | yes |
|
|
123
|
+
| `company.posts.list` | linkedin, x, instagram, tiktok | read | What a company page has posted | yes |
|
|
124
|
+
|
|
125
|
+
### Profile: research
|
|
126
|
+
|
|
127
|
+
| Capability | Providers | Access | What it does | Launch |
|
|
128
|
+
| --- | --- | --- | --- | --- |
|
|
129
|
+
| `profile.read` | linkedin, x, instagram, tiktok, github, web | read | Fetch one profile by URL or handle | yes |
|
|
130
|
+
| `profile.enrich` | linkedin, email | read | Fill title, company, location, history from one handle | yes |
|
|
131
|
+
| `profile.find` | email, phone | read | Find a person's handle on this provider | yes |
|
|
132
|
+
| `profile.verify` | email | read | Check the handle is live and deliverable | yes |
|
|
133
|
+
| `profile.posts.list` | linkedin, x, instagram, tiktok | read | What this profile has posted | yes |
|
|
134
|
+
| `profile.comments.list` | linkedin, x, instagram, tiktok | read | Comments this profile wrote on others' posts | yes |
|
|
135
|
+
| `profile.reactions.list` | linkedin, x, instagram, tiktok | read | Reactions this profile gave | yes |
|
|
136
|
+
|
|
137
|
+
### Profile: as the user (needs `account`)
|
|
138
|
+
|
|
139
|
+
| Capability | Providers | Access | What it does | Launch |
|
|
140
|
+
| --- | --- | --- | --- | --- |
|
|
141
|
+
| `profile.message.send` | email, linkedin, whatsapp, phone, x, instagram | send | Send an approved message | email, linkedin |
|
|
142
|
+
| `profile.inbox.read` | email, linkedin, whatsapp | read | Watch the user's inbox for replies | email, linkedin |
|
|
143
|
+
| `profile.invitations.read` | linkedin | read | Read the user's sent invitations, to see which were accepted or closed | yes |
|
|
144
|
+
| `profile.inmail.send` | linkedin | send | Send an approved InMail | yes |
|
|
145
|
+
| `profile.connection.request` | linkedin | send | Send an approved connection request | yes |
|
|
146
|
+
| `profile.connection.withdraw` | linkedin | send | Withdraw a pending request | yes |
|
|
147
|
+
| `profile.connection.remove` | linkedin | send | Remove a connection | later |
|
|
148
|
+
| `profile.follow` | linkedin, x, instagram, tiktok | send | Follow a person or company | linkedin |
|
|
149
|
+
| `profile.unfollow` | linkedin, x, instagram, tiktok | send | Unfollow | linkedin |
|
|
150
|
+
| `profile.mute` | x, instagram | send | Mute | later |
|
|
151
|
+
| `profile.block` | x, instagram, linkedin | send | Block | later |
|
|
152
|
+
| `profile.call` | phone | send | Call a prospect | later |
|
|
153
|
+
|
|
154
|
+
### Posts and topics
|
|
155
|
+
|
|
156
|
+
| Capability | Providers | Access | What it does | Launch |
|
|
157
|
+
| --- | --- | --- | --- | --- |
|
|
158
|
+
| `topic.track` | any | read | Track a topic for signals | yes |
|
|
159
|
+
| `posts.search` | linkedin, x, instagram, tiktok, web | read | Find posts about a topic | yes |
|
|
160
|
+
| `post.read` | linkedin, x, instagram, tiktok | read | Fetch one post with its type | yes |
|
|
161
|
+
| `post.comments.list` | linkedin, x, instagram, tiktok | read | Who commented, and what they said | yes |
|
|
162
|
+
| `post.reactions.list` | linkedin, x, instagram, tiktok | read | Who reacted | yes |
|
|
163
|
+
| `post.comment` | linkedin, x, instagram, tiktok | send | Comment as the user (as the user) | later |
|
|
164
|
+
| `post.react` | linkedin, x, instagram, tiktok | send | React as the user (as the user) | later |
|
|
165
|
+
| `post.publish` | linkedin, x, instagram, tiktok | send | Publish a post as the user (as the user) | later |
|
|
166
|
+
|
|
167
|
+
### Web, calendar, records, owner, integration
|
|
168
|
+
|
|
169
|
+
| Capability | Providers | Access | What it does | Launch |
|
|
170
|
+
| --- | --- | --- | --- | --- |
|
|
171
|
+
| `web.search` | web | read | Search the web | yes |
|
|
172
|
+
| `web.page.read` | web | read | Read one page | yes |
|
|
173
|
+
| `calendar.read` | google, microsoft | read | Free and busy time | later |
|
|
174
|
+
| `calendar.freebusy.read` | google, microsoft | read | Read the user's free and busy times to offer slots in a reply | later |
|
|
175
|
+
| `calendar.event.create` | google, microsoft | write | Create an event and send the invite from the user's calendar | later |
|
|
176
|
+
| `meeting.book` | google, microsoft | write | Book or propose a meeting | later |
|
|
177
|
+
| `meeting.reschedule` | google, microsoft | write | Move a booked meeting | later |
|
|
178
|
+
| `meeting.cancel` | google, microsoft | write | Cancel a booked meeting | later |
|
|
179
|
+
| `crm.records.search` | hubspot, salesforce, pipedrive, other | read | Find records in the user's CRM | yes |
|
|
180
|
+
| `crm.record.read` | hubspot, salesforce, pipedrive, other | read | Read one CRM record | yes |
|
|
181
|
+
| `crm.record.write` | hubspot, salesforce, pipedrive, other | write | Write back to one CRM record | later |
|
|
182
|
+
| `owner.notify` | push, slack, sms, email | send | Reach the user on their own channel | yes |
|
|
183
|
+
| `webhook.receive` | none | read | Take a push instead of polling | later |
|
|
184
|
+
| `webhook.send` | none | send | Post events to the user's URL | later |
|
|
185
|
+
|
|
186
|
+
`owner.notify` means you can reach your user outside Audienti: chat, push, Slack, SMS or email. Report it
|
|
187
|
+
and Audienti puts a **Tell your user about an alert** task in your queue for each alert it records in
|
|
188
|
+
the workspace; the task carries the message and a link, and you mark it done once you delivered it. An
|
|
189
|
+
agent that has not reported `owner.notify` gets no such task.
|
|
190
|
+
|
|
191
|
+
### Tools you use for yourself
|
|
192
|
+
|
|
193
|
+
| Capability | Providers | Access | What it does | Launch |
|
|
194
|
+
| --- | --- | --- | --- | --- |
|
|
195
|
+
| `tool.media.transcribe` | none | read | Video or audio to text | later |
|
|
196
|
+
| `tool.image.read` | none | read | Read text and content out of an image | later |
|
|
197
|
+
| `tool.proxy.session.start` | none | write | Sticky residential session for your own browser, metered by bandwidth | yes |
|
|
198
|
+
| `tool.proxy.session.rotate` | none | write | New IP on the current session | yes |
|
|
199
|
+
| `tool.proxy.config.read` | none | read | Read region, stickiness and bandwidth cap | yes |
|
|
200
|
+
| `tool.proxy.config.write` | none | write | Set region, stickiness and bandwidth cap | yes |
|
|
201
|
+
|
|
202
|
+
Your runtime's documents, spreadsheets, files, code, memory, tasks and chat are not
|
|
203
|
+
capabilities. Leave them out.
|
|
204
|
+
|
|
205
|
+
## Older reports
|
|
206
|
+
|
|
207
|
+
`audienti agent inventory report` (categories such as `web_search` or `crm`) and the
|
|
208
|
+
per-account `audienti agent capabilities report` with `provider` and `accountRef` are
|
|
209
|
+
still accepted for one release. Audienti translates both into rows. Send the format above
|
|
210
|
+
instead.
|
|
211
|
+
|
|
212
|
+
Translated inventory reports match fewer queue tasks than before:
|
|
213
|
+
|
|
214
|
+
- `company_data` alone matches none of the find, verify or enrich tasks.
|
|
215
|
+
- `linkedin` alone no longer matches find or enrich tasks.
|
|
216
|
+
- `web_search` alone no longer matches enrich tasks.
|
|
217
|
+
- `people_data` alone no longer matches verify tasks.
|
|
218
|
+
- `email` and `linkedin` match send tasks only for their own provider and only with
|
|
219
|
+
`send` access; `social` matches no send task.
|
|
@@ -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,89 @@
|
|
|
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
|
+
Leave out anyone still pending. They stay on the next check until they accept or the
|
|
66
|
+
wait times out.
|
|
67
|
+
|
|
68
|
+
## What the report returns
|
|
69
|
+
|
|
70
|
+
`results` has one entry per result you sent, in the same order. Each person is recorded
|
|
71
|
+
on their own, so one person who cannot be recorded does not hold back the others.
|
|
72
|
+
|
|
73
|
+
- `status`: `recorded` (written now), `already_recorded` (an earlier report wrote it;
|
|
74
|
+
`eventId` is that earlier event) or `not_recorded` (nothing was written).
|
|
75
|
+
- `reason`: why, when there is one. `participation_ended` or
|
|
76
|
+
`participation_unavailable` means the fact was recorded but nobody moves, because
|
|
77
|
+
the person's campaign participation ended or its playbook cannot be read.
|
|
78
|
+
`not_found` with `not_recorded` means the person is no longer in the workspace.
|
|
79
|
+
- `next`: the person's next step after the report, when there is one.
|
|
80
|
+
- `failed`: the accept was recorded, but planning the next step failed. Audienti shows
|
|
81
|
+
the failure on the person's timeline; `reason` holds its code. Sending the same
|
|
82
|
+
accept again returns the same failure until it is resolved.
|
|
83
|
+
|
|
84
|
+
## Errors
|
|
85
|
+
|
|
86
|
+
- `not_claimed`: claim the task first, or your lease expired. Read the queue again.
|
|
87
|
+
- `not_on_list`: you reported a person the task does not list. Report only the listed
|
|
88
|
+
people.
|
|
89
|
+
- `validation_error`: a result has a wrong kind, person id or time.
|