Sliq
Server Details
Run LinkedIn and email outbound from Claude, ChatGPT, or any AI agent.
The last two health checks of this connector failed. If this server requires authentication, missing or expired test credentials may be the cause. A test profile lets Glama authenticate for health checks and discover tools; it is separate from your personal connections.
If you are the author, claim ownership, then add or update a test profile under Admin → Test Profile.
- Status
- Unhealthy
- Uptime
- 37.2% over 42 days
- OAuth
- Works in Glama
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
TDQS
Scored across 160 tools
The descriptions work unusually hard to say 'use this, not that' (e.g. search_linkedin_message_history vs fetch_linkedin_messages_with_person, generate_monitored_post_comment vs queue_linkedin_post_engagement), but with 160 tools there are several parallel families that are easy to confuse: message-tag vs segment-tag CRUD/classify/rate/correct tools, draft_email vs draft_reply vs setup_email_sequence, and get_email_attachment vs read_email_attachment.
Overwhelmingly consistent snake_case with predictable verb_noun shapes (list_/get_/query_/create_/update_/delete_/classify_/correct_). A few names break the verb_noun pattern (matches_icp, escalate_to_team, append_learning) but they still read clearly and the convention never mixes casing.
160 tools is far past any reasonable surface for a single agent context; even discounting the parallel tag-group/segment-group families, the count guarantees selection noise and bloated context. This is an extreme overload regardless of how coherent the underlying domain is.
Coverage is genuinely broad — full CRUD for agents, triggers, prospects, deals, tasks, tag/segment groups, plus discovery, enrichment, monitors, queues, calendar, workspace, and memory. Only minor lifecycle gaps remain (e.g. some delete/cleanup paths are folded into other tools rather than exposed directly).
Available Tools
160 toolsadd_triggerAdd TriggerAInspect
You MUST call get_skill_guide('trigger_code') before writing the trigger's
code — the qualification rules, sandbox globals, tool surface, and out
contract live in the skill. New code runs on the trigger's next firing.
The same goal-sync obligation update_trigger carries applies to creating
one: a new scheduled trigger adds recurring behavior the goal doesn't yet
describe, and the goal is injected into every turn on this agent. After this
call, name what the new trigger does and how often via
update_agent(agent_id=..., goal=...) in the same turn. A flow's per-node
on_enter triggers are already covered by the goal's sequence description
and need no separate mention.
To add a flow node's on_enter enactment — a terminal hook, or re-arming a
node whose row was removed — pass an on_enter trigger with the target
node_id (any node kind; the node must already exist in the sequence). One
on_enter per node — if it already has one, edit that with update_trigger
instead of adding a second. On a send or connection-request node, that trigger's prompt
holds only extra instructions; the message or note itself goes in the node's
message_templates (update_node).
Dict with success, agent_id, trigger_id, next_run_at, and the added trigger's
full dict under trigger, plus a warning when the agent is paused, since its
triggers don't fire until it is set back to active. Read the agent's
other triggers via get_agent_details.
| Name | Required | Description | Default |
|---|---|---|---|
| trigger | Yes | The trigger to add. In its prompt, name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat. | |
| agent_id | Yes | ID of the agent to add the trigger to | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare write=false/destructive=false; the description goes far beyond by disclosing that new code runs on next firing, that the goal is injected into every turn (goal-sync obligation), that paused agents' triggers don't fire, and that one on_enter per node is enforced. This is exactly the extra context annotations can't provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and routing before the dense operational detail. It is long and paragraph-heavy, and a few clauses could be tightened, but each paragraph carries distinct, non-redundant guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description compensates with a <returns> section enumerating success, ids, next_run_at, the trigger dict, and the paused-agent warning. Combined with the code prerequisite and goal-sync obligation, an agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds genuine meaning beyond the schema: how the `prompt` field should reference outside resources by tool identifier, the `node_id`/on_enter constraint, and that code requires the skill guide. It enriches the multi-variant trigger field rather than just restating it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Add a trigger to an existing agent') and immediately distinguishes from siblings by contrasting with creating a new agent and pointing to update_trigger for existing on_enter triggers. An agent can tell this apart from update_trigger/remove_trigger/define_sequence without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when/when-not and alternatives: prefer over creating a new agent, use update_trigger instead when an on_enter already exists, use update_node for message content, and a hard prerequisite to call get_skill_guide('trigger_code') before writing code. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
apollo_enrichApollo EnrichARead-onlyInspect
Pass all candidate domains at once — one call with the full list,
not one call per domain. The tool fans them out to Apollo's
/organizations/enrich endpoint in parallel (ThreadPoolExecutor,
max 20 concurrent, well within Apollo's 1000 req/min limit), so a
batch costs one tool round instead of N.
Org-only (company domains, not people). For person enrichment
(work email, LinkedIn URL), use find_email instead.
Costs 1 Sliq credit per found=True result (free for found=False,
free on BYO Apollo). Soft-fails on insufficient credits — already-
enriched results are still returned.
Per-user 24h cache: any domain this user already enriched — in an earlier run_code sandbox, an earlier call, or an earlier run — is served from cache with no second Apollo call and no second charge (both found=True and found=False are cached). The input list is also deduped by canonical form before fan-out.
Each enriched company is also saved to your canonical company entities, queryable via query_companies. List of dicts, one per input domain, in the same order. Each is either: {found: True, name, domain, industry, employee_count, annual_revenue, founded_year, latest_funding_stage, total_funding, location, linkedin_url, description} OR: {found: False, queried_domain, message}
latest_funding_stage is Apollo's title-case display string
(e.g. "Seed", "Series A", "Series B", ..., "Series Unknown",
"Private Equity", "Post-IPO Equity", "Other") — passed through
verbatim, NOT a lowercase_underscore enum. It is None/absent
when Apollo has no funding data for the org. total_funding
is a USD integer (Apollo defaults it to 0, not null, when
absent). Apollo's funding coverage is patchy, especially for
bootstrapped / PE-owned / privately-held companies. Callers
gating on these fields should decide how to handle missing
data — matches_icp includes-on-null rather than
excludes, so legit rows with thin Apollo coverage aren't silently
dropped, and normalizes case + separators when comparing the
stage string.
| Name | Required | Description | Default |
|---|---|---|---|
| domains | Yes | List of company domains (e.g. ["acme.com", "foo.io"]). Order is preserved in the return list. Duplicates (after canonicalization) are collapsed and the result is shared. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, so the description carries the full burden and delivers: cost model (1 credit per found=True, free on found=False/BYO), soft-fail on insufficient credits, 24h per-user cache with dedup, and parallel fan-out details. This is exactly the kind of context beyond structured fields that earns a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary and logically grouped sections (cost, cache, returns) are well organized, but the returns block is long and the prose is somewhat verbose. Size is justified by complexity, yet a few sentences could be tightened.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema, the <returns> block fully specifies the dict shape for both found=True and found=False, and flags data-quality caveats (patchy funding coverage, title-case stage string, total_funding defaulting to 0, matches_icp null handling). Nothing an agent needs to call or interpret results is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description reinforces ordering and canonical-form dedup, but those are already stated in the schema's own domain param description, so it adds little net new parameter meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (enrich company domains, batch org-only) and explicitly disambiguates from the sibling find_email ('For person enrichment... use find_email instead'). An agent can tell it apart from apollo_search/apollo_save without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance on batching ('Pass all candidate domains at once — one call with the full list, not one call per domain'), the alternative for people (find_email), and the follow-up query path (query_companies). When-to-use and when-not-to-use are both covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
apollo_saveApollo SaveADestructiveInspect
Save people as contacts or companies as accounts in the user's Apollo CRM, where they show in Apollo's web UI. Confirm with the user before calling this tool.
Each contact needs an email; it's the only required field. For a contact whose
email you don't have, get it with find_email first, passing an apollo_search
result's name and apollo_id, or the person's full name and company. Then save
the real name and email it returns as {first_name, last_name, email, title,
organization_name}.
For accounts, the result splits the count into "created" (new rows) and "existing" (already in the CRM — Apollo dedupes by domain); "saved" is their sum, so a re-save reads as saved == requested rather than a false failure.
| Name | Required | Description | Default |
|---|---|---|---|
| type | Yes | "contacts" or "accounts" | |
| items | Yes | List of dicts. For contacts: {first_name, last_name, email (required), title, organization_name}. For accounts: {name, domain}. | |
| sequence_id | No | Optional. If provided, contacts will also be added to this Apollo sequence for outreach. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false / destructiveHint=true; the description goes well beyond by requiring user confirmation, disclosing that records become visible in the Apollo web UI, explaining domain-based dedupe, and pre-empting a false-failure interpretation by defining created + existing = saved. That is exactly the behavioral context the annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action in the first sentence and each following paragraph handles one mode (contacts, accounts). Slightly long, with a few clauses that could be tightened, but no sentence is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers the risky parts of a destructive write (confirmation, dedupe, count interpretation) without an output schema. The gap is that the return shape is explained only for accounts, leaving the contacts response (and any per-item failure reporting) unspecified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so item shapes, the required email, and the enum are already documented; the description mostly restates them. It does add genuinely new semantics (Apollo dedupes accounts by domain, and the find_email return maps directly onto the item fields), but it says nothing about `sequence_id`'s outreach side effect or `on_behalf_of`'s agency-client scoping beyond what the schema states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Save people as contacts or companies as accounts in the user's Apollo CRM') plus the observable outcome ('where they show in Apollo's web UI'), with a clear split between the two modes. An agent can distinguish this from search/enrich/query siblings without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit preconditions and routing: confirm with the user first, and for a contact without an email, call `find_email` with either an `apollo_search` result's `name`/`apollo_id` or the person's full name and company before saving. This is a concrete when-to-use and which-alternative-first instruction, not an inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
apollo_searchApollo SearchARead-onlyInspect
Search Apollo's database for people or companies.
People search is free. Organization search costs 1 Sliq credit per page when the user is on Sliq credits; free when the user has connected their own Apollo key. Use liberally for people; be deliberate for organizations.
People search returns each person's first name plus a masked last name,
and no LinkedIn URLs. The has_email and has_phone fields are
boolean indicators only — the actual email and phone number are
not returned. These masked previews are NOT saved to the Output tab
(no identity to key or link them by) and should not be recorded via
record_search_results. To turn a preview into an actionable, saved
person, call find_email with the result's name and apollo_id — it
resolves the real person and saves them to the user's people (what
query_people reads), with the email it found when they had none. It
doesn't add them to an agent's list: when they belong on one, record
them with record_search_results, keyed by the linkedin_url
find_email returns (its email when there is none).
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | Page number (1-indexed, 25 results per page) | |
| type | Yes | "people" or "organizations" | |
| keywords | No | General keyword search | |
| locations | No | Filter by location (e.g. ["San Francisco", "New York"]) | |
| seniority | No | Filter by seniority level (people only, e.g. ["vp", "director", "c_suite", "manager", "senior"]) | |
| industries | No | Filter by industry (e.g. ["computer software", "financial services"]) | |
| job_titles | No | Filter by job titles (people only, e.g. ["VP Sales", "CTO"]) | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| technologies | No | Filter by technologies used (organizations only, e.g. ["python", "react"]) | |
| company_names | No | Filter by specific company names (people only). Apollo's people endpoint only accepts org IDs, so each name is first resolved to an Apollo organization id via a companies-search round-trip. Pair with `company_domains` when subsidiaries may collide (e.g. "Indigo Ag" → "Indigo Ag Argentina"). | |
| revenue_ranges | No | Filter by company revenue (organizations only, e.g. ["1000000,10000000"]) | |
| company_domains | No | Optional domain per `company_names` entry (positional pairing — must be the same length when set). Acts as a strict disambiguator: when set, only orgs whose `primary_domain` matches are kept; names with no domain match are returned in `unresolved_company_names` and dropped from the filter. | |
| employee_count_ranges | No | Filter by company size — "min,max" headcount bounds, e.g. ["25,200"]. Arbitrary bounds are honoured; an empty side is unbounded ("500," / ",50"). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover readOnlyHint=true; the description adds the behavior that actually matters: per-page credit cost, key-dependent cost waiver, masking of last names and LinkedIn URLs, boolean-only has_email/has_phone, and the fact that previews are not persisted to the Output tab. That is exactly the context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with what it searches and the cost rule, then the masking caveat and the handoff to find_email. Dense and mostly waste-free, though the find_email paragraph is long enough that the key instruction ('call find_email with the result's name and apollo_id') is buried mid-sentence.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 13 params, no output schema, and a single readOnly hint, the description covers what the structured data cannot: cost, return shape, masking, non-persistence, and the follow-up path to save a real person. Nothing an agent needs to call this correctly or interpret the result is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the filter semantics (seniority/job_titles people-only, technologies/revenue_ranges org-only, company_domains positional pairing) live in the schema. The description adds only type-level semantics and cost, which is the baseline expectation when the schema already does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Search Apollo's database for people or companies') and immediately splits the two modes with their own semantics. It also names the downstream sibling (find_email) and the list sibling (query_people), so an agent can place it in the workflow without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit usage policy — 'Use liberally for people; be deliberate for organizations' — with the cost model that justifies the asymmetry, and it tells the agent when to escalate a preview into an actionable person via find_email rather than re-searching. Alternatives and exclusions are stated, not implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
append_learningAppend LearningAInspect
BEFORE COMPOSING YOUR BATCH, filter each candidate entry. A learning is ONLY:
A reusable rule about how the agent should run, that applies to every future firing, not just this one.
A learning is NOT (these are the most common mistakes — produce zero of these):
Per-entity / per-contact facts. Anything naming a specific company, person, deal, or URL. Examples that DO NOT belong here: "Heitman LLC (heitman.com) is a real estate firm in Chicago", "Jamestown Management hired a new CFO", "Alice Chen's first name is 'Alice'", "ExampleCo: 200 waitlist, 30 live users". → Use
track_prospects(agent_id, items=[...])for per-entity lifecycle tracking, OR →update_workspace(section='outputs', key='<your_list>', value=[...])to store as an agent deliverable.Per-run summaries. Anything describing what happened on a specific date or run. Examples: "Follow-up run 2026-04-27: 0 people in 'messaged' stage", "Apr 23 scan: same source returned, no new angles". These are auto-saved to the agent's run history every run — don't duplicate them here.
General user preferences. "User wants concise emails", "user prefers Tuesday meetings". → Use
save_memory.
Valid learnings look like rules-of-thumb, not observations. They're short prose, no proper nouns, no dates, no per-entity data. Good examples:
"PostHog returns 1-day data, not an error — if the query returns one row it's complete, not partial."
"Internal team syncs rarely produce postable ideas — skip quickly to save Tavily budget."
"r/SaaS posts about LinkedIn automation pain are strong leads."
"Tavily score < 0.09 against generic signal_keyword queries is reliably tangential — keep min_score=0.09."
If you cannot rewrite your candidate entry into a rule-of-thumb
shape without naming a specific entity, date, or run number, it
doesn't belong here — store it via track_prospects or
update_workspace instead.
USAGE
Pass learnings=["...", "..."] to add one or more new entries in one
call. The whole batch is atomic — either all entries are appended or
none are (see "Cap behavior" below). Singleton case is
learnings=["..."]. Batching is strictly preferred over multiple
single-entry calls: one round-trip per run is dramatically cheaper
in latency and tokens than one round-trip per insight.
Pass replace_with=[...] to atomically replace the full list — use
this when the prior call returned a "cap reached" ModelRetry asking
you to consolidate.
Scope: per-agent and persistent. Distinct from save_memory (per-user
preferences).
CAP BEHAVIOR
The list is capped at 25 items. If a batch would push the list over
the cap (current_count + batch_size > 25), the WHOLE batch is
rejected via ModelRetry asking you to consolidate first via
replace_with. After consolidating, re-submit your full batch — no
need to track which entries "landed" since none were stored on the
rejected call. Consolidate by DROPPING entries that match the
anti-examples above (per-entity facts, per-run summaries), not by
reshuffling — the cap is a forcing function for hygiene, not a
length limit on the same content.
Dict with success, agent_id, and the new learnings count.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | ID of the agent | |
| learnings | No | New learning entries to append in one batch (mutually exclusive with replace_with) | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| replace_with | No | Full replacement list for consolidation (mutually exclusive with learnings) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false. The description adds substantial behavior beyond that: list is capped at 25; an over-cap batch is rejected ATOMICALLY via ModelRetry; batches are preferred over singletons for latency/token cost; the list is per-agent and persistent. This is rich context the annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and the sectioning (filter criteria, USAGE, CAP BEHAVIOR) is logical. It is long, and the anti-example enumeration plus the closing 'if you cannot rewrite...' paragraph partially restate earlier points, but for a tool whose correctness hinges on hygiene rules the length is largely earned.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No formal output schema, but a <returns> block documents the response shape (success, agent_id, new learnings count). Combined with full coverage of atomicity, cap behavior, scope, alternatives, and consolidation workflow, nothing an agent needs to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema documents all four parameters. The description still adds value beyond it: it explains the atomic-batch semantics of learnings, the consolidation purpose of replace_with, and their mutual exclusivity in operational terms. It does not, however, clarify on_behalf_of beyond what the schema says.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource and scope: 'Append one or more agent-specific operational RULES that would make the NEXT run more reliable,' plus the replace variant. It explicitly distinguishes itself from siblings (save_memory for preferences, track_prospects for per-entity lifecycle, update_workspace for deliverables) so an agent can route correctly without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Unusually strong. It states exactly when to use (rule-of-thumb learnings), when NOT to use (per-entity facts, per-run summaries, user preferences), and names the alternative tool for each exclusion. The cap-consolidation workflow (use replace_with after a ModelRetry) is spelled out explicitly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
attach_to_outreachAttach To OutreachAInspect
Attach a file from the user's library to a queued outreach email.
Only attach when the user explicitly asks to attach a specific file to a recipient's email — never on your own initiative. recipient_email targets a queued email (pending or awaiting approval); attachment_id comes from list_attachments. The same file can be attached to several emails.
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| attachment_id | Yes | a file id from list_attachments. | |
| recipient_email | Yes | the queued email's recipient. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false and destructiveHint=false, so the safety profile is partly covered. The description adds real behavioral context beyond that: the mutation must be user-initiated, targets only pending/awaiting-approval emails, and the same file may be reused across emails (implying non-destructive). It stops short of stating failure behavior or whether the attachment is copied vs. linked.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the action, then the usage guardrail, then the parameter sources. No redundant or filler text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-parameter mutation tool with 100% schema coverage, no output schema, and safety annotations, the description plus schema plus annotations cover everything needed to call it correctly: when to invoke, what the target must be, and where the id comes from.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds genuine meaning for recipient_email ('targets a queued email (pending or awaiting approval)') and reinforces the source of attachment_id (list_attachments), which is new qualifier information beyond the schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb (attach), resource (a file from the user's library), and target (a queued outreach email). The scope is tight enough that an agent can immediately distinguish this from detach_from_outreach and the various list/get siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use ('only attach when the user explicitly asks to attach a specific file to a recipient's email') and an explicit when-not ('never on your own initiative'). It also constrains the target to queued emails (pending or awaiting approval), leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_email_daily_usageCheck Email Daily UsageARead-onlyInspect
Call this before queuing bulk email outreach — the mailboxes list
tells you which mailboxes still have headroom today. Each connected
mailbox has its own daily cap (Gmail/Outlook score reputation per
from address, not per Sliq user). An empty mailboxes list means no
email account is connected — tell the user to connect a Gmail or Outlook
mailbox in Settings.
{
'emails': {
'mailboxes': [
{'email': str, 'provider': str, 'sent_today': int,
'daily_limit': int, 'remaining_today': int,
'is_default': bool},
...
],
},
'queue': {
'pending': int, 'pending_approval': int,
'sent_total': int, 'failed': int,
'next_send': str, # optional
},
}
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
While the annotation confirms it is read-only, the description adds crucial behavioral context by clarifying *how* the usage is calculated: 'Gmail/Outlook score reputation per `from` address, not per Sliq user.' This is vital information that goes beyond the simple 'read-only' hint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is highly focused, starting with a clear summary. While the inclusion of the full return schema makes it long, the explanatory text is efficient and minimizes fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The definition is extremely complete. It outlines the purpose, the optimal usage timing, the meaning of the input parameter, and provides a fully structured expectation of the complex return data (both email status and queue status).
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description provides excellent semantic context for the `on_behalf_of` parameter, explaining that it applies to the agent's 'agency client's email' and affects their 'agents, settings, connected accounts, and credits.' This clarifies the real-world scope of the input.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the specific function: checking per-mailbox email outreach usage limits and queue status. This distinguishes it from other potential reporting or queue management tools in the list.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit instructions on when to call the tool ('Call this before queuing bulk email outreach'). Furthermore, it details the necessary user action if the tool returns an empty list of mailboxes, preventing immediate failure.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_linkedin_daily_usageCheck Linkedin Daily UsageARead-onlyInspect
Call this before sending multiple LinkedIn messages, InMails, or connection
requests to get a fresh read of today's headroom (limits, remaining,
ramp-up) plus the current queue counts — re-call it after queueing or
approving sends, which leave the in-context figures out of date. For the
per-contact queue (names, scheduled times, messages), use
manage_linkedin_invite_queue with action='status'.
Dict with usage counts, limits, queue counts, and configurable_limits
— per action the standard default, the tier-aware ceiling (the max the
user can raise it to), and the user's current custom cap (None = using
the default). Quote ceiling when the user asks how high they can set a cap.
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnlyHint=true, so the safety profile is covered. The description adds a genuinely useful behavioral trait beyond that: the figures are a 'fresh read' whose values go stale after queueing/approving sends, which is exactly the operational nuance an agent needs. It does not discuss error behavior or any throttling on the call itself, so it stops short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary block is front-loaded with the tool's purpose and the usage trigger, and the tagged structure separates summary from returns cleanly. Minor redundancy between the title, the first sentence, and the returns block, but every sentence still carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the `<returns>` block is doing necessary work – it enumerates usage counts, limits, queue counts, and the `configurable_limits` structure (default/ceiling/custom) and even tells the agent to quote `ceiling` for cap questions. Combined with the when-to-call guidance, an agent has everything needed to invoke and interpret the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% – the single `on_behalf_of` parameter is fully documented in the schema (agency client email, cross-account execution, omit for own account). The description adds nothing about it, which is acceptable since the schema does the heavy lifting. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource: 'Check LinkedIn usage limits and invite queue counts.' It also explicitly distinguishes itself from the nearest sibling by naming `manage_linkedin_invite_queue` with action='status' for the per-contact queue. An agent can tell the two apart without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit trigger conditions ('Call this before sending multiple LinkedIn messages, InMails, or connection requests'), a re-invocation rule ('re-call it after queueing or approving sends, which leave the in-context figures out of date'), and names the alternative tool for a related-but-different need. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
classify_message_tag_groupClassify Message Tag GroupAInspect
Classification costs credits (a flat per-message rate). The return carries the count of messages the run will process and the credits it will charge (exact — the same prospect-resolved population the run bills), so you can tell the user the cost; a run never charges more than the balance. If the balance can't cover the estimate the run is not enqueued — the return says so, and you should relay it. Dict with the in-scope message count and estimated credit cost, plus whether the run was enqueued or blocked for insufficient credits. Enqueued -> {"enqueued": true, "group_id", "message_count", "estimated_credits"}. Blocked -> {"enqueued": false, "reason": "insufficient_credits", "group_id", "message_count", "estimated_credits", "balance"}.
| Name | Required | Description | Default |
|---|---|---|---|
| force | No | Re-classify messages already tagged for this group. | |
| channel | No | 'linkedin' or 'email' to bound the population; omit for both. | |
| group_id | Yes | The tag group to classify (from list_message_tag_groups). | |
| agent_ids | No | Scope classification to specific campaigns (agent_tasks ids from a group's `classifiable_campaigns`); omit for every campaign. `[]` classifies zero. | |
| sent_after | No | ISO-8601 timestamp; only messages sent on or after it are classified. Omit to classify all sent messages. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=false and destructiveHint=false. The description adds substantial context beyond that: it is asynchronous, results must be read later, it costs credits at a flat per-message rate, the estimate is exact, a run never exceeds the balance, and the run is blocked (not enqueued) on insufficient credits. This is exactly the behavior an agent must know before calling.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action in the first sentence and structures the rest into summary and returns blocks. It is on the longer side, but each clause (background, credit, blocking) carries distinct information an agent needs, so it earns its length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex, credit-charging, asynchronous operation with no output schema, the description compensates by spelling out both return variants (enqueued vs blocked-for-insufficient-credits) with their field names. Nothing essential to correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds semantic intent beyond the schema: force implies re-tagging after group/tag edits, channel narrows to LinkedIn/email, sent_after bounds the population. It reinforces rather than merely restates the field meanings.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Kick off classification of the group's in-scope sent messages, in the background') and scopes it to the group's criteria. An agent can distinguish this from classify_segment_group and estimate_message_tag_classify_scope without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit conditions for the key modifiers: default incremental catch-up, force=True for a re-tag after editing the group, channel/sent_after to narrow. It also names the follow-up tools (get_message_tag_rates / query_tagged_messages) and says results are not immediate. It stops short of explicitly contrasting itself with the estimate_*_scope siblings, so a 4 rather than a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
classify_segment_groupClassify Segment GroupAInspect
Classification costs credits (a flat per-person rate). The return carries the count of people the run will process and the credits it will charge (exact — the same population the run bills), so you can tell the user the cost; a run never charges more than the balance. If the balance can't cover the estimate the run is not enqueued — the return says so, and you should relay it. Dict with the in-scope person count and estimated credit cost, plus whether the run was enqueued or blocked for insufficient credits. Enqueued -> {"enqueued": true, "group_id", "person_count", "estimated_credits"}. Blocked -> {"enqueued": false, "reason": "insufficient_credits", "group_id", "person_count", "estimated_credits", "balance"}.
| Name | Required | Description | Default |
|---|---|---|---|
| force | No | Re-classify people already tagged for this group. | |
| group_id | Yes | The segment group to classify (from list_segment_groups). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Well beyond the annotations (readOnlyHint=false, destructiveHint=false): it discloses asynchronous execution, the ~15-minute auto-tagging window, credit cost at a flat per-person rate, the balance check that can block enqueueing, and the exact meaning of force. This is exactly the behavioral context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the action and scope before cost and return details, and the returns block is detailed, partly duplicated by the returns section of the description. It is long but nearly every sentence carries decision-relevant information; the enqueued/blocked shapes are slightly redundant with the returns block.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates fully by specifying both return shapes (enqueued and insufficient_credits-blocked) and the side-effect timeline, so an agent can call it, report cost to the user, and relay a failure correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real meaning the schema lacks: what force actually changes (re-classify everyone vs only never-classified people) and the cost implications of the run. group_id and on_behalf_of are already documented in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource+scope: kick off background classification of a group's in-scope canonical people. It is plainly distinguishable from siblings like estimate_segment_classify_scope, correct_segment_classification, and get_segment_rates.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to call (right after creating a group to tag existing people; with force=True after editing the group or its tags) and when not (without force only unclassified people are processed). It also routes the agent to the follow-up tools get_segment_rates and query_segment_people for results.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
correct_message_classificationCorrect Message ClassificationAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| source | Yes | Which table the message is in — 'linkedin_data' (a LinkedIn DM), 'email_data' (a sent email), or 'linkedin_queue' (a connection-request note). This is a message's `source` field from list_sent_messages or query_tagged_messages. | |
| tag_ids | Yes | The corrected tag ids (from the group's tags). At most one for a single-tag comparison; an empty list clears the message to "No match". | |
| group_id | Yes | The tag group the message is classified under (from list_message_tag_groups). | |
| source_id | Yes | The message's row id — a message's `source_id` from query_tagged_messages. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only provide the safety profile (readOnly=false, destructive=false); the description adds genuinely non-derivable behavior: the override persists across later re-tags, newly-added tags are not applied to the message, rates/message views recompute immediately, and an empty list clears to 'No match'. This is exactly the post-annotation context the dimension rewards.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded with the core purpose and the persistence caveat is placed early where it matters. It is dense and slightly long, but nearly every clause carries a distinct fact (override, persistence, recompute, clearing).
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, but the description embeds a returns block confirming the result shape (corrected, group_id, source, source_id, tags). Combined with the behavioral disclosure, an agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all five parameters are already documented at the schema level. The description largely restates the schema's tag_ids and source guidance (at-most-one, empty clears, where to find source/source_id) rather than adding new semantics, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (set/correct) and resource (tags on one sent message in a tag group), and explicitly scopes it to the classifier being 'wrong or skipped it'. It distinguishes itself from the bulk classifier (classify_message_tag_group) by being the per-message human override, so an agent can pick it apart from siblings without opening their schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit triggering conditions (classifier got it wrong or skipped the message), names the tools that supply the identifiers (list_sent_messages / query_tagged_messages), names the tag source (the group's tags), and states the alternative path is unnecessary ('no re-classify needed'). When-to-use is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
correct_reply_outcomeCorrect Reply OutcomeAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| channel | Yes | 'linkedin' or 'email' — which reply table the message_id is in. | |
| outcome | Yes | The corrected outcome — 'interested', 'meeting_booked', 'not_interested' (an explicit decline), or '' for neither (a plain reply). | |
| message_id | Yes | The inbound reply's row id (email_data / linkedin_data PK), from a message-search result. Must be a reply the user received. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false; the description adds substantial behavioral context: it moves the stage in either direction, unlike update_prospect, and not_interested is a rung between replied and the positives that still counts as replied in the funnel. These consequences go well beyond the safety hints and clarify what the correction actually does to prospect state.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and structured with summary/returns tags. It is longer than minimal but information-dense: every clause adds routing guidance, behavioral consequence, or parameter sourcing, with no obvious filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-parameter mutation tool with no output schema, the description covers purpose, alternatives, parameter sourcing, and stage consequences, and supplies a returns summary. Nothing essential an agent needs in order to select or call the tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds meaning beyond the schema by explaining the effect of outcome='not_interested' (explicit decline, stage movement, funnel treatment) and by naming the exact search tools for obtaining message_id, though channel and on_behalf_of semantics remain schema-only.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (re-judge) and resource (one inbound reply's outcome), plus the secondary effect of re-deriving the prospect's stage. It explicitly distinguishes itself from update_prospect by directionality and by requiring a reply behind the correction, so an agent can route between them without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use conditions: lower false meeting_booked/interested, raise under-called outcomes, or record an explicit decline. It names the alternative update_prospect and the precise condition selecting it (marking a rung with no reply behind it), and points to search tools for finding the required message_id.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
correct_segment_classificationCorrect Segment ClassificationAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| tag_ids | Yes | The corrected tag ids (from the group's tags). At most one for a single-tag group; an empty list clears the person to "No match". | |
| group_id | Yes | The segment group the person is classified under (from list_segment_groups). | |
| person_id | Yes | The canonical person to correct (from query_segment_people / query_people). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial behavior beyond annotations: it replaces the person's tags, empty tag_ids clears to "No match", group rates and people views recompute immediately, and the override persists so later re-tagging won't re-classify the person. This is exactly the mutation and persistence context an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the action and scope, then covers key edge case behavior, immediate effects, and persistence. The separate returns block is concise and useful; there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although no output schema exists, the return shape is described in a compact returns block. Required parameters, valid sources for ids, edge cases, immediate effects, and persistence are all covered well enough for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four parameters are already documented. The description reinforces provenance for person_id and tag_ids and notes single-tag group behavior, but mostly restates what the schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: correct the tags on one person when the classifier was wrong. It scopes the action to one (group, person) and explicitly calls itself the human-set override, which distinguishes it from group-level and message-level classification siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear context for use: when the classifier has mis-tagged one person, and gives the provenance for person_id and tag_ids. It does not explicitly name classify_segment_group as the bulk alternative, so it stops short of full when/when-not routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_agentCreate AgentAInspect
Create a background agent that runs automatically based on triggers.
Do NOT create a new agent for follow-ups, event handlers, or scheduled checks
that continue an existing agent's work — use add_trigger on that agent instead.
When the agent matches one of the built-in agent templates — the template_id
enum lists them — set its template_id and seed workspace.inputs per that
agent's skill guide. The template_id runs the template's setup; seed a
template's inputs without it and the agent is not fully wired up. If no template
fits, omit template_id and the agent runs as a general background agent.
You MUST call get_skill_guide('trigger_code') before writing any trigger's
code — the qualification rules, sandbox globals, tool surface, and out
contract live in the skill. code runs on the trigger's next firing.
| Name | Required | Description | Default |
|---|---|---|---|
| goal | Yes | Complete instructions for what the agent should accomplish. Write as if instructing another assistant. Name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat. | |
| title | Yes | Short name for the agent (e.g. "Outreach to Q2 leads", "Email triage") | |
| triggers | Yes | List of triggers that determine when the agent runs. An agent can have multiple triggers of different types (e.g. one schedule plus one event handler) — they fire independently. At least one trigger is required. In each trigger's prompt, name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat. | |
| workspace | No | Optional `{'inputs': {...}, 'outputs': {...}}` dict that seeds the agent's workspace. Name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat. | |
| template_id | No | Optional template ID. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false; the description goes well beyond that by disclosing that a template_id 'runs the template's setup', that omitting it after seeding template inputs leaves the agent 'not fully wired up', that trigger code 'runs on the trigger's next firing', and that get_skill_guide('trigger_code') is mandatory before writing code. It does not cover return value, permissions, or credit consumption, so it is strong but not exhaustive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded, then the exclusion, then template wiring, then the mandatory prerequisite call. Four short paragraphs, each carrying distinct load; slightly dense but no filler sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter creation tool with nested trigger unions and no output schema, the description covers the creation semantics, the template wiring consequence, and the prerequisite skill guide call. It omits what is returned (e.g. an agent identifier) and any account/permission constraints beyond on_behalf_of's schema text.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning beyond the schema: it explains how template_id interacts with workspace.inputs seeding and what happens if you seed inputs without the template. It does not elaborate on on_behalf_of or trigger-level fields, leaving some parameters to the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource+mechanism: 'Create a background agent that runs automatically based on triggers.' It is immediately distinguishable from sibling create/update/delete tools, and the subsequent paragraphs sharpen the scope further rather than restating the name.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit when-not rule with a named alternative: 'Do NOT create a new agent for follow-ups, event handlers, or scheduled checks that continue an existing agent's work — use add_trigger on that agent instead.' It also splits the template vs. general-agent case and states the condition that selects each.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_calendar_eventCreate Calendar EventADestructiveInspect
Use search_emails or search_calendar to find email addresses for prior attendees. For new attendees whose email you don't have, use find_email. Dict with success status and event details
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | Optional event description/notes (plain text) | |
| subject | Yes | Event title | |
| location | No | Optional event location | |
| provider | No | Which calendar to use ('outlook' or 'google'). Only needed if the user has both connected -- ask them which to use, then pass it here. | |
| attendees | No | Optional list of attendees. Each is an object with required "email" and optional "name". Examples: [{"email": "alice@acme.com", "name": "Alice Smith"}] or [{"email": "alice@acme.com"}]. | |
| in_person | No | ALMOST ALWAYS OMIT THIS PARAMETER or set to false. ONLY set to true if the user literally says "in-person", "in person", "at my office", or "no video link". If there is ANY ambiguity, do NOT set this to true. A video meeting link is automatically added when this is false (the default). | |
| end_datetime | Yes | End time as ISO 8601 in the user's LOCAL timezone, not UTC (e.g., "2026-02-17T11:00:00"). Do not include a timezone offset or Z suffix. Default to 30 minutes after start if user does not specify. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| start_datetime | Yes | Start time as ISO 8601 in the user's LOCAL timezone, not UTC (e.g., "2026-02-17T10:00:00"). Do not include a timezone offset or Z suffix. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes beyond the annotations (readOnlyHint=false, destructiveHint=true) by disclosing the auto-included video link, the in_person override semantics, and a mandatory confirmation workflow. It does not address reversibility or failure modes, but the added context is substantial for a mutation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then constraints, then related-tool routing, wrapped in clean summary/returns sections. Every sentence carries actionable content with no padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 9-parameter mutation tool with no output schema, it covers creation semantics, provider choice, video-link behavior, attendee sourcing, and confirmation. It omits edge-case behavior such as duplicate/conflicting events or error handling, leaving a small gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description restates the in_person/video-link coupling already documented in the schema and adds no syntax, format, or default guidance for subject, body, or datetimes beyond what the properties already carry.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Create a calendar event in the user's connected calendar') and immediately scopes it to Outlook or Google. The automatic video-link behavior further distinguishes it from read-side siblings like search_calendar.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit invocation rules: always show details and get confirmation first, use search_emails/search_calendar to source prior attendee addresses, and find_email for new attendees. It names the exact alternative tools and the condition that selects each.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_crm_taskCreate Crm TaskAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | Free-text notes. | |
| title | Yes | What to do, e.g. "Send pricing to Jane". | |
| deal_id | No | Link a deal (query_deals `id`). | |
| due_date | No | The day it's due, YYYY-MM-DD. Use resolve_date first for words like "next Friday". Omit for no due date. | |
| person_id | No | Link a person (query_people `id`). | |
| company_id | No | Link a company (query_companies `id`). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| assignee_email | No | Who should do it, an active teammate's email from list_teammates (not an invited one). Default: the user. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare it is not read-only and not destructive, so the description adds real value: the task is team-visible, may link to person/company/deal or stand alone, and critically sends no reminder/alarm. That 'no notification' trait is a non-obvious behavioral fact an agent must relay to users.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the purpose, follows with examples and a caveat, then a returns note. Structured and readable; the sentence about team visibility and the alarm caveat both earn their place, though there is minor redundancy between the examples and the earlier 'to-do' phrasing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the inline returns tag explains the created task's shape ('same shape as a query_crm_tasks row'). Annotations cover the safety profile and schema covers all parameters, leaving only minor gaps such as permission/assignment constraints not stated in prose.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so each of the 8 parameters is already documented in the schema. The description adds the linking model ('stand alone or link to a person, a company and/or a deal'), which reinforces the relationship among person_id/company_id/deal_id but adds little beyond the schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Create a task...in the user's CRM') and clarifies scope by noting it appears on the team Tasks view and on a linked deal. It is easily distinguished from update_crm_task and query_crm_tasks (the latter named for the return shape).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete usage examples ('remind me to send pricing to Jane on Friday', 'add a task to follow up with Acme') that anchor when to reach for it, plus a clear caveat that it produces no notification. It does not explicitly contrast against update_crm_task or other task tools, so no exclusions are covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_dealCreate DealAInspect
A deal needs a person or a company (or both). Link someone Sliq already knows by id (query_people / query_companies), or pass new_person / new_company to add them. If the person already has an open deal this refuses and names it — update that deal instead, unless the user wants a second one (then pass allow_duplicate=true). The created deal, same shape as a query_deals row.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Deal name, e.g. "Acme - pilot". Usually the company name. | |
| notes | No | Free-text notes. | |
| stage | No | Pipeline stage by name or id (query_deals lists them). Default: the first stage. | |
| amount | No | Deal value as a plain number, e.g. 12000. | |
| person_id | No | The contact, as a query_people `id`. | |
| company_id | No | The company, as a query_companies `id`. | |
| new_person | No | A contact to add: name plus email or LinkedIn URL. Used instead of person_id. | |
| new_company | No | A company to add: name plus domain. Used instead of company_id. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| assignee_email | No | The owner, an active teammate's email from list_teammates (not an invited one). Default: the user. | |
| allow_duplicate | No | Create even if this person already has an open deal. | |
| expected_close_date | No | YYYY-MM-DD. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds behavior beyond the annotations: duplicate-open-deal refusal with an error that names the existing deal, and dedup behavior for new_person/new_company ('an existing person with the same email/LinkedIn is reused, not duplicated'). Consistent with readOnlyHint=false and destructiveHint=false. Doesn't cover permissions or any limits, but the annotations already declare the safety profile.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then relationship linking and the duplicate edge case in priority order. Markup wrappers consume some space and the 'shows up on the Deals view for the whole team' sentence is the least load-bearing, but nothing is confusing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 12-parameter creation tool with no output schema, the description covers the key relational requirement, the duplicate path, and the return shape ('same shape as a query_deals row'). The many remaining parameters are fully documented in the schema, so the description is complete enough without duplicating them.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds a real constraint not visible in the schema: a deal requires a person OR a company (or both), while the schema only marks name required. It also clarifies that person_id and new_person are mutually exclusive alternatives.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Create a deal in the user's CRM pipeline') and explains the underlying semantics ('this is how a person or company gets into the CRM') plus where it becomes visible. The agent can distinguish it from update_deal and query_deals without inspecting schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing: link an existing person/company by id (via query_people / query_companies) or pass new_person / new_company. It also names the failure path ('if the person already has an open deal this refuses and names it — update that deal instead') and the escape hatch (allow_duplicate=true).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_message_tag_groupCreate Message Tag GroupAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | 'single' (one tag per message) or 'multi' (several). | single |
| name | Yes | Short name for the dimension (e.g. "Opening sentence"). | |
| tags | Yes | The tags to create, each {name, description}. Description guides the classifier, so make it discriminating. | |
| criteria | No | The classify scope this question tracks — {channel, agent_ids, positions}, each null = all. `channel` is 'linkedin' or 'email' (null = both); `agent_ids` are campaign (agent_tasks) ids from classifiable_campaigns (null = every campaign); `positions` lists the conversation positions to tag — 'first' (each conversation's first message), 'follow_up' (later messages sent before the prospect replied), and/or 'reply' (messages sent after the prospect replied); null = all. `senders` are the teammate emails whose sent messages to tag (null = every teammate); in a shared workspace, default it to the user's own email unless they ask for teammates. classify reuses this scope, so set it to what the question should track. Omit for all. | |
| description | No | Optional one-line description of what the dimension captures. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only tell the agent this is a non-read-only, non-destructive write. The description adds the key behavioral fact that creation is inert and must be followed by classification, plus the downstream return shape. It does not mention permission requirements or limits on tag counts, which is a minor gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads purpose, then mode selection, then the sequencing caveat, in a compact structured block. The returns block is justified because no output schema exists, though the prose is dense enough to require careful reading.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a six-parameter creation tool with no output schema, the description covers purpose, mode choice, the critical follow-up tool, and the returned dict shape. The rich criteria/on_behalf_of semantics are fully deferred to the schema, which is reasonable, though no error or agency-context caveats are surfaced in prose.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, and the description genuinely adds meaning: it explains the semantic difference between the mode enum values and warns that each tag's description drives the classifier, which is not obvious from the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Create a message tag group') and immediately defines what the resource is ('a dimension to classify sent outreach along') with its tags. It distinguishes itself from the adjacent classify_message_tag_group and clearly contrasts with the segment-group siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes between the two modes with concrete examples ('single' = ask type: time-slot vs yes/no; 'multi' = references-a-post AND references-their-company). It also states the crucial sequencing rule: creating does not classify, so follow with classify_message_tag_group.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_segment_groupCreate Segment GroupAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | 'single' (one tag per person) or 'multi' (several). | single |
| name | Yes | Short name for the dimension (e.g. "Seniority"). | |
| tags | Yes | The tags to create, each {name, description}. Description guides the classifier, so make it discriminating. | |
| criteria | No | The classify scope this segment tracks — {agent_ids, senders}: the campaign (agent_tasks) ids from classifiable_campaigns and the teammate emails whose prospects to tag, each null = all. classify reuses this scope, so set it to what the segment should track; in a shared workspace, default `senders` to the user's own email unless they ask for teammates. Omit for every campaign and teammate. | |
| description | No | Optional one-line description of what the dimension captures. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false, a low bar. The description discloses high-value behavior beyond that: creating a group commits the user to tagging its scope, every person is tagged at a per-person credit charge, and tagging occurs within ~15 minutes of LinkedIn profile lookup or first contact. It also notes the classifier reads tag descriptions and the person's profile fields, plus shared-workspace sender defaulting.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded with purpose and then the mode decision, which is the right ordering. Structure via summary/returns tags is clean and the returns block earns its space since there is no output schema. Slightly long, with a little overlap between the description's mode explanation and the schema's mode description, keeping it below a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter mutation tool with no output schema, the description supplies the missing return-shape documentation inline ({id, name, description, mode, updated_at, criteria, tags}) and covers cost, timing, scope commitment, and the follow-up classify call. Nothing an agent needs to invoke this correctly and set expectations appears to be missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3 and the schema already documents mode, tags, criteria, and on_behalf_of. The description adds meaning beyond the schema by motivating the single/multi choice with examples and explaining that tag descriptions are read by the classifier ('make it discriminating' is in the schema, but the rationale for classifier-driven tagging is expanded). It does not, however, address on_behalf_of at all, so it does not fully cover the parameter surface.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Create a segment group') and parenthetically defines the domain concept ('a dimension to classify your canonical people along'), which is needed since 'segment group' is jargon. It is clearly distinguishable from siblings like list_segment_groups, update_segment_group, delete_segment_group, and classify_segment_group. An agent can tell what this does without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use each mode ('single' when tags are mutually exclusive, 'multi' when several can co-exist) with concrete examples (seniority vs traits). It also names the follow-up tool and its condition: 'Follow with classify_segment_group to tag the people already in scope right away.' The criteria scope guidance ('set it to what the segment should track') further routes correct usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
define_sequenceDefine SequenceAInspect
Author (or replace) the campaign's flow sequence — a DAG of outreach nodes. Each node
carries its own out-edges as typed fields, so the grammar is the schema: a connection
request has accepted / already_connected / timeout slots, a send has replied /
follow_up, a silent action, a manual step and a gift step each have then, a decision has
arms, a terminal has none.
Derive the DAG from the agent's goal + triggers: one node per step, each natural-language
fork a decision, and terminal only for resting end-states (auto-skip, human handoff,
done). A fork is any rule about who gets a step or which steps they get — a filter (keep
only people in the right location, role or seniority, company or industry, skipping the
rest) or a route (a different path of steps by role, school or sector) — and each is its
own decision node rather than a condition inside a send step's on_enter code or prompt. A
wording difference within one step is a {slot} in its message_templates, not a fork.
The flow must be acyclic, every node reachable from start, and every path capped by a
terminal (only a terminal may dead-end) — all enforced.
The shape:
start(exactly one, the DAG head):{id, label, entry}. Holds prospects no send has reached (label it "Not started").entryis the single first touch —{to}for an immediate open, or{to, after: {value, unit}}(units w/d/h/m) to hold that long from when each prospect is tracked. The flow fires it on its own: do NOT queue the first sends yourself on a sequenced campaign — track prospects and let the flow open.connection_requestnode (LinkedIn):{id, label, kind, accepted, already_connected, timeout?, message_templates?}.message_templatesholds its note (see below); without one the step'son_entercode or instructions decide (its minted default code sends no note). Routes BOTH send outcomes; both required.accepted(invite taken) is an advance like a silent action'sthen—{to}to move on once the accept is detected, or{to, after: {value, unit}}to hold the prospect on the request that long after the accept is detected (detection lags the real accept by up to ~8 hours, so the wait counts from detection).already_connected(a CR onto an existing 1st-degree connection, a no-op) is a target node id. Routealready_connectedto aterminalby default (surface the existing relationship for opt-in rather than cold-messaging it); point it at the message step only when the user says to reconnect with existing connections.timeoutis an optional held advance{delay: {value, unit}, to}(e.g. withdraw after no accept).sendnode (a repliable send — LinkedInmessage/inmail, or anemail):{id, label, kind, channel, action, replied, follow_up?, message_templates?}.channelislinkedinoremail(so one sequence spans both);actionis a LinkedIn verb for channel linkedin oremailfor channel email. Routesreplied(required) so a reply advances the prospect off the node;follow_upis an optional held no-reply follow-on{delay, to}.message_templatesholds the step's message (see below).actionnode (a bodyless LinkedIn action — follow / withdraw / resolve / comment / reaction):{id, label, kind: "action", channel: "linkedin", action, then}. Nothing to reply to;thenis its single advance —{to}fires the instant the action's send lands (a bodyless step flows straight into the next node with no hold), or{to, after}to hold first. Aresolveaction may addnotify: true— the visible "View Profile" visit (LinkedIn notifies the person you viewed them), a warm-up touch typically placed later; the default silent resolve just reads profile data to branch on.manualnode (a step a HUMAN performs off-platform that the system can't enact — a call, a handwritten card, a recorded note):{id, label, kind: "manual", action_description, then}. No channel.action_descriptionis the fully-authored ask (no send is composed, no agent run boots — the step is purely a note for the user); entry queues it onto the user's home approval list.thenis the next node's id (a bare string, not a{to}object — unlike a silent action's or a gift'sthen): the prospect advances onto it when the user marks the queued step done, with no timed hold (completion is human-paced). UsemanualONLY when a human must act — never for a step the agent can do itself (that step never happens, since a manual node boots no run): an automatable side-effect (update a CRM, create a task in a connected tool, post a notification) is anautomationnode, and an automatable outreach touch is anaction/sendnode.giftnode (a physical gift Sliq orders online and ships to the prospect's US address — use it whenever the user wants a gift sent as a step):{id, label, kind: "gift", product_url?, max_price_usd?, gift_note?, then}. No channel. Entry queues one gift card per prospect onto the user's approval list, with a short note the enactment run writes for each prospect; nothing is ordered until the user approves the card, where they complete or change the recipient, shipping address, product, price ceiling and note.product_url(the product page) andmax_price_usd(the most the item may cost including tax and shipping, in whole US dollars) are optional defaults pre-filled on every card, andgift_note(at most 240 characters) is the optional step note each prospect's own note is written from; set the ones the user named and leave the rest out. Each approved gift charges the owner's card for gifts the order's actual total, at mostmax_price_usd, plus about $3.30 + 2.9% in fees on top. Set a budget the user names for each gift asmax_price_usdas is, even one they call all-in, and tell them the fees come on top of it: a lower limit can make the order fail for the item they picked, so lowering it is their call.thenis the gift's single advance once it is delivered —{to}to move on at delivery, or{to, after: {value, unit}}to hold that long after it.automationnode (an automated non-send side-effect the AGENT performs — update a CRM record, create a HubSpot task, post a Slack/webhook notification, tag a record):{id, label, kind: "automation", then}. No channel.thenis the next node's id (a bare string, like a manual node's). Author the side-effect itself onto this node's on_enter trigger withupdate_trigger(aprompt, orcodefor a deterministic one) — the same way a terminal node's hook is authored, not a field here; the enactment run carries it out for each prospect then advances. Unlikemanualit boots an agent run and needs no human; unlike aterminalhook it advances. Reach for this when the user wants a step that "does something" automatically mid-sequence and then continues.decisionnode:{id, label, kind, rule, arms}.ruleis the NL fork judgment;armsis a list of{case, to}— eachcasea non-blank condition, the default authored as one too (e.g.case: "else"). A decision has no timed edge and no bare advance — only arms. A case arm may target any enactable node — a send / action / automation step, another decision, or a manual or gift step — or a terminal: the decision run moves each prospect onto its arm, and entering it fires that node's own on_enter enactment on its next run (a send/action composes its send, a nested decision judges its fork, an automation runs its side-effect, a manual step queues the human's task, a gift step queues its gift card), never an inline send. Chain a downstream decision when several arms share one judgment (DAG reuse); use compound arms on one rule ("A and B" / "A and not B" / "else") when one prospect needs several independent conditions judged together; target a manual step to hand a judged arm off to a human.terminalnode:{id, label, kind}. A resting end-state, no out-edges.
Every timed hold — a start after, a connection_request's held accepted or timeout, a
send follow_up, a silent action's or gift step's held then — is {value, unit}. A d (day) unit counts BUSINESS days —
Mon–Fri, skipping Saturday/Sunday — and is the DEFAULT for a day-count delay: a plain
"follow up in 3 days" (like "3 business days") is {value: 3, unit: "d"}. Reach for cd
(calendar day) ONLY when the user explicitly wants calendar days including weekends.
w/h/m (week/hour/minute) are always calendar.
Entering a node fires its enactment: a connection_request / send / action node
composes and enqueues its step, a manual node queues its step onto the user's
approval list, a gift node queues a gift card for each prospect onto that list, an
automation node runs its authored side-effect and advances, a decision judges its
fork and routes each prospect. Run a second channel alongside the first by chaining it
off a step's held advance (a CR timeout, a send follow_up, or a silent action's held
then) — not a second start entry: a prospect holds one flow position, so start takes
exactly one entry.
A send node's message is its message_templates: a one-entry list holding the message itself, with each
personalized part a {slot} — a named field ({first_name}, {company}) or a described
slot ({a line about their recent post}); the enactment run fills every {slot} per
prospect and, unless the step's prompt lets it rephrase, keeps the rest of the wording.
When the user wants to write part of each message themselves, put that part in double
square brackets, like [[where you met]]: the run leaves it as written, and each message waits in
their approval queue, even on a step that would otherwise send on its own; approving it
is refused until they fill it in there. Every other part of the queued message is
complete as it stands, one the user could approve without changing a word (the queue
rejects a leftover {slot}).
The templates are saved with the step and come back on get_campaign_flow; omit
message_templates to leave a step's templates as they are, or pass [] to remove them.
To A/B test a step's message, add a second version to the list: prospects
reaching the step alternate between the two, and get_message_experiments reports which gets
more replies. At current volumes a test takes weeks and only separates meaningfully
different messages (a different opener or ask, not a reworded phrase), so propose versions
that differ that much. Changing either version's text restarts the test. To end a test,
pass just the version you keep.
A connection_request node's message_templates holds its note the same way (at most 300
characters, filled per prospect), where "" is a version sent with no note: ["", note] A/B
tests a note against none, and get_message_experiments reports which gets more requests
accepted.
Anything else the run should know for that step (tone, who to mention, what to skip,
whether it may rephrase the template, what to write when a slot can't be filled) goes in
the step's on_enter trigger prompt via update_trigger — keep
the message itself in message_templates, not in that prompt or in workspace.inputs, so
the user sees and edits it on the step. When you first set a template on a step whose
prompt already carries its message, rewrite that prompt down to the extra instructions.
(workspace.inputs stays the home for a flowless
campaign's templates and for per-agent data no single step owns — criteria, audience,
exclude lists.)
Authoring (or replacing) a sequence on an agent that ALREADY holds untouched prospects enrolls that whole backlog into it — so before defining a sequence on an agent with an existing roster (a monitoring or engager-capture agent especially), confirm with the user that the accumulated prospects should all enter the campaign.
Re-authoring replaces the prior sequence (allowed on a live campaign); a prospect still
resting on a dropped node returns under off_sequence_nodes (with a note on re-homing)
rather than being lost. The node-owned on_enter triggers reconcile in the same call:
every node kept in the new DAG (same id, any kind) keeps its on_enter row and the
prompt/code on it, a new enactable node gets a fresh row auto-minted, and only a node
dropped from the DAG entirely has its on_enter row deleted. A new resolve /
connection_request / follow node is minted with a default deterministic enactment code (it
enacts clean with no agent run — edit it for a non-standard step; a connection request's note
goes in its message_templates, not the code); every other
kind mints code-less. A reused id keeps its existing row — so after changing what that id's node
does, review its on_enter content; an unmodified minted default is regenerated for the new
kind automatically, but an edited one is left as you wrote it.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | ID of the agent to attach the sequence to. | |
| sequence | Yes | The flow DAG (typed per-kind nodes). Validated for structural integrity, reachability, and acyclicity. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only give readOnlyHint=false and destructiveHint=false. The description goes far beyond: re-authoring replaces the prior sequence on a live campaign, displaced prospects surface under `off_sequence_nodes`, node on_enter rows reconcile by id (kept/added/deleted), a minted default code is regenerated for the new kind but an edited one is preserved, gift approvals and fee mechanics, and backlog enrollment. This is exactly the disclosure the annotations cannot provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first sentence and the rest is organized by node kind, then by timing/template behavior and re-authoring rules. It is very long, but for a nested discriminated-union DAG grammar most of it earns its place; some overlap with the schema's own node descriptions keeps it out of the top tier.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
A highly complex tool with no output schema and thin annotations, yet the description covers DAG construction rules, node semantics, timing units, template/A-B behavior, and the reconciliation side-effects of re-authoring. An agent has what it needs to construct a valid sequence and to predict the call's consequences.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning beyond it: the per-kind grammar and why each edge exists, the `d`-is-business-days/`cd`-is-calendar distinction, the bare-string vs `{to}` `then` divergence between manual/automation and other nodes, and the `[]`/omit semantics for `message_templates`. That is substantive semantic enrichment over the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource: "Author (or replace) the campaign's flow sequence — a DAG of outreach nodes." It immediately distinguishes itself from siblings like update_node, setup_linkedin_sequence, and add_trigger by establishing that this is the full-DAG authoring call, and it names its own read-back counterpart (get_campaign_flow) later.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Extensive when-to-use guidance for node-kind selection (manual ONLY when a human must act; a side-effect is automation not manual; a wording difference is a `{slot}` not a fork; route `already_connected` to terminal by default unless the user says reconnect). It also carries a critical pre-condition (confirm before enrolling an existing backlog). It stops short of an explicit "use this instead of setup_linkedin_sequence" statement, so it is clear context rather than full when/when-not/alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_agentDelete AgentADestructiveInspect
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | ID of the agent to delete | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructiveHint=true, but the description goes beyond them by disclosing soft-delete semantics: the user stops seeing it, it stops running, and the record is retained internally for a short window. This is exactly the kind of consequence detail annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the action and consequence, then the confirmation requirement, then the return shape. The XML-tag scaffolding is slightly verbose but each section carries useful content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, no-output-schema tool, the description covers effect, data retention, required user confirmation, and the return shape ('Dict with success status'). Nothing an agent needs to call it safely is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both agent_id and on_behalf_of are already fully documented, including the agency-client semantics of on_behalf_of. The description adds nothing beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Delete an agent') and is clearly distinguishable from siblings like update_agent, create_agent, and get_agent_details. An agent can identify the operation without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit precondition ('ALWAYS confirm with the user before calling this'), which is meaningful for a destructive tool. It does not, however, name when to delete vs. alternatives such as deactivating, so it falls short of full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_message_tagDelete Message TagADestructiveInspect
| Name | Required | Description | Default |
|---|---|---|---|
| tag_id | Yes | The tag to delete (from a group's tags list). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, so the safety profile is covered. The description adds genuinely new context: 'Irreversible' clarifies there is no undo, and 'the rest of the group is untouched' defines the blast radius. It does not cover permission/auth needs, but that is a minor gap against the annotation coverage.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the destructive action and its irreversibility, followed by a compact returns block. Every clause earns its place with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the returns block usefully documents the response shape ({"deleted": true, "tag_id": <id>}), and irreversibility plus scope are disclosed. A note on required permissions for a destructive operation would round it out, but nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, including a detailed description for on_behalf_of (agency client email, omit to act on your own account), so the schema carries full parameter semantics. The description adds nothing beyond what the schema already documents, making the baseline 3 appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Delete a tag and its classifications') and immediately scopes it against the group-level sibling by noting 'the rest of the group is untouched'. An agent can distinguish this from delete_message_tag_group and delete_segment_tag without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The 'rest of the group is untouched' clause implies this is the narrow, single-tag deletion versus the group-level tool, but no alternative is named and no explicit when/when-not condition is given. Usage is inferable rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_message_tag_groupDelete Message Tag GroupADestructiveInspect
| Name | Required | Description | Default |
|---|---|---|---|
| group_id | Yes | The tag group to delete (from list_message_tag_groups). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, but the description adds genuinely new information beyond them: the deletion is irreversible AND cascades to child tags and classifications. That cascade scope is not derivable from the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Extremely tight: one summary sentence covering effect, cascade, and irreversibility, plus a compact returns block. Nothing is wasted and the destructive nature is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive two-parameter tool, the description covers the effect, cascade scope, irreversibility, and the return shape (given no output schema exists). It omits any precondition or permission context, but the schema fills the parameter side.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the description adds nothing about group_id or on_behalf_of semantics. Baseline 3 is appropriate when the schema fully carries parameter documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Delete) and resource (tag group) and clarifies the cascade scope: 'all its tags and classifications.' This distinguishes it from delete_message_tag (single tag) and delete_segment_group, though no sibling is named explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides no when-to-use guidance, no prerequisites (e.g. the group must exist / be fetched via list_message_tag_groups), and no note about when to prefer update_message_tag_group over deletion. The only routing hint lives in the schema's group_id description, not the description text.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_segment_groupDelete Segment GroupADestructiveInspect
| Name | Required | Description | Default |
|---|---|---|---|
| group_id | Yes | The segment group to delete (from list_segment_groups). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, but the description adds real value beyond them: it discloses the cascade effect on tags and classifications and flags the operation as 'Irreversible.' It also documents the return payload, which the absence of an output schema leaves otherwise unknown. It does not mention required permissions or whether on_behalf_of changes behavior, keeping it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The destructive scope and irreversibility are front-loaded in a single tight summary, and the return shape is separated cleanly. Every sentence earns its place with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter destructive tool with full schema coverage and annotations carrying the safety profile, the description closes the remaining gaps: cascade scope, irreversibility, and the return value. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both group_id and on_behalf_of are already fully documented, including the sourcing of the ID and the agency-client semantics. The description adds nothing parameter-specific, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Delete) and resource (segment group), and goes further by defining the cascade scope: 'all its tags and classifications.' This distinguishes it from siblings like delete_segment_tag or delete_message_tag_group, which operate on narrower resources.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the destructive verb and the schema's '(from list_segment_groups)' hint for sourcing the ID, but there is no explicit when-to-use guidance or named alternatives (e.g., update_segment_group to modify rather than remove). Adequate but leaves routing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_segment_tagDelete Segment TagADestructiveInspect
| Name | Required | Description | Default |
|---|---|---|---|
| tag_id | Yes | The tag to delete (from a group's tags list). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, so the description's job is to add detail, and it does: it discloses the cascading deletion of the tag's classifications and confirms the sibling tags in the group survive. It also gives the return shape, which is valuable since no output schema exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences plus a compact return spec, with the destructive scope front-loaded before the return detail. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 2-parameter destructive tool with no output schema, the description covers the key facts: cascade behavior, irreversibility, survival of the rest of the group, and the response payload. Only permission/authorization context is absent, and the schema covers account targeting via on_behalf_of.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, including a full explanation of on_behalf_of and its agency-client semantics, so the schema carries the load. The description adds nothing about either parameter; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Delete a segment tag') and immediately scopes the blast radius: the tag's classifications go too, but 'the rest of the group is untouched.' That implicitly separates it from delete_segment_group, though no sibling is named explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use guidance, no mention of the alternative update_segment_tag for non-destructive edits, and no prerequisites (permissions, ownership). 'Irreversible' is a warning, not routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
detach_from_outreachDetach From OutreachAInspect
Remove a file already attached to a queued outreach email.
recipient_email targets a queued email (pending or awaiting approval); id is
the attachment's id as shown on that email in the queue status read. Email
queue only, mirroring attach_to_outreach.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | the attachment's `id` from the queue status read. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| recipient_email | Yes | the queued email's recipient. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=false and destructiveHint=false; the description adds the material context that the operation is confined to queued (pending/awaiting-approval) emails, which explains why a "Remove" action is flagged non-destructive and reassures the agent no sent mail is touched. It does not cover failure behavior or whether the email re-enters approval, but with annotations present the bar is lower.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action, then preconditions, then scope in three tight sentences with no filler. Minor waste from the id sentence, which duplicates the schema text almost verbatim.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-required-param mutation with no output schema and thin annotations, the description covers scope, the state precondition, and where the attachment id comes from. It leaves open whether the underlying file is deleted or merely unlinked and what happens when the email is already sent, which keeps it short of complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds real meaning for recipient_email (it must identify a queued email in pending/awaiting-approval state), but its explanation of id largely restates the schema's own "the attachment's `id` from the queue status read," and on_behalf_of is not mentioned at all in the description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Remove a file already attached to a queued outreach email") and bounds scope with "Email queue only, mirroring attach_to_outreach," which lets an agent place it relative to the attach/list/read attachment siblings. It stops short of naming a true alternative, but the inverse-relationship framing does most of the differentiation work.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete precondition: recipient_email targets a queued email in the pending or awaiting-approval state, plus an explicit scope limit (email queue only). No exclusions or alternative tools are named for the case where the email is not queued, so it is clear context rather than full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
draft_emailDraft EmailAInspect
Use search_emails or search_linkedin_message_history to find email addresses for prior correspondents. For new contacts whose email you don't have, use find_email. Dict with success, draft_id, provider, and draft details (to, cc, bcc, subject, body)
| Name | Required | Description | Default |
|---|---|---|---|
| cc | No | Optional list of CC recipients, same format as "to" | |
| to | Yes | List of To recipients. Each is an object with required "email" and optional "name" key. ALWAYS pass a list (even for a single recipient), and ALWAYS pass dicts (not bare strings). Examples: - With name: [{"email": "alice@acme.com", "name": "Alice Smith"}] - Without name: [{"email": "alice@acme.com"}] | |
| bcc | No | Optional list of BCC recipients, same format as "to" | |
| body | Yes | Email body in Markdown, rendered to HTML at send time for every provider (Gmail, Outlook, Superhuman). Write hyperlinks as [text](url) so the link reads as its anchor text. A bare or parenthesized URL left in the text is usually autolinked by the mail client, so it still clicks through — it just shows the raw URL instead of a label, which is what users mean when they say a link "isn't hyperlinked". Single newlines are preserved as line breaks. | |
| mailbox | No | Email address of a connected mailbox (e.g. 'alice@acme.io'). Omit to use the user's default mailbox. When the user has multiple mailboxes connected, ask which to use rather than guessing — surfacing the choice is the agent's job. | |
| subject | Yes | Email subject line | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations (readOnlyHint=false, destructiveHint=false) already convey the write-but-non-destructive profile, and the description adds useful context: drafts are routed to the connected provider automatically with no provider argument. It doesn't cover permission/auth requirements or whether drafts are later visible in the mailbox, so it stops short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action and uses structured summary/returns tags so the key facts are scannable. The XML-wrapped structure is slightly heavier than plain prose but every sentence still carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-param tool with no output schema, the description supplies the return shape (success, draft_id, provider, draft details), the provider-routing behavior, and the confirmation-before-send handoff. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already carries the recipient shape, markdown body rendering, mailbox selection, and on_behalf_of semantics. The description adds only the meta-note that no provider argument is needed, which is marginal value beyond what the schema provides; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Create a new email draft') and immediately names the scope (user's connected email account, provider-agnostic routing). It distinguishes itself from send_email and from draft_reply by framing this as net-new draft creation in the connected mailbox.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly prescribes the workflow: confirm with the user before calling send_email, and names the alternatives for address lookup (search_emails, search_linkedin_message_history, find_email) with the condition that selects each. The mailbox param description also tells the agent to ask the user rather than guess when multiple mailboxes exist.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
draft_replyDraft ReplyAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| cc | No | Optional list of additional CC recipients. Each is an object with required "email" and optional "name". Example: [{"email": "bob@acme.com"}] | |
| bcc | No | Optional list of BCC recipients, same format as "cc". | |
| body | Yes | Reply body in Markdown, rendered to HTML at send time — same rules as draft_email: hyperlinks as [text](url), single newlines preserved as line breaks. | |
| email_id | Yes | The database id of the email to reply to (from search_emails results) | |
| reply_all | No | If true, reply to all original recipients (Reply All). Default: false (reply to sender only). | |
| on_behalf_of | No | A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false and destructiveHint=false; the description adds the crucial behavioral point that this only stages a draft and must never be sent without user confirmation, which goes beyond the annotations. It also discloses the return dict contents, though it omits details like whether an existing draft gets overwritten or thread-subject handling.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and workflow in the first lines; the XML-ish summary/returns tags add light structural overhead but every sentence carries actionable content. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description supplies the return shape (success, draft_id, provider, original email context), plus the prerequisite tool call and the mandatory confirmation step. An agent has everything needed to invoke it safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, with email_id, body Markdown rendering, reply_all, cc/bcc, and on_behalf_of fully documented in the schema. The description only reinforces email_id sourcing from search_emails and adds nothing about the other parameters, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (create a reply draft) scoped to an existing thread, and names the provider coverage (Outlook or Gmail). It is clearly distinguishable from sibling draft_email (new message) and send_email (dispatch).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly prescribes the workflow: call search_emails first to get the id, pass it here, then present the draft and get confirmation before send_email. This is when-to-use plus a hard guardrail against a harmful alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
edit_monitor_membersEdit Monitor MembersAInspect
add and remove are LinkedIn profile URLs or vanity slugs for people, and
full linkedin.com/company/... URLs for company pages (a bare company name or
slug is read as a person's slug). A swap is one call
(add=[new], remove=[old]). Both forms of one profile (full URL and bare
slug) collapse to a single watched member, so re-adding someone already watched
is a no-op. The change takes effect on the monitor's next scheduled run.
Use this instead of re-running setup_linkedin_monitoring whenever the user just
wants to tweak who's watched ("also watch Bob", "drop Alice"): setup would force
re-passing every existing member, and since each watched profile is billed per
run, one dropped URL on the re-echo silently stops watching that person. To
change schedule, voice, or the action flags, use setup_linkedin_monitoring
(members are preserved there when omitted); adding the very first members or
resetting the whole list is setup too.
Dict with status ('active'), name, members_count, added, removed,
unresolved, and message.
| Name | Required | Description | Default |
|---|---|---|---|
| add | No | LinkedIn profile URLs/slugs or company page URLs to start watching. | |
| name | Yes | the monitor to edit (must already exist on this agent). | |
| remove | No | LinkedIn profile URLs/slugs or company page URLs to stop watching. | |
| agent_id | Yes | The agent the monitor lives on. | |
| on_behalf_of | No | A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false; the description adds substantial context beyond that: incremental semantics, one-call swap, both URL and slug collapse to a single member so re-adding is a no-op, and the change takes effect on the next scheduled run. It also discloses the per-run billing consequence of mishandling members, which is not derivable from annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core operation before the alternatives and the returns block, and every section earns its place. It is on the verbose side with narrative asides (the billing warning), but that content is load-bearing rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with annotations covering the safety profile and a described return shape (status, name, members_count, added, removed, unresolved, message), nothing an agent needs to call it correctly is missing. On-behalf-of behavior is also documented in the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds meaning the schema lacks: add/remove accept person URLs or vanity slugs and full company URLs, a bare company name is read as a person slug, and swapping is a single call (add=[new], remove=[old]). This is real semantic value beyond the field descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb pair (add and/or remove) and resource (watched people or company pages on an existing list-mode LinkedIn monitor) and explicitly scopes it as incremental. It distinguishes itself from setup_linkedin_monitoring by name, so the agent can tell the two apart without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('tweak who's watched: also watch Bob, drop Alice') versus when to use the alternative setup_linkedin_monitoring (change schedule, voice, action flags, add the very first members, or reset the whole list). It even explains why setup is dangerous to reuse here (a dropped URL silently stops watching someone).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
enrich_linkedin_profilesEnrich Linkedin ProfilesARead-onlyInspect
This is the tool for questions about a list of people: filtering a list by
connection count or seniority, labelling who works where, or filling in
headlines before drafting. experience carries every role with its dates,
so it also answers career-history questions — how long someone has been in
seat, where they worked before, whether they were promoted internally, who
is an alum of a given company — without a separate lookup. It does not touch
the user's LinkedIn account, so it neither consumes their daily
profile-lookup budget nor carries any account-safety risk — prefer it over
per-person lookups whenever you have more than a couple of people to enrich.
Costs 0.1 Sliq credits per profile returned; misses are free. A profile this user enriched in the last 24h is served from cache, so re-calling does not look up or charge again.
Enriching a profile also links it to its primary employer's canonical company, which may need a one-time web-domain lookup: 0.05 credits the first time a given company is looked up (cached after, so it never charges twice for the same company). A batch spanning many unfamiliar employers costs a little beyond the per-profile total; fold that into any estimate you give the user.
How many to run is a spend question: run a list of up to 500 straight away. Past 500, tell the user how many profiles it is and what that costs — the count times 0.1 credits — and wait for a go-ahead before running it; a batch that size also takes several minutes, so say so in the same breath. In a background run there is nobody to ask, so run it and report the spend in your summary.
How to call it is a separate question, and the answer is almost always
run_code. A direct return is truncated at 50KB, and one senior profile's
career history can be a third of that on its own — so a direct call on a
dozen executives shows you two of them, after charging for all twelve, since
credits are spent inside the tool before anything is truncated. Only a
handful of profiles fit. From run_code nothing is truncated: the rows stay
in the sandbox and you print only the filter, count, or summary you need.
Call it directly only for a few people whose full profiles you intend to
read.
What it cannot tell you: whether the user is already connected to someone,
their network distance, or shared connections. Those describe the user's own
relationship to the profile and only a LinkedIn-account lookup can answer
them — use setup_linkedin_sequence(action_type='resolve') when the decision
genuinely depends on connection status.
One entry per input, in input order — either a profile dict, or an
{'error': ...} entry for a profile that could not be resolved. The
error says which case it is: no profile exists for the identifier, or
the lookup did not finish and the identifier should be retried.
experience and education cover the person's whole history and are
long for senior people — a 25-year career can run 20+ roles. They arrive
in LinkedIn's display order, which is NOT sorted by date: roles at the
same employer sit next to each other, so the first entry is not reliably
the current one. Sort on start_date.get('year') when you need
chronology. Consecutive entries at one employer are usually one tenure
with internal promotions, but check the dates before saying so — a gap
between them means they left and came back, which is a different story
to tell. Dates are {'year': 2014, 'month': 'Feb', 'text': 'Feb 2014'};
month is missing when LinkedIn shows only a year, and a date can be
empty entirely, so reach for .get() rather than indexing. An
end_date of {'text': 'Present'} means the role is current, and
someone can hold several at once.
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| linkedin_urls | Yes | LinkedIn identifiers to enrich — full profile URLs, bare slugs, or encoded provider ids, in any mix. Capped at 1000 per call; split a longer list across calls. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark it read-only, and the description adds substantial operational context beyond that: credit costs, caching, employer-domain lookup charges, 50KB truncation behavior, batch semantics, and background-run handling. It also discloses what the tool cannot answer, which is important for avoiding misuse.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although long, it is front-loaded and organized around decisions the agent must make: when to use it, how to call it, how much it costs, and how to interpret results. Nearly every sentence carries operational value for a complex bulk-enrichment tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Complete for a tool with no output schema: the `<returns>` section explains output ordering, error shapes, `experience`/`education` caveats, date structure, and current-role representation. The cost, truncation, and batch details cover the main risks an agent needs to manage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are already documented in structured data. The description reinforces that `linkedin_urls` accepts mixed URL/slug/provider-id inputs and mentions batching, but it does not add much parameter meaning beyond the schema and does not discuss `on_behalf_of`.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: bulk LinkedIn profile lookup for people the caller already has an identifier for. It also enumerates the returned fields and clearly distinguishes itself from per-person lookups and connection-status lookups.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use guidance: prefer it over per-person enrichment whenever there is more than a couple of people, call through `run_code` for larger lists, and ask for approval above 500 profiles. It also names `setup_linkedin_sequence(action_type='resolve')` for connection-status questions it cannot answer.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
escalate_to_teamEscalate To TeamAInspect
Sliq stamps the affected user, the surface, and the agent automatically, so write
the summary for a teammate with no other context: what the user wanted, what went
wrong, and what you already tried.
Dict confirming the flag was raised, with a note on what to tell the user next.
| Name | Required | Description | Default |
|---|---|---|---|
| summary | Yes | Plain-text description of the problem and what you already tried. | |
| agent_id | No | The agent this concerns. Defaults to the current agent when in one; pass it only to point at a different agent. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations mark it as non-read/non-destructive but only weakly characterize the write. The description adds real value: Sliq auto-stamps user/surface/agent, a human reads and follows up, and it is a one-way async handoff. It does not cover rate limits or auth requirements, but the post-call behavior is well disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose and the precondition, with every sentence contributing. The XML-ish summary/returns structure is slightly heavier than needed but not padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-parameter, no-output-schema tool this is complete: it covers when to use it, what to put in the summary, what the system fills in automatically, and what the return contains (a note on what to tell the user). Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds genuine meaning: it tells the agent how to write `summary` (what the user wanted, what went wrong, what was tried) and implies agent_id is usually unnecessary because it is auto-stamped. This goes beyond the schema's terse field text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (flag a problem to the Sliq team) and clarifies the exact situation that triggers it — something the agent cannot resolve with its own tools after trying. This distinguishes it clearly from siblings like request_user_action, report_enactment_blocked, and retry_blocked_enactment.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit when ('when you're stuck and a human should step in... after trying') and a when-not ('rather than switching subject or channel when you've hit a wall'). It stops short of naming a specific sibling alternative, so it is clear but not fully routed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
estimate_message_tag_classify_scopeEstimate Message Tag Classify ScopeARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| channel | No | 'linkedin' or 'email' — the value you'd pass as `criteria.channel`. Omit for both. | |
| senders | No | In a shared workspace, the teammate email addresses whose sent messages to count. Omit for every sender in the workspace. Any email not in the workspace is dropped; if that leaves no valid teammate, the count is zero. | |
| agent_ids | No | The campaign (agent_tasks) ids the group would track, from list_classifiable_campaigns — the value you'd pass as `criteria.agent_ids`. Omit for every campaign. | |
| positions | No | The conversation positions to count — 'first' (each conversation's first message), 'follow_up' (later messages sent before the prospect replied), and/or 'reply' (messages sent after the prospect replied) — the value you'd pass as `criteria.positions`. Omit for all. | |
| sent_after | No | ISO-8601 timestamp; count only messages sent on or after it. Omit for all time. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, but the description goes beyond them by stating 'Nothing is created or charged' and explaining the simulation semantics ('Only messages at the given conversation positions count, as in a real run'). It could still say more about what happens on invalid/empty scope inputs, but the added context is meaningful.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose, followed by usage guidance and alternatives, then a compact <returns> block naming the exact keys. Every sentence earns its place with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having no output schema, the description supplies a <returns> block declaring the dict shape ({message_count, estimated_credits}), and covers preview semantics, scoping, and cost. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents every parameter (channel, senders, agent_ids, positions, sent_after, on_behalf_of). The description adds the 'as in a real run' scoping note but no syntax or format beyond what the schema provides, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: estimates the first classify run for a message tag group that does not exist yet, returning message count and credit charge. It is clearly distinguishable from classify_message_tag_group (exact figure once the group exists) and from estimate_segment_classify_scope (segment vs. message tag).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use it ('tell the user the cost before creating the group') and names the alternative with its triggering condition ('once the group exists, classify_message_tag_group returns the exact figure'). The agent has a clear routing rule.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
estimate_segment_classify_scopeEstimate Segment Classify ScopeARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| senders | No | The teammate emails whose people the first run would tag, as you'd pass to classify_segment_group. Omit for everyone. | |
| agent_ids | No | The campaign (agent_tasks) ids the segment would track, from list_classifiable_campaigns — the value you'd pass as `criteria.agent_ids`. Omit for every campaign. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, and the description reinforces this with 'Nothing is created or charged,' which is exactly the kind of side-effect disclosure an agent needs before quoting a cost. It also documents the return payload (person_count, estimated_credits). It stops short of richer traits such as whether the estimate is cached or how long it takes, so it is strong but not exhaustive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action in the first line and uses tagged sections for summary and returns, with no filler. The wording is dense but each sentence carries a distinct fact (what it estimates, when to use it, what it costs the caller). Slightly heavy for the amount of content, keeping it at a 4.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although there is no output schema, the description supplies a returns section naming the dict keys, and the summary covers usage, safety, and the pre/post-creation alternative. Combined with 100% schema coverage for the three optional parameters, an agent has everything required to invoke and interpret this call.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already carries full parameter semantics, including the cross-references to list_classifiable_campaigns and list_agency_clients. The description only indirectly gestures at the parameters ('people it would tag', 'campaigns the segment would track'), so the baseline 3 applies rather than a higher score.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('estimate the first classify run of a segment group that doesn't exist yet') and immediately bounds the scope to people tagged and credits charged for tracked campaigns. It explicitly distinguishes itself from the sibling classify_segment_group, which returns the exact figure for an existing group, so an agent can pick correctly without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit when-to-use ('tell the user the cost before creating the group') and names the alternative plus the condition that selects it ('once the group exists, classify_segment_group returns the exact figure for its run'). The pre-creation vs post-creation boundary is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
exa_find_peopleExa Find PeopleAInspect
Use AFTER companies have been identified (company-find step in the same agent, user named a target, CRM lookup, etc.) to surface specific decision-makers. Results are always LinkedIn URLs.
Before composing a search, call get_skill_guide('exa_find_people') —
it owns the query vs criteria split that decides whether
results come back useful or noisy (exclusions in query backfire;
they belong in criteria).
Every person a search returns is checked against its criteria, one
at a time and whichever provider served it, and results holds only
the matches, most senior first; non-matches come back in rejected as
{name, reason} with no URL. Put exclusions in criteria.
Pass company_domain or company_linkedin_url whenever you have one. A
search carrying either is answered by a people database that filters on
that identifier, so it cannot return someone at a same-named company.
Without one, the search falls to a name-matched provider, and the check
is what keeps a same-named company's staff out, reading each person's
current and past roles against criteria.
Both providers read a profile snapshot refreshed periodically, not live reality, so someone who changed jobs since the last crawl can still pass.
Accepts a list of ExaPeopleSearch — pass one for a single-company
lookup, or N for a batch, fanned out in parallel (max 5 concurrent, since
the name-matched provider has shown 5xx instability under bursty load).
Each search routes on its own, so one batch can be served by both
providers. Each call retries once on 5xx inside the HTTP client.
An identifier-pinned search matching more people than can be returned at
once comes back in its own slot as {error, query} asking you to narrow
by titles (and locations, at a multinational); the rest of the batch
is unaffected. Re-send that one search narrowed.
Costs 0.5 Sliq credits per search that returns without error, whichever
provider served it, so an empty result set still charges (the call was
made). Free on BYO Exa (a connected Exa key). Soft-fails on insufficient
credits: the data is returned and the charge is capped at the remaining
balance.
List of per-search result dicts in input order. Each is either
{results: [...], rejected: [...]} on success, or
{error: '...', query: '...'} on failure (after the in-client
retry). results entries are {name, url, summary, verdict, verdict_reason, seniority_level}, most senior first, with url
always a LinkedIn profile URL, summary holding name, current_title,
current_company, location and tenure, verdict 'qualified', or
'unclear' when the profile says too little to judge, and
seniority_level one of c_level, vp, director, senior_manager,
manager, ic, unknown (both null when the check couldn't run).
rejected entries are {name, reason} only.
On insufficient Sliq credits the batch is refused before running: it
raises InsufficientCreditsError rather than returning per-search slots.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | No | Optional — a specific agent to save the checked people into. Omit it to use the running agent, which is the usual case. Only a throwaway lookup outside any agent returns results without saving. | |
| searches | Yes | List of `ExaPeopleSearch` objects. Each has its own `query` + `criteria`. For a single-company lookup, pass a list of one. | |
| list_name | No | Short kebab slug naming the Output-tab list bucket (e.g. 'acme-execs'). When this runs in an agent, the people the check judged are saved and linked as `agent_search_results` person rows automatically, each with its verdict (the rejected ones hidden from the list's default view) — deduped by profile, re-runs update in place; `list_name` names their list, and absent it they land in the 'default' list. Query them by reference with `query_search_results` instead of re-typing URLs from `results`. The `results` return is unchanged either way. | |
| num_results | No | Candidates Exa retrieves and ranks per search before the check runs. Default 10; max 100. Matches cluster at the top of Exa's ranking, so 10 usually holds the right people for a well-formed query; the returned `results` are shorter after the check. If `results` is thin and `rejected` is long, reword the query (the reject reasons say whether and how) rather than raising this — the tail ranks are lower-relevance anyway. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Far exceeds the annotation bar (readOnlyHint=false, destructiveHint=false). It discloses provider routing, per-provider filtering semantics, snapshot staleness, batch fan-out with a 5-concurrency cap, in-client retry on 5xx, per-search cost (0.5 credits, charged even on empty results), BYO-Exa free tier, and soft-fail/capped charging on insufficient credits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and structured via summary/returns tags, but it is very long and repeats the exclusions-in-criteria point across two paragraphs. Dense and useful, yet slightly bloated for a tool description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex, high-stakes tool with no output schema, the embedded <returns> block fully documents the per-search result/rejected/error shapes, field semantics, and the insufficient-credits exception. Nothing an agent needs to call or interpret it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the baseline is 3, but the description adds real meaning beyond it: the query-vs-criteria split, 'put exclusions in criteria', the rationale for passing company_domain/company_linkedin_url, and the single-vs-batch framing of the searches list. It goes beyond repeating the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Find), resource (people on LinkedIn), and scope (one or more company searches), and positions itself precisely in a workflow ('Use AFTER companies have been identified'). An agent can tell this apart from the upstream company-find step without ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use it (after companies identified: company-find step, user-named target, CRM lookup) and adds a mandatory prerequisite, calling get_skill_guide('exa_find_people') for the query-vs-criteria split. This is actionable routing guidance rather than implied context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
exa_search_newsExa Search NewsAInspect
A find-companies News-mode discovery source, used alongside
search_news_events and tavily_search — the News-mode skill owns
the routing (which signals fan out here). Each query retrieves the
top recent news articles matching it, and each article comes back as
pre-extracted company rows (name, domain, one-line event), so unlike
tavily_search there is no article-extraction step. Covers narrative
signals PredictLeads' typed categories miss (AI initiatives,
role-scoped exec changes, plant incidents) and fills its zero-result
gaps on typed events.
Compose queries in positive form describing the event sought, e.g. "manufacturing companies announcing new plant openings in the United States" — one query per signal, all in one call (the tool fans them out in parallel; one tool call is one LLM round). Don't embed exclusions — vector retrieval has no concept of "not", so naming an excluded term biases results towards it.
Each query retrieves the 10 most relevant recent articles. Pass
lookback_days to bound how far back to look (default 30); articles
published before that window are excluded server-side. A news scan
passes the window from its trigger prompt.
Costs 0.5 Sliq credits per query that returns without error, so a batch of 4 queries costs 2 credits; a failed query is free. Free on BYO Exa (a connected Exa key). Soft-fails on insufficient credits: the charge is capped at the remaining balance.
Each company is then checked against criteria, one company at a time,
reading its name and its article's headline and event, and only the
matches come back in companies; the rest come back in rejected as
{name, reason}. It adds a few seconds per ten companies.
When this runs in an agent, every company with a domain is saved to the
Output tab as a company row (deduped by domain) with its verdict,
carrying its article in data.signals — except a company whose domain
is the article's own host, which is often the outlet's domain misread as
the company's. The rejected ones are hidden from the list's default
view.
Dict with companies, the matching companies; rejected, the
companies the check turned down, as {name, reason}; count, every
company found, rejected ones included; and created_identifiers, the
domains of matching companies this call newly added to the list.
Each company row is {company_name, domain, event, headquarters, source_url, title, published_at, query, verdict, verdict_reason} —
verdict is 'qualified', 'unclear' when the row says too little to
judge, or null when the check couldn't run; domain is
lowercased-bare or None when the article didn't reveal one (resolve
those downstream before recording), event is a one-line
description of what happened, source_url/title/published_at
identify the article, and query names the originating query. Rows
are deduped by domain across queries (first article wins).
If every query failed with an upstream error and nothing landed,
also exa_available: False plus a human-readable error — a
source outage, NOT a genuinely empty feed. Partial failure (some
queries errored, others returned) stays silent and reports only
the companies.
On insufficient Sliq credits the call is refused before running —
it raises InsufficientCreditsError instead.
| Name | Required | Description | Default |
|---|---|---|---|
| queries | Yes | One positive-form news query per signal. Pass a list of one for a single-signal lookup. | |
| agent_id | No | Optional — a specific agent to save the companies into. Omit it to use the running agent, which is the usual case. | |
| criteria | Yes | What makes a company a match for this search, in plain words: the event it must show and the kind of company (e.g. "A manufacturer that itself opened or is opening a new US plant"). Each company is checked against it, and the check reads nothing else of your instructions, so make it no narrower than what this call searches for. A role that is part of the signal belongs here; who you'll contact at a matching company doesn't, since the check reads that as a rule about which companies match. | |
| list_name | No | Short kebab slug naming the Output-tab list bucket. Absent, companies land in the 'default' list. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| lookback_days | No | Days of news history to scan (default 30). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: credit cost (0.5/query, failed queries free, BYO Exa free, soft-fail capped at remaining balance), the per-company criteria check and its latency, Output-tab persistence with domain dedup and the article-host exclusion, `exa_available: False` on total outage vs silent partial failure, and `InsufficientCreditsError`. This is unusually thorough behavioral disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Long but organized into summary/returns sections and front-loaded with purpose, then query-composition rules, then cost, then the criteria check and persistence. Almost every sentence carries non-redundant information, though the returns block is dense enough that a little tightening would help.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter, side-effecting discovery tool with no output schema, the description covers the full contract: return keys, row shape and verdict values, dedup rules, failure/outage signaling, cost model, and the credit-refusal path. Nothing an agent needs to invoke or interpret the call is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: `queries` should be positive-form, one per signal, fanned out in a single call, and `criteria` must be no narrower than the search, with the explicit note that a contact role belongs in criteria only when it is part of the signal. `lookback_days` semantics (server-side window, default 30) are also restated with purpose.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource: 'Discover companies from recent news via Exa's semantic news search.' It explicitly positions itself against siblings `search_news_events` and `tavily_search`, and clarifies the key differentiator (pre-extracted company rows, no article-extraction step). An agent can tell what this does and how it differs without opening another schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the alternatives and explains what this source adds ('covers narrative signals PredictLeads' typed categories miss', 'fills its zero-result gaps on typed events') plus concrete query-composition rules (positive form, one query per signal, no embedded exclusions). It stops short of an explicit 'use this when X, use the sibling when Y' decision rule because routing is delegated to the News-mode skill, so not a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
extend_find_searchExtend Find SearchAInspect
Add more results to a named list on an agent.
Reuses the list's original criteria — does NOT support refinement. If the user wants to narrow the criteria, tell them to start a new list (call start_find_search with a fresh list_name).
Refuses when: no webset on file for the list, a run is already in
progress, the per-list cap is already reached, or (sliq mode) the balance
can't afford even one more result. When the balance covers fewer than
requested, the count is clamped and the result carries a clamp_notice —
surface it to the user.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | The agent hosting the list. | |
| list_name | Yes | The slug the list was created with (the `list_name` column of `query_search_results`). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| additional_count | No | How many more results to fetch (default 20). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=false / destructiveHint=false, but the description adds the failure modes, the per-list cap behavior, the balance-clamping rule, and the presence of a `clamp_notice` field the agent must surface. This is unusually rich behavioral disclosure beyond structured data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then constraints and failure modes in terse bullets. Every sentence carries distinct operational information; nothing is redundant padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by disclosing the `clamp_notice` return field and the refusal outcomes. It stops short of describing the general result shape (run id, counts), so it is nearly but not fully complete for an async, credit-consuming operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four parameters (including on_behalf_of and additional_count) are already documented in the schema. The description reinforces list_name semantics indirectly but adds no syntax, format, or bounds detail beyond what the schema provides; baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource+scope: adds more results to a named list on an agent, reusing the list's original criteria. This clearly separates it from start_find_search (new list) and query_search_results (read results), both of which are named or implied in the sibling set.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when not to use it ('does NOT support refinement') and routes to the alternative ('call start_find_search with a fresh list_name'). It also enumerates refusal conditions (no webset, run in progress, cap reached, insufficient balance), leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fetch_linkedin_messages_with_personFetch Linkedin Messages With PersonARead-onlyInspect
Query the local store with search_linkedin_message_history first. Reach for
this tool only when that misses, when you need messages older than the
roughly two-week local backfill window, or when you must be certain whether a
conversation exists — this reads LinkedIn's own synced history, so an empty
result here means no such conversation, not merely "outside the local
window". Fetched messages are written back to the local store, so a later
search_linkedin_message_history sees them too — unless whoever's history
this is keeps only prospects' data and this person isn't one of their
prospects; then they're returned but not stored.
This is not a name-lookup tool: pass the person's LinkedIn provider_id,
sourced from a prospect row, search_linkedin_connections, or a prior local
message — never guessed from a name.
On success, a dict with success=True, chat_id (the chat holding the
newest message; pass to setup_linkedin_sequence with
action_type='message' to reply), count,
truncated (True when older messages exist beyond the fetch cap),
messages — newest first, each with direction
('sent'/'received', or 'unknown' when LinkedIn omits the sender flag),
message_datetime (ISO), and content (truncated to 1000 chars) — and
connection_request_note: the note on your accepted connection request,
which LinkedIn replays into the chat when they accept, as
message_datetime + content (null when there's none). It's the invite,
not a message you sent them, so it's kept out of messages and count. On
failure, success=False and error; an error that mentions
reconnecting means the user must reconnect LinkedIn in Settings.
| Name | Required | Description | Default |
|---|---|---|---|
| provider_id | Yes | The other person's LinkedIn provider_id (the `sender_provider_id` / `recipient_provider_id` on message and connection rows). | |
| on_behalf_of | No | A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only supply readOnlyHint=true, but the description adds substantial behavior beyond that: fetched messages are written back to the local store (with a documented exception for non-prospect histories), empty results mean no conversation rather than a window miss, results can be truncated at a cap, the replayed connection-request note is kept out of `messages`/`count`, and reconnect errors require user action. These are exactly the side effects and edge cases annotations can't convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and the routing rule, then side effects, then parameter sourcing, then returns — a logical order with no filler sentences. It is fairly long, but the length is justified by the tool's complexity and the absence of an output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the `<returns>` block carries the burden and does so thoroughly (success flag, chat_id, count, truncated, message fields, connection_request_note, failure/error semantics). Combined with the side-effect and sourcing notes, an agent has everything needed to call and interpret this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, and the description goes further by specifying how to source `provider_id` (from a prospect row, `search_linkedin_connections`, or a prior local message) and warning never to guess it from a name. That sourcing guidance adds real meaning beyond the schema's field-level descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (fetch) and resource (LinkedIn message history with one person) with scope (full history, live via Unipile). It explicitly distinguishes itself from the sibling `search_linkedin_message_history` and even clarifies what it is NOT (a name-lookup tool), so an agent can separate it from neighbors without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use (after the local store misses, for messages older than the ~2-week backfill, when certainty about conversation existence is required), names the alternative to try first, and states when-not to use it (name lookup). This is the when/when-not/alternatives trifecta.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fetch_post_engagersFetch Post EngagersAInspect
Visibility follows the user's LinkedIn account: any post it can view works, and private or deleted posts return a plain error. The list is capped at 500 reactions + 500 comments per post; on a bigger post the return flags the cut-off. Data fetched within the last 6 hours is served from the database at no cost; a real fetch counts one action against the shared daily engagement-fetch budget and paces its LinkedIn requests, so a post with hundreds of engagers takes around half a minute — set that expectation with the user before calling this on a big post.
When this runs in an agent, every person who engaged is saved as an
agent_search_results person row, rendered in the agent's Output tab and
queryable via query_search_results: data.person carries the name and
headline, data.provider_id the LinkedIn id, and data.columns the
engagement (Engagement, Comment, Post, Post URL). Company pages that reacted
or commented aren't people, and the user isn't a lead, so neither is saved;
both stay in the engagement rows. Re-running updates existing rows rather than duplicating
them. Pass list_name to name their list; absent, they land in the
'default' list.
Each person new to the list is checked against criteria, one at a
time, reading their headline and engagement, and saved with the
verdict; the rejected ones are hidden from the list's default view and
from query_search_results. The check adds about 20 seconds per
hundred people. To keep everyone who engaged, write broad criteria,
such as "Anyone who engaged with this post". A person already on the
list keeps their verdict, so to screen a post's engagers against new
criteria, pass a new list_name.
After this returns, read the saved people with the query_search_results
call in next_step, which is scoped to this post: one list can hold
several posts' engagers. Judge anyone the check kept as unclear or left
without a verdict yourself, then queue outreach for the people who fit
with one batched setup_linkedin_sequence call passing each person's
data.provider_id. Send-time resolution skips anyone already connected,
so they don't need pre-filtering here. Every engager, the rejected ones
included, also stays queryable with query_linkedin_post_engagements
using post_id = <post_analytics_id>.
On success, a dict
{'success': True, 'post_analytics_id': int, 'author_name': str,
'is_own_post': bool, 'post_text_snippet': str, 'from_cache': bool,
'reactions_stored': int, 'comments_stored': int,
'unique_engagers': int, 'truncated': bool,
'budget': {'used_today': int, 'limit': int},
'engagers_preview': [first 10 engagers, unchecked], 'next_step': str,
'list'?: {'list_name', 'created', 'updated', 'total', 'rejected'}}.
unique_engagers counts company pages too; list.total counts only the
people saved, rejected ones included, so it can be lower. list.created
and list.updated count the people the list shows, and list.rejected
the ones the check turned down.
On failure, {'success': False, 'error': str} when the post isn't visible to the
user's account, the daily budget is exhausted, or LinkedIn actions
are paused after a rate limit.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | No | Optional — a specific agent to materialize the engager list into. Omit it to use the running agent, which is the usual case. | |
| criteria | Yes | Who counts as a match, in plain words (e.g. "Heads of Sales at B2B software companies; not recruiters"). | |
| post_url | Yes | LinkedIn post URL (activity, ugcPost, and share forms all work) or a raw activity ID. | |
| list_name | No | Short kebab slug naming the list bucket (e.g. 'launch-post-engagers'). Absent, engagers land in the 'default' list. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only tell us readOnlyHint=false and destructiveHint=false; the description carries the real behavioral load: 500+500 cap with a truncation flag, 6-hour cache, one-action budget cost, request pacing (~30s for thousands of engagers), re-run dedupe, company pages and the user excluded, and the ~20s-per-100-people criteria check. This is rich, non-redundant disclosure beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The prose is long but front-loaded under a <summary> tag with the action and when-to-use first, followed by a structured <returns> block. Nearly every sentence carries operational value, though it is denser than strictly necessary and a few clauses (e.g. repeated queryability notes) could be tightened.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema, the embedded <returns> block fully documents the success dict (ids, counts, truncation, budget, preview, next_step, list stats) and the failure shape with named causes. Combined with criteria/list/agency parameter notes, an agent has everything needed to call and interpret this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description meaningfully extends it: it explains list_name's 'default' fallback and the tactic of passing a new list_name to re-screen against new criteria, plus guidance to write broad criteria to keep everyone. These are usage semantics the schema doesn't state.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Fetch the people who reacted to or commented on a LinkedIn post ... store them as queryable engagement rows') with clear scope (own or anyone's post). It is easily distinguished from siblings like query_linkedin_post_engagements (read engagers) and query_search_results (read saved people), so an agent can tell fetch-and-store apart from query.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use it ('when the user wants to turn a post's engagers into leads', with example utterances), embeds the downstream flow (read via query_search_results next_step, re-query via query_linkedin_post_engagements), and warns to set time expectations before calling on a big post. Failure conditions (private/deleted post, exhausted budget, paused actions) are named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_companies_by_tech_stackFind Companies By Tech StackAInspect
Use ONLY when the user explicitly wants to discover companies BY THEIR
INSTALLED TECHNOLOGY — e.g. "find DTC brands using Shopify and Klaviyo",
"who runs Snowflake AND Looker", "US e-commerce companies using HubSpot".
For hiring-signal discovery (companies posting jobs that mention a tech),
use theirstack_search instead — that surfaces investment intent,
whereas this surfaces installed base.
That installed base is what TheirStack DETECTS from job-posting text, not from crawling storefronts, so it only sees companies that hire and name the tool in their JDs — treat a company's absence as "not detected here," not "not using it" (a storefront-sniffing method like BuiltWith/Wappalyzer would surface more).
For ecommerce-platform tools, results may include the platform's SERVICE
PROVIDERS (agencies, ISVs) alongside actual merchants — job mentions don't
distinguish "we run on Shopify" from "we sell to Shopify merchants," so
name the kind of company you want in criteria and the check filters
them out. Recruiting agencies are always excluded (company_type =
direct_employer), matching theirstack_search.
Costs 1.5 Sliq credits per company returned; a failed search is free.
In chat, STATE THE COUNT AND THE COST in the same reply as the
results — every time, without stopping to ask first. Use the user's
number when they gave one, otherwise the default: "pulled 25
companies — 37.5 credits; say if you want more, max 100." Price the
companies actually returned, which count gives: fewer than limit
costs less. Because this endpoint pages, prefer one page at the
user's number over silently walking offset past it — every extra
page is more credits.
Each company returned is then checked against criteria, one company
at a time, reading its name, firmographics and technologies, and only
the matches come back in companies; the rest come back in rejected
as {name, reason}. It adds a few seconds per ten companies.
When this runs in an agent, every company with a domain is saved to the Output tab as a company row (deduped by domain) with its verdict, carrying its firmographics and matched technologies; the rejected ones are hidden from the list's default view.
The tool resolves each technology name to a TheirStack catalog slug
(calling /v0/catalog/technologies per name; popularity tiebreak; exact
name match wins), then queries /v1/companies/search with
company_technology_slug_and so EVERY supplied technology must be
present on the returned company.
{
"companies": [
{
"id": str, "name": str, "domain": str | None,
"industry": str | None, "employee_count": int | None,
"country_code": str | None, "linkedin_url": str | None,
"technologies_found": [
{"slug": str, "name": str, "confidence": str,
"jobs": int, "last_date_found": str},
...
],
"verdict": str | None, # 'qualified', 'unclear', or None when the check couldn't run
"verdict_reason": str,
},
...
],
"rejected": [{"name": str, "reason": str}, ...], # companies the check turned down
"count": int, # companies in this page, rejected ones included; the number billed
"total_matches": int, # universe size for this query (or None)
"created_identifiers": [str], # domains of matching companies this call newly added to the list
}
On upstream failure (timeout / 5xx / connection error), returns
{"companies": [], "count": 0, "theirstack_available": False}
so the agent can read the flag and degrade gracefully.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Companies this ONE call returns, 1-100 (values outside are clamped), default 25. Pass the user's number when they named one. | |
| offset | No | Number of results to skip for pagination. Pass `limit`, 2*`limit`, ... to fetch subsequent pages. Re-run with the same filters to get a stable order. Each page is billed like a fresh pull, so page only when the user asked for more. | |
| agent_id | No | Optional — a specific agent to save the companies into. Omit it to use the running agent, which is the usual case. | |
| criteria | Yes | What makes a company a match for this search, in plain words: the kind of company you're looking for (e.g. "Consumer brands that sell through their own Shopify store, not agencies or app developers"). Each company returned is checked against it, and the check reads nothing else of your instructions, so make it no narrower than what this call searches for. A role that is part of the signal belongs here; who you'll contact at a matching company doesn't, since the check reads that as a rule about which companies match. | |
| industry | No | Industries the company must match, picked from this parameter's list of TheirStack industry names (e.g. ["Retail", "Higher Education"]). A name also matches its sub-industries: "Manufacturing" covers "Motor Vehicle Manufacturing". Some parents are broad ("Consumer Services" includes non-profits and religious institutions), so name a parent only when all of it fits and list specific industries otherwise. | |
| list_name | No | Short kebab slug naming the Output-tab list bucket (e.g. 'shopify-brands'). Absent, companies land in the 'default' list. | |
| confidence | No | TheirStack tech-detection confidence band. Pass ["high", "medium"] to drop one-off / stale mentions. Defaults to None (all confidence levels). Valid values: "high", "medium", "low". If a confidence-filtered search returns zero matches for a technology that plausibly has users, retry without `confidence` and judge per-company strength from `technologies_found[].confidence` instead — TheirStack's confidence aggregates are intermittently incomplete for some technologies. | |
| company_city | No | City/state substrings the company HQ must match (e.g. ["Atlanta", "Chicago", "Washington"]). Case-insensitive substring match, OR-combined across the list. Do NOT include the `(?i)` flag — TheirStack rejects it on this field. Pair with `country_codes` to keep matches scoped. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| technologies | Yes | Human product names — e.g. ["Shopify", "Klaviyo"]. Use canonical product names; names with no catalog match raise ModelRetry, and ambiguous names resolve to the most popular match. AND semantics: the company must use ALL supplied technologies. Max 10 per call. | |
| country_codes | No | ISO alpha-2 HQ country filter. Pass ["US"] to scope to US companies — usually the right default. | |
| min_revenue_usd | No | Minimum company annual revenue in USD (e.g. 10_000_000 for $10M+). | |
| industry_excludes | No | Industries to exclude, sub-industries included, from the same list as `industry`. | |
| max_employee_count | No | Maximum company headcount. Defaults to None; consider passing e.g. 5000 if "uses Shopify" alone returns 10K+ matches dominated by enterprise outliers. | |
| min_employee_count | No | Minimum company headcount. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare destructiveHint=false, so the description carries the behavioral burden and does so richly: it discloses that detection comes from job-posting text (absence != not using), that ecommerce results may include agencies/ISVs, that recruiting agencies are always excluded, the 1.5 credits/company cost model, that a failed search is free, the per-company criteria check cost in seconds, the save-to-Output-tab dedupe behavior, and graceful degradation via theirstack_available=False.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads purpose and the sibling routing well, but the cost/count protocol (state count and cost every time, price the companies actually returned, page only when asked) is restated across several sentences and the embedded <returns> block duplicates what a returns schema would normally carry, making it longer than the task requires.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 15-parameter mutation-adjacent search with no output schema and no annotation coverage of behavior, the description covers identification limits, cost accounting, criteria filtering, result landings, and failure mode — nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description still adds value by tying count/cost to the actual returned count, warning to prefer one page at the user's number over walking offset, and reiterating AND semantics and catalog-slug resolution for technologies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Find companies whose technographic profile matches a target tech stack') and immediately distinguishes itself from its closest sibling by contrasting 'installed technology' discovery against theirstack_search's hiring-signal discovery. An agent can route between the two without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit 'Use ONLY when the user explicitly wants to discover companies BY THEIR INSTALLED TECHNOLOGY', with concrete trigger examples ('find DTC brands using Shopify and Klaviyo'), plus an explicit exclusion routing to theirstack_search and the condition that selects it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_emailFind EmailARead-onlyInspect
Costs 1 Sliq credit per verified email; a BYO-Apollo key makes the Apollo
leg free. The default chain runs MoltSets first and fills its misses with
Apollo, so a contact that comes back {'error': ...} has been tried by both
providers and is final — report those gaps and move on rather than
re-running them.
When only one provider ran — a BYO-Apollo user's default, or an explicit 'apollo' pin — the other is still untried, and the result says so. That paid lookup is the user's call: in a background run with no one to answer, report how many were missed and stop; in an interactive chat, tell them the count and offer a MoltSets backfill (1 credit/hit), re-calling with provider='moltsets' on just those contacts only after they say yes.
A contact this user already resolved in the last 24h is served from a cache,
so re-calling does not look up or charge again.
One entry per input contact, in input order — either a hit dict
(email, email_status, name, title, linkedin_url) or an
{'error': ...} miss — followed by any warnings.
Each verified email is also written for you onto the person's canonical
profile if it had none, which is the address every campaign tracking that
person sends to and the one the Email column on the Output tab shows
wherever they're on a list — you do not match or persist emails back onto
rows yourself. It adds no one to an agent's list: when a person found here
who isn't on it yet belongs there, record them with record_search_results.
| Name | Required | Description | Default |
|---|---|---|---|
| contacts | Yes | People to look up. Each needs a name, plus either an apollo_id or at least one of linkedin_url, company_name, or company_domain. | |
| provider | No | Leave unset (default) to let the chain pick — almost always right; don't pin a source the user didn't ask for. Set 'apollo' or 'moltsets' to pin a single source: 'apollo' runs Apollo alone (free with BYO Apollo, 1 credit/hit on Sliq's key); 'moltsets' runs MoltSets alone (1 credit per hit). Use 'moltsets' for the backfill the user approved after a run where only Apollo went out. Pinning skips the fallback, so coverage may be lower. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint, so the description carries the real burden and does: per-hit credit cost, the free Apollo leg with a BYO key, the 24h result cache that prevents re-lookup and re-charge, provider-chain fallback semantics, and a genuine side effect (verified emails are written to the person's canonical profile and it does not add people to an agent's list). This is well beyond what the annotation conveys.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and the summary/returns structure keeps the dense operational detail navigable. It is long, and the MoltSets-backfill instruction is restated from the `provider` schema description, which is mild redundancy rather than waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the embedded returns block describes the exact response shape (one entry per contact in input order, hit dict keys, error misses, trailing warnings) plus the profile-write side effect and the routing to `record_search_results` for adding people to a list. Nothing material is left for the agent to guess.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real meaning: why `provider` should normally be left unset, what each pinned value costs, and that pinning skips fallback and lowers coverage. `contacts` batching and the parallel-lookup behavior are also described. It adds less for `on_behalf_of`, keeping it off a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ("Find email addresses for one or more people") and immediately clarifies the single-contact vs batch calling pattern, so an agent knows exactly what the tool returns. It does not explicitly differentiate itself from near neighbors like apollo_enrich or find_phone_number, so it stops short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use guidance (leave `provider` unset; don't pin a source the user didn't ask for), names the conditions that select alternatives (BYO-Apollo key, explicit 'apollo' pin, MoltSets backfill), and prescribes behavior for both unattended and interactive runs, including the user-approval gate before backfilling. Miss handling is spelled out precisely: report `{'error': ...}` contacts as final rather than re-running.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_linkedin_urlFind Linkedin UrlARead-onlyInspect
For email outreach where the agent might later add LinkedIn touches, prefer
setup_email_sequence(enrich=True) over calling this tool separately —
enrich folds the lookup into the queue boundary so the resulting tracking
row carries both identifiers.
Costs 1 Sliq credit per verified hit. Free when the user has connected
their own Apollo key.
Dict with success, linkedin_url, and person fields when verified;
{success: False, error: ...} when Apollo returns no match; a 429
throttle adds rate_limited: True (transient — retry later, not a
permanent miss).
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Optional full name to narrow the Apollo match. | |
| Yes | Email address to look up. | ||
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, so the description carries the rest. It adds cost semantics (1 Sliq credit per verified hit, free with the user's own Apollo key), the 429 rate-limit behavior as transient vs. a permanent miss, and empty-match handling — real behavioral traits not in the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose in the first line and uses summary/returns tags to separate concerns. It is denser than strictly necessary — the mixed-channel justification runs long — but nearly every sentence informs invocation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the returns block fully specifies the success shape, the failure shape, and the transient rate-limit flag. Cost, account-scoping, and error behavior are all covered, leaving nothing an agent needs to call this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and each parameter is documented in-schema. The description mentions the email input and on_behalf_of's account context, but adds no format or syntax detail beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource+input: 'Find a LinkedIn profile URL for a person given their email, using Apollo.io.' It then distinguishes itself from siblings by naming find_email as its inverse and find_phone_number as a consumer of its output, so an agent can place it precisely in the tool graph.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit scenarios (mixed-channel outreach, supplying the URL find_phone_number requires) and names a preferred alternative — setup_email_sequence(enrich=True) — with the condition that selects it. When-to-use and when-to-prefer-an-alternative are both covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_phone_numberFind Phone NumberARead-onlyInspect
Pass a list of LinkedIn profile URLs — one entry for a single person, all of
them at once for a batch (they are looked up in parallel, costing one tool
call against the loop guard, not N). Returns one result per URL in the same
order. Airscale brokers providers like RocketReach. Persists nothing — use
the returned numbers however the agent needs them (e.g. tell the user, or
stash them on prospects via update_prospect).
A LinkedIn profile URL is the only accepted input — this tool cannot look a
person up by email or name. When a record (a HubSpot/CRM contact, a prospect
row, a spreadsheet line) has no LinkedIn URL but does have the person's
email, first call find_linkedin_url(email=...) to resolve one, then pass
that URL here. Apply this resolve-then-lookup step to every record, not just
the first; skip the phone lookup for any person you cannot get a URL for.
Costs 8 Sliq credits per number found, when run on Sliq's shared Airscale
account. Misses (no number on file) and upstream failures are free. A
worst-case check runs up front on the Sliq path: the whole batch is refused
unless the balance covers 8 credits per URL (any URL could be a hit). So
no API call is ever spent on a lookup that can't be billed, and a user low
on credits is told to top up or connect their own key. If the user has
connected their own Airscale key (BYO, via the integrations page), lookups
run against that account instead — no Sliq credit gate and no Sliq credit
charge.
A list of result dicts in input order.
Per hit: {status: 'success', found: True, phone_number, phone_numbers, provider, linkedin_url, credits_charged: 8} (0 on the BYO path) —
phone_number is the first number, phone_numbers the full list.
Per miss: {..., found: False, phone_number: None, phone_numbers: [], credits_charged: 0}. A malformed (non-LinkedIn) URL or
an upstream failure on one URL becomes a per-item {found: False, error, credits_charged: 0} in that slot — it never aborts the rest of the batch.
If the up-front worst-case credit check fails, it raises
InsufficientCreditsError (no lookups are attempted).
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| linkedin_urls | Yes | LinkedIn profile URLs, e.g. ["https://www.linkedin.com/in/janedoe"]. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare only readOnlyHint=true, and the description adds rich context beyond that: 8 credits per hit, free misses/failures, a worst-case up-front credit gate that raises InsufficientCreditsError, the BYO-key path with no Sliq charge, and one loop-guard call for a parallel batch. The <returns> block fully documents per-hit, per-miss, and per-item error shapes. The one gap is that it doesn't state outright that 'persists nothing' is consistent with the read-only annotation, but the coverage is strong, so I'd place this at 4 rather than 3.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded and every paragraph covers distinct territory (inputs, costs, BYO path, returns). It is dense and slightly verbose — credit semantics appear in two places and the returns block is lengthy — but for a tool with this many operational caveats the length is largely justified.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No structured output schema exists, yet the <returns> block fully specifies hit, miss, and per-item error shapes plus the InsufficientCreditsError path. Combined with the cost model and resolve-then-lookup workflow, an agent has everything needed to invoke it correctly and interpret results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are documented in the schema and the baseline is 3. The description adds value by explaining the array semantics — one entry for a single person, the full list for a batch, results returned in input order and looked up in parallel — which the schema's terse 'LinkedIn profile URLs, e.g. [...]' does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (find phone numbers), the exact input it consumes (LinkedIn profile URLs), the backing provider (Airscale/RocketReach), and its batch scope. An agent can distinguish it from find_email and find_linkedin_url without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states the constraint (LinkedIn URL is the only accepted input, not email/name), and names the alternative and the exact condition that selects it: call find_linkedin_url(email=...) first, then pass the URL here. It even mandates applying that step to every record, not just the first.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_warm_intro_pathsFind Warm Intro PathsARead-onlyInspect
Pass targets as a list of LinkedIn identifiers — profile URLs, username
slugs, or provider_ids — for the people the user wants to reach. Runs in the
background, pacing 1-5 minutes between targets to stay within LinkedIn's
safety limits; results land as one row per target under list_name in the
agent's entity store — read them with query_search_results(agent_id, where_clause="list_name = '<list_name>'"). Each row carries the mutual
connections — the shared 1st-degree connections who can introduce the user.
For each target the search returns at most 10 mutual connections. A target that comes back with all 10 may have more the search didn't surface — treat that target's list as a sample rather than the complete set, and say so when you report it.
list_name is the bucket the target rows land in. Pass a short descriptive
slug naming this set of targets (e.g. 'acme-intros', 'series-a-leads');
distinct slugs let one agent hold several independent warm-intro searches,
and reusing a slug accumulates into one list.
Each target costs 1 profile lookup + 1 LinkedIn search against the daily budgets (~50-60 lookups for an established account, a quarter of that while a new one ramps up; the LinkedIn search budget is shared with all people searches), plus 1 credit only when a mutual connection is found for that target. An already-connected target with shared connections still costs that credit — the warmer path is still worth surfacing. Checking a target also links it to its primary employer's company, which may need a one-time web-domain lookup: 0.05 credits the first time a given company is looked up (cached after, so it never charges twice for the same company). Targets beyond today's budget are deferred — re-run tomorrow to continue. Confirm with the user before passing a large list. Only one warm-intro search can run at a time per account. A status dict in one of three forms. {'success': True, 'status': 'running', ...} on kickoff {'success': True, 'status': 'queued', ...} when another warm-intro search already holds the account's single slot — this one is queued behind it and starts automatically when the active one finishes {'success': False, 'error': str} on validation / budget failure
| Name | Required | Description | Default |
|---|---|---|---|
| targets | Yes | LinkedIn identifiers (profile URLs, slugs, or provider_ids) of the people to find warm-intro paths to. | |
| list_name | Yes | short kebab slug naming this set of targets. Required. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Extremely rich: discloses background execution, 1-5 minute pacing to respect LinkedIn safety limits, per-target credit costs, daily budget consumption and deferral, the one-at-a-time account slot with queuing, and the 10-mutual-connection sampling caveat. This goes well beyond the lone readOnlyHint annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a summary, then organized by topic (targets, background/read-back, sampling caveat, slug semantics, costs). It is long but nearly every sentence carries operational weight; only minor trimming is possible.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex async, credit-consuming, budget-limited tool with no formal output schema, the description covers return states (running/queued/error via the returns block), costs, deferral, and result retrieval. Nothing an agent needs to invoke it correctly appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: it clarifies target identifier formats, explains that list_name is a bucket slug whose reuse accumulates results, and points to list_agency_clients for on_behalf_of. It adds value beyond the schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb + resource + scope: find which of the user's 1st-degree LinkedIn connections can introduce them to each target person. This is clearly distinguishable from siblings like search_linkedin_connections and search_linkedin_people, and the opening summary line makes the goal unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains what targets to pass, that it runs in the background with pacing, and directs the agent to read results via query_search_results. It advises confirming with the user before a large list. However, it never explicitly contrasts this with alternative people/connection search tools, so the when-not guidance is incomplete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
generate_monitored_post_commentGenerate Monitored Post CommentADestructiveInspect
Prefer this over queue_linkedin_post_engagement (with your own text) when acting
on a post from the agent's monitored-posts feed. Pass a row id from
query_monitored_posts. Only posts with no queued comment are eligible
('filtered', 'skipped', 'skipped_no_credit', 'captured', 'surfaced');
already-commented, queued, or user-rejected posts are refused.
A queued-status dict in one of three forms.
{'queued': True, 'awaiting_approval': bool, 'status': 'queued', 'draft_comment': str, 'agent_id': int, 'message': str}
on success (1 credit charged); agent_id is the agent monitoring the post
{'queued': False, 'deduped': True, 'message': str} if a comment is already queued
{'queued': False, 'error': 'not_found'|'not_eligible'|'out_of_credits'|'no_draft'} otherwise
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| monitored_post_id | Yes | The `id` of a monitored_posts row (from query_monitored_posts). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial behavior beyond the annotations: 1-credit charge, feed status flip to 'queued', dedupe behavior, refusal cases, and detailed return/error forms. These disclosures are critical for a destructive, credit-consuming mutation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action and then provides eligibility and return details in a structured summary/returns format. The length is justified by the mutation, credit charge, and multiple result states.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description supplies detailed return shapes and error states. Combined with eligibility rules, cost, and side effects, it is complete enough for an agent to call and interpret results correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents both parameters. The description reinforces that monitored_post_id comes from query_monitored_posts but adds little parameter-level meaning beyond the schema, and it does not elaborate on on_behalf_of.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: draft and queue a comment for one monitored post. It also distinguishes the tool from queue_linkedin_post_engagement and identifies the feed/source context, so an agent can select it without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use guidance, names the preferred alternative, explains the source of monitored_post_id, and lists eligible versus refused post states. This is enough to decide use versus non-use.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_agent_detailsGet Agent DetailsARead-onlyInspect
list_agents returns only one-line summaries — call this to get the full
picture for a specific agent. Run-level history is
returned here as recent_runs; each entry carries its run id, which you
pass to get_run_transcript for that run's full tool-by-tool transcript.
Agent-specific operational learnings are returned as a top-level learnings
list (managed via append_learning).
Dict with agent fields, workspace, learnings, recent_runs, recent_runs_truncated
(True when older runs remain past this page of recent_runs), and tracking_summary.
When the agent has prospects, tracking_summary.funnel carries the derived outreach
rollup the agent workspace shows — per-channel funnels, connection
acceptance_rate, reply_rate, and the people-level lead_funnel — so read rates
and lead buckets from there rather than deriving them off the raw stage counts.
lead_funnel.reached holds the numbers on the funnel's rungs (per rung, everyone who got
at least that far); its other stage keys count only the people currently at that stage.
mcp_changes lists changes made outside Sliq by the user's or a teammate's MCP clients
(such as Claude) calling Sliq tools directly, newest first, one entry per burst of calls
to one tool: id, tool_name, client_label (with the teammate's email when a teammate
made it), count, first_at, last_at, and latest_arguments (the burst's latest
call's arguments). Pass an entry's id as change_id to
get_mcp_change_calls to read every call in the burst.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | ID of the agent to load | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| recent_runs_limit | No | How many of the most recent runs to include (default 10, max 50) | |
| recent_runs_offset | No | Runs to skip for paging the recent_runs list (default 0). When `recent_runs_truncated` is True, re-call with recent_runs_offset += recent_runs_limit to page into older runs. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safety profile is covered. The description goes beyond that with real behavioral context: pagination via `recent_runs_truncated`/offset, the distinction that `lead_funnel.reached` counts cumulative rungs while other stage keys count current people, and where MCP-originated changes are surfaced. It does not discuss auth or rate limits, but for a read call that is minor.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the summary and the sibling differentiation before the long `<returns>` block, which is organized and mostly earns its space given no output schema exists. The returns section is verbose, but the detail is functional rather than padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the full burden of explaining the return shape and does so thoroughly: field inventory, truncation flag, funnel rollup semantics, and cross-tool id routing. Nothing an agent needs to interpret or follow up on the response is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four parameters (including `on_behalf_of` and the paging pair) are already fully documented in the schema. The description adds no parameter-level syntax or semantics beyond what the schema provides, matching the baseline of 3 when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Load full details for a single agent') and enumerates the payload (workspace, learnings, recent runs, tracking summary). It explicitly contrasts itself with `list_agents`, which returns only one-line summaries, so the agent can select between siblings without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit trigger ('when the user asks about a specific agent's progress, history, or contents') and names the alternative (`list_agents`) with the condition that selects this tool. The chained-tool routing (run id -> get_run_transcript, change id -> get_mcp_change_calls) further clarifies when to reach for this call.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_campaign_flowGet Campaign FlowARead-onlyInspect
sequence is the authored DAG (null for a flowless campaign): sequence.start
is the origin node and sequence.nodes the body nodes, each with its id, label, and
kind (start / connection_request / send / action / manual / gift / automation / decision /
terminal) plus that kind's typed routing — a connection_request's accepted /
already_connected / timeout / message_templates (its note), a send's channel / action / replied / follow_up /
message_templates (two while it A/B tests a second version), a silent action's channel / action / then, a manual node's
action_description / then, a gift node's product_url / max_price_usd / gift_note /
then, an automation node's then (its side-effect is authored on
its on_enter trigger), a decision's rule / arms, a terminal (a sink). Timed edges carry {value, unit} (unit
w/d/h/m). A
connection_request node is always LinkedIn (its channel/action are implicit in its kind);
the resolve stage is the action == 'resolve' silent-action node — it looks each profile
up, then the campaign flows onward. Walk sequence to see which step feeds which and what
gates each hop; join it with nodes by node id for per-node occupancy.
nodes is the per-node occupancy map, keyed by node id: by_status, current, blocked,
pending, removed, and off_sequence. It covers every sequence node plus any foreign id
the queue throughput or the cursor carries. The start node's current counts every prospect
not yet routed onto any node. removed counts prospects removed from the campaign at that
node (skipped on its channel, or on every channel for a node without one): they still rest
there, and are left out of current, blocked, and pending. off_sequence is True for a node id prospects rest on that the
current sequence no longer defines — a position an earlier flow edit stranded — surfaced so
those prospects stay visible (such an id is absent from sequence, so read its counts here).
pause is the per-scope pause breakdown (null for a non-outreach agent): campaign.paused
holds every action node; linkedin.reason (needs_reconnect / auto / manual, with until
on an auto rate-limit pause) holds every LinkedIn action node while email stays live;
connection_request.until holds only connection_request nodes; and steps.paused lists the
node ids whose own step is paused — nothing new is composed there until it resumes, though
sends already queued still go out. Read it to answer "why is
step 3 not firing" per node — a paused scope explains a node with pending occupants but no
throughput.
Dict with sequence (the authored DAG as stored, or null for a flowless campaign),
nodes {node_id: {by_status, current, blocked, pending, removed, off_sequence}}, and pause
(per-scope pause breakdown; see above). blocked is how many resting prospects have a
blocked enactment (see report_enactment_blocked; clear one for retry with
retry_blocked_enactment); pending how many rest on an action/manual/decision node
whose step hasn't run for them yet — for a manual node, awaiting the human who performs
it — no intervention needed, unlike blocked. Null sequence and empty nodes for a
flowless campaign with no queue activity.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | ID of the agent (campaign) to read. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With only readOnlyHint in annotations and no output schema, the description carries the full behavioral burden and does so richly: it explains that `removed` prospects still rest at a node but are excluded from `current`/`blocked`/`pending`, that `off_sequence` ids are absent from `sequence` yet visible in `nodes`, and that paused steps compose nothing new while queued sends still go out. These edge-case semantics are exactly what an agent needs and are absent from structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded and uses backticks/sections well, but the body is an extremely dense wall of text covering every node kind and routing field. Much of that field-by-field enumeration is verbose relative to the decision an agent needs to make, so it is longer than the call warrants.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex, read-only tool with no output schema, the description fully covers the return shape (`sequence`, `nodes`, `pause`), the flowless-campaign edge case (null `sequence`, empty `nodes`), and cross-references to related tools (report_enactment_blocked, retry_blocked_enactment). Nothing needed to interpret the result is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description adds a genuine nuance beyond the schema by noting the read is not limited to the agent in context and can target any of the user's campaigns. The `on_behalf_of` agency-client behavior is fully covered by the schema itself.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Read the live Campaign Flow for an agent') and immediately scopes the payload: the authored `sequence` DAG, per-node occupancy (`current`), and queue throughput (`by_status`). It is clearly distinguishable from siblings like get_agent_details or get_node_history because it defines its own output model.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete when-to-use triggers ('where are my prospects in the campaign', 'how many are stuck at the connect step', 'why is step 3 not firing') and clarifies scope ('Pass any of the user's campaigns; it is not limited to the agent in context'). It does not, however, name alternative tools (e.g., get_node_history for history) or state exclusions, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_chat_messagesGet Chat MessagesARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of messages to return (default 50, max 100). Most recent messages are returned. | |
| offset | No | Source messages to skip for paging (default 0). offset counts the underlying session messages (newest first), so re-call with offset += limit to page into older history while `has_more` is True. | |
| session_id | Yes | The ID of the chat session to retrieve messages from. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already declares the safety profile, so the bar is lower. The description adds genuine behavioral context the annotation does not: the return payload shape, the meaning of `has_more`, and that older messages remain pageable. It does not mention auth or per-account constraints beyond what on_behalf_of's schema already says.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary sentence with the routing instruction second, then a compact returns block. No filler, and the structured tags make the output contract scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description carries the full return-value burden and does so with the returns block (title, count, has_more, message fields). Combined with schema-documented pagination, an agent has everything needed to call and consume this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so limit, offset, session_id and on_behalf_of are already fully documented, including the paging semantics of offset. The description adds no parameter-level detail beyond the schema, which is the expected baseline when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Retrieve readable messages from a past chat session") and implicitly distinguishes itself from search_chat_history by positioning itself as the read step after discovery. An agent can tell which of the two chat tools to call without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs to call search_chat_history first to obtain the session_id, which is a real precondition and routes the agent correctly. It stops short of stating when *not* to use this tool (e.g., live/active sessions or non-chat message sources), so it is clear context without full exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_credit_usageGet Credit UsageARead-onlyInspect
Current Sliq credit balance, 30-day spend by agent, the card saved for gift purchases, and — for a workspace member — the shared team block.
Returns {'balance': float, 'usage_by_agent': [{'agent_id', 'title',
'credits_used', 'is_agent'}, ...], 'gift_card': str, 'team': None |
{'workspace_name', 'your_role', 'seats_used', 'seats_active', 'seat_limit',
'per_member_spend': [{'user_email', 'credits_used_30d', 'is_you'}, ...]}}.
gift_card is the label of the card the user saved under Settings → Billing
for gift purchases (e.g. 'Visa •••• 4242'), or '' when no card is saved.
balance/usage_by_agent are the same rollup the Billing page renders;
spend not tied to an agent groups by activity label (e.g. 'Standalone chat', 'Data
extraction'). team is None for solo users and otherwise mirrors the
Billing team card: seats_used counts invited+active seats (spoken for),
seats_active only active ones. per_member_spend excludes removed members
and unattributed charges, so it does not sum to pool spend — it is spend over
the last 30 days.
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, and the description goes well beyond that: it states balance/usage_by_agent mirror the Billing page rollup, that spend without an agent groups by activity label, that team is None for solo users, that seats_used counts invited+active while seats_active counts only active, and that per_member_spend excludes removed members and unattributed charges so it does not sum to pool spend.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but front-loaded with a single-sentence summary, then a return-shape block, then field-by-field semantics. Density is justified because there is no output schema to carry the return contract, though a couple of clauses could be trimmed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and a nested, non-obvious return value, the description fully documents the response keys and their edge-case semantics (empty gift_card string, None team, attribution rules). An agent has everything needed to interpret the result correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single parameter on_behalf_of already has a thorough schema description, so the baseline of 3 applies. The prose adds nothing about the parameter, though it does helpfully frame what the returned data represents when acting on behalf of a client.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence enumerates exactly what is retrieved — credit balance, 30-day spend by agent, saved gift card, and the team block — naming a very specific resource that cannot be confused with siblings like check_email_daily_usage or check_linkedin_daily_usage.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied: an agent reading 'balance', 'spend by agent', 'per_member_spend' will infer this is the billing/credit lookup tool, and the schema's on_behalf_of note implies the agency-client case. But the description never states when to reach for this instead of related usage tools, nor any preconditions or exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_email_attachmentGet Email AttachmentBRead-onlyInspect
Provide email_data_id + attachment_id (from search_emails results). Dict with filename, mimeType, size, and url (CloudFront URL for the file)
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| attachment_id | No | The attachmentId from the attachments metadata | |
| email_data_id | No | ID of the email_data record (from search_emails) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description says the tool will 'upload it to S3,' which is a write/side-effect operation, while the annotations declare readOnlyHint=true. This directly contradicts the annotation and misrepresents the tool's mutation profile. Per the rubric, a contradiction with annotations scores 1.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Very concise and front-loaded: the first sentence states the action and result, the second gives usage context, and a short returns block follows. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple fetch-and-upload tool with no output schema, the description supplies purpose, usage context, required IDs, and return fields. It is nearly complete, but the readOnly/upload contradiction leaves the behavioral profile ambiguous.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all three parameters, including on_behalf_of, attachment_id, and email_data_id. The description adds minor context by repeating that email_data_id and attachment_id come from search_emails results, but this adds little beyond the schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: fetching an email attachment and uploading it to S3 to return a URL. It is clear what the tool does, but it does not differentiate itself from similar siblings such as read_email_attachment or read_attachment.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear context: 'Use this when you need to access a file attached to an email.' It also tells the agent which IDs to supply and where they come from. However, it does not state when not to use this tool or name alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_emailsGet EmailsARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| ids | Yes | List of email IDs from search_emails results (max 20). Pass the same on_behalf_of the search used. | |
| on_behalf_of | No | A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safety profile is covered. The description adds real behavioral value by describing the return shape and the 6000-char content truncation, which the agent could not infer from the schema. It omits batch-failure or rate-limit behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short, front-loaded statements with a dedicated returns block and zero filler. Every sentence carries actionable information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter read tool with annotations and no output schema, the description covers purpose, sequencing, and return shape/truncation, which is nearly everything needed. Minor gaps like how partial ID failures are reported keep it from a 5.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with both ids (max 20, must match the search's on_behalf_of) and on_behalf_of documented in the schema. The description adds no parameter-level detail beyond what the schema already provides, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Fetch full email content by ID') and explicitly positions it relative to search_emails, so an agent can distinguish the read-after-search tool from the search tool itself.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says to use it after search_emails to read emails needing closer inspection, giving a clear invocation context. It does not state a when-not condition or name other alternatives, which keeps it short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_integration_statusGet Integration StatusARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint, but the description adds substantial behavior: the needs_reconnect flag on linkedin/google_calendar/fathom, the different meaning of 'email' per integration, the fact that a missing key means 'not checked' rather than disconnected, and the dual slack flags. This is exactly the context an agent cannot get from the annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary followed by a returns block, and every sentence carries real information (edge-case flags, email semantics, missing-key semantics). It is long but dense rather than padded; minor verbosity keeps it from a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description correctly carries the full burden of documenting the return shape, including per-integration exceptions and the missing-key caveat. Nothing needed to interpret the result is absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single on_behalf_of parameter is fully documented in the schema itself. The description does not add syntax or behavioral detail about that parameter, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource — reports which integrations the account has connected — and defines the keying convention (same integration_id passed to get_tool_connect_url). An agent can immediately tell this apart from the connect-link sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit trigger conditions: call before telling a user an integration is unavailable, and before minting a connect link for one they claim is already connected. It also names the related tool by id, so routing is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_linkedin_monitorsGet Linkedin MonitorsARead-onlyInspect
Default (no monitor_id): one summary per monitor with members_count (the
number of watched LinkedIn profile and company page URLs — list mode only, 0 for network/topic)
and members_sample (the first 5 URLs), not the full roster, since a list
monitor can watch thousands. To read a monitor's full roster, pass its id
(returned on each summary) as monitor_id; that returns the URL list paged by
offset/limit with a truncated flag — re-call with offset += limit while
truncated. The URLs are in the exact shape setup_linkedin_monitoring takes
back. run_weekdays is weekday ints Mon=0 … Sun=6 in the user's timezone.
last_run_at is null before the monitor's first run.
By default, a dict
{'monitors': [{'id', 'name', 'mode', 'is_active', 'members_count',
'members_sample', 'keywords', 'writing_instructions', 'post_filter',
'draft_comments', 'fetch_engagers', 'capture_authors', 'engager_icp',
'engager_instructions', 'run_weekdays', 'recency_days', 'last_run_at'}, ...]} — an empty list if the
agent has no monitors. With monitor_id: {'monitor_id', 'name',
'members_count', 'members' (this page's URL list), 'offset', 'limit',
'truncated'}. post_filter is the user's own description of which posts
the monitor keeps (blank keeps every post). engager_icp
filters who capture collects (blank means everyone) and engager_instructions
is what to do with them (blank means take no action); both are blank when
fetch_engagers and capture_authors are both off, and both need re-passing when
re-arming capture.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max roster URLs per drill-down page (default 200, capped at 200). | |
| offset | No | Roster rows to skip when paging a monitor_id drill-down (default 0). | |
| agent_id | Yes | The agent whose monitors to read. | |
| monitor_id | No | Drill into one monitor's watched-profile roster by its `id`. Omit for the per-monitor summary of all monitors on the agent. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=true; the description adds substantial non-obvious behavior: the default omits the full roster and returns only members_count/members_sample (first 5) because a list monitor can watch thousands, the truncated flag drives paging, empty list when no monitors, last_run_at null before first run, and run_weekdays isn't disclosed elsewhere. This is exactly the extra context annotations can't carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads purpose in the <summary> and separates returns into a distinct block, with no filler sentences. It is dense and slightly long, but with no output schema the returns prose is load-bearing rather than redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read tool with 5 parameters, no output schema, and no nested objects, the description supplies both invocation semantics (summary vs. drill-down, paging) and a full return-shape breakdown including blank-field meanings. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: monitor_id is a drill-down key whose value comes from each summary's 'id', offset/limit are a paging pair with a specific retry protocol, and run_weekdays is documented as weekday ints Mon=0…Sun=6 in the user's timezone. It does not add anything for on_behalf_of beyond the schema, keeping it from a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ("Read the LinkedIn post monitors configured on an agent") and immediately enumerates the fields exposed (mode, schedule, writing instructions, active actions). It implicitly distinguishes itself from the write-path sibling setup_linkedin_monitoring, so an agent can tell read from write without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit operational guidance: read the current config with this tool before editing because setup_linkedin_monitoring overwrites fields not re-passed, and carry forward unchanged values. It also gives the exact when-to-use condition for monitor_id (full roster drill-down) vs. the default summary, plus the offset += limit retry loop.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_linkedin_postsGet Linkedin PostsARead-onlyInspect
RECENCY WINDOW: only posts from the last posted_within_days days (default 7)
are returned — this tool answers "what has this person posted lately", not
"show me their post history". A profile with nothing in that window comes back
with profiles[<key>]['status'] == 'no_recent_posts' and no posts for that
profile — that is normal and does not mean the lookup failed. In that case,
personalize off the prospect's role, company, or headline instead of forcing a
stale post reference; do not widen posted_within_days just to find something
to quote unless the user asked for older posts specifically.
Only the profile's own original posts count as "recent activity" — reshares and quote-posts are excluded.
ALWAYS BATCH: pass every profile URL in ONE call — batching is both cheaper and
faster than one call per profile. Up to 1000 profiles per call; split a larger
list across calls. Each returned post carries a profile_input field identifying
which profile it came from (the matched input identifier).
COST: 0.02 credits per unique scrapeable profile searched, PLUS 0.5 credits for
each profile that actually has a post in the window. If the user has fewer
credits than profiles, only the affordable first profiles are looked up and the
rest are reported in skipped_profiles_due_to_credits.
LARGE-BATCH COST GATE: because each profile can cost up to 0.52 credits, a call
that would search more than 100 profiles is refused with a ModelRetry that
states the exact credit cost, UNLESS large_batch_approved=True is passed. Set
large_batch_approved=True ONLY after the user has seen the credit
cost and agreed to it — in interactive chat, that means you told them the number
and they said yes; in stored trigger code, ONLY if the user explicitly approved
this recurring spend when the trigger was set up. Do not set it reflexively to
silence the retry.
A dict with the following keys.
posts: list of {url, text, author, posted_at, days_ago, profile_input, reactions, comments} — newest-first per profile.
reactions/commentsare engagement COUNTS, not the people who engaged — usefetch_post_engagersfor the actual list of people.total: number of posts returned.
profiles: dict keyed by the normalized profile identifier (the same value as each of that profile's post's
profile_input), each{'status': 'ok'|'no_recent_posts'|'not_found', 'posts': int}— covers every attempted or cached profile.not_foundmeans the actor could not resolve the target (renamed/private/deleted);no_recent_postsmeans it resolved but nothing fell in the window. (Distinct from the top-levelunresolvable_profileslist below, which is inputs rejected at URL classification and never sent to the actor.)posted_within_days, profiles_lookup_count, credits_charged, and optionally skipped_profiles_due_to_credits / warning / unresolvable_profiles.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max posts per profile (default 3, max 5) | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| linkedin_urls | Yes | List of PUBLIC profile URLs (linkedin.com/in/<slug>) or bare usernames, e.g. ["https://linkedin.com/in/johndoe", "janedoe"]. Sales Navigator URLs (linkedin.com/sales/lead/...) and company URLs are NOT scrapeable — they are skipped and reported in `unresolvable_profiles`, and are NOT charged. If you only have a Sales Navigator URL, resolve a public /in/ URL first (e.g. via Apollo) before calling this. | |
| posted_within_days | No | Only return posts published in the last N days (default 7). | |
| large_batch_approved | No | Confirm a lookup above the cost-gate threshold. Only set True once the user has seen and approved the credit cost (see the LARGE-BATCH COST GATE note above). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations carry only readOnlyHint, and the description goes well beyond it: the recency window semantics, exclusion of reshares/quote-posts, `no_recent_posts` vs `not_found` handling, per-profile credit cost and gate, ModelRetry refusal behavior, and skipped-profiles outcome. This is exactly the behavioral context annotations alone cannot supply.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a one-line purpose and organized under labeled sections (RECENCY WINDOW, ALWAYS BATCH, COST, LARGE-BATCH COST GATE) with a returns block. It is long, and the cost-gate rule is restated in both the body and a parameter description, but for a tool with this many behavioral constraints the length is largely earned.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, but the embedded <returns> block fully documents the return shape (posts, total, profiles statuses, credits, skipped profiles) and even clarifies a subtle distinction between `profiles[].status` and top-level `unresolvable_profiles`. Nothing an agent needs to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning beyond the schema: the cost-gate mechanics for `large_batch_approved`, the 'do not widen' guidance for `posted_within_days`, and the profile_input batching rationale for `linkedin_urls`. The schema even back-references the description's cost-gate note, so this adds genuine value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Fetch recent LinkedIn posts from one or more profiles') and immediately scopes it against the broader reading of 'post history'. It also names the sibling relationship, pointing to `fetch_post_engagers` for engagement details, so an agent can distinguish it without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use it ('to personalize outreach off something they actually said'), how to batch ('pass every profile URL in ONE call'), and when-not (do not widen `posted_within_days` just to find something to quote; do not set `large_batch_approved` reflexively). It also routes the engager-list need to the correct sibling.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_luma_eventsGet Luma EventsARead-onlyInspect
Fetches every page (no truncation), so the returned list is the complete
calendar — upcoming and past events alike. Use it to answer "which event
is next", to find an event's id for get_luma_guests, or when a
luma_event_created trigger fires and you need the full event context.
Dict with events array and count. Each event dict is Luma's event object,
the useful fields being: id (starts with "evt-", the key
get_luma_guests takes), name, start_at / end_at (ISO 8601 UTC),
timezone (IANA), url (the lu.ma page), location_type,
meeting_url, visibility, registration_open, require_approval,
spots_remaining, description_md.
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint=true annotation, the description discloses pagination behavior ('Fetches every page (no truncation)'), result ordering, and that both upcoming and past events are included — meaningful behavioral context an agent cannot infer from the annotation alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads purpose in the summary and separates return details into a dedicated block. The field list in returns is long but earns its place given there is no output schema to document the shape.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-value burden and does so thoroughly, enumerating event fields (id, name, start_at/end_at, spots_remaining, etc.). Purpose, usage, and returns are all covered; only explicit when-not-to-use guidance is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%; the single on_behalf_of parameter is fully documented in the schema (agency client email, sourced from list_agency_clients, omit for own account). The description adds nothing beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List all events on the user's Luma calendar') with ordering ('most recent first') and scope ('complete calendar — upcoming and past'). It explicitly relates itself to the get_luma_guests sibling by supplying the event id that sibling consumes, so an agent can place it in the tool graph.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives three concrete when-to-use cases: answering 'which event is next', sourcing an id for get_luma_guests, and supplying context after a luma_event_created trigger. It does not name a when-not-to-use condition or an alternative list tool, but the positive routing is clear and actionable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_luma_guestsGet Luma GuestsARead-onlyInspect
Paginates to exhaustion — the returned list is the complete guest roster, every approval status included (approved, pending approval, waitlisted, invited, declined). Deciding "who hasn't registered yet" from a partial roster produces false negatives that re-nag people who already signed up, so there is deliberately no page or field trimming here.
When this runs in an agent, each guest is also saved and linked to the agent's
Output tab as an agent_search_results person row (keyed by their LinkedIn
identifier when the form captured one, else their email), so the roster
persists without a separate record step. Best-effort: a persist failure
never fails the read. Re-running updates rows in place; a guest the user has
removed from the list stays removed.
Dict with event_id, guests array, count, and (when saved in an agent) a
list summary {list_name, created, updated, total}. Each guest dict is
Luma's guest object, the useful fields being: user_email, user_name,
approval_status, registered_at, invited_at (null when the guest
found the event themselves), registration_answers (the event form's
question/answer pairs — a LinkedIn-URL question shows up here),
utm_source (which outreach channel drove the registration, when the
invite link carried ?utm_source=).
| Name | Required | Description | Default |
|---|---|---|---|
| event_id | Yes | The Luma event id from get_luma_events (starts with "evt-"). | |
| list_name | No | Short kebab slug naming the Output-tab list bucket. Absent, guests land in the 'default' list. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true; the description goes well beyond that, disclosing the non-obvious persistence side effect (guests saved as agent_search_results rows), its best-effort/never-fails-the-read semantics, in-place update on re-run, and the sticky removal behavior. These are exactly the traits an agent could not infer from structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary followed by a clear returns block; every section is purposeful. The rationale for exhaustive pagination is slightly verbose but justified by the false-negative risk, and nothing is wasted structurally.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description supplies a returns block enumerating the useful guest fields (user_email, approval_status, utm_source, registration_answers, etc.), and it covers the side-effect and idempotency behavior. An agent has everything needed to call and interpret it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all three parameters (event_id, list_name, on_behalf_of) are already documented in the schema, including the get_luma_events cross-reference and agency-account semantics. The description adds no additional parameter meaning, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('List every guest of a Luma event') and adds scope ('full registration detail', complete roster across all approval statuses). An agent can distinguish this from get_luma_events and send_luma_invites without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains the context that selects this tool — you need the exhaustive roster to decide 'who hasn't registered yet' — and warns that a partial roster causes false negatives. It doesn't explicitly name alternative tools or state when-not to use it, so it stops short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_mailbox_deliverabilityGet Mailbox DeliverabilityARead-onlyInspect
Call this when the user asks why their emails land in spam or the junk folder, or before launching a campaign to confirm the sending mailbox is configured to deliver. Each mailbox reflects the last DNS check — Sliq re-checks daily and on connect, so the data is current without asking the user to re-run it.
verdict is the worst of the four record statuses: 'fail' (a record is
misconfigured and may block or junk sends, so is_send_ready is False),
'warn' (advisory — delivery still works), or 'pass' (all four clean). score
is a 0-100 roll-up that score_tier buckets into 'good' / 'needs_work' /
'critical'. Each checks entry carries the per-record diagnostic (what is
wrong) and fix_instruction_markdown (how to fix it, null when that record
passes) — relay these when the user wants to fix a failing record. A
mailbox's deliverability is None when it has never been checked — tell the
user to open that mailbox in Settings and run the DNS check. An empty
mailboxes list means no email account is connected.
{
'mailboxes': [
{
'email': str, 'provider': str, 'is_default': bool,
'deliverability': {
'domain': str,
'verdict': 'pass' | 'warn' | 'fail',
'score': int,
'score_tier': 'good' | 'needs_work' | 'critical',
'is_send_ready': bool,
'last_checked_at': str, # ISO 8601
'dns_provider': str,
'checks': [
{'record_type': 'spf' | 'dkim' | 'dmarc' | 'mx',
'status': 'pass' | 'warn' | 'fail',
'raw_record': str, 'diagnostic': str,
'fix_instruction_markdown': str | None},
...
],
},
},
...
],
}
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses freshness behavior ('Sliq re-checks daily and on connect, so the data is current without asking the user to re-run it'), null-state handling for never-checked mailboxes, and empty-list meaning. It also explains what the per-record diagnostic and fix_instruction_markdown fields are for and when they are null.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well front-loaded with the purpose and trigger conditions, and the structured <returns> block keeps return-shape detail out of the prose. It is dense and somewhat long, but nearly every sentence carries distinct information an agent needs (verdict semantics, fix relaying, null handling).
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no formal output schema, the description compensates fully by inlining the return structure and defining every verdict, score_tier, is_send_ready, and checks field, plus the two degenerate states (deliverability=None, empty mailboxes). An agent has everything needed to call and interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter (on_behalf_of) has 100% schema description coverage, so the schema already carries its full meaning. The description adds nothing about acting on behalf of an agency client, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb+resource statement ('Read the DNS deliverability verdict (SPF, DKIM, DMARC, MX) for each connected mailbox'), naming the exact data domain and scope. No sibling tool covers deliverability, and the field breakdown immediately tells an agent what this returns.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit trigger conditions: 'Call this when the user asks why their emails land in spam or the junk folder, or before launching a campaign.' This is clear context for invocation, though it names no exclusions or alternative tools (there are none obvious in the sibling list, so the omission is minor).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_mcp_change_callsGet Mcp Change CallsARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max calls to return (default 100, max 200). | |
| offset | No | Calls to skip from the start of the burst (default 0). When `has_more` is True, re-call with offset += limit. | |
| agent_id | Yes | ID of the agent the change was made to. | |
| change_id | Yes | The change's id: the `id` of a `get_agent_details` `mcp_changes` entry, also shown as `(change N)` in the agent's Recent Activity. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already covers the safety profile, so the bar is lower. The description adds real behavioral context beyond annotations: oldest-first ordering, the burst-grouping model, and the crucial caveat that a change entry retains only the latest call's arguments. It does not restate pagination mechanics, but the schema's offset/has_more text covers those.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary that states the operation, ordering, and the key caveat before the example, followed by a compact returns block. Slightly prose-heavy but each sentence carries information; nothing is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema, yet the description supplies a returns block naming `calls` (id, created_at, arguments, ordered oldest first) and `has_more`. Combined with the schema's complete parameter docs, an agent has everything needed to call this correctly and page through results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all five parameters, including the change_id provenance and the offset/has_more loop. The description adds only conceptual framing (the burst grouping) rather than parameter-level meaning, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Read every call in one change made outside Sliq by the user's or a teammate's MCP clients', with explicit scope (oldest first). It distinguishes itself from the change entry itself, which 'carries only the latest call's arguments', so an agent can tell it apart from get_agent_details without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear use condition ('use this when you need the others') and a concrete example (every prospect an `update_prospect` burst edited). It also points to where change_id comes from (get_agent_details `mcp_changes`). No explicit when-not-to-use or alternative tool named, which keeps it just short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_meeting_transcriptGet Meeting TranscriptARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| meeting_id | Yes | The `id` of the meeting (from `search_meetings` results). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, and the description adds genuinely new behavioral context: the transcript is untruncated while the summary is capped at 6000 chars, and a no-match returns {'meeting': None}. It does not cover auth needs or rate limits, but for a read-only tool the disclosure is strong.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded and every sentence earns its place; the structured summary/returns tags aid scanning. Slightly verbose, but the returns block is justified because there is no output schema to carry that burden.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully compensates by enumerating the returned meeting fields (id, provider, title, transcript, summary, share_url, meeting_url, attendees, speakers, duration) and the no-match case. An agent has everything needed to call and consume the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both parameters are already documented in the schema (meeting_id sourced from search_meetings, on_behalf_of for agency-scoped execution). The description adds no syntax or format detail beyond that, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Fetch the full, untruncated transcript for a single meeting') and explicitly contrasts with the sibling search_meetings result preview. An agent can tell it apart from search_meetings and get_run_transcript without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use guidance ('Use after search_meetings when the 10000-char transcript preview was cut off') and names the triggering condition plus a concrete example of the scenario. Nothing is left to inference about when this tool is selected.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_message_experimentsGet Message ExperimentsARead-onlyInspect
Each test carries versions A and B with their template text, sent / replied /
interested / meetings counts, and a verdict: too_early until each version has 30
sends, then the leader with its probability of scoring higher on the test's metric —
leading, or wins at 95%. A send step's metric is replied. A connection request's is
accepted, with an accepted count per version; there an empty version text is a request
sent with no note, and recent requests haven't all been answered yet, so the version accepted
faster reads a little ahead early on. Relay that probability plainly, and say "too early"
when it is; at current volumes a test takes weeks. "Replied" counts any reply on that channel
after the send, including replies to later steps.
The tests (experiments), newest first, as
{experiments: [{node_id, node_label, metric, started_at, ended_at (null while running), ended_by, versions: [{label, template_id, text, sent, replied, interested, meetings, accepted (connection requests only)}], verdict}]}, where ended_by says how a past test ended — keep_a / keep_b when that
version went on alone, edit_a / edit_b when that version was edited (which restarts
the test), edit otherwise — and is null while it runs; across every agent each also carries
agent_id, agent_title and owner_email. The experiment at experiment_index also
carries each version's messages (newest first: {prospect_id, prospect_name, person, sent_at, text, outcome}), filtered to outcome and paged 50 at a time from offset,
with messages_total. A past test that sent nothing is left out, so an empty
experiments means no test in that scope is running or ever sent a message.
| Name | Required | Description | Default |
|---|---|---|---|
| offset | No | Where the page of messages starts. | |
| node_id | No | A send or connection-request step's node id, to scope to that step. Needs `agent_id`. | |
| outcome | No | Only messages whose prospect reached this outcome (meeting counts as interested and replied; interested counts as replied; on a connection request, replied counts as accepted). None = every sent message. | |
| agent_id | No | ID of the agent (campaign) to scope to. Omit for every agent. | |
| on_behalf_of | No | A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| experiment_index | No | Which experiment's messages to include, 0 = newest. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation it discloses substantial behavior: verdict semantics (too_early until 30 sends, then leader with probability, wins at 95%), metric differences (replied vs accepted), the connection-request caveat about faster-accepted versions reading ahead early on, and how empty results should be interpreted. This is exactly the kind of context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary/returns structure is front-loaded and each section is purposeful, but the prose is dense and long, with some detail (e.g. the extended 'replied' definition) that could be tightened. Overall efficient for the amount of behavioral nuance conveyed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description carries the full return-shape burden — and it does, spelling out the experiments array, version fields, ended_by values, the messages sub-array with paging, and the empty-result meaning. Nothing an agent needs to call and interpret it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 100% schema coverage the baseline is 3, but the description adds real meaning: it explains how agent_id and node_id combine to define scope, how experiment_index selects which test's messages appear, and the paging/outcome-filter relationship. It goes beyond restating the schema fields.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — reading A/B tests of flow-step messages and connection-request notes — and immediately contrasts with the alternatives by describing the scoping modes (one step, one agent, or every agent). An agent can distinguish it from siblings like get_node_history or query_analytics without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It names concrete triggering questions ('which version is winning', 'how did the last test go') and explains how to scope the call, which gives clear context for when to reach for it. However, it never names a competing tool or states an explicit when-not condition, so routing is implied rather than directed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_message_tag_ratesGet Message Tag RatesARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| origin | No | 'sliq' for conversations with prospects only, 'imported' for imported conversations only; omit for both. Imported conversations have no connection-request notes, so `accepted` over them is empty. | |
| channel | No | 'linkedin' or 'email' to bound the population; omit for both. | |
| measure | No | 'accepted', 'replied', 'interested', or 'meeting'. | replied |
| senders | No | In a shared workspace, the teammate email addresses whose messages to score. Omit to score every sender in the workspace. Any email not in the workspace is dropped; if that leaves no valid teammate, the result is empty — it does NOT fall back to the whole workspace. | |
| group_id | Yes | The tag group to score (from list_message_tag_groups). | |
| agent_ids | No | Scope to one or more campaigns (agent_tasks ids from a group's `campaigns`); omit for all campaigns. Campaign membership is the prospect's current one. | |
| positions | No | The conversation positions to score — 'first' (each conversation's first message), 'follow_up' (later messages sent before the prospect replied), and/or 'reply' (messages sent after the prospect replied); omit for all. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=true; the description adds substantial behavior beyond that: how each measure is scored (accepted over one connection-request note; replied/interested/meeting over reply-capable messages sent before the outcome), that the same prospect counts once per distinct tag, that imported conversations have no campaign so `agent_ids` excludes them, and that `positions` is a no-op for `accepted`. These are non-obvious semantics an agent could not infer.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded and the returns block is cleanly separated, and nearly every clause carries real semantics rather than filler. The summary runs long with heavy parenthetical nesting, which makes it dense to parse, but the leverage per sentence is high.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter read-only analytics tool with no output schema, the description supplies the output shape (group_id, group_name, mode, measure, tags with tag_id/tag/k/n/pct/signal), the 'thin'/'early'/'solid' thresholds, and the synthetic null-tag 'No match' row plus how to inspect those messages via query_tagged_messages(untagged=true). Nothing material needed to call and interpret it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, but the description adds genuine cross-parameter meaning beyond the schema: the interaction of `positions` with `measure` (no-op for accepted, always-empty `replied` under reply-only), and why `agent_ids` silently drops imported conversations. It does not add much for `senders`/`on_behalf_of` beyond what the schema already states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (per-tag outcome rate for one measure over a group's classified messages) and immediately grounds it in concrete questions ('which opening sentence gets the most replies'). It is clearly distinguishable from siblings like get_segment_rates (segment-level) and get_tag_cross_tab by naming the unit of analysis (tag, conversation-level, one data point per prospect per tag).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives strong contextual guidance: it points to list_message_tag_groups for group_id and the `campaigns` list for agent_ids, and warns that `positions` of only 'reply' makes `replied` empty (use `interested`/`meeting` instead). It does not, however, explicitly contrast itself with near-siblings such as get_segment_rates or get_tag_cross_tab, so the when-not-to-use case is left implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_message_tag_unclassified_countGet Message Tag Unclassified CountARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| group_id | Yes | The tag group to count for (from list_message_tag_groups). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the `readOnlyHint`, the description discloses important behavioral details: shared-agent runs count only the current shared agent, the count is scoped to the group's tracked channel, campaigns, message positions, teammates, and criteria, and sentinel 'No match' rows are excluded. These are non-obvious semantics that materially affect interpretation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core count definition and uses summary/returns structure. It remains dense with several qualifying clauses, but most clauses carry useful disambiguation for this domain rather than redundant filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although there is no output schema, the description explicitly documents the return shape as `{group_id, unclassified_messages}`. Combined with the scope, shared-agent behavior, and sentinel-row distinction, it gives an agent enough to call and interpret the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both `group_id` and `on_behalf_of` are already documented in the input schema. The description adds scope context around the group's classify scope, but it does not add parameter-specific syntax or format details beyond the schema baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a precise count operation: sent messages in the group's classify scope that have no classification row yet. It also distinguishes the result from sentinel 'No match' rows and links it to the complementary `classified_messages` count, so an agent can tell exactly what this tool returns.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear usage context by saying this is the population a `classify_message_tag_group` run with force off and no scope arguments would process. It also distinguishes the count from sentinel 'No match' rows, though it does not explicitly name when to prefer this over other counting or classification-adjacent tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_node_historyGet Node HistoryARead-onlyInspect
Reads the append-only position log directly, so an off_sequence node id a later flow
edit dropped still returns its history. A node no one has moved off — including the start
node, which stores no position rows of its own — comes back empty.
Dict with node_id, count, and prospects array. count is how many prospects went through and
moved on in total (independent of limit/offset, so the page count is
ceil(count / limit)); prospects is the requested page, newest visit first, each
{id, name, email, linkedin_url, provider_id, current_node_id, visited_at} —
current_node_id is the node they rest on now, visited_at the ISO time they last
held node_id.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max prospects returned per page, newest visit first (default 50, capped at 200). | |
| offset | No | How many of the newest-first prospects to skip. Page through a node with more than `limit` history by re-calling with offset 0, then `limit`, then `2*limit`, … until `offset >= count`. | |
| node_id | Yes | The node whose history to read — a node id from get_campaign_flow. | |
| agent_id | Yes | ID of the agent (campaign) to read. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, it discloses that the read hits the append-only position log so dropped off_sequence node ids still resolve, and that nodes nobody moved off — including the start node, which stores no position rows — return empty. It also clarifies that count is total and independent of limit/offset, driving the pagination formula, which the annotation does not cover.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary block front-loads the core purpose, scope, and alternative routing, followed by a structured returns block. It is on the long side and repeats some schema-level detail, but nearly every sentence carries differentiating information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description supplies a returns block enumerating the dict shape and every prospect field, plus empty-result and dropped-node edge cases. Nothing an agent needs to call and interpret this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the parameters are already documented, but the description adds real meaning about how limit and offset interact with count (page count = ceil(count/limit)) and clarifies agent_id is not restricted to the in-context agent. That is more than the schema supplies for pagination behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
It states a precise verb and resource — reading a Flow node's 'went through' history of prospects who held the position and moved off — and explicitly contrasts it with get_campaign_flow's live occupancy and query_prospects filtered on current_node_id. An agent can distinguish it from every nearby sibling without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives concrete question-shaped triggers ('who passed through the connect step', 'who has already been through step 2') and names the two alternatives with the condition that selects each ('for who rests on a node now, filter query_prospects on current_node_id'). It also states the tool accepts any of the user's campaigns, not just the agent in context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_outreach_approvalGet Outreach ApprovalARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | No | The agent whose effective settings to read. When omitted, reads the current agent's settings if this chat is tied to one, otherwise the account-wide settings. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only supply readOnlyHint=true; the description adds substantial context the annotation cannot: the meaning of `pending_approval`, the override-vs-account-default resolution, and the per-subtype keying semantics. It even documents the return shape, which matters since no output schema exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose, then the diagnostic use case, then scope. The XML-style summary/returns sections are slightly verbose in structure but each sentence carries information, so little is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite the absence of an output schema, the description documents the returned dict keys (`settings`, `override_active`, `account_settings`) and their true/false semantics. Combined with full parameter coverage, an agent has everything needed to call and interpret this read.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the description's scope explanation for agent_id largely restates what the schema already says ('reads the current agent's settings if this chat is tied to one, otherwise the account-wide settings'). It adds the override nuance for the resolved scope but does not introduce new syntax or format detail beyond the schema baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (read) and resource (per-subtype outreach approval settings) and explains what the settings decide: auto-send vs wait for approval. It is clearly distinguishable from the mutating sibling set_outreach_approval.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use guidance: read this before telling a user a send looks stuck, because a message in `pending_approval` is a normal gated state rather than a failure. It also states the scope-selection conditions for passing vs omitting agent_id.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_outreach_windowGet Outreach WindowARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint, but the description adds real behavioral context: sends only occur inside the window, empty active_days means outreach is paused, a closed window is expected rather than a failure, and the window is user-edited under Settings with no tool able to change it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the one-line purpose, then rationale, then a structured returns block. The XML-style tags are slightly heavier than plain prose, but every sentence carries information an agent needs and nothing is padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by enumerating every returned field (open_now, window_start, window_end, active_days, timezone) with formats and the null legacy case. Nothing an agent needs to interpret or act on the result is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single optional parameter (on_behalf_of) is fully documented in the schema, including how to obtain the value from list_agency_clients. The description adds nothing about it, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource and the exact fact it returns: the account's outreach activity window plus whether it is open right now. It also implicitly separates itself from queue-management siblings by telling the agent this is the diagnostic to consult when a queue looks idle.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the triggering situation ('when a queue looks idle, check this before telling the user something is broken') and the negative case ('a closed window ... is a normal hold, not a failure'). It even states a constraint on other tools: a send cannot be moved into a closed window.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_pending_approvalsGet Pending ApprovalsARead-onlyInspect
Covers four channels, each pending row bucketed by action-type category exactly
as the page groups them: LinkedIn connection requests / messages / follows /
post engagement, emails, gifts, and manual actions. Same-post
comment+reaction rows collapse into one engagement item, matching the page.
A gift item carries the order its card shows (product_url, max_price_usd,
shipping_address, with the note as its summary) and an error; the user
completes any missing piece on the card before approving it. max_price_usd is the
most the item may cost including tax and shipping; approving charges the owner's card
for gifts the order's actual total, at most max_price_usd, plus about $3.30 + 2.9% in
fees on top. error is blank
unless the user approved the card and the order was refused: it then gives the
reason, and the card waits for the user to fix the order and approve it again.
owner_has_gift_card is false when the agent's owner has saved no card for gifts;
approving then orders nothing until the owner adds one under Settings → Billing.
Sequence-produced items (LinkedIn, email, and a manual or gift step queued by its
flow node) carry node_id/node_label naming the campaign-flow node that
authored them — null for items created outside a sequence.
A manual item also carries completion_criteria: how to advance the prospect once
the user reports what they did. Cross-agent for the owner; for one agent's items,
pass agent_id, and for one step of its flow, also pass that step's bucket and
keep the items carrying its node_id. A shared operator sees only the shared agent.
Bounded by default so an approval-gated campaign holding thousands of drafts
doesn't return every full message. The default gives per-bucket/per-agent counts
plus a short preview per bucket (first few items); pass bucket to page that one
bucket's full item list. A bucket's preview_truncated (or count exceeding the
preview) tells you to drill in.
By default (no bucket), a dict
{'pending': {'total': int, 'buckets': [{'bucket', 'title', 'count', 'preview_truncated': bool, 'agents': [{'agent_id', 'agent_title', 'count'}], 'preview': [item, ...]}]}, 'recently_resolved': {'window_days': int, 'total': int, 'items': [...]}, 'recently_shelved': {'window_days': int, 'total': int, 'items': [...]}}. The preview holds the
bucket's first few full items. Recently-resolved lists only rows with a
transition timestamp — manual done/dismissed, LinkedIn/email sent;
cancelled or rejected drafts don't appear there. Recently-shelved
lists rows the shelf-life sweep shelved (an unstarred draft that aged
past its approval window); each item's target_provider_id is the prospect
handle for person-addressed LinkedIn drafts (connection_request / message /
inmail / follow) so a swept draft matches back to a prospect — blank for
comment/reaction (post-addressed) and for email.
Drill-down (bucket set): {'bucket', 'title', 'count', 'offset', 'limit', 'truncated': bool, 'items': [item, ...]} — full-text items, newest-first;
count is the whole bucket, truncated means more beyond this page (re-call
with offset += limit).
| Name | Required | Description | Default |
|---|---|---|---|
| bucket | No | Drill into one bucket for its full-text items, paged. One of 'email', 'linkedin_invite', 'linkedin_message', 'linkedin_follow', 'engagement', 'gift', 'manual'. Omit for the bounded counts+preview. | |
| offset | No | Rows to skip in drill-down mode (default 0); re-call with `offset += limit` while `truncated` is true. Ignored without `bucket`. | |
| agent_id | No | Scope every section to one agent's items. Omit for every agent. | |
| on_behalf_of | No | A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| resolved_within_days | No | Recency window for the recently-resolved section (default 7). Only used in the default (no-`bucket`) mode. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint=true already covering the safety profile, the description adds substantial behavioral context: results are bounded by default with counts+preview, preview_truncated signals drill-in, gift items can carry an error after a refused order, approving charges the owner's card up to max_price_usd plus ~$3.30 + 2.9% fees, and a shared operator only sees the shared agent. These are non-obvious operational traits that go well beyond the annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a summary tag and organized by mode (default vs drill-down), with the returns block clearly delineated. It is long and dense — the gift fee arithmetic is borderline — but nearly every sentence conveys decision-relevant behavior rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no formal output schema, the description fully specifies both return shapes (default rollup vs bucket drill-down), the meaning of count/truncated/preview_truncated/offset, and edge cases like target_provider_id mapping and cancelled-vs-sent resolution. An agent has everything needed to call and interpret this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds meaningful interplay semantics: passing bucket switches to paged full-text drill-down while omitting it returns bounded counts+preview, offset is re-called as offset += limit while truncated, and agent_id can be combined with bucket/node_id to isolate one flow step. This goes beyond the per-parameter schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List everything waiting on the user — the same pending-approval rollup the home Approvals page renders') and scopes it precisely to the pending/resolved/shelved rollup. An agent can distinguish this from siblings like get_outreach_approval or request_user_action without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit usage contexts: answering 'what's waiting on me?', verifying a request_user_action was handled, checking whether a draft was shelved, or before re-flagging a prospect. It also explains scoping (cross-agent, agent_id, bucket/node_id for one flow step). It stops short of naming a direct alternative tool to prefer instead, so it is clear context without explicit exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_personalization_guideGet Personalization GuideARead-onlyInspect
Return the user's personalization guide — how they want a person researched before you write to them. Call this only when you are going to personalize a message; follow the returned guide. When none is set, returns a note and you should fall back to the default research rules in the outreach craft skill.
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=true, so safety is covered. The description then adds behavior the annotations cannot express: the empty-state returns a note rather than an error, and the caller must follow the returned guide. It does not cover scoping/auth or rate considerations, but the empty-state disclosure is genuinely useful.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each load-bearing: what it returns, when to call it, and what to do on the empty case. The purpose is front-loaded and there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description still tells the agent what comes back (the guide, or a note when unset) and what to do with it. The chain into the outreach craft skill fallback closes the main gap; only a fuller description of the returned guide's structure is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter on_behalf_of is fully documented in the schema (100% coverage), including its source (list_agency_clients) and default behavior, so the schema carries the load. The description adds nothing about it, which matches the baseline-3 rule for high coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Return the user's personalization guide') and immediately defines the resource's content ('how they want a person researched before you write to them'). This scope is clearly distinguishable from near-siblings like get_writing_style_guide, get_skill_guide, and get_user_memory.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly gates invocation ('Call this only when you are going to personalize a message') and specifies a fallback path when the resource is empty ('fall back to the default research rules in the outreach craft skill'). Both the when-to-use and the when-empty alternatives are named, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_privacy_settingsGet Privacy SettingsARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses the setting's behavioral effects: it restricts storage to prospects, prevents non-prospect messages from triggering agents, and deletes previously stored non-prospect data when enabled. It also explains the downstream implication for search results, which is critical context an agent would otherwise lack.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the setting identity and then layers on effects and usage guidance in a logical order. It is somewhat verbose, but every sentence contributes useful context and there is little waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a simple read-only tool with one parameter and no output schema, the description is complete: it explains the setting's meaning, implications, and how to interpret empty search results, and it even describes the return value shape. An agent has everything needed to call and interpret it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single parameter on_behalf_of is fully documented in the schema. The description adds no additional parameter-level semantics, so the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the exact privacy setting, its location, and states that no tool changes it, clearly identifying this as a read-only getter. It is immediately distinguishable from siblings by focusing on the 'Only keep data on prospects' setting.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly explains when to consult this setting (when a search for stored messages/emails/connections finds nothing for a non-prospect) and names the alternative tool fetch_linkedin_messages_with_person for reading LinkedIn conversations directly. This gives clear when-to-use and when-to-use-alternative guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_run_transcriptGet Run TranscriptARead-onlyInspect
Use this when a run's one-line summary isn't enough (e.g. "why did you skip
person X in that run?"). Get a run's id from get_agent_details — each entry
in its recent_runs list carries one; this returns the same message parts the
web Activity tab renders for that run.
The transcript is paginated over its message parts — a heavy run records many.
Page with offset while has_more is true.
Dict with run_id, the run's summary message + details, status
('completed'/'failed'), created_at, total_messages (parts in the whole
run), returned_messages (parts in this page), offset, has_more, and a
messages array (role, content or tool_name/args, created_at) sliced to
[offset, offset+limit). Empty messages and total_messages 0 when the run
recorded no audit session (e.g. it failed before the agent started).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max message parts to return (default 100, max 200). | |
| offset | No | Number of parts to skip from the start of the run (default 0). | |
| run_id | Yes | An agent-run id — the `id` of a `get_agent_details` `recent_runs` entry (also shown as `(run N)` in the agent's Recent Activity feed). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, but the description adds substantial behavioral context: the transcript is paginated over message parts, paging is driven by offset while has_more is true, and it discloses the edge case where total_messages is 0 and messages is empty (run failed before the agent started). This goes well beyond the annotation's safety profile.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the one-sentence summary, then usage, then pagination, then returns, using clear section tags. Despite its length, every sentence carries distinct information (when-to-use, id provenance, paging, empty-run behavior) with no repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, but the description fully specifies the return shape (run_id, summary, status, created_at, total_messages, returned_messages, offset, has_more, messages) including the degenerate empty case. Combined with explicit paging guidance, an agent has everything needed to call and consume this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, but the description adds real meaning: run_id is sourced from get_agent_details' recent_runs (also shown as '(run N)' in the Activity feed), and offset's role in pagination is spelled out. It stops short of explaining limit/on_behalf_of in the prose, relying on the schema for those.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Read the transcript of one past background agent run — every tool call and reasoning step'). It distinguishes itself from nearby siblings like get_agent_details (summary-level) and get_chat_messages by scoping to a single run's audit session.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the trigger condition ('when a run's one-line summary isn't enough') and the concrete upstream step to obtain the required id from get_agent_details' recent_runs list. No inference is required to decide when or how to call it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_segment_funnelGet Segment FunnelARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | The campaign (agent) whose funnel to split. | |
| group_id | No | The segment group to split by, from `groups`. Omit for the first group that tags this campaign. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses non-obvious behavior: each person is counted once so rates diverge from get_segment_rates, `funnel.reached` is cumulative while other stage keys are current-stage-only, signal thresholds (thin/early/solid), the 'No match' tag, multi-tag double counting, and the ~15 minute tagging lag.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded with the definition and the key caveat before the returns block, and every clause carries information. It is long, but the length is justified because there is no output schema to carry the return-shape documentation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully documents the return shape (groups, selected, funnel, signal, not_tagged, out_of_credits) and the semantics of each field, so an agent can both call and interpret the result without guessing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all three parameters are already documented, but the description adds interpretive meaning by tying agent_id/group_id to the funnel's output structure and to the drill-down call in query_agent_people, and by explaining what omitting group_id yields (selected: null when nothing covers the campaign).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence gives a specific verb+resource+scope: one campaign's lead funnel split by a segment group's tags, with concrete example questions. It explicitly distinguishes itself from the sibling get_segment_rates and explains the counting difference (one person once vs. per-channel).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states when to use it (tag-filtered funnel questions like 'how are VPs doing'), names the alternative get_segment_rates and the condition that selects it, and routes the agent to query_agent_people with the exact arguments needed to drill into the people behind a number.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_segment_ratesGet Segment RatesARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| channel | No | 'linkedin' or 'email' to bound the population; omit for both. | |
| measure | No | 'accepted', 'replied', 'interested', or 'meeting'. | replied |
| senders | No | In a shared workspace, the teammate emails whose prospects to score (a group's `senders`). Omit to score every teammate. Any email not in the workspace is dropped; if that leaves no valid teammate, the result is empty — it does NOT fall back to the whole workspace. | |
| group_id | Yes | The segment group to score (from list_segment_groups). | |
| agent_ids | No | Scope to one or more campaigns (agent_tasks ids from a group's `campaigns`); omit for all campaigns. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint, and the description adds substantial behavioral context beyond that: the unit of scoring (one data point per prospect-channel, one per prospect when several team members reach the same person), the synthetic 'No match' row appended when anyone is unclassified, and the confidence thresholds behind `signal` ('thin' <10, 'early' <30, else 'solid'). This is exactly the kind of context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Reasonably front-loaded: the summary opens with the verb and resource, the scoring model follows, and return shape is separated into a returns block. It is long but most sentences carry unique information; a couple of illustrative examples could be trimmed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, yet the returns block fully describes the return dict (group_id, group_name, mode, measure, tags and their fields), explains k/n semantics, and clarifies signal. Nothing an agent needs to interpret or call the tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so parameters are already documented in the schema (including the senders no-fallback rule and channel enum). The description restates agent_ids scoping and the group's campaigns list but adds no syntax or format detail for measure, channel, senders, or on_behalf_of. Baseline 3 applies when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (per-tag outreach outcome rate for a measure over a segment group's classified people) with concrete use cases ('which seniority replies most'). The scoring model described (one data point per prospect-channel) makes it clearly distinguishable from siblings like get_message_tag_rates and get_segment_funnel.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear usage context: pass agent_ids to scope to campaigns, use list_segment_groups to find a group's campaigns, and query_segment_people(untagged=true) to list the 'No match' people. It does not explicitly contrast itself with the similarly named sibling rate/funnel tools, so the when-to-use-this-vs-that guidance is implicit rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_segment_unclassified_countGet Segment Unclassified CountARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| group_id | Yes | The segment group to count for (from list_segment_groups). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, so the description carries the interesting behavior: on a shared agent it counts only the current shared agent, and it clarifies that 'No match' people are excluded from this remainder. That is genuine context beyond the safety hint, though it says nothing about cost, latency, or freshness of the count.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core answer is front-loaded in the first clause, and the summary tag keeps supporting detail (shared agent, No match distinction) contained. The XML tag scaffolding and the separate returns block make it slightly heavier than needed, but no sentence is padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although there is no output schema, the <returns> block gives the exact return shape ({group_id, unclassified_people}), and the summary covers scope, shared-agent behavior, and the neighbor metric it should be paired with. Nothing an agent needs to call and interpret this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both parameters (`group_id`, `on_behalf_of`) are fully documented in the schema, so the schema does the heavy lifting. The description's reference to `criteria` and the shared-agent scoping adds a little framing but no syntax or format detail beyond what the schema already provides. Baseline 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb+resource ('how many people ... have no classification row yet') and scopes it to the group's classify criteria. It also explicitly distinguishes this count from the 'No match' people, who are already inside `classified_people`, which is exactly the confusion an agent would otherwise hit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear context for when this is the right number: it is the population a `classify_segment_group` run with force off would process, and it pairs with `classified_people` from `list_segment_groups`. It does not name an explicit alternative tool to use instead, so it stops short of full when/when-not routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_skill_guideGet Skill GuideARead-onlyInspect
Return the best-practice guide for a Sliq workflow — read and follow it
before acting on that workflow. skill selects the guide.
| Name | Required | Description | Default |
|---|---|---|---|
| skill | Yes | ||
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true is consistent with a retrieval tool, so no annotation contradiction. Beyond the annotations, the description adds genuine behavioral context that the call returns prescriptive content meant to be followed before acting, which shapes how the agent should treat the output. It does not cover failure behavior for an invalid skill or how large the returned guide may be.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with no filler; the return value is front-loaded and the parameter note is appended where it belongs. Nothing here could be cut without losing information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read-only lookup with a single enum parameter, the description covers what it returns and the obligation to read it before acting. With no output schema, one could ask for more detail on the guide's structure or on invalid-skill handling, but nothing essential to correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 50%: `on_behalf_of` is well documented in the schema while `skill` has no description, only a 31-value enum. The description's '`skill` selects the guide' adds a small amount of meaning but does not explain what each enum value corresponds to beyond its self-descriptive name. Baseline 3 is appropriate given the enum carries most of the semantic weight.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Return the best-practice guide for a Sliq workflow') and ties the `skill` parameter to guide selection, so the agent knows exactly what comes back. It does not distinguish itself from adjacent guide-style siblings such as get_personalization_guide or get_writing_style_guide, which an agent must infer from name alone.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit usage instruction: 'read and follow it before acting on that workflow,' which tells the agent both when to call it and what to do with the result. It stops short of stating exclusions (e.g., which workflows have no guide, or when the other guide tools are preferred), so it is clear context rather than full when/when-not routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_tag_cross_tabGet Tag Cross TabARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| channel | No | 'linkedin' or 'email' to bound the population; omit for both. | |
| measure | No | 'accepted', 'replied', 'interested', or 'meeting'. | replied |
| senders | No | In a shared workspace, the teammate emails whose outreach to score (a group's `senders`). Omit to score every teammate. Any email not in the workspace is dropped; if that leaves no valid teammate, the result is empty — it does NOT fall back to the whole workspace. | |
| col_kind | Yes | 'message' or 'segment', as `row_kind`, for `col_group_id`. | |
| row_kind | Yes | 'message' when `row_group_id` is a message tag group (from list_message_tag_groups), 'segment' when it is a segment group (from list_segment_groups). | |
| agent_ids | No | Scope to one or more campaigns (agent_tasks ids from a group's `campaigns`); omit for all campaigns. Campaign membership is the prospect's current one. | |
| col_group_id | Yes | The group for the grid columns — a different group from the row one. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| row_group_id | Yes | The group for the grid rows. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only supply readOnlyHint, so the description carries the real behavioral burden and does so richly: only non-empty cells are returned, a multi-class group's cells can sum past the prospect total, prospects unclassified in a segment group land in no cell, and swapping row/col returns transposed cells. These are non-obvious result-shape traits an agent could not derive from the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The lead sentence is front-loaded and the summary/returns tagging structures the block well, but the body is a dense ~180-word paragraph where the edge-case sentences run together. Length is largely justified by real edge cases, though a bulleted treatment would scan faster.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the embedded returns section compensates by fully describing the grid dict (axes, cells, k/n/pct, signal thresholds, ordering by cell volume). Combined with the edge-case coverage, an agent has everything needed to interpret and call the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, but the description adds meaning beyond the schema by explaining that message and segment group ids are numbered separately so 'each id travels with its kind' — clarifying why row_kind/col_kind are required. It also explains the segment-through-person categorization constraint that affects what a group selection returns.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a precise verb and resource ('Cross two tag groups into a 2-D grid of one measure's outcome rate') and immediately frames the value as 'the interaction a single-dimension rate hides,' which distinguishes it from the single-group rate siblings. It also clarifies the two axes can independently be message or segment groups, leaving no ambiguity about what the tool computes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives concrete motivating questions ('does a warm opener plus a soft ask beat a warm opener plus a hard ask') and positions the tool as the 2-D interaction counterpart to the single-group rate tools. It stops short of an explicit when-not rule or naming the alternative tools (get_message_tag_rates / get_segment_rates) directly, so an agent must infer the routing rather than being told it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_tool_connect_urlGet Tool Connect UrlARead-onlyInspect
If the user says a service is already connected, call get_integration_status first — the
service may already be active and need no link.
A link that opens fine but whose connection then fails is not a link problem — a fresh link routes
to the same connect flow and fails the same way. Mint one fresh link and have the user retry once; if
it still doesn't connect, the connection is stuck (not a link issue) — call escalate_to_team with the
integration and what the user tried rather than re-minting again.
For linkedin, gmail, and outlook the return also includes data_backfill — a sentence stating
how far back Sliq syncs history once connected. Relay it with the link so the user knows older
data won't appear and how to request a longer backfill; when sharing several connect links in
one message, merge the notes into a single short disclaimer instead of repeating one per link.
Dict with the connect URL, integration name, the data_backfill note
(linkedin/gmail/outlook), and connect_note (linkedin)
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| integration_id | Yes | one of 'gmail', 'outlook', 'linkedin', 'slack', 'hubspot_mcp', 'attio', 'clarify', 'salesforce', 'notion', 'linear', 'fathom', 'granola', 'grain', 'zoom', 'otter', 'fireflies', 'circleback', 'superhuman', 'github', 'asana_mcp', 'atlassian_mcp', 'googledrive', 'googledocs', 'googlesheets', 'reddit', 'exa_websets', 'apollo' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint, so the description carries the behavioral burden and does so richly: it explains that a fresh link routes to the same connect flow (so a broken connection is not a link problem), prescribes the escalation path, and documents the data_backfill/connect_note fields returned for specific integrations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then justification, then troubleshooting. It is long, and the multi-clause sentence about a link that opens but fails to connect is dense, but nearly every sentence carries actionable routing or output guidance, so little is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by summarizing the return dict and the integration-specific note fields. Combined with clear escalation guidance and the caller-identity semantics covered by the schema, an agent has everything needed to call and use this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and both parameters (integration_id with its full enum list, on_behalf_of) are documented in the schema. The description adds no parameter-level detail beyond what the schema already provides, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states a specific verb and resource: generate a clickable URL to connect a tool. It is immediately distinguishable from siblings like get_integration_status (which checks state) or escalate_to_team (which is named as the fallback path).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use routing: if the user claims the service is already connected, call get_integration_status first. It also gives a clear when-not-to-repeat rule (mint one fresh link, retry once, then escalate rather than re-minting) and names the alternative tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_user_memoryGet User MemoryARead-onlyInspect
The user's saved memory — the durable facts, preferences, and context Sliq has learned about them across conversations. Read it to ground a reply in what's already known before asking the user to repeat something. It is account-wide (shared across all their agents), not per-agent. When none is saved yet, returns a note saying so.
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, so the description usefully adds that the memory is account-wide across all agents (not per-agent) and discloses the empty-state behavior — it returns a note when nothing is saved yet. It does not describe the shape or size of what comes back, but the empty-state and scope disclosures go beyond the annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: what the memory is, the reason to read it, and the scope plus empty-state caveat. Front-loaded with the resource definition rather than preamble.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-required-param, read-only tool with no output schema, the description covers purpose, scope, and the no-data case — enough to call it correctly. It stops short of telling the agent what the returned memory looks like in practice, which would help an agent decide how much to trust it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so on_behalf_of is fully documented in the schema itself. The description mentions account scope but never connects it to the agency-client parameter, so it adds nothing beyond the structured field. Baseline 3 is correct here.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names the specific resource (the user's saved memory) and enumerates exactly what it holds — durable facts, preferences, and learned context. The scope note ('account-wide, not per-agent') further pins it down and separates it from per-agent reads like get_agent_details or query_agent_people.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear use case: read it to ground a reply before asking the user to repeat themselves. No explicit when-not or named alternatives (e.g. search_chat_history, get_personalization_guide, get_writing_style_guide), so routing among the several 'read saved context' siblings is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_writing_style_guideGet Writing Style GuideARead-onlyInspect
Return the user's writing style guide for one channel, to follow when drafting that channel's outreach. Pass channel='linkedin_dm' for a DM or connection note, or channel='email' for an email. A set guide is the user's own voice and overrides the default craft rules; returns a note when none is set.
| Name | Required | Description | Default |
|---|---|---|---|
| channel | Yes | A channel a user can set a writing style guide for. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, so safety is covered elsewhere. The description adds genuinely non-obvious behavior: a set guide overrides the default craft rules, and the tool returns a note rather than failing when no guide is set — both useful for correct downstream handling. It stops short of stating what the guide payload looks like structurally, but the empty-state and precedence semantics are the important disclosures.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four short sentences with zero filler: purpose first, then concrete channel examples, then the precedence rule, then the empty-state behavior. Every sentence carries information an agent would otherwise have to guess.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description bears the return-value burden and handles the most consequential case (no guide set returns a note) plus how the result should be applied. That is adequate for a simple two-parameter read, though it does not describe what a populated guide contains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3 even though the description also explains channel values. It clarifies 'linkedin_dm' and 'email' with concrete use cases, which is value-add, but it omits the third enum value ('linkedin_comment') and says nothing about on_behalf_of beyond what the schema already states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Return the user's writing style guide') plus the scope constraint ('for one channel') and the downstream purpose ('to follow when drafting that channel's outreach'). This resource is distinct from adjacent siblings like get_personalization_guide and get_skill_guide, so an agent can tell them apart without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description establishes clear context — call this before drafting a channel's outreach and use the returned guide as the user's voice. It does not state exclusions or explicitly route to the related guide tools (get_personalization_guide, get_skill_guide), leaving that comparison to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
google_news_searchGoogle News SearchARead-onlyInspect
A find-companies News-mode discovery source, used alongside
search_news_events, tavily_search, and exa_search_news — the
News-mode skill owns the routing (which signals fan out here). Its
edge is Google's local and trade-press index: hyper-local expansion
stories, plant incidents within hours of publication, and
small-market executive moves the other sources' indexes miss.
Returns raw articles, not company rows — extract the company from
each article's title/snippet the same way as tavily_search results.
Compose KEYWORD-style queries — the terms a journalist's headline would contain, e.g. 'appoints new chief marketing officer' or 'breaks ground manufacturing facility'. Google matches keywords, not meaning: full-sentence semantic phrasing ("A company that recently...") returns ZERO results. Geographic scoping via keywords is brittle (a location term matches any article containing the word, wherever the company is) — prefer scoping by event keywords and filtering location downstream. One query per signal, all in one call (the tool fans them out in parallel; one tool call is one LLM round).
Expect the same story to arrive several times from different
outlets — Google clusters dilute a page's effective yield to ~5–7
distinct companies. Dedup is on exact URL here; cross-outlet
same-story dedup happens downstream at record_search_results.
Articles are recency-bounded server-side by Google's coarse
day/week/month/year buckets, using the smallest bucket covering
lookback_days (default 30) — a bucket can exceed the window, so also
check each row's published_at when the window matters. A news scan
passes the window from its trigger prompt.
Costs 0.05 Sliq credits per query that returns without error; a
failed query is free. Companies you record from its articles are not
charged. Soft-fails on insufficient credits: the charge is capped at
the remaining balance.
Dict with articles array and count. Each article row is
{title, url, source, snippet, date, published_at, query} —
source is the publisher name, date is Google's relative
publish string ("2 days ago"), published_at is a coarse UTC
ISO estimate derived from it (None when unparseable), and
query names the originating query. Rows are deduped by URL
across queries.
If every query failed with an upstream error and nothing
landed, also google_news_available: False plus a
human-readable error — a source outage, NOT a genuinely
empty feed. Partial failure (some queries errored, others
returned) stays silent and reports only the articles.
On insufficient Sliq credits the call is refused before running —
it raises InsufficientCreditsError instead.
| Name | Required | Description | Default |
|---|---|---|---|
| queries | Yes | One keyword-style news query per signal. Pass a list of one for a single-signal lookup. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| lookback_days | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With only readOnlyHint=true in annotations, the description carries rich extra context: cost (0.05 Sliq credits per successful query, free on failure), soft-fail/capped charge on insufficient credits, server-side recency bucketing that can overrun lookback_days, URL-only dedup with clustering dilution to ~5-7 companies, and silent partial failures. This is exactly the behavior an agent needs beyond the safety hint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Length is substantial but every section is front-loaded and earns its place (query construction, dedup expectation, recency semantics, cost model, returns). Structured with summary/returns tags rather than a wall of prose. Slightly verbose for the conciseness bar, but no filler sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description fully supplies the return contract (article row shape and field meanings), plus failure semantics: google_news_available:False with error on total outage, silent partial failure, and InsufficientCreditsError refusal. Nothing an agent needs to call or interpret results is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 67% and the description compensates materially: it explains query composition (keyword-style, one per signal, fanned out in parallel, full-sentence phrasing returns zero) and documents the semantics of lookback_days (smallest day/week/month/year bucket covering it, default 30). on_behalf_of is left to the schema, which already documents it well.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Discover companies from recent news via Google News keyword search') and explicitly positions itself against named siblings (search_news_events, tavily_search, exa_search_news). It even articulates its distinctive edge over those sources (local and trade-press index coverage).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the alternatives it is used alongside and explains the condition that favors it ('hyper-local expansion stories, plant incidents within hours, small-market executive moves the other sources' indexes miss'). It also gives clear operational guidance (prefer event keywords, scope location downstream). It stops short of an explicit when-not rule, deferring routing to the 'News-mode skill'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
import_linkedin_historyImport Linkedin HistoryAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| window | Yes | How far back from today to import: '30d', '3m', '6m' or '1y'. Only history older than what's stored is new. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false, and the description adds substantial context beyond that: it is free, runs in the background, takes tens of minutes to an hour, exposes live state in context, and enumerates failure modes. This is exactly the kind of behavioral detail structured fields cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary/returns structure is front-loaded and purposeful, and the return-shape sentence is compact. The long parenthetical on timing is slightly dense but each clause carries real value, so it is well-sized without being wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by describing the return shape ({started: true, message_backfill} or {started: false, error}) and its failure conditions. Combined with the timing and account-scoping notes, an agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both `window` (with enum values) and `on_behalf_of` are already documented in the schema, setting the baseline at 3. The description adds no window-format or account-scoping detail beyond what the schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The summary states a specific verb (import) and resource (older LinkedIn message history), and anchors it to the UI equivalent 'Import more message history', which removes ambiguity. It doesn't explicitly name the search_linkedin_message_history sibling it differs from, so it falls short of a 5 that would fully route the agent among siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear context for use (importing older history, right after connecting LinkedIn, the up-to-one-hour indexing wait) and states the two blocking conditions (LinkedIn not connected, import already running). It stops short of explicitly saying when to prefer this over fetching/searching existing messages, so no full when/when-not/alternative breakdown.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_agency_clientsList Agency ClientsARead-onlyInspect
The client accounts the caller works as an agency, their own and their teammates',
with each client's credit balance and Slack reply alerts and each person's LinkedIn
sending state. Pass the email of a member of any group but 'team' as on_behalf_of on
a Sliq tool to run that call in that client's account.
Returns {'groups': [{'key', 'name', 'operator_email', 'members': [{'email',
'display_name', 'linkedin'}], 'credits': {'available', 'level' (0 ok, 1 low, 2 almost
out)} | None, 'reply_alerts'}, ...]}. The first group (key 'team') is the caller's own
team, with operator_email and reply_alerts None. On every other group
operator_email is the client's main operator, and reply_alerts is which of the
members' LinkedIn replies post to the agency's Slack channel: 'all', 'interested' (a
reply classified interested or meeting booked, or any reply from a prospect already at
meeting booked), or 'off'. A member's linkedin is
None when they have no LinkedIn account connected, else {'linkedin_reason'
('needs_reconnect' | 'auto' paused by LinkedIn limits until linkedin_until |
'manual' paused by the user | None sending), 'linkedin_until',
'connection_request_until' (connection requests alone held until then, or None)}.
A caller who works no clients gets {'groups': []}.
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only carry readOnlyHint=true. The description far exceeds that, disclosing the full return shape, the special meaning of the first 'team' group (operator_email and reply_alerts are None), the reply_alerts enum semantics, the linkedin pause reasons and their timestamps, and the no-clients edge case. This is rich behavioral context beyond what structured fields provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense and long, but the length is largely earned because no output schema exists and the nested return structure must be conveyed. The purpose is front-loaded, though the embedded return-schema prose is somewhat wall-of-text and could be better organized.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and only one parameter, the description carries the full burden of return-value documentation and does so exhaustively, including group structure, field meanings, enums, None cases, and the empty-result case. Nothing an agent needs to interpret the response is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single `on_behalf_of` parameter is fully documented in the schema itself, so the baseline of 3 applies. The description reinforces the parameter's role by tying it to list_agency_clients output, but adds no syntax or constraint detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: the client accounts the caller works as an agency, both their own and their teammates'. This clearly distinguishes it from siblings like list_teammates and list_agents, and the agent knows exactly what population will be returned.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete usage context by explaining how the returned emails are consumed: pass a member's email (from any group but 'team') as `on_behalf_of` on another Sliq tool to act in that client's account. It does not spell out an explicit when-not-this-tool condition versus alternatives, so it falls just short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_agentsList AgentsARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| include_completed | No | Set to True to include completed agents. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, but the description adds substantial operational detail beyond that: completed agents are excluded by default, trigger_count only counts triggers that can still fire (one-time fired triggers excluded), and the return value is a dict with an 'agents' key that is not directly iterable. This is exactly the kind of context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded and the XML-tagged returns block is justified because there is no output schema. It is slightly redundant, repeating the get_agent_details pointer in both the summary and returns section, but overall it is well organized and each part serves a purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a simple read-only list tool with no output schema, the description fully compensates by documenting the return structure, key access pattern, per-field contents, and omitted fields. With annotations covering safety and the schema covering inputs, nothing an agent needs to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both parameters (on_behalf_of, include_completed) are already fully documented in the schema. The description only restates the default-excludes-completed behavior, adding no syntax, format, or constraint detail beyond what the schema provides; a baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List the user's agents') and enumerates what each summary contains: status, run counts, trigger count. It also names and differentiates the sibling tool get_agent_details(agent_id) for goal, workspace, and full trigger bodies, so an agent can tell the two apart without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly provides the alternative route: use this tool for one-line summaries, and drill into a single agent with get_agent_details(agent_id) when goal, workspace, or full trigger bodies are needed. That effectively communicates both when to use this and when to switch tools, with no inference required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_attachmentsList AttachmentsARead-onlyInspect
List the files in the user's attachment library — id, filename, type, size.
Defaults to global company-context files plus files belonging to the current agent or chat. Set all_agents=True to list the user's whole library across agents. Use a returned id with read_attachment or attach_to_outreach.
| Name | Required | Description | Default |
|---|---|---|---|
| all_agents | No | list the whole library instead of just the current agent's files. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, so the description carries the extra burden and discharges it partly: it discloses the default result scope (company-context + current agent/chat) and what the all_agents flag changes. It does not mention anything about the on_behalf_of acting context, pagination, or result limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences: what it lists first, the default scope second, the widening flag third, and the follow-up tools last. No filler and nothing important buried.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, but the description names the returned fields, the default scope, the flag that changes it, and the downstream consumers, which is everything an agent needs to call and use this read-only list tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description goes slightly beyond the schema's own wording by spelling out what the default scope actually contains (global company-context files plus current agent/chat files), which the all_agents schema description does not state. on_behalf_of is left entirely to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('List the files in the user's attachment library') and even enumerates the returned fields (id, filename, type, size). It also names sibling tools (read_attachment, attach_to_outreach) that consume the output, so an agent can distinguish this listing tool from the attachment-reading tools without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit default scope (global company-context files plus files belonging to the current agent or chat) and an explicit conditional for widening it (all_agents=True). It also routes the agent forward ('use a returned id with read_attachment or attach_to_outreach'). It stops short of stating when this tool should NOT be used versus the other attachment-fetching siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_classifiable_campaignsList Classifiable CampaignsARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already signals a safe read, so the bar is lower, but the description still adds real context: shared-agent scope behavior ('lists only the current shared agent') and the empty-state semantics. It does not, however, mention pagination or ordering, which is a minor gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose, then gives the when-to-use routing and a separate returns block. Every sentence earns its place with no repetition, and structured tags aid scanning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
A read-only list tool with no output schema, yet the description supplies the return shape (campaigns list of {agent_id, name}) and the empty condition. Combined with the annotation, an agent has everything needed to call and interpret it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single on_behalf_of parameter is fully documented in the schema, including its agency-client semantics and the omit-to-act-on-own-account default. The description adds no parameter detail beyond that, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (list) and resource (campaigns) with the exact scope: campaigns the user's/team's prospects belong to, framed as the valid options for criteria.agent_ids. This clearly distinguishes it from siblings like list_message_tag_groups and list_segment_groups.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use it ('read it when setting up the first group') and when it becomes redundant ('once groups exist, each group's classifiable_campaigns carries the same list'), effectively naming an alternative source. Routing is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_message_tag_groupsList Message Tag GroupsARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only supply readOnlyHint=true; the description adds substantial behavioral context beyond that: `criteria` is the persisted classify scope (null axis = all), `stale` means the group was edited since last classification, `classify_running` indicates an in-flight run with row progress, and shared-agent calls restrict `campaigns`/`classifiable_campaigns`/`senders` to the current agent and its owner. These are non-obvious operational traits an agent could not infer from structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Structure is strong and front-loaded: a tight <summary> precedes a <returns> block, so a reader gets the gist in one sentence. The return-field prose and inline JSON blob are dense and long, but with ~20 output fields and no output schema the length is largely earned rather than padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description must document the return, and it does so field by field, including the downstream meaning of `campaigns` vs `classifiable_campaigns` and how each maps to agent_ids on get_message_tag_rates/query_tagged_messages/classify_message_tag_group. Nothing an agent needs to consume the response or chain to the next tool appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter (on_behalf_of) has 100% schema description coverage, so the schema already carries its full semantics including the list_agency_clients provenance. The description adds nothing about this parameter, so the baseline 3 for high-coverage schemas is correct.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The summary states a specific verb and resource ('List the user's message tag groups') and goes further by defining what a tag group actually is ('the dimensions their sent outreach is classified along') with concrete examples. It also positions the tool relative to its consumers ('Read this first to get a `group_id` for the rate / message tools'), so an agent can place it without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a clear sequencing directive — read this first to obtain a `group_id` for the rate/message tools — and names update_message_tag_group for edits and classify_message_tag_group for refreshing stale groups. There is no explicit when-not-to-use guidance or a named sibling list alternative (e.g. list_segment_groups), so it falls short of the 5 bar.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_prospect_eventsList Prospect EventsARead-onlyInspect
Spans every campaign by default; pass agent_id to scope to one. To page further
back, re-call with before set to the oldest returned event's occurred_at.
Dict with count, a summary block (replies_this_week, accepts_this_week,
posts_found_this_week, meetings_booked_this_week), and an events array. Events are newest-first; each is
{id, kind, channel, owner_email, occurred_at, time_precision, subject_type, snippet,
turn, prospect: {id, name, linkedin_url, provider_id, title, company,
has_conversation}, agent: {id, title} | null, queued_followup: {...} |
null}. turn is 'user' when a draft awaits the user's approval, 'sliq'
when a send is queued, else 'none'; queued_followup carries that drafted
next step when one exists. prospect.id is null when the event isn't
attributable to a tracked row.
| Name | Required | Description | Default |
|---|---|---|---|
| kinds | No | Sources to include — any of 'accept', 'linkedin_reply', 'email_reply'. Omit for all three. | |
| limit | No | Max events (default 50, capped at 200). Page older with `before`. | |
| before | No | ISO 8601 timestamp cursor; only events strictly older are returned (pass a prior page's oldest `occurred_at`). | |
| agent_id | No | Scope the events and the summary to one campaign. Omit for the cross-campaign feed. Note `turn` stays prospect-scoped — it can read 'user' off a draft awaiting approval under a different campaign for the same person; `queued_followup` is campaign-matched and won't. | |
| on_behalf_of | No | A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true; the description goes well beyond that by explaining the newest-first ordering, the 7-day summary payload, the default cross-campaign scope versus agent_id scoping, the `turn` semantics ('user' = draft awaiting approval, 'sliq' = send queued), and the null-prospect.id edge case. This is rich behavioral context the agent could not infer from annotations alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a tight summary sentence that captures the scope, the union of sources, and the event annotation. The returns block is long, but with no output schema it is doing necessary work; a few fields (e.g. the full event object shape) could be trimmed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With five optional parameters, no output schema, and a non-obvious cross-source result shape, the description supplies what is missing: the full return structure, the summary block contents, ordering, pagination mechanics, and scoping behavior. Nothing needed to call it correctly is absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the baseline is 3, but the description adds cross-parameter interaction meaning: agent_id scopes events and the summary while `turn` remains prospect-scoped and `queued_followup` is campaign-matched. That nuance explains how parameters interact rather than merely restating defaults.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb+resource ('Read the cross-channel prospect activity feed') and defines the resource as the newest-first union of three specific event sources. It also distinguishes itself from the alternative approach of stitching together the LinkedIn, email, and queue readers, so an agent can identify it without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete triggering questions ('who replied this week', 'who's waiting on me', 'did anyone accept'), names the alternative it replaces, and specifies when to scope with agent_id versus leaving it cross-campaign. Pagination guidance ('re-call with `before` set to the oldest returned event's occurred_at') is explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_segment_groupsList Segment GroupsARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With only readOnlyHint in annotations, the description adds substantial behavioral context: shared-agent scoping for `campaigns`/`senders`, `stale` meaning, `classify_running` progress semantics, and `criteria` null = all with automatic tagging. These are traits an agent cannot infer from the annotation alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded and the `<returns>` block is organized logically around the response shape. It is dense and lengthy for a list tool, but the verbosity is largely justified by the absence of an output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema, the description documents the return structure in full, including nested `criteria`, `tags`, `campaigns`, `classifiable_campaigns`, and shared-agent caveats. Nothing an agent needs to call and interpret this tool is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the sole parameter `on_behalf_of` is fully documented in the schema itself. The description adds no parameter-specific syntax or format beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List the user's segment groups') and defines what a segment group is with concrete examples ('Seniority', 'Role function'). It also distinguishes the tool from downstream rate/people tools by explaining that this is the source of `group_id`.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says 'Read this first to get a `group_id` for the rate / people tools,' giving clear context for when to call it. However, it never states when not to use it or names a sibling list tool as an alternative, so it stops short of the 5 criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_sent_messagesList Sent MessagesARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Case-insensitive text to find in the message or the recipient's name. | |
| limit | No | Page size, at most 200. | |
| offset | No | Page offset. `total` is the count before paging, so the whole tail is reachable. | |
| origin | No | 'sliq' for messages to prospects only, 'imported' for imported conversations only; omit for both. | |
| tag_id | No | Only messages carrying this tag (from list_message_tag_groups); takes precedence over tag_state. | |
| senders | No | In a shared workspace, the teammate emails (yours included, if wanted) whose messages to list; omit for only your own. A teammate who doesn't share conversations, or an email outside the workspace, is dropped; if none remain the list is empty. | |
| positions | No | 'first', 'follow_up' and/or 'reply' to narrow by message type; omit for all. A connection-request note is always 'first'. | |
| tag_state | No | 'all', 'tagged' (at least one tag in any group) or 'untagged' (no tags; a "No match" result counts as untagged). | all |
| prospect_id | No | Only messages sent to this prospect. | |
| provider_id | No | Only LinkedIn messages sent to this LinkedIn provider id — how to list an imported conversation's messages, which have no prospect_id. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint already declaring safety, the description still adds substantial non-obvious behavior: default is the user's own messages only, `senders` expands to teammates, shared-agent calls are scoped to that agent's campaign, imported messages have null prospect_id and are addressed via person.provider_id, and `outcome` is the prospect's funnel stage rather than the message's effect. This is rich context beyond the annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Content is front-loaded and sectioned into summary and returns, and the length is largely justified by 11 parameters and no output schema. The opening sentence is a dense run-on with nested parentheticals that could be broken up, costing a point.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 11-parameter, read-only list tool with no output schema, the definition covers filtering, scoping, and the full return shape (counts, message fields, tags, groups, outcome, look_up banner) including edge cases like imported conversations and in-progress lookups. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter is already documented in the schema. The description only restates a few of them (senders for teammates, tag_state='untagged') without adding syntax or semantics the schema lacks, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a precise verb+resource and scope: every message the user sent, to a prospect or in an imported LinkedIn conversation, with all tag-group tags, mirroring the Analytics → Messages tab. It clearly distinguishes itself from siblings by naming correct_message_classification, list_message_tag_groups, and look_up_imported_people as related but distinct.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly enumerates when to use it ('to see which messages have no tags yet (tag_state='untagged')', 'to read a message's tags across all groups at once', 'to find a message to tag with correct_message_classification') and gives the condition that routes to the alternative. It also states the shared-agent scoping caveat.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_teammatesList TeammatesARead-onlyInspect
The caller's active-workspace teammates — the same roster the Team page shows.
Returns {'teammates': [{'email', 'display_name', 'role' ('admin'|'member'), 'status' ('invited'|'active'), 'is_you', 'joined_at', 'invited_by', 'share_conversations', 'allow_team_actions'}, ...]}, ordered by join time. Includes the caller (flagged 'is_you'). A solo user with no workspace gets {'teammates': []}.
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already covers the safety profile, but the description adds real context beyond annotations: ordering by join time, inclusion of the caller via 'is_you', the exact field set on each teammate, and the solo-user empty-list case. It does not cover pagination or any rate limits, so it stops short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first clause, followed by return shape, ordering, and edge cases. The field enumeration is dense but justified given there is no output schema; nothing reads as filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description correctly compensates by documenting the return object, ordering, and the empty-workspace case, which is what an agent needs to interpret the response. The only gap is absent routing guidance to sibling listing tools.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single on_behalf_of parameter is fully documented in the schema, so the schema does the heavy lifting. The description never mentions the parameter, so it adds nothing over the structured field — the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('list the caller's active-workspace teammates') and pins the scope with 'the same roster the Team page shows', which cleanly separates it from siblings like list_agency_clients and list_team_shared_agents. An agent can identify the tool without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied through scope language ('active-workspace', the Team page analogy) and the on_behalf_of schema note about agency clients. There is no explicit when-to-use versus list_team_shared_agents or list_agency_clients, and no stated prerequisites such as needing workspace membership.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
look_up_imported_peopleLook Up Imported PeopleAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| senders | No | In a shared workspace, the teammate emails whose imported people to look up; omit for only your own. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial context beyond annotations: it runs in the background, writes results to person records, may take many minutes, costs credits with specific pricing, skips duplicate work, and raises when balance is insufficient. These are exactly the operational traits an agent needs before invoking a non-readonly background job.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Dense but front-loaded: purpose, operational behavior, cost, exclusions, and error behavior are all presented efficiently. The separate returns section keeps the main summary focused.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Complete for this tool despite no output schema: the description explains the return shape, background behavior, cost implications, and failure conditions. An agent has enough information to call it correctly and set user expectations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are already documented in the input schema. The description does not add significant semantics for senders or on_behalf_of beyond what the schema provides, making the baseline 3 appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: looking up LinkedIn profiles of people with imported conversations whose profiles have not yet been looked up. It also identifies the exact UI equivalent, the Messages tab's Look up, which distinguishes it from broader enrichment siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly explains when to use it and includes exclusion behavior: people already looked up or currently in a lookup are skipped. It does not explicitly compare against sibling tools such as enrich_linkedin_profiles, so it falls short of full alternative guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
manage_email_outreach_queueManage Email Outreach QueueADestructiveInspect
Bulk operations (status, approve-all, cancel_all) act on the current agent's queue when run inside an agent, and across the whole account from account-level chat — except a bulk cancel_all or approve-all, which from account-level chat is rejected unless agent_id names the campaign, so it can't hit every campaign at once. Targeting one person by recipient_email reaches them in any agent. The next send time and per-mailbox usage stay account-wide (the send rate is per-mailbox).
For pausing/resuming the campaign, use update_agent to set the agent status to 'paused' or 'active' instead. Dict with action result and current queue status
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | For 'update' — new body text. | |
| action | Yes | One of: - 'status': Get full queue details (pending/sent/failed items). The `pending_items` / `pending_approval_items` lists cap at 50 each, each with a `pending_truncated` / `pending_approval_truncated` flag — when a list's flag is True, pass `offset` (offset += 50) to read the next page. Each item carries the `mailbox` it sends from; the account-level `mailboxes` list gives each mailbox's remaining daily headroom. The top-level `cancelled_by_kind` maps each cancel cause to its count (`recipient_replied`, `task_deleted`, `auto_shelved`, …; pre-taxonomy rows under `unknown`) — read it to answer "how much outreach was cancelled, and why?". - 'update': Edit a queued email's subject and/or body. Requires recipient_email. Pass subject and/or body to change — no cancel-and-re-queue needed. Send order is derived, not per-row scheduled; to pause or resume the campaign use update_agent (see the note above). A draft on a step running an A/B test carries `ab_version` ('A', 'B', or 'left_out' when its send won't count) on its status row and in this action's result; updating one takes ab_version too — the version the edited draft reads as — or ab_version alone to re-file it unedited. - 'approve': Send authorization belongs to the user, so this action needs a message that arrived after the drafts were queued and explicitly says to approve or send them. A message from before the drafts existed cannot authorize them — a request to review them, to approve them later, or to be able to approve from chat means: report that the queue is awaiting approval and end your turn; the user's next message decides. Once authorized: if recipient_email is given, approve only that item, otherwise approve ALL pending_approval items. An email whose subject or body still holds a [[slot]] for the user to fill in stays awaiting approval and comes back in `still_held` with the slot to fill: tell the user, or fill it with 'update' once they give you the wording. From account-level chat an approve-all is rejected unless agent_id names the campaign — it must not fire every campaign's drafts into sending at once. - 'cancel': Cancel a queued email. Pass recipient_email to cancel that one person. To cancel the ENTIRE pending queue — which drops drafts the user already approved — you must pass cancel_all=True; an unscoped cancel without it is rejected. From account-level chat a cancel_all is rejected unless agent_id names the campaign to cancel — it must not clear every campaign's queue at once. | |
| offset | No | For 'status' only — skip this many items in each queued list before returning the next 50. Use it to page through a queue larger than 50 (offset=50 for items 51-100, etc.). | |
| subject | No | For 'update' — new subject line. | |
| agent_id | No | For 'cancel' and 'approve' — scope a bulk cancel_all / approve-all to one campaign's queue. Required from account-level chat (no active agent) and in any call with on_behalf_of, where an unscoped bulk cancel or approve is rejected; call list_agents to get the id (list_team_shared_agents for a teammate's campaigns). | |
| ab_version | No | Required for 'update' of a draft in an A/B test — the version the edited draft now reads as: 'A', 'B', or 'left_out' when it fits neither, so its send isn't counted in the test; its current version when the edit doesn't change that. Omit for a draft not in a test. | |
| cancel_all | No | For 'cancel' only — confirm a queue-wide cancel when no recipient_email is given. Guards against silently tearing down the whole campaign. | |
| on_behalf_of | No | A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| recipient_email | No | For 'update', 'approve', and 'cancel' — target a specific recipient. Leave empty to apply to all items (not valid for 'update'; for 'cancel', requires cancel_all=True). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations mark this as destructive, and the description substantiates that with critical warnings: cancel_all drops already-approved drafts, approve-all can fire every campaign unless scoped, and account-level bulk operations are rejected without agent_id. It also explains the approval authorization requirement in useful detail beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded and structured around the main purpose, bulk behavior, and the update_agent alternative. It is lengthy for a tool description, but most of the length is justified by the destructive multi-action semantics and approval edge cases.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given nine parameters, destructive bulk operations, approval authorization nuances, and no output schema, the description provides enough behavioral context for correct invocation. The action enum in the schema covers status return details, so the description does not need to restate them.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description still adds meaningful cross-parameter context about bulk scope, account-level rejection behavior, and the need for agent_id in certain bulk calls, though much of the per-parameter detail is already present in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the exact resource (email outreach queue) and the concrete operations: check status, update/cancel items, and approve pending items. It also distinguishes the tool from update_agent for pausing/resuming campaigns, so an agent can select it confidently.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use each action (status, update, approve, cancel), including how bulk operations behave inside an agent versus account-level chat. It also gives a direct alternative for campaign pausing/resuming and clear approval authorization rules.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
manage_inboxManage InboxBInspect
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | The operation to perform. One of: - 'list_labels': List available labels (Gmail) or categories (Outlook). - 'apply_label': Apply a label/category to emails. Creates if it doesn't exist. Requires email_ids and label_name. - 'remove_label': Remove a label/category from emails. Requires email_ids and label_name. - 'archive': Archive emails (removes from inbox, keeps in account). Requires email_ids. - 'mark_read': Mark emails as read. Requires email_ids. - 'unsubscribe': Unsubscribe from a mailing list via List-Unsubscribe header. Requires email_ids (one email). ALWAYS confirm with the user before calling this. - 'search_by_label': Search emails by label (Gmail) or category (Outlook). Requires label_name. Returns matching emails directly from the provider API. | |
| mailbox | No | Email address of a connected mailbox. Optional — when not given and email_ids are present, the mailbox is inferred from the email row's connected_email. When neither is available and the user has multiple mailboxes, ask which to use. | |
| provider | No | Which email account to use ('outlook' or 'gmail'). Only needed if the user has both connected and no email_ids are provided. | |
| email_ids | No | List of email IDs from search_emails results. | |
| label_name | No | Label name for apply_label, remove_label, or search_by_label actions. | |
| max_results | No | Maximum number of emails to return for search_by_label (default 20, max 50). | |
| on_behalf_of | No | A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| exclusive_labels | No | For apply_label only. A set of mutually exclusive labels (e.g. inbox organization categories). When provided, applying a label automatically removes any other exclusive_labels from older messages in the same thread. This keeps one label per thread. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare this is a write operation but not destructive. The description adds that it supports labeling, archiving, marking read, and unsubscribing, but omits side effects such as archive removing messages from the inbox while keeping them in the account, apply_label creating labels when absent, and unsubscribe requiring confirmation. Those details exist only in the schema, so the description adds only limited behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded and wastes no space. The summary states the resource and main operations, and the returns note is useful given there is no output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The rich schema compensates for much of the thin description, including detailed action semantics and requirements. However, the description does not cover routing versus siblings, mailbox/provider selection, or confirmation-sensitive behavior, and its return description is only generic.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the action enum thoroughly documents action-specific requirements and parameters. The description itself adds no parameter-level meaning, so the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear verb and resource: "Manage emails in the user's inbox," and names several supported operations. It is specific enough to identify the tool's domain, though "manage" is broad and it does not explicitly distinguish this tool from siblings like search_emails or get_emails.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no explicit when-to-use or when-not-to-use guidance relative to alternatives. It does not explain that email IDs likely come from search_emails, nor when to use search_by_label versus search_emails, leaving routing to inference from the schema.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
manage_linkedin_invite_queueManage Linkedin Invite QueueADestructiveInspect
Bulk operations (status, approve-all, cancel_all) act on the current agent's queue when run inside an agent, and across the whole account from account-level chat — except a bulk cancel_all or approve-all, which from account-level chat is rejected unless agent_id names the campaign, so it can't hit every campaign at once. Targeting one person by target_provider_id reaches them in any agent. Pacing / next_scheduled / daily usage stay account-wide (the LinkedIn rate limit is per-account). Dict with action result and current queue status
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | One of: - 'status': Get full queue details (pending/sent/failed items with names and times). The `pending_items` / `pending_approval_items` / `failed_items` lists cap at 50 each, each with a matching `*_truncated` flag — when a list's flag is True, more rows exist past this page; pass `offset` (offset += 50) to read them. Each `failed_items` row carries `last_error`, the reason that send failed — read it to answer "why wasn't X sent?". `pending_approval_items` rows carry `hold_reason` when the row was held for a reason beyond plain approval gating (e.g. the resolver's name-mismatch hold, or a [[slot]] the user has to fill in). Queued post comments and reactions carry `post_url` and a `post_text` excerpt of the post the action targets, so you can tell which post a queued comment is on; `post_text_truncated` True means the target post ran longer than the excerpt shown. All four lists share one field shape; a `resolving_items` row (profile not yet resolved) has a blank `target_provider_id` and its `message` is the copy that will send once it resolves — edit or cancel it by its `target_identifier`. Every `pending_items` / `resolving_items` row carries `projected_send_day` + `projected_send_time` (a resolving row's is its projected lookup time, or its projected send time once it's a resolve-then-send) and a `queue_position` — the row's place in the one ordered lookup+send timeline, so the two lists interleave into what actually fires next. The top-level `send_forecast` is account-wide: `days` counts queued sends per day and `eta_date` is the day the last one goes out. A `resolve` row in `resolving_items` queues no invite itself, so `estimated_invites` counts per day the connection request each is expected to lead to, and `invites_sent_by` is the day the last connection request goes out, queued or estimated (null when none is due or one won't fit in the forecast window) — read it to answer "when will all my invites be sent?". The top-level `cancelled_by_kind` maps each cancel cause to its count (`recipient_replied`, `task_deleted`, `auto_shelved`, …; pre-taxonomy rows under `unknown`) — read it to answer "how much outreach was cancelled, and why?". - 'update': Update a queued item's message text. Name the item by target_provider_id (a pending row's pid) OR, for a resolving row that has no pid yet, by target_identifier (its URL/slug, shown on the row). Use this to edit a queued message — including on resolving rows, which still hold the message that will send once their profile resolves. When a target has more than one queued action — a post with both a comment and a reaction, or a person with a connection request and a follow — also pass action_type to name which one to edit. A draft on a step running an A/B test carries `ab_version` ('A', 'B', or 'left_out' when its send won't count) on its status row and in this action's result; updating one takes ab_version too — the version the edited draft reads as — or ab_version alone to re-file it unedited. - 'approve': Send authorization belongs to the user, so this action needs a message that arrived after the drafts were queued and explicitly says to approve or send them. A message from before the drafts existed cannot authorize them — a request to review them, to approve them later, or to be able to approve from chat means: report that the queue is awaiting approval and end your turn; the user's next message decides. Once authorized, this moves pending_approval items to 'pending'; they then send in queue order at your account's pace. If target_provider_id is given, approve only that item. Otherwise approve ALL pending_approval items (optionally filtered by action_type). A connection request, message or InMail whose text still holds a [[slot]] for the user to fill in stays awaiting approval and comes back in `still_held` with the slot to fill: tell the user, or fill it with 'update' once they give you the wording. Only touches items already awaiting approval — it does NOT run pending profile lookups or pre-approve resolving rows (those aren't in the approvals UI, so nobody has reviewed them; they resolve on their own schedule and, if the user's settings gate the action, surface for approval once resolved). From account-level chat an approve-all is rejected unless agent_id names the campaign — it must not fire every campaign's drafts into sending at once. - 'cancel': Cancel a queued item. Pass target_provider_id to cancel that one person or post — every queued action for them — or target_identifier to cancel a resolving row that has no pid yet. To cancel the ENTIRE pending queue — which drops drafts the user already approved — you must pass cancel_all=True; an unscoped cancel without it is rejected (an action_type or node_id filter alone is still a bulk cancel and also needs cancel_all=True). Narrow a cancel_all to one flow stage with node_id (e.g. restart only the m2 messages without touching m3) — the only way to separate two stages that share an action_type. From account-level chat a cancel_all is rejected unless agent_id names the campaign to cancel — it must not clear every campaign's queue at once. - 'pause': Pause the queue (stops sending, keeps items queued). Not available on a teammate's behalf; approve, cancel, or update their items instead. Pass resume_at to schedule an automatic resume ("pause while I'm on vacation, resume July 20"); without it the pause is indefinite and only an explicit 'resume' restarts sending. - 'resume': Resume the queue (pending items drain in queue order at your account's pace). Not available on a teammate's behalf; approve, cancel, or update their items instead. | |
| offset | No | For 'status' only — skip this many items in each queued list before returning the next 50. Use it to page through a queue larger than 50 (offset=50 for items 51-100, etc.). | |
| message | No | For 'update' — new message text. 300 chars max for connection requests. | |
| node_id | No | For 'cancel' only — narrow a cancel_all to the queue rows on one flow node (the row's flow stage, e.g. 'm1', 'm2'), parallel to action_type. action_type is the send mechanism (connection_request / message / inmail / ...), not the stage, so every message stage shares action_type='message' — node_id is the only handle that isolates one (m1 and m2 are both action_type='message', so only node_id restarts m1 without touching m2). Still a bulk cancel — needs cancel_all=True, not a substitute for it. | |
| agent_id | No | For 'cancel' and 'approve' — scope a bulk cancel_all / approve-all to one campaign's queue. Required from account-level chat (no active agent) and in any call with on_behalf_of, where an unscoped bulk cancel or approve is rejected; call list_agents to get the id (list_team_shared_agents for a teammate's campaigns). | |
| resume_at | No | For 'pause' — ISO datetime when sending should automatically resume. Must be in the future. A naive datetime is interpreted in the user's timezone. Use resolve_date first for natural language ("July 20", "in 2 weeks"). | |
| ab_version | No | Required for 'update' of a draft in an A/B test — the version the edited draft now reads as: 'A', 'B', or 'left_out' when it fits neither, so its send isn't counted in the test; its current version when the edit doesn't change that. Omit for a draft not in a test. | |
| cancel_all | No | For 'cancel' only — confirm a queue-wide cancel when no target_provider_id is given. Guards against silently tearing down the whole campaign. | |
| action_type | No | The kind of queued action to target, as shown on each queue row in the 'status' result (e.g. 'comment', 'reaction', 'connection_request', 'follow'). For 'update', pass it when a target carries more than one queued action so the right row is chosen. For 'approve' and 'cancel', it narrows the bulk operation to one kind — a bare target_provider_id on 'cancel' cancels every queued action for that person or post. Not a substitute for cancel_all. | |
| on_behalf_of | No | A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| target_identifier | No | For 'update' and 'cancel' of a resolving row (profile not yet resolved, blank target_provider_id) — the row's URL/slug, shown on the row in the 'status' result. Ignored when target_provider_id is set. | |
| target_provider_id | No | For 'update' and 'cancel' — the person's pid or post URN to target. A resolving row has no pid yet — target it by target_identifier instead. For 'cancel', leave empty (with cancel_all=True) to cancel all pending items. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds rich behavior beyond annotations: randomized human-like pacing inside a per-user activity window, account-wide LinkedIn rate limits, approval authorization tied to user messages, cancel_all dropping already-approved drafts, pause indefinite vs resume_at, and account-level chat restrictions on bulk operations. Annotations only mark it destructive/not read-only; the description explains exactly what gets destroyed and when calls are rejected.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the queue purpose and tool-selection guidance before the account/agent scoping caveats. Despite dense sentences, every clause supports correct invocation (activity window, approval authorization, bulk restrictions) and there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 12-parameter destructive queue manager with no output schema, the definition covers actions, approval prerequisites, cancellation safety, paging, and account/agent scoping. The top-level returns block is minimal, but the action schema supplies detailed status output fields and error/hold semantics, making the overall definition complete enough.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema does the heavy lifting. The top-level description still adds scope semantics not fully captured by the parameter schema—bulk operations act on the current agent's queue inside an agent but across the account at account level, and target_provider_id reaches the person in any agent—so it earns above the baseline but not full credit.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Manage the LinkedIn outbound queue,' and enumerates the actions (status, update, approve, cancel, pause, resume). It also distinguishes the tool from query_prospects for post-send prospect lifecycle, so an agent can identify its scope without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says to use it to check status, update/cancel items, or pause/resume the queue, and routes full prospect lifecycle lookups to query_prospects. The per-action schema reinforces when each action applies, but the top-level description already gives clear when-to-use and the main alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mark_monitor_events_notifiedMark Monitor Events NotifiedAInspect
Mark monitor events as included in a digest notification. Call this after composing a Slack digest to prevent re-notifying the same events. Only affects events belonging to the current user; in an agent run it scopes to the run's agent.
| Name | Required | Description | Default |
|---|---|---|---|
| event_ids | Yes | List of agent_monitor_events IDs to mark as notified | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare this is a non-read, non-destructive mutation, and the description adds the behavior that matters: the effect is to suppress re-notification, it is scoped to the current user and, in an agent run, to the run's agent. It does not say whether the call is idempotent or what happens to IDs outside that scope, so it stops at 4.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the action, then the usage trigger, then the scoping caveat. No restatement of the title and no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter mutation with no output schema and annotations covering the safety profile, the description covers action, timing, and scope adequately. It leaves open the return value and partial-failure behavior, which is a minor rather than blocking gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds a real constraint on which event_ids are valid — only events belonging to the current user or the run's agent are affected. That is scope information the schema does not express.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (mark monitor events) plus the exact state transition applied (as included in a digest notification). An agent can tell immediately this is the write-side counterpart to search_monitor_events rather than a search or listing tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Call this after composing a Slack digest to prevent re-notifying the same events" gives a concrete trigger condition and ordering, which is exactly the guidance needed for a bookkeeping mutation. It stops short of naming when not to call it or pointing at search_monitor_events as the discovery step, so it is short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
matches_icpMatches IcpARead-onlyInspect
Runs the two mechanical checks — employee-count band and funding-stage
normalized membership, both include-on-null — and combines them with the
caller's industry and location judgments. Industry fit and location are NOT
decided here: the agent judges both and passes its verdicts in.
{passed, location_ok, size_ok, size_null_included, size_contested,
industry_ok, funding_stage_ok, funding_stage_null_included}. passed is
the AND of location_ok, size_ok, industry_ok, funding_stage_ok.
Size is not a pure hard gate — it includes-on-uncertain-data like funding, because Apollo undercounts privately-held / industrial firms:
size_null_includedTrue → Apollo had no headcount; included anyway.size_contestedTrue → Apollo's count is below the smallest band floor (probably an undercount); included anyway. Either flag → score that Company-size evaluation 5 (amber "verify"), not
A count in a gap between bands or above the ceiling is a confident mismatch → size_ok False (drop). Score Industry from
industry_verdict(fit → 10, uncertain → 5) and Location fromlocation_verdict(in → 10, uncertain → 5).
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| employee_count | No | Candidate headcount. None when unknown. | |
| industry_verdict | Yes | The agent's industry judgment — 'fit' (confidently in an ICP industry), 'uncertain' (plausibly in, but signals thin or conflicting — kept and flagged), or 'off' (drop). | |
| location_verdict | Yes | The agent's judgment of whether the candidate's HQ lies within the configured ICP locations — 'in', 'uncertain' (plausibly in, but the location data is partial or it sits just outside a listed place — kept and flagged), or 'out' (drop). | |
| icp_funding_stages | No | Configured funding stages — optional; unset or empty makes the funding check a no-op (everything passes it). | |
| latest_funding_stage | No | Apollo's title-case stage string ('Series B', 'Seed'); None/absent under patchy coverage. | |
| icp_employee_count_ranges | Yes | Configured size ranges, each a 'min,max' pair ('25,200'), an empty side meaning unbounded ('500,', ',50'). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint already tells the agent this is side-effect free, but the description adds substantial context beyond that: the include-on-null semantics for both size and funding, the meaning of 'under-count' vs. confident mismatch, and even the downstream scoring consequence (amber 5 vs. 10). This is exactly the kind of behavioral nuance annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary block followed by a returns block is a sensible structure, and because there is no output schema the return-value prose earns its space. The returns section is dense and repetitive in places (the include-on-null rationale is restated across flags), which keeps it from a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the full burden of explaining the return object and does so completely, enumerating every field, defining 'passed' as the AND of the four verdicts, and giving scoring guidance. All seven parameters are accounted for and no obvious decision-relevant information is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so per the baseline a 3 is the floor, but the description adds real meaning about how parameters combine — the two mechanical checks are AND-ed with the caller's verdicts, and an unset funding-stage list makes that check a no-op. It never elaborates on on_behalf_of or employee_count beyond what the schema says, keeping it out of 5 territory.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence names a precise role: a per-candidate ICP gate scoped to 'find-companies News mode'. The description goes further and delineates what the tool does not do ('Industry fit and location are NOT decided here'), so an agent can distinguish this mechanical gate from judgment-bearing siblings like query_companies without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clear context for use — per-candidate evaluation inside the find-companies News flow — plus an explicit boundary that the agent must supply industry/location verdicts rather than derive them here. It does not name a specific alternative tool or state when this gate should be skipped, so it stops short of a full when/when-not/alternatives statement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
move_prospect_to_nodeMove Prospect To NodeAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| node_id | Yes | A connection_request / send / action / manual / gift / automation / decision / terminal node id in the agent's sequence (not start). | |
| agent_id | Yes | ID of the agent whose flow the prospect is on. | |
| prospect_id | Yes | The prospect's `id` — the outreach_prospects primary key, as returned on every query_prospects row and shown on the flow roster line. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false, so the description carries the behavioral load and does so well: it discloses that each call appends a fresh position row and preserves prior positions on the log (a key non-destructive mutation trait), and that the move fires the node's on_enter enactment on the next run with concrete consequences per node kind. It stops short of stating auth/credit requirements, but is otherwise rich.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose and is structured with summary/returns blocks, but the middle is dense with stacked em-dash parentheticals and repeated node-kind enumerations (once in the purpose, again in the enactment sentence) that partially duplicate each other. Efficient enough, but not maximally tight.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-param mutation tool with no output schema but an inline returns block, the description covers routing intent, side-effect behavior, idempotency-like re-route semantics, and the enactment consequence. An agent has everything needed to call it correctly without further context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four parameters are already documented in the schema, including node_id's kind list and on_behalf_of's agency-client semantics. The description adds no additional parameter syntax or format detail beyond what the schema provides, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (move) and resource (prospect onto a flow node) and explicitly carves out the subset of moves it handles — those the automatic position writes can't place. It names the sibling concept (automatic paths) so an agent can distinguish this tool from the flow-definition tools like update_node and get_campaign_flow.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives both when-to-use (decision-arm move and `then` target the automatic paths don't cover) and an explicit when-not / do-not ('do not compose the send here'), plus the re-route case (calling again corrects or re-routes). Nothing is left to inference about intent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_agent_peopleQuery Agent PeopleARead-onlyInspect
Each row: id (the person id query_people uses), display_name, spine (title, company,
location, headline), linkedin_url, linkedin_provider_id, email, and:
degree: the user's LinkedIn degree to the person, '1', '2', '3' or 'out_of_network'; null when unknown. A person the user is connected to reads '1'.intro: the warm-intro check.stateis one of not_checked, checking, queued, found, cold, connected, unresolved.connectorslists the user's 1st-degree connections who know the person (name, headline, identifier, provider_id).mutual_connections_truncatedtrue meansconnectorsis a sample of a longer list.shared_connections_countis LinkedIn's total.result_idis the search row the check read, null when the person has none.outreach_stage: the person's lead-funnel bucket on this agent, '' when not in outreach here.prospect_id,email_stageandlinkedin_stageare their prospect row on this agent, null when not enrolled.removal_reasonsays why they were removed from the campaign, '' when they weren't or no reason was recorded.listsandsources: the search lists and discovery tools that surfaced the person.columnsholds the researched [label, value] pairs.verdict,verdict_reasonandrejected_on: the curation recorded on the person's search rows, as the Verdict column shows it. 'rejected' when every row is rejected, 'qualified' when any row is qualified, else 'unclear' (screened but too thin to judge) when any row is, else null, with the reason from the latest row carrying it. On agroup_by='list'bucket page, or under a "run" filter whose runs all saved to one list, it's the verdict on that list's row.rejected_onis {lists, reason}: the lists whose own verdict is 'rejected' while the person'sverdictisn't, with the latest rejected row's reason; itslistsis empty otherwise and on those two reads. A person rejected on every row is left out unless a "verdict" filter asks for 'rejected', a "run" filter is set orinclude_rejectedis set — the same as the user's default view — though a tracked prospect always shows.criteria, only withinclude_criteria: why the person matched, as their opened row shows it. Per list they're in, {list_name, evaluations}; each evaluation is acriterionwith thereasoning, a verdict (satisfied'yes', 'no' or 'unclear', or a numericscorewhere 7 and up is met) and http(s)references. Criteria run about 2KB per person, so ask for them on a narrow read (aq, one bucket, or a smalllimit) or from run_code. A list where all of the person's rows are rejected is left out unless the read includes rejected people.
To count people per group, pass group_by alone. To list one group's people, pass group_by
plus a bucket key taken from those counts. Otherwise you get everyone, sorted by name. Page
until next_cursor is null by passing it back as cursor. A direct call returns 25 rows by
default; for a wide pull, call this from run_code with limit up to 200 (nothing truncates
there) and print only the fields you need.
A dict. A group_by without a bucket returns group_counts, a list of
{key, count} with the largest group first and the '' group last ("degree" keeps
closest-first order; "connector" entries add label), and total, the matching people
each counted once. Every other call returns results (rows shaped as above) and
next_cursor, plus total (every matching person) when
group_by is omitted. Every call also returns filtered_out_count: how many people this
same call matches once a "verdict" filter on 'rejected' is added to filters, with the same
q, group_by and segment_group_id and no bucket, each person counted once. Only under
the same search, filters and grouping as the user's People table does it equal the number
that table shows as "didn't match".
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | A substring matched against name, title, headline and company. | |
| limit | No | Rows per page (default 25, max 200). | |
| bucket | No | One key from `group_counts` for the same `group_by`; returns that group's people. Needs `group_by`. | |
| cursor | No | The `next_cursor` from the previous page; omit for the first page. | |
| filters | No | Clauses that must all match. For "degree", "outreach_stage", "list" or "source", use {"column_key": <dimension>, "operator": "is_any_of", "values": [keys]} with keys as group_counts returns them ('' matches no value). For "segment", use the same shape with the segment keys above. For "verdict", use the same shape with any of 'rejected' (the people the user's default view filters out, labelled "Didn't match" in their table), 'qualified' and 'unclear'; it matches each person's `verdict`, or with group_by="list" their verdict on each list, and 'rejected' brings the rejected people in without `include_rejected`. For "run", use the same shape with run ids from `query_search_runs` as strings; it matches the people those search runs found, rejected ones included. When those runs all saved to one list and group_by isn't "list", a "verdict" filter reads that list alone, as on its group_by="list" bucket page. For "name", "title", "headline", "company" or "location", use {"column_key": <column>, "operator": "contains", "text": <substring>}. | |
| agent_id | Yes | The agent whose People tab to read. | |
| group_by | No | The dimension to count or to pick a bucket from. "degree" keys are '1', '2', '3', 'out_of_network'; "connector" keys are a connector's provider_id (else their identifier or name) and carry the connector's name as `label`; "outreach_stage" keys are funnel buckets; "list" and "source" keys are list names and discovery tools; "segment" keys are tag names in `segment_group_id`'s group, plus 'No match' for people the classifier couldn't place; "title", "company" and "location" key on the person's own value. A '' key is the group with no value (for "segment", people the group hasn't classified). A person with several lists, sources, connectors or tags counts in each. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| include_criteria | No | Also return each row's `criteria`. Default False. | |
| include_rejected | No | Also return (and count) the people left out as rejected. Default False. | |
| segment_group_id | No | The segment group (from list_segment_groups or get_segment_funnel) a "segment" filter or group_by reads. With it, each row also carries `segment`, its tag names in that group. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With only readOnlyHint=true from annotations, the description carries the real behavioral burden and does so richly: the default view's exclusion of fully rejected people and how include_rejected/verdict/run filters override it, the ~2KB cost of include_criteria and the recommendation to narrow the read, the 25-row direct default vs 200 via run_code, and pagination until next_cursor is null. It also explains non-obvious derived semantics (filtered_out_count, rejected_on, verdict rollup rules) that an agent could not guess.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Roughly a screen and a half of prose for an 11-parameter read is long, but it is front-loaded with a one-paragraph summary, then organized into per-field bullets and a separate returns block, and nearly every clause carries decision-relevant detail (degrees, intro state machine, verdict rules, pagination). It is dense rather than padded, though the row-field enumeration could be trimmed for a casual reader.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description fully compensates with a <returns> section covering group_counts shape and ordering, results/next_cursor/total, and the subtle filtered_out_count definition relative to the user's People table. Combined with the row-shape listing, an agent has everything needed to call it and interpret the response.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is already 100%, so the baseline is 3, but the description adds real meaning beyond it: it ties group_by/bucket together as a two-step count-then-drill pattern, states the practical cost of include_criteria and the workaround (a q, one bucket, or a small limit, or run_code), and explains that include_rejected aligns with the user's default view. These are operational semantics the schema alone does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Read one agent's People tab: the people its searches found plus the prospects it tracks') and immediately names the sibling it overlaps with ('the person id query_people uses'). It also declares a unique capability ('the only read that carries each person's LinkedIn degree and warm-intro connectors'), which lets an agent distinguish it from query_people and query_segment_people without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Spells out the questions it answers ('who is 1st-degree, who can introduce the user to someone, how many people are in each list, source or outreach stage'), the alternative read for wide pulls ('call this from run_code with limit up to 200'), and the two calling modes ('pass group_by alone' to count vs 'group_by plus a bucket' to list a group). When-to-use and alternatives are explicit rather than inferred.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_analyticsQuery AnalyticsBRead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | Number of days of snapshots to look back (default 7). Use `days=0` to fetch only the most recent snapshot — which under the daily-window collector covers yesterday UTC. This is the right call for the setup-time "show the user a sample of yesterday's data" flow, since it matches what the scheduled digest will show. Ignored for metric="digest_view". | |
| metric | Yes | One of "traffic", "sources", "top_pages", "top_clicks", "engagement", "daily_digest", "digest_view", "custom". Use "digest_view" to read exactly what the user sees on this agent's Analytics Output tab — the derived today + history entries (each with week-over-week deltas) + PostHog link; the other metrics return raw snapshot data. | |
| custom_query | No | Raw HogQL SQL query (only for metric="custom"). Must be a valid SELECT statement, e.g. "SELECT properties.$pathname as path, count() as views FROM events WHERE event = '$pageview' GROUP BY path ORDER BY views DESC LIMIT 10". Do NOT pass natural language — this is sent directly to the PostHog query API. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already tells the agent this is a safe read, so the description bears a lighter burden. It adds the data provenance (PostHog) and a one-line return shape, but says nothing about auth/on-behalf-of constraints, rate limits, or the fact that metric="custom" executes raw SQL against a third-party API.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Extremely short and front-loaded, with the purpose in the first sentence. The <returns> block ('Dict with the requested analytics data') is largely filler that repeats nothing useful, but overall there is no verbosity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with eight metric modes, a custom HogQL path, and an agency impersonation parameter, the description is very thin. The schema compensates for most of it, and no output schema exists so returning a terse shape is acceptable, but the agent gets no orientation on how the modes differ.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds no parameter meaning beyond the schema; the metric enum, days semantics, and HogQL rules are all documented in the schema itself. Nothing in the description clarifies or corrects those.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The summary states a concrete verb (Query) and resource (the user's website analytics) plus the data source (PostHog), which is enough for an agent to identify the tool. It does not need to differentiate from siblings because no other analytics tool exists in the set, but it also never frames the eight metric modes it supports.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use guidance, no mention of alternatives, and no prerequisites. The only routing help an agent gets (which metric to pick, days=0 for a single snapshot) lives entirely in the schema's parameter descriptions, not in the tool description.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_companiesQuery CompaniesARead-onlyInspect
Columns: id, display_name, domain (normalized bare host), linkedin_company_id, owner_email,
data (JSONB), created_at, updated_at. Sliq's CRM is deals and tasks: to see whether a company
has a deal or a task, use query_deals (status='all') / query_crm_tasks with company_id.
JSONB queries: data->>'some_key' ILIKE '%...%'.
Firmographic columns (captured from company enrichment; blank/NULL until enriched): linkedin_url (bare slug), industry, description, location (HQ), logo_url, employee_count, follower_count, founded_year, company_type, annual_revenue, total_funding, latest_funding_stage.
To list the people you know at a company, take an id from here and call query_people with
where_clause="company_profile_id = <id>".
Pass group_by for per-bucket counts over ALL your companies instead of a row list — a
whole-set aggregate, never capped at the 200-row limit, so it answers "how many companies
per industry/location" without paging. Ignores where_clause.
In row mode (group_by omitted) — a dict with count, truncated, and items array (each row
{id, display_name, domain, linkedin_company_id, owner_email,
linkedin_url, industry, description, location, logo_url, employee_count, follower_count,
founded_year, company_type, annual_revenue, total_funding, latest_funding_stage, data,
created_at, updated_at}).
In aggregate mode (group_by set) — a dict {group_by, groups} where groups is a list of
{key, count} ordered by count descending.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max results (default 50, capped at 200). | |
| offset | No | Rows to skip for paging (default 0). When the result is truncated, re-call with offset += limit for the next page. | |
| group_by | No | Aggregate mode, returned instead of the row list — whole-set per-bucket counts over all your companies (not capped by `limit`), bucketed by "industry" or "location". Omit for the row list. | |
| order_by | No | SQL ORDER BY (default: created_at DESC). | created_at DESC |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | No | SQL WHERE condition (default: all your companies). Examples: "domain = 'stripe.com'", "display_name ILIKE '%acme%'". | 1=1 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnlyHint annotation: explains rows accrue from contacts' employers/searches/domain lookups, that a company may have no linked people, that firmographic columns are blank/NULL until enriched, and that group_by is a whole-set aggregate never capped by limit and ignores where_clause. This is exactly the kind of context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose, then logically grouped into columns, firmographics, relationships, and aggregation, with a separate returns block. It is lengthy, but each block (routing, column list, group_by semantics) carries distinct decision-relevant information, so almost every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter, heavily-siblinged query tool with no structured output_schema, the description supplies the return shape inline (row mode vs aggregate mode), paging behavior, and relationship navigation. Nothing an agent needs to select or invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the baseline is 3; the description adds real interpretation on top, clarifying that group_by returns per-bucket counts over ALL companies rather than a capped row list and that it ignores where_clause. It also documents the JSONB query pattern (data->>'key' ILIKE). Marginal but genuine value over the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Query your canonical companies') and defines the entity precisely as 'one deduped row per real-world company.' It explicitly distinguishes this from the sibling query_monitored_companies ('NOT the LinkedIn post-monitoring list'), so an agent can route without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names concrete use cases (look a company up by domain, list tracked companies, answer company-entity questions) and explicitly routes adjacent needs to siblings: query_deals/query_crm_tasks for deal/task status and query_people for the people at a company. It also states the group_by alternative and its tradeoff, leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_crm_tasksQuery Crm TasksARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Text match on the task title, or the linked contact, company, or deal name. | |
| limit | No | Max tasks (default 25, capped at 200). | |
| offset | No | Tasks to skip for paging; re-call with offset += limit while has_more is true. | |
| status | No | 'open' (default), 'done', or 'all' (open and done). | open |
| deal_id | No | Only tasks on this deal. | |
| person_id | No | Only tasks linked to this person (a query_people `id`). | |
| company_id | No | Only tasks linked to this company (a query_companies `id`). | |
| due_before | No | Only tasks due before this day (YYYY-MM-DD). For "overdue" pass today. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| assignee_email | No | Only tasks assigned to this teammate. For "my tasks" pass the user's own email. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true; the description adds real behavioral context beyond that: deterministic sort order, team-wide sharing semantics, and the follow-up-suggestion exclusion, plus a note that the embedded deal object is partially populated (open_task_count/next_task blank, go to query_deals). It stops short of describing rate limits or result-size behavior, but the safety profile is already covered by annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Uses summary/returns sections with the purpose and ordering stated first, then the use cases. Tight overall, though the returns field list is a long enumeration whose individual entries largely duplicate what the agent would learn from a response; it earns its place but is on the verbose side.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 10 params, no output schema, and no required params, the description compensates well: it documents ordering, sharing semantics, the exclusion rule, the paging shape (total/has_more/tasks), and clarifies which nested fields are unpopulated. Nothing an agent needs to call or interpret this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the schema itself already maps intents to params (due_before: 'For "overdue" pass today'; assignee_email: 'For "my tasks"'). The description restates some of that intent mapping in prose but adds no syntax, format, or interaction detail beyond the schema, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('List CRM tasks') and immediately adds distinguishing scope: the Tasks view, sort order (earliest due first, undated last), and that tasks are team-shared. An agent can tell this apart from create_crm_task/update_crm_task and from query_deals without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names concrete user intents ('what's on my plate', 'what's overdue', 'what's next with Acme') and an explicit downstream workflow ('find a task's id before update_crm_task'). It also carves out an exclusion — Sliq-proposed but unaccepted follow-ups are not returned — which is exactly the kind of boundary an agent needs to avoid wrong answers.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_dealsQuery DealsARead-onlyInspect
Every result also carries the pipeline's stages (in order) and a pipeline_summary
(open count and amount, closing this month, won/lost counts, win rate) for the whole
pipeline — the summary ignores the filters.
Dict with paging info, the stages, the pipeline summary, and the deals page.
{total, has_more, stages: [{id, name, position}], pipeline_summary: {open_count,
open_amount, closing_this_month {count, amount}, won_count, lost_count, win_rate},
deals: [{id, name, amount (decimal string or null), status, stage {id, name,
position}, person_profile {id, name, title, email, linkedin_url, ...} or null,
company_profile {id, name, domain} or null, assignee_email, expected_close_date,
notes, created_at, stage_changed_at, closed_at (when it was won or lost; null while
open), archived_at, open_task_count, next_task {id, title, due_at} or null,
suggested_task, others_at_company {company {id, name}, count, other_open_deal} or
null (how many other people at the deal's company have had outreach sent or are on a
deal there, and whether another open deal sits there), data}]}.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Text match on the deal name, company name, or contact name. | |
| limit | No | Max deals (default 25, capped at 200). | |
| stage | No | Only deals in this stage, by name (e.g. "Proposal") or id. Open deals sit in a stage; won/lost is the status, not a stage. | |
| offset | No | Deals to skip for paging; re-call with offset += limit while has_more is true. | |
| status | No | 'open' (default), 'won', 'lost', or 'all'. | open |
| person_id | No | Only deals linked to this person (a query_people `id`). | |
| company_id | No | Only deals linked to this company (a query_companies `id`). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| assignee_email | No | Only deals owned by this teammate (an active teammate from list_teammates). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With only readOnlyHint available, the description carries real extra weight: it discloses that deals are team-shared, that ordering depends on status (manual board order vs newest-first), and the non-obvious caveat that pipeline_summary ignores the supplied filters. These are behavioral traits an agent could not derive from the annotations or schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and routing guidance, and the summary/returns structure keeps the long return-shape block skimmable. It is longer than strictly needed for a list tool and carries some UI trivia ('which the team sets by dragging cards'), though most length is justified since there is no output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description compensates by enumerating the return dict down to nested fields, the stages array, and the pipeline_summary contents. Combined with ordering, sharing, and the filter-vs-summary caveat, an agent has everything required to call and interpret this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description goes beyond the schema by explaining how status interacts with closed_at for 'won this month' queries, while the schema itself clarifies stage-vs-status ('won/lost is the status, not a stage') and the offset/has_more paging loop. Together they leave no parameter ambiguous.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List deals in the user's CRM pipeline') and immediately disambiguates it from siblings by noting it is the Deals view and is the lookup step before update_deal. The ordering semantics (open = board order stage-by-stage; won/lost/all = newest first) further pin down exactly which resource view an agent gets.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete usage phrases ('what's in my pipeline', 'does Jane have a deal', 'what did we win this month') plus the exact recipe for the last one (status='won', then compare closed_at), and routes the agent forward to update_deal once an id is found. Nothing about when to reach for this tool is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_linkedin_post_engagementsQuery Linkedin Post EngagementsARead-onlyInspect
Use this to find warm leads, identify who engaged with specific posts, or filter engagers by role/company. Chain with query_linkedin_posts when drilling into a specific post, or filter on the post_analytics_id that fetch_post_engagers returns ("post_id = 42").
Columns: id, post_id, engagement_type ('reaction'|'comment'), reaction_value (LIKE|ENTERTAINMENT|EMPATHY|PRAISE|INTEREST|APPRECIATION, empty for comments), author_name, author_provider_id, author_headline, author_profile_url, comment_text, engaged_at, fetched_at, created_at. Dict with count, truncated, and engagements array (each row carries the columns above).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | default 50, max 200. | |
| offset | No | Rows to skip for paging (default 0). When the result is truncated, re-call with offset += limit for the next page. | |
| order_by | No | default "engaged_at DESC NULLS LAST" (comments have timestamps, reactions don't). | engaged_at DESC NULLS LAST |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | No | SQL WHERE. Examples: "engagement_type = 'comment'" "author_headline ILIKE '%VP%'" "post_id = 42" "engagement_type = 'reaction' AND reaction_value = 'PRAISE'" | 1=1 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already covers the safety profile, but the description adds real context beyond annotations: the source of the rows, the fact that comments carry timestamps while reactions don't (explaining the default ordering), and the shape of the return (count, truncated, engagements). It does not discuss rate limits or credit cost, so it falls short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary, then usage/chaining, then a compact column glossary – a rational order with little waste. The column enumeration is dense but earns its place as the only place these fields are documented, though it makes the block longer than strictly necessary.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-param read tool with no output schema, the description plus inline <returns> covers paging (truncated/offset), default ordering, and row columns well. The only gap is the post_analytics_id vs post_id naming ambiguity, which could cause a wrong WHERE clause on first use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds genuine meaning: it enumerates engagement_type and reaction_value values and the author columns, and gives where_clause examples. It loses a point for a naming inconsistency – it points at 'post_analytics_id that fetch_post_engagers returns' then illustrates with 'post_id = 42', while the column list only defines post_id.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Query individual reactions/comments on tracked LinkedIn posts') and immediately scopes the data source (own posts plus third-party posts from fetch_post_engagers). This distinguishes it cleanly from siblings like fetch_post_engagers (which fetches) and query_linkedin_posts (which returns posts, not engagers).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit use cases ('find warm leads, identify who engaged with specific posts, filter engagers by role/company') plus explicit chaining guidance with query_linkedin_posts and the upstream producer fetch_post_engagers. An agent knows both when to reach for this tool and how to sequence it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_linkedin_postsQuery Linkedin PostsARead-onlyInspect
Use this to answer "which of my posts performed best?", "what's my average engagement?", "show me posts from last month", etc. This is the account-global table, with no agent/monitor dimension — for the feed a specific agent's monitors discovered (and to act on it), call query_monitored_posts.
Columns: id, social_id, text, share_url, posted_at, impressions, reactions, comments, reposts, is_own_post, author_name, author_headline, fetched_at, created_at, plus a derived engagement_rate string (reactions+comments+reposts over impressions, matching the UI) — '0.0' for posts with no impression data (e.g. third-party posts). The engagement_rate is computed after the query runs, so it can't appear in where_clause or order_by; filter and sort on the raw columns instead. Dict with count, truncated, and posts array (each row carries the columns above plus the derived engagement_rate).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | default 25, max 200. | |
| offset | No | Rows to skip for paging (default 0). When the result is truncated, re-call with offset += limit for the next page. | |
| order_by | No | default "posted_at DESC". For top performers: "impressions DESC". | posted_at DESC |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | No | SQL WHERE. Examples: "posted_at >= NOW() - INTERVAL '30 days'" "impressions > 1000" "text ILIKE '%hiring%'" "is_own_post = false" | 1=1 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond readOnlyHint: it discloses the refresh cadence (metrics refreshed daily M-F), the semantic meaning of is_own_post and where third-party author data lives, and the non-obvious constraint that the derived engagement_rate is computed after the query runs so it cannot appear in where_clause or order_by. It also explains truncation and the offset += limit paging pattern. This is exactly the behavioral context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary block is front-loaded with the scope statement first, then usage examples, then the sibling pointer, then the caveats — a sensible information hierarchy. It is long and the column enumeration borders on redundant with the returns block, but nearly every sentence carries operational value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although there is no formal output schema, the description includes a returns block describing count, truncated, and the posts array with the derived engagement_rate. Combined with the column list, paging guidance, and account-scoping note for on_behalf_of, an agent has everything needed to call and interpret this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning on top: it explains the derived engagement_rate cannot be used in where_clause or order_by and that raw columns must be filtered/sorted instead, which directly constrains two of the five parameters. It also implies order_by choices (top performers) that the schema only partially covers.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Query the LinkedIn posts the user tracks') and immediately scopes what is included: own posts plus third-party posts registered via fetch_post_engagers, with is_own_post as the discriminator. It explicitly contrasts itself with query_monitored_posts, so an agent can route between the two account-global vs. agent-scoped tables without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete triggering questions ('which of my posts performed best?', 'what's my average engagement?') and states the when-not condition plus the named alternative: for the feed a specific agent's monitors discovered, call query_monitored_posts. That is explicit use/avoid guidance tied to a sibling.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_monitored_companiesQuery Monitored CompaniesARead-onlyInspect
mode="search" (default): individual company rows. mode="stats": aggregate counts, with optional group_by for analytics.
Columns: display_name, domain, stage ('active'/'removed'), data (JSONB),
agent_id, created_at, updated_at. JSONB queries:
data->>'last_sweep_status' = 'error'. For outreach prospects, call
query_prospects.
In search mode, {count, truncated, items array}.
In stats mode, {total, by_stage: {active: N, removed: M},
groups?: {: {total, by_stage}}}.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | "search" to list rows, "stats" for aggregate counts. | search |
| limit | No | Max results (default 50, capped at 100). Search mode only. | |
| offset | No | Rows to skip for paging (default 0, search mode only). When the result is truncated, re-call with offset += limit for the next page. | |
| agent_id | No | Filter to a specific agent. Omit for cross-agent queries. | |
| group_by | No | (stats mode only) JSONB data key to group by; each group carries the same by_stage map as the top-level stats. | |
| order_by | No | SQL ORDER BY (default: created_at DESC). Search mode only. | created_at DESC |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | No | SQL WHERE condition (default: all). Examples: "stage = 'active'", "domain ILIKE '%.io'" | 1=1 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint, so the description carries most of the load and does so: it discloses that search results can be truncated, describes the shape of both return modes, and shows the JSONB access syntax needed to filter. It stops short of stated row caps or performance caveats, which are instead found in the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary with mode semantics, then columns, then a compact returns block. Sentences are dense with usable information (mode defaults, JSONB syntax, sibling pointer) and nothing is redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, yet the description supplies both return shapes, the truncation signal, and pagination context (offset guidance lives in the schema). For an 8-parameter read-only query tool, an agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real value by listing the available columns (display_name, domain, stage, data, agent_id, created_at, updated_at) and giving a concrete JSONB predicate example, which directly informs how to write where_clause and order_by. That goes beyond what the schema alone conveys.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Query an agent's monitored companies') and enumerates two modes with the default. It distinguishes itself from query_prospects, but says nothing about how it differs from the very similar sibling query_companies, leaving that ambiguity for the agent to resolve.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains when to pick each mode (rows vs aggregate counts) and explicitly routes outreach lookups to query_prospects. Missing explicit guidance on choosing between this and query_companies/query_agent_people, but the mode selection and exclusion are clearly stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_monitored_postsQuery Monitored PostsARead-onlyInspect
mode="search" (default): individual post rows. mode="stats": per-status counts.
Each row is one post a monitor surfaced, with the comment Sliq drafted for it
(draft_comment) and the post's lifecycle status: 'discovered' (claimed,
pre-draft), 'filtered' (the monitor's post filter set it aside, so no action ran
on it), 'queued' (drafted + enqueued), 'commented' (comment sent),
'rejected' (user declined the draft), 'skipped' / 'skipped_no_credit' (no
comment drafted), 'captured' (a capture ran on it — its engagers, or its
author when a person — and it wasn't commented), 'surfaced' (no comment and no
capture ran — a surface-only monitor, or a company page's or the user's own post
on an author-only monitor — just surfaced in the feed to act on manually).
author_captured is whether author capture ran on the post's author, a person;
the monitor's list holds them unless the user removed them.
filter_reason is the post filter's one-sentence reason for keeping or setting
aside the post ('' when the monitor has no filter). Read it beside the filtered
and kept posts when the user wants to tune their monitor's post filter.
data holds the author's headline and public_identifier. Link a post's author
with the derived author_profile_url on each search row — a person's profile, or
the company page when a company posted. It is computed after the query runs, so it
can't appear in where_clause or order_by.
reaction_status is the independent reaction track: '' (no reaction),
'queued' (a reaction enqueued), 'reacted' (sent), 'rejected' (user declined it),
'skipped' (deduped) — disjoint from status since one post can be both
commented and reacted to. reaction_type is which reaction landed
(like/celebrate/support/love/insightful/funny, '' until sent).
Columns: id, monitor_id, post_urn, post_url, author_name, author_provider_id,
data, post_text, reaction_count, comment_count, posted_at, status, filter_reason,
draft_comment, reaction_status, reaction_type, author_captured, created_at. Pass a row's id to
generate_monitored_post_comment to draft + queue a Sliq comment for it.
Pass agent_id to scope to that agent's monitors — omitting it reads every
monitored post across all the user's agents. To act on recent posts (e.g. queue
a reaction on this agent's fresh finds), pass agent_id and filter on
posted_at; this keeps actions on the agent's own scoped feed rather than the
account-wide analytics.
This is the monitor-scoped feed. query_linkedin_posts is a different table —
the account-global analytics of the user's own posts plus any post they've
fetched engagers for, with no agent/monitor dimension. Use that for "how did my
posts perform"; use this for "what did this agent's monitors find".
In search mode, {count, truncated, items array}.
In stats mode, {total, by_status: {discovered: N, commented: M, ...}}.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | "search" to list rows, "stats" for per-status counts. | search |
| limit | No | Max results (default 50, capped at 100). Search mode only. | |
| offset | No | Rows to skip for paging (default 0, search mode only). When the result is truncated, re-call with offset += limit for the next page. | |
| agent_id | No | Scope to one agent's monitors. Omit for cross-agent queries. | |
| order_by | No | SQL ORDER BY (default: created_at DESC). Search mode only. | created_at DESC |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | No | SQL WHERE condition (default: all). Examples: "status = 'commented'", "posted_at >= NOW() - INTERVAL '24 hours'", "author_name ILIKE '%founder%'" | 1=1 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=true and the description never contradicts that, so the safety profile is covered. The description adds substantial non-annotation context: the full status lifecycle values, the independent reaction_status track, and that author_profile_url is computed post-query so it cannot appear in where_clause/order_by. That last point and the status semantics are genuinely useful behavioral disclosure beyond the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is long, but the routing distinction and mode behavior are front-loaded and the status/reaction enumerations are load-bearing given there is no output schema. The explicit column list is somewhat redundant against the described row semantics, which keeps it short of a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Output schema is absent, but the description supplies a <returns> block describing the search-mode {count, truncated, items} and stats-mode {total, by_status} shapes, plus per-field semantics for row columns. Nothing an agent needs to call or interpret results is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description goes beyond it by clarifying the consequence of omitting agent_id ('reads every monitored post across all the user's agents') and by flagging that the derived author_profile_url cannot be used in where_clause or order_by — a constraint not obvious from the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('query the posts an agent's LinkedIn monitors discovered') and immediately scopes it as the monitor-scoped feed rather than account-global analytics. It explicitly names the sibling it is not (query_linkedin_posts) and states why, so an agent can disambiguate without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives an explicit routing rule: 'Use that for "how did my posts perform"; use this for "what did this agent's monitors find"'. It also explains when to pass agent_id vs omit it, and when to use search vs stats mode. Both the when and the when-not are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_peopleQuery PeopleARead-onlyInspect
Columns: id, display_name, title, headline, company, location,
summary, linkedin_url, linkedin_provider_id, email, mail_host, connections_count, follower_count,
is_open_profile, is_premium, work_experience (JSONB), education (JSONB), linkedin_looked_up_at,
company_profile_id, owner_email, data (JSONB), created_at, updated_at. company_profile_id is
the person's canonical employer FK (NULL when unresolved). Sliq's CRM is deals and tasks: to
see whether someone has a deal or a task, use query_deals (status='all') / query_crm_tasks
with person_id.
mail_host is the email provider hosting this person's address (Google Workspace,
Microsoft 365, …), read from their domain's MX record: google, microsoft, gateway,
other, unknown (no mail record, or the domain's DNS is broken), or '' with no email or
no successful lookup yet.
gateway means a security gateway (Mimecast,
Proofpoint) receives the mail first, which hides the provider behind it.
connections_count / follower_count are NULL when never fetched (not 0); is_open_profile /
is_premium are NULL likewise. JSONB queries: data->>'some_key' ILIKE '%...%'.
work_experience and education are the person's full LinkedIn employment and education
history — the same arrays enrich_linkedin_profiles returns, already stored on the person from
the last profile lookup, so reading them here costs nothing (answer "where did they go to
school", "are they an LSU alum", "how long in seat" from these instead of a fresh enrich).
Each is a list, in LinkedIn display order (NOT sorted by date). linkedin_looked_up_at is when
the person's full LinkedIn profile was first looked up: NULL means it never was, and a time
with empty lists means LinkedIn had no history to give. These arrays are large for senior
people — a broad limit=200 read that returns them can exceed the 50KB direct-return cap and
truncate; for a wide pull, either narrow the where_clause or call this from run_code
(nothing truncates there) and print only the fields you need.
To list the outreach prospects tracking a person, take an id from here and call
query_prospects with where_clause="person_id = <id>".
Each row also carries segments: the person's segment tags across every segment group they're
classified into — [{group_id, group_name, tag}], tag 'No match' where the classifier couldn't
place them (empty when no group has classified them). Segments are the LLM-defined
people dimensions from the Explore Segments surface (e.g. Seniority -> VP); manage them with the
segment tools (list_segment_groups etc.).
Pass group_by for per-bucket counts over ALL your people instead of a row list — a whole-set
aggregate, never capped at the 200-row limit, so it answers "how many people per "
without paging. outreach_stage buckets each person by their most-advanced lead-funnel stage
across every campaign; segment buckets each person by their tag in one segment group (pass
that group's id as segment_group_id). Both ignore where_clause.
In row mode (group_by omitted) — a dict with count, truncated, and items array (each row
{id, display_name, title, headline, company, location, summary, linkedin_url,
linkedin_provider_id, email, mail_host, connections_count, follower_count, is_open_profile,
is_premium, work_experience, education, linkedin_looked_up_at, company_profile_id,
owner_email, segments: [{group_id, group_name, tag}], data, created_at, updated_at}).
In aggregate mode (group_by set) — a dict {group_by, groups} where groups is a list of
{key, count} ordered by count descending; for "segment" the keys are tag names, 'No match'
for people the classifier couldn't place, and '' for people the group hasn't classified.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max results (default 50, capped at 200). | |
| offset | No | Rows to skip for paging (default 0). When the result is truncated, re-call with offset += limit for the next page. | |
| group_by | No | Aggregate mode, returned instead of the row list — whole-set per-bucket counts over all your people (not capped by `limit`). "title" / "company" / "location" bucket by that column's value; "outreach_stage" by the person's collapsed lead-funnel bucket; "segment" by the person's tag in the `segment_group_id` group. Omit for the row list. | |
| order_by | No | SQL ORDER BY (default: created_at DESC). | created_at DESC |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | No | SQL WHERE condition (default: all your people). Examples: "display_name ILIKE '%chen%'", "title ILIKE '%VP%'", "company_profile_id = 42", "linkedin_url = 'some-slug'". | 1=1 |
| segment_group_id | No | Required with group_by="segment" — the segment group (from list_segment_groups) to bucket by. Ignored otherwise. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only provide readOnlyHint=true, and the description goes well beyond that: NULL semantics for connections_count/follower_count/is_open_profile, mail_host enum meanings, JSONB query syntax, the 50KB truncation cap and the run_code workaround, and the fact that group_by ignores where_clause. This is unusually rich behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a summary that leads with the core identity, then details. It is long, but the length is largely earned given the column semantics, two result modes, and no output schema. A few of the column enumerations could be trimmed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the inline <returns> block fully describes both row and aggregate modes, and the description covers modes, column semantics, aggregation, truncation, and sibling routing. Nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real meaning beyond it: group_by's whole-set aggregate behavior (never capped by limit), the requirement of segment_group_id for group_by='segment', and the interplay with where_clause. It stops short of restating every parameter, but the added semantics justify a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Query your canonical people (`person_profiles`) — one deduped row per real person') and immediately distinguishes itself from query_prospects by explaining the dedup vs per-owner tracking distinction. An agent can tell exactly what entity this returns without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete use cases ('is <name> someone we know', 'people who are VPs at fintech companies'), names the alternative for outreach progress (query_prospects), and routes deal/task questions to query_deals/query_crm_tasks. It also tells the agent to read work_experience/education here instead of a fresh enrich_linkedin_profiles call.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_prospect_researchQuery Prospect ResearchARead-onlyInspect
Call this BEFORE researching a lead to avoid re-doing work (and re-spending credits), or to reference prior findings in a follow-up, or to answer "what did you find about X?". Dict with count, truncated, and items array (each row {id, agent_id, person_linkedin_slug, person_linkedin_pid, person_email, subject_kind, company_domain, display_name, content, url, source, created_at}).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max findings to return (default 50, newest first). | |
| offset | No | Number of findings to skip, for paging past `limit`. | |
| person | No | A lead handle (linkedin_url / linkedin_provider_id / email) to get just that lead's findings; omit for all research on the agent. | |
| agent_id | Yes | The agent whose saved research to read. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true; the description goes further by disclosing the credit-spend motivation and the exact return envelope (count, truncated, items with field list), which matters because there is no output schema. No auth or cross-account behavior is discussed in the description, but the schema's on_behalf_of field carries that.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the one-line summary, then tucks usage triggers and the return shape into tagged blocks that are easy to scan. The enumerated returns field list is verbose but earns its place because no output schema exists.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-parameter read tool with no output schema, the description supplies the reader's philosophy (check before re-spending) and the full return shape, which is the main gap the missing output schema would otherwise leave. It is not exhaustive on pagination semantics or agency-account behavior, but those live in the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so limit/offset/person/agent_id/on_behalf_of are already fully documented in the schema. The description adds no parameter-level detail (no mention of the person filter or agency client scoping), so baseline 3 is correct.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource: 'Read back personalization research already saved for an agent.' The phrase 'already saved' cleanly separates it from the write-side sibling save_prospect_research, so an agent can route correctly without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives three concrete invocation scenarios: before researching a lead (to avoid re-spending credits), to reference prior findings in a follow-up, and to answer 'what did you find about X?'. Strong positive guidance, though it names no explicit when-not condition or alternative tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_prospectsQuery ProspectsARead-onlyInspect
mode="search" (default): individual prospect rows. mode="stats": aggregate counts, with optional group_by for analytics.
Columns: id, person_id, display_name, email, mail_host, linkedin_url,
linkedin_provider_id, title, headline, company, location, email_stage, linkedin_stage,
priority (True = this prospect's LinkedIn sends jump the queue), current_node_id,
entered_at, enact_blocked_at, enact_blocked_reason, data (JSONB), agent_id, created_at,
updated_at, linkedin_messages_sent, last_outbound_at.
There's no rollup stage column; filter on email_stage or linkedin_stage
to isolate a funnel (both non-null — not-started is ''; gate absence with
= ''). JSONB queries: data->>'title' ILIKE '%founder%'. For
monitored companies, call query_monitored_companies.
id is the prospect's outreach_prospects primary key — returned on every
search row and queryable in where_clause. When an event hands you a roster
of prospect primary keys (a flow node's on_enter enactment carries them in
event['prospect_ids']), resolve the whole roster with id IN (...) and order_by="id",
so it queues in upload order.
person_id is the canonical-person FK — the query_people entity this outreach row
resolves to. Each row also carries that person's canonical identity line — title,
headline, company, location — joined from that query_people row: the deduped,
typed, filterable values (the prospect's own data blob may carry a separate
per-enrollment copy under data->>'title'/'company'). For the rest of the profile —
summary, work history, education — call query_people with id = <person_id>.
person_id = <id from query_people> lists every prospect row tracking that person. Use it
to cross between the two: query_people returns one deduped row per real person,
query_prospects returns the per-owner tracking rows under it. These are your own prospects
only — a teammate's rows for the same person need on_behalf_of.
Each search row also carries lead_bucket, the derived lead-funnel bucket
(one of not_started/queued/contacted/engaged/replied/interested/meeting_booked/
not_interested/finished/not_reached/already_connected) — a read-only returned field, not a
queryable column. To filter by funnel position, gate on
email_stage/linkedin_stage, never on lead_bucket. Each row also carries a read-only
removal_reason: for a removed prospect (a 'skipped' stage), why they were taken out of
the campaign; '' otherwise or when no reason was recorded.
current_node_id / entered_at are the prospect's Campaign Flow position — the
sequence-DAG node they currently rest on and when they arrived. current_node_id is
NULL for a prospect on the start node / not yet routed onto any node. The node id is
opaque; resolve it to a label and kind — and read the per-node occupancy counts — with
get_campaign_flow. Filter on it to list one node's occupants
(current_node_id = '<id from get_campaign_flow>', or current_node_id IS NULL for the
start node) or to find stalled prospects (entered_at <= '<7 days ago>').
enact_blocked_at / enact_blocked_reason answer "why did this prospect stop?" —
non-null means their current node's step could not be performed and won't retry
until unblocked (enact_blocked_at IS NOT NULL lists a campaign's stuck
prospects). Fix the cause, then clear the block with retry_blocked_enactment.
A search row for a prospect who has been queued a gift also carries gifts, a read-only
returned field listing every gift to them, oldest first: {id, agent_id, status, cancel_kind, node_id, product_url, tracking_url, error, created_at, placed_at, delivered_at}. agent_id is the campaign the gift was queued in: usually the row's own,
but a gift carried over when duplicate records of the person merged can belong to another
campaign, and it is null once that campaign is deleted. status is pending_approval (a
card waiting on the user's approval), placing, placed, shipped, delivered,
failed (error says why) or cancelled. cancel_kind says why a cancelled gift was
cancelled, and is null on every other status: user_rejected (the user rejected the card),
prospect_moved (the prospect had already left the gift step when the user approved the
card), prospect_skipped (the prospect was removed from the campaign), task_deleted (the
campaign was deleted) or duplicate (a merge left another gift on the same step). node_id names the gift step in that campaign's flow,
null for a gift queued outside the flow.
mail_host is the email provider hosting the person's address (Google Workspace,
Microsoft 365, …), read from their domain's MX record when the address was saved:
google, microsoft, gateway (a security gateway such as Mimecast or Proofpoint
receives the mail first, which hides the provider behind it), other (any other host),
unknown (no mail record, or the domain's DNS is broken), or '' when there's no email
or no lookup of it has succeeded yet. To filter or route on who hosts a prospect's mail
("Google-hosted only"), read this column, since a domain name alone doesn't show it.
linkedin_messages_sent / last_outbound_at are the count and latest timestamp
of outbound LinkedIn messages sent to the prospect (hand-sent ones included; a
connection-request note isn't a message, even though LinkedIn replays it into the
chat when they accept).
For any follow-up check, gate on linkedin_messages_sent, never on
linkedin_stage alone. linkedin_stage = 'messaged' is a flat bucket — it can't
tell a prospect who's had only the first message from one who's already had several,
so selecting on the stage (even with a last_outbound_at cutoff) re-sends a
follow-up that already went out. Gate the exact step: linkedin_stage = 'messaged' AND linkedin_messages_sent = 1 AND last_outbound_at <= '<5 days ago>' is the first
follow-up; = 2 with a 7-day cutoff is the second. Both fields reflect synced
LinkedIn history, which usually syncs within minutes: reliable for day-grained
follow-up checks.
Stage meanings — translate these for the user (say "still being looked up", not
"resolving"). email_stage and linkedin_stage share one vocabulary; a rung marked
"(LinkedIn)" never appears on email. not_started (blank stage, never enrolled) is
reported separately in the stats return, not a stored stage.
resolving (LinkedIn) — looking up their profile before anything can send; NOT yet contacted.
warming (LinkedIn) — a warm-up flow is engaging their content (comments, reactions) to build familiarity, before the first outreach or between steps; not a send in itself.
graduated (LinkedIn) — the warm-up finished; what it triggers next (a connection request, an intro message, or nothing) depends on the flow.
pending — scheduled to go out (show the scheduled time if present).
sent — the connection request (LinkedIn) or the email has been delivered.
connected (LinkedIn) — they accepted the connection request.
messaged (LinkedIn) — a follow-up message went out after they connected.
replied — they replied (a neutral/unclassified reply). Either channel.
interested — they replied with clear interest (a positive reply). Ranks above replied. Either channel.
meeting_booked — they agreed to or booked a meeting in their reply. The top rung. Either channel.
not_interested — they replied with an explicit decline. Ranks above
replied, below the positives — still a replier: include it in any "everyone who replied" count. Either channel.skipped — deliberately not pursued: a manual/agent cancel, or a cross-agent duplicate. Either channel.
unreachable (LinkedIn) — a permanent failure: invalid/locked/not-found profile, or they don't accept invites.
already_connected (LinkedIn) — already a 1st-degree connection, so the request was a no-op; not pursued by default, but you can message them directly.
withdrawn (LinkedIn) — the connection request sat unaccepted past the withdrawal window and was auto-retracted.
Acceptance (LinkedIn connection-request accept rate) does NOT come from the connected
count — accepters advance to messaged/replied, so connected undercounts. Read it from
get_agent_details (tracking_summary.funnel carries per-channel funnels with the connection
acceptance_rate), also surfaced on the per-agent LinkedIn CR acceptance line in your Active Agents context; it matures over ~a week
(an accept lands up to ~8h after the click, requests sit sent for days), so a campaign's first week
reads low. Who/when — who accepted or replied and when, a week-over-week trend — comes from
list_prospect_events, not stats: stats are current positions, the event feed is the history.
Analytics — mode="stats" with group_by a JSONB data key (the per-enrollment
data->>'...', not the canonical top-level identity columns) surfaces patterns:
group_by="title" (which job titles reply/engage), "company", "source" (which lead sources
perform). Each group carries the same per-channel stage maps; everyone who replied is the sum
of every reply rung — replied, interested, meeting_booked, not_interested (the
classified rungs rank above a neutral reply) — the replied count alone undercounts. Report
per-channel — don't mix the email and LinkedIn maps.
Acceptance has no per-group breakdown — it reads only off the per-agent CR line above. Surface a
pattern proactively when one emerges (e.g. "seed-stage companies reply at ~3× the Series-B rate"),
not only when the user asks.
Dedup before acting — to check whether you've already tracked or acted on someone, query by
whichever identifier you have (omit agent_id to look across all your agents):
where_clause="email='...' OR linkedin_url='...' OR linkedin_provider_id='...'".
In search mode, {count, truncated, items array}.
In stats mode, {total, by_email_stage: {...}, by_linkedin_stage: {...},
not_started, groups?: {: {total, by_email_stage, by_linkedin_stage}}}.
not_started is the count of prospects with neither channel stage
populated (never enrolled) — in total but in neither stage map.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | "search" to list rows, "stats" for aggregate counts. | search |
| limit | No | Max results (default 50, capped at 100). Search mode only. | |
| offset | No | Rows to skip for paging (default 0, search mode only). When the result is truncated, re-call with offset += limit for the next page. | |
| agent_id | No | Filter to a specific agent. Omit for cross-agent queries. | |
| group_by | No | (stats mode only) JSONB data key to group by; each group carries the same per-channel stage maps as the top-level stats. | |
| order_by | No | SQL ORDER BY (default: created_at DESC). Search mode only. | created_at DESC |
| on_behalf_of | No | A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | No | SQL WHERE condition (default: all). Examples: "email_stage = 'replied'", "linkedin_stage = 'connected' AND email_stage = 'sent'", "current_node_id IS NULL", "data->>'title' ILIKE '%founder%'" | 1=1 |
| include_messages | No | If False (default), `data.message_sent` is omitted from each row. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only supply readOnlyHint=true, yet the description adds substantial behavioral context beyond that: derived read-only fields (lead_bucket, removal_reason), the full stage vocabulary with semantics, how enact_blocked_at signals non-retrying stalled prospects, the gifts array schema and cancel_kind meanings, and mail_host provenance from MX records. It also explains sync latency ('usually syncs within minutes') and acceptance maturation (~a week), which are not in any structured field.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with summary and the two modes, then organized under bold headers (Stage meanings, Analytics, Dedup before acting), which keeps a very long body navigable. It is still unusually long and the gift cancel_kind enumeration and some column notes could be trimmed, but nearly every block maps to a real query decision, so it is not padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, and the description compensates by embedding a returns section describing search-mode ({count, truncated, items}) and stats-mode ({total, by_email_stage, by_linkedin_stage, not_started, groups}) shapes, including the subtlety that not_started is in total but in neither stage map. For a 9-parameter, two-mode tool this is complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the baseline would be 3, but the description goes well beyond the schema: it explains group_by must target a JSONB data key (not the canonical top-level identity columns), shows concrete where_clause idioms (id IN (...), current_node_id IS NULL, data->>'title' ILIKE), prescribes order_by='id' to preserve upload order for rosters, and notes that omitting agent_id widens to all agents. This is genuine added meaning over the schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Query outreach prospects for campaign progress') and immediately splits it into two named modes (search vs stats) with defaults. It also explicitly names what it is not — pointing to query_people, query_monitored_companies, and list_prospect_events for adjacent data — so an agent can differentiate it from siblings without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use routing: 'For monitored companies, call query_monitored_companies', 'For the rest of the profile ... call query_people with id = <person_id>', and 'Who/when ... comes from list_prospect_events, not stats'. It also states when/when-not for follow-up gating ('gate on linkedin_messages_sent, never on linkedin_stage alone') and for analytics via group_by. Alternatives and exclusion conditions are all named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_search_resultsQuery Search ResultsARead-onlyInspect
Columns: id, agent_id, entity_type, list_name, source, identifier,
display_name, verdict, verdict_reason, data (JSONB), created_at, person_id,
company_profile_id. Filter by list with "list_name = 'oil-gas'". source is
the discovery tool that surfaced the row ('agent' when no discovery tool did;
'legacy' for rows older than the column). verdict is the curation call
recorded on the row with record_search_results, or by the check a search
tool, a post monitor's engager capture or a Websets list runs on the rows it
saves — 'qualified', 'rejected', 'unclear' for a row
screened but too thin to judge, or NULL for a row not yet screened — with
verdict_reason saying why. Rejected rows are left out unless you pass
verdict='rejected', include_rejected or run_id, the same as the user's default
view, so a where_clause on verdict alone never reaches them.
person_id / company_profile_id are the canonical person / company FK ids
(NULL until the row is linked). A person's profile (title, company) and a
company's firmographics (industry, location, employee count, description) — the
values the agent's People and Companies tabs show — live on the linked record, not
in the row's own data: read them with query_people / query_companies on
those ids, and filter on them through the id, e.g. "company_profile_id IN (SELECT
id FROM research_app_company_profiles WHERE industry ILIKE '%oil%')".
For a person's LinkedIn degree and warm-intro connectors, or the agent's people
counted by list, source, degree or connector, use query_agent_people instead.
data has no single schema — its shape follows whichever source wrote the
row (it is the agent-authored blob). Most person rows nest under person
(data->'person'->>'position',
data->'person'->'company'->>'name') and most company rows under company
(data->'company'->>'name'), but other sources
write those fields flat. A path that doesn't exist yields NULL rather than
an error, and a NULL comparison drops the row — so a filter aimed at the
wrong shape comes back empty or short and reads as a genuine miss. Read a
page of rows without a data filter first, then filter against the shape
you see.
Person rows are annotated with the team connection overlay (mirrors the Find
UI): a matched row's data gains connection_status: {you: bool, teammates: [{email, name, first_name}]} naming the viewer/teammates who are 1st-degree
to that prospect; unmatched rows and company rows carry no such key. A
top-level connections_coverage: {you: 'none'|'partial'|'csv_uploaded', team_missing: int} reports the owner's own connection coverage and how many
active teammates still lack a CSV upload. The viewer is the agent owner.
In row mode (group_by omitted) — a dict with count, truncated, and items
array (each row {id, agent_id, entity_type, list_name, source, identifier,
display_name, verdict, verdict_reason, data, person_id, company_profile_id,
columns, created_at}),
plus connections_coverage {you, team_missing}. columns is the normalized
[label, value] projection of the row's agent-researched data.columns.
In aggregate mode (group_by set) — a dict {group_by, groups} where groups
is a list of {key, count, qualified, rejected, unclear, unscreened} ordered by
count descending. count is every row the call counts, and the other four split
it by verdict (unscreened: no verdict yet); rejected rows are ones the screen
ruled out, not matches. With group_by "list",
each group also carries criteria: the search criteria a Websets list screens
its rows against, '' for any other list.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum results (default 50, max 200). | |
| offset | No | Rows to skip for paging (default 0). When the result is truncated, re-call with offset += limit for the next page. | |
| run_id | No | Only the rows whose person or company this search run returned, rejected ones included, on the list it saved to (an `id` from `query_search_runs`). Omit for every row. | |
| verdict | No | Only the rows with this verdict, 'qualified', 'rejected' or 'unclear' ('rejected' reads the rows the user's default view filters out). Omit for every verdict. | |
| agent_id | Yes | Filter to a specific agent (required). | |
| group_by | No | Aggregate mode, returned instead of the row list — per-bucket counts over the whole agent (not truncated by the row cap). "list" buckets rows by list_name; "company" buckets people by employer; "source" buckets rows by the discovery tool that surfaced them. Omit for the row list. | |
| order_by | No | SQL ORDER BY (default: created_at DESC). | created_at DESC |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | No | SQL WHERE condition (default returns all rows for the agent). Examples: "data->'person'->>'position' ILIKE '%VP%'", "entity_type = 'person'", "list_name = 'oil-gas-operators'". | 1=1 |
| include_rejected | No | Also return (and count) the rows marked rejected. Default False. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With only `readOnlyHint=true` from annotations, the description carries the burden and delivers: rejected rows are excluded unless `verdict='rejected'`, `include_rejected`, or `run_id` is passed; a `where_clause` on `verdict` alone never reaches them; non-existent `data` paths yield NULL and drop the row silently; the team connection overlay and `connections_coverage` keys are described. This is unusually rich disclosure of behavior beyond the annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose and cleanly sectioned into `<summary>` and `<returns>`, with the columns list and shape warnings placed where they are needed. It is dense and long, and some verdict explanation overlaps the schema text, but nearly every sentence carries non-obvious operational detail.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter tool with no output schema, the description supplies the missing return contract (row mode vs aggregate mode, `columns` projection, group verdict breakdowns, `criteria` for list groups) plus the edge cases an agent would otherwise trip on. Nothing needed to call it correctly appears absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning beyond it: `source` values ('agent' when no discovery tool surfaced the row, 'legacy' for old rows), `verdict` NULL vs 'unclear' semantics, `person_id`/`company_profile_id` FK purpose, and a concrete `where_clause` example. It stops short of documenting paging/limit behavior, which the schema already handles.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Query the agent's entity row store (`agent_search_results`)") and names the exact contents (people and companies recorded from discovery search, CRM reads, enrichment, pasted lists). It also distinguishes itself from siblings by name — `query_people`/`query_companies` for linked-record fields and `query_agent_people` for LinkedIn degree/connectors.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing to alternatives: linked-record fields via `query_people`/`query_companies` on the FK ids, and LinkedIn/warm-intro questions via `query_agent_people`. It also gives an actionable procedure for the tricky case — "Read a page of rows without a `data` filter first, then filter against the shape you see" — and explains the default-view behavior that hides rejected rows.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_search_runsQuery Search RunsARead-onlyInspect
To see the people or companies one run found, pass its id to query_agent_people as a
{"column_key": "run", "operator": "is_any_of", "values": [run ids as strings]} filter, for the
people it found still on the agent, or to query_search_results as run_id, for the rows of the
people or companies it found on the list it saved to (none for a 'user_added' run, which saves to
no list). Both include the rejected ones.
A dict with runs and total, the number of matching runs; page with offset += limit while
offset + limit < total. Each run carries id; source, the discovery tool as the
source column of query_search_results names it, or 'user_added' for people the user
added; entity_type, 'person' or 'company', what it looks for, set even when it found
no one; arguments, what the run was called with (for a Websets run, its list's search
settings) and, unless the run's source is 'user_added', list_name, the list it saved
to, even when the call named none; started_at; finished_at, null while a Websets search
is still running; found, the
people or companies it returned, ones the agent already had included; matched and
filtered_out, those whose row in the list the run saved to is qualified or rejected now
(both 0 for people the user added, who get no list row); and new,
those this run brought to the agent: no earlier run returned them and they weren't on
the agent before it started.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Runs per page (default 50, max 200). | |
| offset | No | Runs to skip for paging (default 0). | |
| agent_id | Yes | The agent whose search runs to list. | |
| person_id | No | Only the runs that found this person (the person `id` query_agent_people returns). | |
| company_id | No | Only the runs that found this company (a row's `company_profile_id`). | |
| entity_type | No | Only the runs that look for people ('person') or for companies ('company'); omit for both. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With only readOnlyHint=true in annotations, the description carries the rest and does so richly: paging via offset += limit while offset + limit < total, finished_at null while a Websets search is still running, matched/filtered_out computed as of now, the exact definition of 'new', and that user_added runs save to no list. This is substantive behavioral context beyond the annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The <summary>/<returns> structure is front-loaded and purposeful, and every claim about return fields earns its place given there is no output schema. The returns block is dense and long, verging on over-specification, which keeps it from a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter read tool with no output schema, the description compensates by fully enumerating the return fields (id, source, entity_type, arguments, list_name, started_at, finished_at, found, matched, filtered_out, new) and their edge cases. An agent has everything needed to call and interpret it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3; the schema already documents limit, offset, agent_id, person_id, company_id, entity_type, and on_behalf_of. The description's parameter-relevant contribution is mainly the run-id-as-string filter syntax for a different tool, and most of its prose describes return fields rather than the input parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb+resource ('List the searches that found an agent's people and companies') and defines precisely what a 'run' is, including all four kinds of events it covers. It distinguishes itself from the closest siblings (query_agent_people, query_search_results) by naming them and describing the routing between them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear routing guidance: to see what a run found, pass its id to query_agent_people or query_search_results, with the exact filter shape. It does not explicitly state when NOT to use this tool or contrast its scope against query_search_results at a top level, so it stops short of full when/when-not coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_segment_peopleQuery Segment PeopleARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Page size (default 200). | |
| offset | No | Page offset (default 0). | |
| tag_id | No | Narrow to people carrying this tag; omit for all classified people. | |
| channel | No | 'linkedin' or 'email' to read the outcome from; omit for both. | |
| senders | No | Narrow to people reached by these teammates (emails from a group's `senders`), and read their outcome from those teammates' prospects only. Omit for every teammate; an email not in the workspace is dropped, and if none remain the result is empty. | |
| group_id | Yes | The segment group to read (from list_segment_groups). | |
| untagged | No | Narrow to the 'No match' people (classified as nothing, empty `tags`); takes precedence over `tag_id`. Omit for all classified people. | |
| agent_ids | No | Scope to people with a prospect in one or more campaigns (agent_tasks ids); omit for all. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already declares the safety profile, so the bar is lower, and the description adds real behavioral detail beyond it: result ordering (newest classification first), paging semantics ('total is the count before paging, so the whole tail is reachable'), untagged taking precedence over tag_id, and the senders edge case where an unknown email is dropped and an empty remainder yields an empty result. Auth/rate-limit context is absent but not expected for a read tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a one-sentence summary of the tool's role before the filter and paging guidance, which is the right order. It is dense and somewhat long, and several filter explanations duplicate the schema, but almost every sentence carries information an agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the full return-value burden and does so thoroughly: it documents the returned dict shape (group_id, total, people[]), the nested person identity used by the shared person-card modal, and that outcome is OR-ed across in-scope prospects. For a nine-parameter read tool this is complete enough to call correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every one of the nine parameters is already documented in the schema, including the same tag_id/untagged/senders/agent_ids semantics the description restates. The description adds essentially no syntax or format detail beyond the schema, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('the per-person drill-down for a segment group') and enumerates exactly what each row contains: classified people newest-first, their tags, the canonical person identity, and OR-ed outcome flags. The framing as a 'drill-down' implicitly positions it against the aggregate segment tools (get_segment_funnel, get_segment_rates, get_segment_unclassified_count), so an agent can distinguish it without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear operational context via the filter semantics — tag_id for tagged people, untagged=true for 'No match' people, agent_ids to scope by campaign, senders to scope by teammate — and notes paging behavior. It does not, however, explicitly say when to choose this over query_people/query_agent_people or the aggregate segment tools, so there is context but no stated alternatives or exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_tagged_messagesQuery Tagged MessagesARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Page size (default 200). | |
| offset | No | Page offset (default 0). | |
| origin | No | 'sliq' for conversations with prospects only, 'imported' for imported LinkedIn conversations (ones the user started with someone who isn't a prospect) only; omit for both. | |
| tag_id | No | Narrow to messages carrying this tag; omit for all classified messages. | |
| channel | No | 'linkedin' or 'email' to bound the population; omit for both. | |
| senders | No | In a shared workspace, the teammate email addresses whose messages to return. Omit to return every sender's. Any email not in the workspace is dropped; if that leaves no valid teammate, the result is empty — it does NOT fall back to the whole workspace. | |
| group_id | Yes | The tag group to read (from list_message_tag_groups). | |
| untagged | No | Narrow to the 'No match' messages (classified as nothing, empty `tags`); takes precedence over `tag_id`. Omit for all classified messages. | |
| agent_ids | No | Scope to one or more campaigns (agent_tasks ids from a group's `campaigns`); omit for all campaigns. Campaign membership is the prospect's current one. | |
| positions | No | The conversation positions to return — 'first' (each conversation's first message), 'follow_up' (later messages sent before the prospect replied), and/or 'reply' (messages sent after the prospect replied); omit for all. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already establishes the safety profile, and the description layers substantial extra context on top: newest-first ordering, total counted before paging, connection-request notes forced to 'first', outcome flags reflecting the conversation's reached stage rather than this message's effect, and imported conversations carrying null prospect_id. These are behavioral facts an agent cannot recover from the annotations or schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded with the core identity and filter semantics, and the returns block is a justified use of space since no output schema exists. There is mild redundancy — position semantics appear in the summary, the returns block, and the schema — but structure remains scannable and every section earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the returns block fully carries the burden: it enumerates the payload shape, explains position/origin/person/company/outcome semantics, and clarifies null behavior for imported people. Combined with paging behavior and precedence rules, an agent has everything required to call and interpret this tool without opening another definition.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline would be 3, but the description adds real meaning beyond the schema: the precedence of untagged over tag_id and the interaction where a connection-request note only appears when 'first' is included in positions. Those interaction rules are not visible from reading the individual parameter descriptions in isolation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening identifies the tool precisely as 'the per-message drill-down for a group: classified sent messages, newest first' — a specific verb+resource+scope that separates it from aggregate siblings like get_message_tag_rates. It also names where group_id comes from (list_message_tag_groups) and where source/source_id feed into correct_message_classification. It stops short of explicitly naming the sibling an aggregate-oriented agent should use instead.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description walks through the filter modes one by one (tag_id, untagged=true, agent_ids, positions) and states the effect of each, so an agent knows which knob selects which population. It does not, however, state when to prefer this tool over alternatives such as get_tag_cross_tab or list_sent_messages, leaving the tool-level choice implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
queue_giftQueue GiftAInspect
Use it when the user asks to send someone a gift, and on a campaign flow's gift step,
where the card counts as that step for the prospect and the step's product, price
ceiling and note fill whatever you leave out. Queued cards show in
get_pending_approvals under the gift bucket.
An approved gift is charged to the payment card the agent's owner saved for gift purchases
under Settings → Billing; with none saved, approving orders nothing and links there to add one.
That payment card is charged the order's actual total, at most max_price_usd, plus about
$3.30 + 2.9% in fees on top.
A dict with success true, gift_id (the card's gift id) and status (the card's
status). On a flow step, nothing is queued and success is false with an error
sentence when the prospect already has a gift in progress for the step (the error
names it) or has since moved off the step (reason is then 'stale_enactment').
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | ID of the agent (campaign) the prospect is tracked on. | |
| gift_note | No | The note sent with the gift, at most 240 characters. | |
| product_url | No | Link to the product page of the gift to buy. | |
| prospect_id | Yes | The prospect's `id` — the outreach_prospects primary key, as returned on every query_prospects row and shown on a flow roster line. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| max_price_usd | No | The most the item may cost including tax and shipping, in whole US dollars. Pass a budget the user names for the gift as is, even one they call all-in, and tell them the fees come on top of it: a lower limit can make the order fail for the item they picked, so lowering it is their call. | |
| shipping_address | No | The recipient's name, US shipping address and phone number; pass only the fields you know. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false, but the description goes well beyond: it discloses the human-approval gate, US-address-only shipping, that a gift charges the owner's saved billing card (and that with none saved, approval orders nothing), the fee model (~$3.30 + 2.9%), and pre-fill semantics for all inputs. This is rich behavioral context the annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a summary tag and organized with a returns block; every section carries weight. It is longer than typical, and the fee/billing detail could be tightened, but nothing is redundant filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, yet the embedded <returns> block documents `success`, `gift_id`, `status`, and the specific `error`/`reason` cases for flow steps. Combined with the usage and billing coverage, an agent has everything needed to invoke and interpret the call.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds genuine meaning: every passed value is a changeable pre-fill, and `max_price_usd` is framed as a budget whose fees come on top and which can cause order failure if lowered. That interpretive guidance exceeds the schema's field-level docs.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Queue a physical gift to a prospect as a card on the user's approval list') and immediately clarifies the mechanism (Sliq orders and ships only after user approval). It distinguishes itself from siblings by pointing to `get_pending_approvals` and the campaign-flow gift step, so an agent can place it without opening another schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the triggering conditions: 'when the user asks to send someone a gift, and on a campaign flow's gift step.' It further explains flow-step semantics (card counts as the step; step product/price/note fill gaps) and the failure conditions (existing gift in progress, stale_enactment), giving the agent enough to decide when and how to call it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
queue_linkedin_post_engagementQueue Linkedin Post EngagementADestructiveInspect
Exactly one of comment_text or reaction_type must be provided.
For a comment, pass comment_text (the text to post). For a reaction,
pass reaction_type (one of 'like', 'celebrate', 'support', 'love',
'insightful', 'funny'). The agent picks reaction_type='like' by
default for most posts; the user can change it on the approval card.
Resolves the post's social_id (URN format) from the URL, then creates a linkedin_invite_queue item with action_type='comment' or 'reaction' depending on which body field was set. The pre_save signal resolves the per-subtype approval policy — gate (pending_approval) or auto-send (pending).
With on_behalf_of it queues account-level on that account's queue, tied to none of your
agents.
A queued-status dict in one of three forms.
{'queued': True, 'awaiting_approval': bool, 'message': str} on success;
awaiting_approval is False when the engagement will post automatically
{'queued': False, 'deduped': True, 'message': str} when this post already
has a live engagement of the same kind (report the message; not an error)
{'queued': False, 'error': str} on resolution failure
| Name | Required | Description | Default |
|---|---|---|---|
| post_url | Yes | LinkedIn post URL (e.g. 'https://linkedin.com/posts/...-activity-123-...') | |
| post_text | No | Excerpt of the post text (for the approval card display) | |
| author_name | No | Name of the post author (for the approval card display) | |
| comment_text | No | The comment text to post. Mutually exclusive with reaction_type. | |
| on_behalf_of | No | A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| reaction_type | No | One of 'like', 'celebrate', 'support', 'love', 'insightful', 'funny'. Mutually exclusive with comment_text. | |
| prospect_identifier | No | The warming prospect's tracking-item identifier (LinkedIn URL/slug) — links this engagement back to the prospect for the system-maintained `data.comments_posted` (comments) or `data.reactions_posted` (reactions) counter. Required for warm-up flow; safe to omit for one-off engagements unconnected to a warm-up campaign. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial context beyond the annotations: it discloses the approval gate vs auto-send path, dedup behavior (existing live engagement of the same kind), the account-level scoping rule for on_behalf_of, and how the outcome is surfaced. Nothing here contradicts destructiveHint=true / readOnlyHint=false, and the mutation consequences are well explained.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded and the mutually-exclusive rule is stated early, so the important parts come first. It is somewhat bloated by internal implementation detail ('pre_save signal', 'linkedin_invite_queue item with action_type=...') that does not help an agent decide or call correctly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having no output schema, the description embeds a full returns block covering success (queued/awaiting_approval/message), dedup, and error forms, plus the comment-vs-reaction requirement. For a 7-param mutation tool this is complete enough to invoke safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, but the description adds real meaning: exactly-one-of comment_text/reaction_type, the default 'like' choice and where the user can change it, and that prospect_identifier is required for warm-up flows but safe to omit otherwise. This goes well past restating the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Queue an engagement (comment or reaction) on a LinkedIn post') and immediately distinguishes its scope from read-side siblings like query_linkedin_post_engagements. An agent knows exactly what this tool does without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear when-to-use context: default lands in pending_approval unless auto-approve is enabled for the linkedin_comment/linkedin_reaction subtype, and it instructs the agent to relay the returned message rather than promise review. It does not name an alternative sibling for the queuing decision, so it stops short of full alternative routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_attachmentRead AttachmentARead-onlyInspect
Read a slice of any file in the user's attachment library, by id.
Use this when the attachment summary doesn't contain enough data to answer. For spreadsheets, paginate with row_start/row_end (default window = 500 rows). For PDFs/PPTX and text files, use page_start/page_end (1-indexed, inclusive; default window = 10 pages). When the result has truncated=True, advance page_start to read on.
| Name | Required | Description | Default |
|---|---|---|---|
| sheet | No | for multi-sheet xlsx, the sheet name (default: first sheet) | |
| columns | No | list of column names to keep (default: all) | |
| row_end | No | zero-indexed, exclusive (default: row_start + 500) | |
| page_end | No | 1-indexed, inclusive (PDFs/PPTX/text files) | |
| row_start | No | zero-indexed, inclusive | |
| page_start | No | 1-indexed, inclusive (PDFs/PPTX/text files) | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| attachment_id | Yes | a file id from list_attachments or an [Attachment: id=N] summary |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint=true already covering the safety profile, the description adds genuine behavioral context: per-format pagination windows (500 rows, 10 pages), 1-indexed inclusive semantics, and the truncated=True continuation protocol. It doesn't discuss size limits or failure modes, keeping it from a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose, then a compact usage trigger, then the format-specific pagination rules. Every sentence carries operational information with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-param reader with no output schema, the description covers the usage trigger, pagination mechanics, and the truncated=True return signal. It omits mention of sheet/columns/on_behalf_of behavior, though those are documented in the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real semantics beyond the schema: the default row window of 500, the default page window of 10, and the rule for advancing page_start when truncated. The non-pagination params (sheet, columns, on_behalf_of) are left to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource+scope: 'Read a slice of any file in the user's attachment library, by id.' It clearly distinguishes itself from list_attachments (which enumerates) and the email attachment readers by operating on the attachment library by id.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear trigger condition: 'Use this when the attachment summary doesn't contain enough data to answer,' which tells the agent when to escalate from summary to full read. It does not explicitly name alternative sibling tools, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_email_attachmentRead Email AttachmentARead-onlyInspect
Use this to read the contents of an attached document (e.g. "analyze this
spreadsheet", "summarize this PDF"). Parseable types (.csv, .tsv, .xlsx, .pdf,
.pptx, .txt, .md) come back readable — pull row/page slices with read_attachment(attachment_id).
Any other type is saved to the user's library but isn't readable; use
get_email_attachment instead if you only need to fetch or transfer the file.
Provide email_data_id + attachment_id (from search_emails results). Dict with attachment_id (pass to read_attachment), filename, mimeType, summary.
| Name | Required | Description | Default |
|---|---|---|---|
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| attachment_id | No | The attachmentId from the attachments metadata | |
| email_data_id | No | ID of the email_data record (from search_emails) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint=true already declared, the description adds real side-effect context the annotation cannot convey: the attachment is persisted as a chat attachment, unparseable types are saved to the library but not readable, and a handle is returned. It does not explicitly reconcile the persistence with the read-only hint, but it does not contradict it either, since no source data is modified.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded verb chain followed by tightly organized usage and return blocks; the <summary>/<returns> structure makes it scannable. Slightly long for a three-parameter read tool, with a small amount of overlap between the type list and the sibling routing advice.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description carries the return contract itself and does so: attachment_id, filename, mimeType, summary, plus the downstream call to make with that handle. Nothing needed to invoke the tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds practical semantics the schema omits: it tells the agent that email_data_id and attachment_id must actually be supplied and where each comes from (search_emails results, attachment metadata), even though the schema marks all three parameters optional.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource chain: fetch an email attachment, persist it as a chat attachment, return a handle. It also explicitly separates itself from two siblings (read_attachment for slicing parsed content, get_email_attachment for fetch/transfer only), so an agent can route without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete when-to-use triggers ("analyze this spreadsheet", "summarize this PDF"), states the boundary condition (parseable types are readable, everything else is only saved), and names the alternatives plus the condition that selects each.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
record_search_resultsRecord Search ResultsAInspect
The discovery tools (exa_find_people, search_linkedin_people,
fetch_post_engagers, theirstack_search, find_companies_by_tech_stack,
exa_search_news, search_news_events) already save and link their own matches
when run in an agent. Use this tool on their rows to filter them (a verdict
on each screened row, below) or to add to a saved row — columns, signals,
or evaluations — always under the same identifier and list_name the
discovery tool saved it with; a different list_name makes a second row.
Dedup per (agent, identifier, list_name). Existing rows: data['signals']
accumulates across runs (union by URL); other fields are last-writer-wins.
Unset fields preserve existing values. Cross-list duplicates of the same
identifier are allowed (intentional — two lists may track the same entity
for different reasons).
Identifiers are stored canonical — the registrable domain (eTLD+1) for
companies (https://www.Acme.com/ and news.acme.com both → acme.com),
bare slug for LinkedIn (https://linkedin.com/in/JohnDoe/ → johndoe).
Match the canonical form when querying via query_search_results.
Empty-list semantics: signals: [] preserves accumulated signals (the
cross-run contract); evaluations: [] REPLACES (LLM-rewritten per run).
Put every user-requested display attribute — a metric, a person's email,
any column the user asked to see — in data.columns as {label: value},
one entry per column under the label the user asked for. columns merges
by key-union across runs (new values win). The Output tab and CSV render
these as columns; a value written to a top-level extra key instead is not
surfaced.
Set each result's source to the discovery tool that surfaced that
candidate — per row, so a batch merged from several tools keeps each row's
origin. Leave it at 'agent' only for rows no discovery tool surfaced.
A Websets list screens its own new rows against the search's criteria, and
a discovery tool checks the rows it saves against the criteria it was
called with, each giving a row a verdict, 'unclear' when its details are too
thin to judge, so a verdict you record on those rows overrules that one. For
any other row, this is also
how you filter what you screened: give each screened row a verdict —
'qualified' for one you keep, 'rejected' with a verdict_reason for one you
drop — under the identifier and list_name it was saved with, so the verdict
lands on that row.
To mark a row already saved, re-record it with its identifier, display_name
and list_name plus the verdict; the data you leave out stays as stored. A
rejected row stays saved but drops out of the list the user sees by default;
the table counts it as "didn't match" beside its own count, and the user
clicks that count to see those rows with their reasons. query_search_results /
query_agent_people leave it out the same way unless you ask for rejected
rows. A row recorded without a verdict keeps whatever verdict it already has.
{created, updated, total, created_identifiers} — created_identifiers are
the canonical identifiers of the rows this call added (not ones it merged into).
| Name | Required | Description | Default |
|---|---|---|---|
| results | Yes | The people/company rows to upsert; each SearchResult carries its identifier, display_name, source, any signals/columns to store, and optionally its verdict and verdict_reason. | |
| agent_id | Yes | The agent these rows belong to. | |
| list_name | Yes | short kebab slug naming the bucket these rows belong to (e.g. 'oil-gas-operators', 'fintech', 'companies'). Required. Distinct slugs are separate lists on the agent's People and Companies tabs; reuse the same slug across calls to accumulate into one list. For uncategorized runs, pick a single descriptive slug (e.g. the entity_type plural — 'companies' / 'people') and use it consistently. | |
| entity_type | No | Whether `results` are companies or people ('company' by default). | company |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations (readOnlyHint=false, destructiveHint=false) only establish that this is a non-destructive write. The description goes far beyond: dedup key (agent, identifier, list_name), signals union-by-URL vs. last-writer-wins, the asymmetric empty-list semantics (signals: [] preserves, evaluations: [] replaces), canonical identifier normalization (eTLD+1, LinkedIn slug), and that source/verdict are recorded only under specified conditions. This is precisely the behavioral context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded correctly with the core purpose and sibling routing, but the body is very long and repeats the verdict/rejected-row mechanics across multiple paragraphs (rejected rows dropping out of the default list, how counts display, how query tools hide them). For a complex tool most of it earns its place, yet there is real redundancy that could be tightened.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-parameter upsert tool with nested data payloads and no output schema in the structured fields, the description covers merge/union semantics, canonicalization, verdict lifecycle, and source attribution, and even includes a returns section documenting `{created, updated, total, created_identifiers}`. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds genuinely non-schema semantics: the canonical identifier forms to match when querying, why every display attribute must go in `columns` (key-union merge, otherwise unsurfaced), and how list_name reuse accumulates vs. creates a new list. It stops short of 5 only because several of these points are also richly restated in the schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Upsert people/company rows into the agent's entity row store (`agent_search_results`)') and immediately scopes it to rows the agent assembled itself (pasted roster, CRM read, resolved search results). It then explicitly separates itself from the discovery tools by name, so an agent can route correctly without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the alternatives (exa_find_people, search_linkedin_people, fetch_post_engagers, theirstack_search, etc.) and states the exact condition for choosing this tool over them: only to filter their rows, add columns/signals/evaluations, or record a verdict. The when-not is also implied clearly ('Leave it at agent only for rows no discovery tool surfaced').
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
register_manual_linkedin_invitesRegister Manual Linkedin InvitesAInspect
Only registers invites still pending; anything else comes back as not_pending — which can
mean already accepted or never sent, so don't assume acceptance (check the prospect's
linkedin_stage before messaging). Unmatched prospects that have a LinkedIn URL but no
provider_id yet come back under resolving: Sliq looks up their profile in the background
to identify them (in progress, not failed). Attach a resolved_profile trigger to act on
each result — an already-connected one sends no acceptance event, so it won't engage on its
own. Use this only after the prospects are tracked and the user has confirmed they sent the
requests themselves.
Usually returns in seconds: the pending-invitations read stops as soon as every named prospect is matched, and recently hand-sent invites sit at the top of the list. When some prospects aren't found there, it pages the user's full backlog with paced gaps to protect their LinkedIn account — up to a couple of minutes on a large backlog — so warn the user about the possible wait only when registering invites sent long ago or likely already accepted, not for a just-sent batch.
Pass the specific people the user named in identifiers. Only set all_tracked=True
when the user said they hand-invited the whole campaign — it registers every tracked
prospect that has a real pending invitation, which can pull in an unrelated pending
invite, so it's a deliberate opt-in rather than the default.
Dict with registered / already_registered / not_pending / resolving / ambiguous /
skipped lists, agent_has_accept_trigger (whether the agent has an accept trigger — a
sequence campaign advances on accept via its flow regardless), and a summary message.
| Name | Required | Description | Default |
|---|---|---|---|
| node_id | No | Optional. The sequence-DAG connection_request node the hand-sent CR satisfies (mirrors setup_linkedin_sequence's node_id) — stamped on the recorded row so its acceptance advances the flow onto the next node. Omit to auto-detect the agent's sole CR node; pass it explicitly on a multi-CR sequence. Rejected with ModelRetry if it isn't a connection_request node in the agent's sequence. | |
| agent_id | Yes | The agent whose tracked prospects were manually invited. | |
| all_tracked | No | Register every tracked prospect in the agent — use only when the user invited the whole campaign by hand. | |
| identifiers | No | LinkedIn URLs, provider_ids, or names of the people the user invited by hand. Required unless `all_tracked` is set. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false; the description goes far beyond by disclosing that it cancels the now-redundant queued connection request, that unmatched prospects return under `resolving` as in-progress background lookups, that `not_pending` is ambiguous, and the timing behavior (seconds normally, up to a couple of minutes when paging a large backlog, with paced gaps to protect the account). This is unusually rich context for a mutation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a one-line summary before the operational detail, and the returns block is clearly separated. It is long, but the length tracks the genuine complexity; a few clauses (e.g. the repeated restatement of the accept-trigger caveat) could be tightened.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers the return lists, the `resolving` in-progress state, trigger attachment advice, timing expectations, and the all_tracked caveat. Given there is no output schema, the description supplies everything an agent needs to interpret results and call the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real semantic value for `identifiers` (pass the specific people the user named) and `all_tracked` (deliberate opt-in with the risk of an unrelated pending invite). It adds little for `node_id` and `on_behalf_of`, which the description does not touch, so it stops short of a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource: registers manually-sent LinkedIn connection requests so Sliq runs the accept→message sequence. It clearly distinguishes itself from siblings like manage_linkedin_invite_queue and setup_linkedin_sequence by describing reconciliation of hand-sent invites against real pending invitations.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit preconditions ('use this only after the prospects are tracked and the user has confirmed they sent the requests themselves') and explicit selection rules between `identifiers` and `all_tracked`, including the downside of the latter (can pull in an unrelated pending invite). It also tells the agent not to assume acceptance and to check linkedin_stage first.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
remove_triggerRemove TriggerAInspect
Removing a flow node's on_enter enactment un-arms that node — prospects entering it
no longer enact until you re-arm it with add_trigger. To stop it firing while keeping
its authored content, pause it with update_trigger(status='paused') instead.
Dict with success, agent_id, and next_run_at. Read the agent's surviving
triggers via get_agent_details.
| Name | Required | Description | Default |
|---|---|---|---|
| trigger_id | Yes | ID of the trigger to remove (from the agent's trigger list). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description openly describes a delete that discards authored trigger content ('re-arm it with add_trigger'), while annotations declare destructiveHint=false. That is a direct reversal on the destructive axis and could lead an agent to remove a trigger without the confirmation a destructive hint would prompt; the otherwise-good consequence detail cannot offset a contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The consequence and the pause alternative are front-loaded in the first paragraph, and the flow-node on_enter warning earns its space. It runs long for a single-id delete, with the returns block adding bulk, but no sentence is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, and the description supplies the return shape (success, agent_id, next_run_at) plus where to verify surviving triggers via get_agent_details. For a single-parameter delete with full schema coverage, nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and trigger_id is already documented as coming from the agent's trigger list, so baseline 3 applies. The description only restates 'by id' and says nothing about on_behalf_of, adding no meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Delete one trigger from its agent by id') plus the scope of consequences. It is clearly distinguishable from add_trigger, update_trigger, and delete_agent without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It names the alternative explicitly: 'To stop it firing while keeping its authored content, pause it with update_trigger(status='paused') instead,' and points to add_trigger for re-arming. The condition that selects removal versus pausing is spelled out, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
report_enactment_blockedReport Enactment BlockedAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| reason | Yes | Short machine-friendly cause, e.g. 'linkedin_disconnected', 'unreachable_profile', 'no_valid_recipient'. | |
| node_id | Yes | The flow node whose step could not be performed — must be the node the prospect currently rests on. | |
| agent_id | Yes | ID of the agent whose flow the prospect is on. | |
| prospect_id | Yes | The prospect's `id` — the outreach_prospects primary key, as returned on every query_prospects row and carried on flow on_enter events (event['prospect_ids']). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false and destructiveHint=false; the description goes beyond them by explaining the mutation precisely — the block is scoped to the current node, it stops re-dispatch of that step, and any later move onto another node clears the block (consistent with non-destructive). It does not discuss permissions or limits, so it is not fully exhaustive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary states the action and the effect, followed by a compact returns block. The tag-based structure is efficient, though the returns sentence is thin enough to feel like filler rather than substance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the returns block covers the response shape (prospect, node, recorded reason), and the description covers trigger, side effect, reversibility, and the mandatory-call rule. Nothing an agent needs in order to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter including reason, node_id, and on_behalf_of is already documented. The description only restates node scoping and the reason categories, adding essentially no syntax or format detail beyond the schema; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (report a flow step that could not be performed) and the exact side effect (marks the prospect blocked on the node they rest on). It is clearly distinguishable from siblings like retry_blocked_enactment and move_prospect_to_node without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a strong usage trigger: 'Call this for every prospect you cannot enqueue or route during a flow step — never silently skip one,' plus the diagnostic criterion 'retrying identically would fail identically' that separates this from a transient failure. It never explicitly names the alternative (retry_blocked_enactment / move_prospect_to_node), so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
request_user_actionRequest User ActionAInspect
Prefer on-platform tools when the channel is automatable: setup_linkedin_sequence
for LinkedIn DMs and connection requests, the email tools (draft_email,
setup_email_sequence) for email outreach, find_email for contact lookups.
Use this tool only after ruling those out.
This tool is for a one-off ask about a particular prospect. When the off-platform
step is a step of a sequenced campaign — every prospect reaching that point gets it,
and the campaign continues once it's done — author a manual node in
define_sequence instead: entry queues the same row, and marking it done advances
the prospect onto the node's next step.
Dedup is per pending row on (agent, prospect, bucket_name): if the same
prospect already has a pending row for the same bucket the tool returns
created=False with the existing row's bucket and action_description so you
can see what's already queued. Re-flagging the same prospect for a different
bucket is allowed; re-flagging after the user marks a prior row done or
dismisses it is also allowed.
Dict with created (bool), action_id (int or None), id (int), and reason (str).
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | ID of the campaign agent this request belongs to. | |
| bucket_name | Yes | Short label naming the kind of ask (e.g. "voice-note", "intro-call"). Also the third dimension of the pending-dedup key — two different buckets can coexist as pending rows on the same prospect. | |
| prospect_id | Yes | The prospect's `id` — the outreach_prospects primary key, as returned on every query_prospects row. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| action_description | Yes | One-line description of what the user should do. | |
| completion_criteria | No | How the agent should advance the prospect after the user sends an update about what they did. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false, but the description discloses far more: the exact dedup key `(agent, prospect, bucket_name)`, the idempotent return path (`created=False` plus the existing row's bucket and action_description), and the conditions under which re-flagging is permitted. Nothing contradicts the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose and the primary sibling exclusion (queue_gift) are front-loaded in the first sentence, and the summary/returns sections are cleanly delimited. It is on the long side, and the dedup paragraph is dense, but each segment carries distinct decision-relevant information rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter mutation tool with no output schema, the description covers everything an agent needs: routing against siblings, one-off vs campaign scoping, the dedup contract, and even an embedded returns block describing the created/action_id/id/reason dict. No material gap remains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning beyond the schema by explaining bucket_name's dual role as the third dimension of the pending-dedup key and noting that two buckets can coexist as pending rows. It does not, however, explain on_behalf_of or completion_criteria beyond what the schema text already says.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb+resource ('Flag a prospect for the user to act on directly') and scopes it to off-platform or human-judgement asks, then enumerates concrete examples (voice memo, phone call, handwritten card, event, hiring decision). It actively distinguishes itself from named siblings like queue_gift and setup_linkedin_sequence, so an agent can route correctly without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use ('off-platform or genuinely needs human judgement'), explicit when-not ('Prefer on-platform tools when the channel is automatable'), and it names the concrete alternatives for each automatable channel (setup_linkedin_sequence, draft_email, setup_email_sequence, find_email). It also resolves the one-off vs sequenced-campaign ambiguity by routing the latter to a `manual` node in define_sequence.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resolve_dateResolve DateARead-onlyInspect
Also use this to convert times in other timezones to the user's local timezone. For example, if someone says "2pm PST" and the user is in Eastern, call resolve_date("2pm PST") and it will return the equivalent time in the user's timezone. Use standard timezone abbreviations (PST, PDT, EST, EDT, CST, CDT, MST, MDT, GMT, UTC, etc.) -- "Pacific" or "Eastern" alone won't parse.
IMPORTANT: When using the result for trigger_at in add_trigger, ALWAYS
use the utc_iso field from the response (e.g. "2026-04-04T04:56:00Z").
The time and time_12h fields are in the user's LOCAL timezone — passing
them with a "Z" suffix will create triggers at the wrong time.
Dict with date, weekday, and (when time is present) time, time_12h,
and utc_iso (UTC ISO format for use in triggers).
| Name | Required | Description | Default |
|---|---|---|---|
| expression | Yes | Natural language date/time expression (e.g. "Tuesday", "next Monday", "2pm PST", "3:30 PM EDT Thursday") | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, and the description adds substantial context beyond that: the local-vs-UTC distinction between the time/time_12h and utc_iso fields, the critical warning to use utc_iso for trigger_at, and the parsing limitation that full names like 'Pacific' won't work. These are exactly the behavioral traits an agent needs to avoid silent errors.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose, then layers timezone conversion and the utc_iso warning in a logical order, with summary/returns tags aiding scanning. Slightly long, but nearly every sentence carries non-redundant operational value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although no formal output schema exists, the description includes a returns section describing the dict fields (date, weekday, time, time_12h, utc_iso), so the agent knows what comes back and how to use it. Combined with usage and parsing caveats, the definition is complete for a 2-parameter read tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% so the baseline is 3, but the description adds meaning beyond the schema by constraining the expression format (standard timezone abbreviations required, examples given) and clarifying the timezone-conversion semantics. It does not elaborate on on_behalf_of, which the schema already documents, keeping it above baseline but not a full 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (resolve) and resource (natural language date/time expression) with concrete examples, and explicitly distinguishes itself from siblings like create_calendar_event and add_trigger by describing itself as a prerequisite conversion step. An agent can identify its role without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to call it (before search queries, create_calendar_event, or any operation needing a concrete date) and adds a second distinct use case (timezone conversion). It even names the downstream consumer add_trigger and how to feed it, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
retry_blocked_enactmentRetry Blocked EnactmentAInspect
A retry with the cause still in place fails and re-blocks identically — fix the
cause in the same call: pass email for an email-shaped block (email_not_found,
a missing address) and/or linkedin_url for a wrong or missing LinkedIn
identifier (missing_identifier, a failed resolution). Both fix the EXISTING
prospect's person — never re-add the person via track_prospects with the new contact
info, which creates a duplicate prospect. A cr_already_sent block has no fix
here: Sliq's records show it already sent this person a connection request. Tell
the user; if they have since re-invited by hand, register_manual_linkedin_invites
picks the campaign up from that invite; otherwise drop the prospect with
update_prospect(stage='skipped'). A name_mismatch block means the LinkedIn profile
belongs to someone other than the person they were added as (data.supplied_name):
retrying continues the campaign with that profile — do it only when it's the same
person under another name; otherwise skip the prospect and track the intended person
(a linkedin_url fix is refused on this block).
Dict confirming the prospect, the node they rest on, the cleared reason,
and any contact fixes applied (email / linkedin_url). An email fix lands on
the person's profile, the prospect's only address.
| Name | Required | Description | Default |
|---|---|---|---|
| No | An address to stamp on the prospect before retrying (optional) — the same write as update_prospect's `email`; refused if another person in the workspace already carries it. | ||
| agent_id | Yes | ID of the agent whose flow the prospect is on. | |
| prospect_id | Yes | The prospect's `id` — the outreach_prospects primary key, as returned on every query_prospects row. | |
| linkedin_url | No | A corrected LinkedIn profile URL or slug (optional). Applied as an unverified claim the retried send verifies through the profile resolver, so a wrong-person pairing is held for adjudication like any other. On a previously-verified profile (a dead or reassigned URL) the old verification is cleared and the corrected identity re-verifies fresh; refused only when a teammate's prospect verified the same person — remove this prospect and track the correct profile instead. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=false and destructiveHint=false; the description adds substantial context beyond that: it mutates person contact data, retry is asynchronous (~15 min, longer when queued), a retry with the cause still present re-blocks identically, and certain fixes are refused (linkedin_url on name_mismatch).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is long, but front-loaded with a <summary> and each sentence carries a distinct constraint (timing, per-block fixes, refusal conditions). Dense rather than padded, though the volume is at the edge of what an agent needs in one read.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description supplies a <returns> block describing the confirmation payload. Combined with the per-block guidance and async timing, an agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: it maps `email` to email-shaped blocks and `linkedin_url` to identifier blocks, and warns the fix applies to the EXISTING prospect rather than re-adding via track_prospects. This goes beyond the schema's per-field text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('clear a prospect's blocked flow step so it retries') plus the side effect of fixing contact info in the same call. It explicitly names related siblings it is not (report_enactment_blocked, query_prospects, track_prospects), so an agent can distinguish it without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('blocked prospect is excluded from every enactment selection until unblocked'), when-not ('cr_already_sent has no fix here'), and alternatives per case (register_manual_linkedin_invites, update_prospect(stage='skipped'), track_prospects). The block-reason taxonomy maps conditions to the right action.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_memorySave MemoryAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| content | Yes | The full updated memory document (markdown) | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds real value beyond the annotations by disclosing the critical behavior that this REPLACES the entire document rather than merging or appending — the single most important thing to know before calling a write tool whose destructiveHint is false. It also states the return shape ('Dict with success status'). It does not mention permission requirements or what happens on partial/invalid input, which is why it is not a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Short, XML-tagged and front-loaded: the replacement warning appears in the second clause rather than buried. Minor redundancy between 'Pass the COMPLETE updated memory' and 'pass the full result', which restates the same instruction.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 2-parameter write tool with no output schema, the description covers what it does, the overwrite semantics, the workflow, and the return value. Nothing essential to invoking it correctly is missing; only edge behavior (errors, credit/agency scoping) is left to the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so a baseline of 3 applies; the description earns above baseline by hammering the 'COMPLETE / full result' constraint on `content`, clarifying that replacing semantics apply rather than a patch. It says nothing about `on_behalf_of`, but the schema documents that parameter thoroughly, so no compensation is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Save the user's memory document') and immediately clarifies the semantic mode: full replacement of the document. This implicitly but clearly distinguishes it from an append-style sibling (append_learning) and from get_user_memory, since the agent knows this overwrites rather than appends.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear usage procedure: read the current memory from context, make changes, pass the full result. It establishes the precondition (you must already hold the current document), but it never explicitly names an alternative tool or a 'do not use this when…' case, so routing guidance is inferred rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_prospect_researchSave Prospect ResearchAInspect
Call this after you synthesize a "why I'm reaching out" angle for a recipient — once per recipient, batching all of that person's findings (and you may batch across recipients). Each finding is YOUR synthesized insight ("former Stripe PM, just posted about scaling support"), NOT raw scraped post text or an article abstract.
Pass the lead as a handle (the LinkedIn URL / provider id / email you already
hold) in person — do NOT hand-type a canonical identifier; the tool
canonicalizes it for you. A company finding (subject_kind='company') still
attaches to the person it personalizes; set company_domain to record the
company.
Dict with created (the count of saved research rows).
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | The agent these findings belong to. | |
| findings | Yes | One ProspectFinding per synthesized insight; each carries the lead handle it personalizes, its subject_kind, and the source. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only tell the agent this is a non-read, non-destructive write. The description adds substantial context beyond that: the tool auto-canonicalizes the identifier so the caller must not hand-type one, findings are batched per person, company findings still attach to the person and require company_domain, and it even discloses the return shape (a count of saved rows). This is exactly the value-add expected over annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action and purpose in the opening sentence, then uses summary/returns structure to separate intent from return value. The prose is somewhat dense and long, with a bit of redundancy around findings content, but nearly every sentence carries actionable information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a nested write tool with no formal output schema, the description covers the essentials: what to pass, how identifiers are handled, per-recipient batching, and the return count. It omits auth/permission requirements and any limits on finding volume, which are minor gaps given the mutation annotations already present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, and the description adds real meaning: `person` must be a handle (LinkedIn URL / provider id / email) rather than a typed canonical identifier, and `company_domain` is required for subject_kind='company'. It clarifies the content-vs-source distinction for findings, though it does not explain the source enum values or the on_behalf_of parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Persist/persist synthesized personalization research') and resource (prospect research findings on an agent) plus the reason it exists (visibility on the agent page). It is clearly the write counterpart to the sibling query_prospect_research, so an agent can distinguish them without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit timing and cadence: 'Call this after you synthesize a why-I'm-reach-out angle,' 'once per recipient,' and batching rules per recipient and across recipients. It also defines what NOT to pass (raw scraped post text or article abstract), which is a clear when-not condition, though it never names the read alternative query_prospect_research for comparison.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_calendarSearch CalendarARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results (default 50, max 200). Only override if user says specific number like "show me 10 events". For vague quantities ("couple", "few", "recent"), omit this parameter to use default. | |
| offset | No | Rows to skip for paging (default 0). When the result is truncated, re-call with offset += limit to fetch the next page. | |
| order_by | No | SQL ORDER BY clause (default: event_start DESC) | event_start DESC |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | Yes | SQL WHERE clause (without WHERE keyword). Use ONLY these columns: provider, subject, event_start, event_end, location, meeting_link, organizer, attendees, is_all_day, is_cancelled, has_recurrence. For organizer/attendees JSONB, use: organizer::text ILIKE '%pattern%' or attendees::text ILIKE '%pattern%' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safety profile is covered. The description adds real value by disclosing the return shape (count, truncated flag, events array) with no output schema present, and the truncated field implicitly communicates paging semantics beyond what the annotations state.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Short and clearly front-loaded, with the core purpose in the first clause and return information separated into a labeled section. The pointer 'listed under where_clause below' is slightly awkward since where_clause lives in the schema rather than below the description, but it costs little.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only query tool with a fully documented 5-parameter schema and no output schema, the description supplies the two things structured fields do not: the backing table and the return envelope (count/truncated/events). Nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter (limit, offset, order_by, on_behalf_of, where_clause) is already documented in detail, including defaults, max values and pagination behaviour. The description only reinforces the where_clause column constraint that the schema already enumerates, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search user's calendar events') and even names the backing table (table_calendar_data), so an agent can tell it is a read/query tool. It does not, however, distinguish itself from near-neighbours like search_meetings, get_luma_events, or create/update_calendar_event, so sibling differentiation is left to inference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use or when-not-to-use guidance relative to alternatives such as search_meetings or search_emails. The only directive is 'use the exact column names listed under where_clause', which is a parameter-usage constraint rather than task-selection guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_chat_historySearch Chat HistoryARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of sessions to return (default 20, max 50). | |
| keyword | No | Search term to match against session title and summary (case-insensitive). Leave empty to browse recent sessions. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| created_after | No | Only return sessions created after this date (ISO format, e.g. '2026-01-15'). | |
| created_before | No | Only return sessions created before this date (ISO format, e.g. '2026-03-01'). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already tells the agent this is a non-mutating read. The description adds the return shape (count + sessions array with id/title/summary/dates/message_count), which is genuinely useful. Beyond that it discloses nothing further — no pagination behavior, no ordering guarantee, no note that keyword searches only title and summary rather than message bodies, which is a meaningful limitation an agent would want.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose and scoped by XML-style tags for returns. The usage sentence is slightly padded ('something they talked about before' restates 'previous conversations'), but overall it is short and earns most of its length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, and the description compensates by describing the returned dict's fields, so an agent knows what to expect. Combined with a fully documented input schema and a readOnly annotation, the definition is usable, with the only real gap being the scope of the keyword match and result ordering.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and every parameter (limit, keyword, on_behalf_of, created_after, created_before) is documented in the schema, including the default/max on limit and the ISO date format. The description mentions no parameters, so the schema carries the whole load — the baseline 3 for a fully covered schema is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb and resource — 'search past chat sessions from the user's conversation history' — and states what comes back (sessions with titles, summaries, dates), which implicitly separates it from the message-level sibling get_chat_messages. However, it never names that sibling explicitly, so the distinction still requires inference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear trigger conditions: 'when the user asks about previous conversations, past discussions, or wants to find something they talked about before.' That is concrete context for invocation. It stops short of naming when NOT to use it or pointing to an alternative such as get_chat_messages for retrieving the contents of a specific session.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_emailsSearch EmailsARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results (default 50, max 200). Only override if user says specific number like "show me 10 emails". For vague quantities ("couple", "few", "recent"), omit this parameter to use default. | |
| offset | No | Rows to skip for paging (default 0). When the result is truncated, re-call with offset += limit to fetch the next page. | |
| order_by | No | SQL ORDER BY clause (default: email_datetime DESC) | email_datetime DESC |
| on_behalf_of | No | A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | Yes | SQL WHERE clause (without WHERE keyword). Use ONLY these columns: provider, sender_email, sender_email_lower (WHERE only; not returned; pass lowercased values), sender_name, recipients, is_outbound (BOOLEAN — true = sent from the connected mailbox, false = received), subject, content, email_datetime, attachments, reply_outcome (inbound replies only; '' / 'interested' / 'meeting_booked' / 'not_interested' — the reply's classified outcome). For participants, use sender_email_lower = ANY(ARRAY['foo@example.com', ...]). Resolve the email address first if you only have a name. For attachments JSONB, use: attachments::text ILIKE '%filename%' or attachments != '[]'::jsonb to find emails with attachments. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already covers the safety profile, and the description adds genuinely useful behavior: results are truncated to 200 chars and content_length holds the true length. This extra context (truncation, how to get full data) goes beyond the annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary with the key routing hint, and every sentence earns its place (truncation, column names, get_emails handoff). The nested XML tags add slight overhead but no real confusion.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, but the description compensates by describing the return dict (count, truncated, emails, content_length) and the truncation contract, which is what an agent needs to interpret results correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the where_clause columns are exhaustively documented in the schema itself. The description only points the agent at that column list rather than adding new meaning, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Search user's emails from table_email_data") and explicitly distinguishes itself from the sibling get_emails by positioning itself as a truncated preview tool. An agent can tell what it does without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the alternative and its condition: use this for 200-char previews, call get_emails with the IDs for full content. This is clear routing guidance, though it doesn't cover how it relates to other search tools (e.g., search_chat_history, search_linkedin_message_history).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_linkedin_connectionsSearch Linkedin ConnectionsARead-onlyInspect
position and company are connection-time snapshots and may be stale — treat them as a starting point, not current ground truth. Dict with count, truncated (True when more rows exist past this page), and connections array.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results (default 50, max 200). Only override if the user says a specific number. For vague quantities ("a few", "recent"), omit this parameter to use the default. | |
| offset | No | Rows to skip for paging (default 0). When the result is truncated, re-call with offset += limit to fetch the next page. | |
| order_by | No | SQL ORDER BY clause (default: added_date DESC). | added_date DESC |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | Yes | SQL WHERE clause (without WHERE keyword). Use ONLY these columns: first_name, last_name, position, company, url, email_address, connected_on, linkedin_provider_id, added_date. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already covers the safety profile, but the description adds real value beyond annotations: it warns that 'position' and 'company' are connection-time snapshots that may be stale, and it documents the return shape (count, truncated flag, connections array). It stops short of stating auth/credit implications, which the on_behalf_of parameter hints at.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and resource, then the exclusions, then the staleness caveat, with return values isolated in a dedicated tag. Every sentence carries information; no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, yet the description supplies a <returns> block covering count, truncated, and the connections array, plus the staleness caveat and sibling routing. For a read-only search tool this is fully sufficient for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all five parameters including the allowed where_clause columns. The description only adds a pointer ('Use the exact column names listed under where_clause below'), which is useful signposting but not new semantic content. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search the user's established first-degree LinkedIn connections') and names the underlying table. It also names the siblings it is NOT ('search_linkedin_message_history', 'query_prospects'), so an agent can select it without opening another schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly frames when to use it ('find existing relationships by name, title, or company') with concrete example queries, and names the two alternatives plus the condition that selects them. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_linkedin_message_historySearch Linkedin Message HistoryARead-onlyInspect
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results (default 50, max 200). Only override if the user says a specific number like "show me 10 messages". For vague quantities ("couple", "few", "recent"), omit this parameter to use the default. | |
| offset | No | Rows to skip for paging (default 0). When the result is truncated, re-call with offset += limit to fetch the next page. | |
| order_by | No | SQL ORDER BY clause (default: message_datetime DESC). | message_datetime DESC |
| on_behalf_of | No | A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | Yes | SQL WHERE clause (without WHERE keyword). Use ONLY these columns: provider, chat_id, sender_name, sender_provider_id, recipient_name, recipient_provider_id, content, message_datetime, reply_outcome (inbound replies only; '' / 'interested' / 'meeting_booked' / 'not_interested' — the reply's classified outcome). To read your thread with a person, match their provider_id alone: `sender_provider_id = '<id>' OR recipient_provider_id = '<id>'`. Keep their name out of that clause: a name ILIKE is a substring match, so it pulls in other people's threads (`'%Ana%Li%'` also matches "Diana Lim"). Match on a name only to find someone whose provider_id you don't have; when a hit's counterpart name is their full name, take it as theirs and re-read by that counterpart's provider_id. An empty result isn't proof of no prior contact: this local store holds only a bounded initial backfill from around when the account was connected, forward. When you need certainty, call `fetch_linkedin_messages_with_person` with their provider_id — it reads LinkedIn's full synced history live. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the read-only annotation, the description discloses that the store is a bounded local backfill from account connection forward, that empty results do not prove no prior contact, and that message content is truncated to 1000 chars. It also notes that the accepted connection request note is excluded from these results.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is structured and front-loads the core purpose, then adds return and usage notes. It is somewhat dense, but the detail is relevant for a search tool with nuanced data-scope caveats.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description supplies the essential return shape (count, truncated, messages array with truncated content) and explains how to use the returned `chat_id`. It also covers the key completeness limitation of the local store.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the detailed per-parameter descriptions already carry most semantics. The tool description still adds useful invocation meaning, such as calling once per person when checking multiple people and passing a returned `chat_id` directly into `setup_linkedin_sequence`.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: searching the user's LinkedIn message history (synced conversations). It also distinguishes the tool from `fetch_linkedin_messages_with_person` by noting that this searches the local synced store while the sibling reads live full history.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit usage conditions, including looping once per person when checking several people, not needing to screen before a first touch, and using `fetch_linkedin_messages_with_person` when certainty about full history is required. These are concrete when-to-use and alternative-tool cues.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_linkedin_peopleSearch Linkedin PeopleARead-onlyInspect
One call runs one query, returning ~10 matches by default (one page). To go
deeper on a single query — "find people in my network matching my ICP" — pass
max_results (up to 100): the tool pages through the matches for you, each
~10-profile page counting as one search against the daily budget. For someone
whose profile URL or provider_id you already have, read their degree from
query_agent_people or a resolve look-up and pass the identifier straight
to setup_linkedin_sequence to queue outreach; a name search would only add
namesakes. To search different people — a list of names, or one filter per
company — loop this tool inside a run_code block, one call per name or
company (that's breadth; max_results is depth on one query). Searches are
paced a few seconds apart
and serialized across this user's LinkedIn work, so a deep search or a long
loop can take a couple of minutes; tell the user to expect a short wait before
a large run. If a search comes back paused or rate-limited, stop and tell the
user which searches remain — the account is paused and further calls won't run
until it lifts.
Scope filters combine with keywords and can be used alone for a single
filtered search:
connections_of— restrict to the first-degree connections of specific people, passed as theirprovider_ids (as returned by an earlier search or profile lookup). To work up to a buyer through someone the user just connected with, pass that person inconnections_ofand the target company inadvanced_keywords={'company': 'Acme Corp'}to surface who they know there.network_distanceis a separate filter on the user's own degree and combines with this — add [2] to keep just the connections the user isn't already directly linked to.advanced_keywords— native LinkedIn keyword sub-filters: a dict with any offirst_name,last_name,title,company,school(each a string).companymatches loosely, so its results can include people who don't work there now (a past employer, a similarly named organization).profile_language— ISO 639-1 codes (e.g. ['en']) that narrow any of the above to profiles written in those languages. A refinement, not a search on its own — pair it with keywords or another filter.
Each profile the search returns is then checked against criteria, one
profile at a time, and only the matches come back in profiles; the rest
come back in rejected as {name, reason}, with no profile URL, so they can't
be acted on. The check reads the name, headline and location, so a criterion
a headline can't show (company size, industry) doesn't reject anyone. It adds
a few seconds per ten profiles.
When this runs in an agent, the profiles are saved with their verdicts and
linked to the workspace Output tab automatically (deduped by profile), the
rejected ones hidden from the list's default view. Pass list_name (a short slug) to name their list — a
discovery search, the people connected to someone, prospects to work through;
reuse the same slug across a loop or follow-up searches to gather everything
into one list. Absent a slug, they land in the 'default' list. Outside an
agent, results are returned only.
Returns up to max_results profiles with provider_id, name, headline,
network_distance, location, and profile_url. Each match's headline is the
member's own tagline: often their current role and company (e.g. to see which
companies 2nd-degree matches work at), but frequently a title alone, so a
headline that omits a company is not evidence they work elsewhere. total_count is LinkedIn's full match count for the query when it
returns one, but LinkedIn now omits it on most Classic searches (so it's often
null): only say "showing N of ~M" when it's a number exceeding the profiles
returned, and never invent a total. Use has_more — True when more results
exist beyond those returned — to decide whether to offer to pull more.
Present the results to the user so they can pick the right person. An error
about being "heavily queued" is transient pacing back-pressure — retry shortly
rather than reporting it as not found.
That field list is the whole of it — a search result carries no current
employer, connection count, follower count, or employment history. Present
what comes back as it is; a search the user wanted to look at is finished at
that point. When the ask genuinely needs one of the missing fields — a
connection-count threshold, employment history to personalize from, or a
per-company verdict or count from company-filtered searches ("which of
these accounts do I have a path into") — pass the matches' profile URLs to
enrich_linkedin_profiles, which returns them for the whole list in one
paid call (connections_count is the field a connection-count filter reads;
company is the current employer) and spends no LinkedIn account budget. A
per-company verdict needs it because an answer summed over people hides the
headlines that would show a loose match. When the user will read a
company-filtered list themselves, present it and offer that enrichment to
confirm who works there now. When the decision also turns on
whether the user is already connected to them, use
setup_linkedin_sequence(action_type='resolve') instead; connection status
is the one thing enrichment cannot answer.
Rate-limited — shares one daily LinkedIn search budget with all other
LinkedIn people searches.
On success, a dict
{'success': True, 'profiles': [...], 'rejected': [...], 'total_count': int | None, 'has_more': bool, 'searches_remaining_today': int}. profiles holds the
matches among the up to max_results found, each with a verdict
('qualified', or 'unclear' when the profile says too little to judge) and
its verdict_reason, and a null verdict when the check couldn't run;
rejected holds the rest as {name, reason}. total_count is the query's full match count when
LinkedIn returns one (often null since its Aug-2026 Classic Search change),
so lean on has_more for whether more results exist; and
searches_remaining_today is the post-search budget, so you can size a
follow-up loop without re-checking. In an agent, also saved_to_list (the
list the profiles were saved to) and saved_count, how many of them the list shows; outside an agent, a
passed list_name yields a null saved_to_list with a persist_note. If
the account tripped its pause partway
through paging, the (still valid) partial results come back with
paused: True and a note — surface it: further searches won't run until
the pause lifts.
On a failed search: {'success': False, 'profiles': [], 'rejected': [], 'error': ..., 'searches_remaining_today': int}. On a pre-flight refusal (daily limit
reached or account paused), searches_remaining_today is omitted:
{'success': False, 'error': ...}.
| Name | Required | Description | Default |
|---|---|---|---|
| criteria | Yes | What a match is for this search, in plain words: the person you're looking for ("Jane Doe, currently at Acme Corp"), or the roles, seniority, region and employer that qualify someone ("Sales leaders, VP and above, based in the US"). Each returned profile is checked against it. | |
| keywords | No | A single search query, typically a person's name (e.g. "John Smith"). Omit when searching by `connections_of` / `advanced_keywords` alone. To search many names, loop the tool in `run_code`, one name per call. | |
| list_name | No | Optional short slug naming the Output-tab list the matches are saved under when this runs in an agent. Absent, they save to the 'default' list. Reuse the same slug across a loop or follow-up searches to gather them into one list (deduped by profile). | |
| max_results | No | Max profiles to return for this one query (default 10 = one page; capped at 100). The tool pages the search internally to reach this many, each ~10-profile page costing one search from the daily budget. Use it to go deep on a single ICP/network query; for many *different* queries, loop the tool instead. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| connections_of | No | Optional list of provider_ids; restricts results to people connected to those individuals. | |
| network_distance | No | Optional LinkedIn degree filter. Pass [1] for first-degree connections, [2] for second-degree, [3] for third-degree-or-more, or combinations like [1, 2]. | |
| profile_language | No | Optional list of ISO 639-1 codes (e.g. ['en']). | |
| advanced_keywords | No | Optional dict of native keyword sub-filters (first_name / last_name / title / company / school). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Although readOnlyHint=true already signals a non-mutating operation, the description adds extensive behavioral context: internal paging against a shared daily search budget, pacing and serialization delays, criteria-checking behavior, automatic list saving in agents, partial results during an account pause, and specific error handling for 'heavily queued' responses. It does not contradict the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded and information-dense, but it is very long and includes some repetition, especially around total_count, enrichment, and the fields returned by a search. Most sentences earn their place, but trimming redundancy would improve conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given nine parameters, no output schema, and a read-only annotation, the description is complete: it explains return fields, rejected profile behavior, has_more and total_count caveats, saved list behavior, failure modes, pacing, and enrichment needs. An agent has enough information to call the tool correctly and interpret results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 100% schema coverage the baseline is 3, but the description adds substantial meaning beyond the schema: how keywords, connections_of, advanced_keywords, and profile_language combine; how max_results drives paging and budget consumption; how connections_of expects provider_ids; and how criteria is applied to each profile using name, headline, and location. It also clarifies loose company matching and list_name slug reuse.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (search LinkedIn for people) and explicitly covers name searches, connection-scoped searches, and profile-filter searches. It distinguishes itself from adjacent tools like enrich_linkedin_profiles, query_agent_people, resolve lookups, and setup_linkedin_sequence.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use and when-not-to-use guidance: use max_results for depth on one query, loop inside run_code for breadth across names or companies, use enrich_linkedin_profiles when missing fields such as connection count or employment history are needed, and use resolve when connection status matters. It also names alternatives and the conditions that select them.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_meetingsSearch MeetingsARead-onlyInspect
When the user has Grain, Granola, or Otter connected, prefer this tool first (Fathom data is local and faster) and fall back to grain_*/granola_*/otter_* only if no relevant results or the user explicitly references that source.
Dict with count, truncated, and meetings array (summary truncated to 6000 chars, transcript truncated to 10000 chars — use get_meeting_transcript(id) to fetch the full transcript for a specific meeting)
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results (default 10, max 20). Only override if user says a specific number like "show me 5 meetings". For vague quantities ("couple", "few", "recent"), omit this parameter to use default. | |
| offset | No | Rows to skip for paging (default 0). When the result is truncated, re-call with offset += limit to fetch the next page. | |
| order_by | No | SQL ORDER BY clause (default: meeting_date DESC) | meeting_date DESC |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| where_clause | Yes | SQL WHERE clause (without WHERE keyword). Use ONLY these columns: provider, title, meeting_date, duration, speakers, attendees, meeting_url, share_url, summary, transcript. For speakers/attendees JSONB, use: speakers::text ILIKE '%pattern%' or attendees::text ILIKE '%pattern%' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safety profile is covered; the description then adds genuinely new behavioral detail: results are truncated (summary to 6000 chars, transcript to 10000 chars), the response shape includes count/truncated/meetings, and get_meeting_transcript is the escape hatch for full transcripts. It does not restate anything the annotations already provide, though it stays silent on rate limits or cost of the query.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and the routing rule are front-loaded in the summary block, and the returns block is short and information-dense with no filler. The XML-style scaffolding is slightly heavier than plain prose would be, but every sentence carries a distinct instruction.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-value burden and does so: it names the dict keys, the truncation limits, and the follow-up tool. Combined with full schema coverage for parameters, an agent has what it needs to call and page correctly, though the truncation/pagination interaction could be stated a touch more explicitly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter (limit, offset, order_by, on_behalf_of, where_clause) is already documented in the schema. The description only re-emphasizes the where_clause column restriction, which duplicates the schema text rather than adding syntax examples or edge-case guidance. Baseline 3 is appropriate when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search user's meeting data from table_meeting_data') and scopes it to a named data table, which is enough to separate it from generic query/search siblings like query_search_results or search_calendar. It does not, however, explicitly contrast itself with the closest relevant sibling, get_meeting_transcript, except incidentally in the returns block.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit preference rule and fallback condition: prefer this tool first when Grain/Granola/Otter are connected, and fall back to grain_*/granola_*/otter_* only when no relevant results appear or the user names that source. That is a real when-to-use / when-to-switch decision an agent can act on without opening any schema.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_monitor_eventsSearch Monitor EventsARead-onlyInspect
Search the user's monitor event log. Use for scheduled digests (only_unnotified=True) and ad-hoc questions ('what's the latest on Stripe?').
Returns a dict with total (full match count), truncated (True when more
rows exist past this page — page with offset to reach them), and events.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max results (default 50, max 200) | |
| since | No | ISO datetime or natural language like 'last 7 days' (optional) | |
| offset | No | Rows to skip for paging (default 0). When `truncated` is True, re-call with offset += limit to fetch the next page. | |
| agent_id | No | Filter by specific monitor agent. Omit to search across all your agents (inside an agent run, an omitted agent_id defaults to that run's agent). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| company_domain | No | Filter by company display name or domain — matches either (e.g. 'stripe.com' or 'Stripe'). Optional. | |
| min_confidence | No | Minimum confidence score 1-10 (default 1) | |
| only_unnotified | No | Only return events not yet included in a digest (default False) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnlyHint=true). The description adds behavior beyond annotations by documenting the return shape (total, truncated, events) and the paging implication of `truncated` — valuable since no output schema exists. Does not mention auth or scoping caveats beyond what schema says.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with purpose, then usage, then return contract. No filler, though the return-value sentence is only partially leveraged given the schema already handles paging detail.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only search tool with full schema coverage, the description adequately covers purpose, two key use cases, and the non-trivial return contract (total/truncated/events). Minor gap: no mention of how results differ across agent_id/on_behalf_of scoping, but the schema covers that.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents all 8 parameters thoroughly. The description only adds meaning for one parameter (only_unnotified=True for digests); the rest of its parameter-related value duplicates the schema. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource: 'Search the user's monitor event log.' This clearly distinguishes it from generic search siblings, though it does not explicitly differentiate from near-neighbors like mark_monitor_events_notified or search_news_events.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives two concrete usage contexts: scheduled digests via only_unnotified=True and ad-hoc questions like 'what's the latest on Stripe?'. Clear usage guidance, but no explicit exclusions or named alternatives (e.g. when to use mark_monitor_events_notified instead).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_news_eventsSearch News EventsAInspect
⚠️ DISCOVERY ONLY — NOT a company lookup. This returns a global feed of the
most recent events matching categories + company_locations (deduped by
company domain, ~10 rows). It takes NO company / domain / name input and
CANNOT be scoped to a specific company or to a list of known companies.
Use it ONLY for find-companies discovery ("surface net-new companies that
just did X"). To research signals for a SPECIFIC named company or an
uploaded prospect/account list, this is the WRONG tool — use
tavily_search with a company-scoped query (e.g. " customer
success news 2026") instead. search_monitor_events(company_domain=...)
works too, but only for companies the user already monitors.
Use this when the user asks to DISCOVER companies by a signal that maps to one of the supported categories below. Only fall back to tavily_search if this returns no results or the signal isn't covered.
Costs 0.3 Sliq credits per event record PredictLeads returns, up to 10 records per location (so up to 3 credits per location). A location query that fails is free.
Each event's company is then checked against criteria, one company at a
time, reading its name and the event, and only the events at matching
companies come back in events; the rest come back in rejected as
{name, reason}. It adds a few seconds per ten events.
When this runs in an agent, every event company with a domain is saved to
the Output tab as a company row (deduped by domain) with its verdict,
carrying the event in data.signals; the rejected ones are hidden from
the list's default view.
Supported categories (pass one or more in a single call):
Expansion: expands_facilities, expands_offices_in, expands_offices_to,
opens_new_location, increases_headcount_by, attends_event
Investment: receives_financing, goes_public, invests_into,
invests_into_assets, has_earnings, has_revenue, has_valuation
Leadership: hires, leaves, promotes, retires_from
Contract: signs_new_client, loses_client
Cost cutting: decreases_headcount_by, closes_offices_in
New offering: launches, is_developing, integrates_with
Partnership: partners_with, ends_partnership_with
Acquisition: acquires, merges_with, sells_assets_to
Challenges: declares_bankruptcy, files_suit_against, has_issues_with
Recognition: receives_award, recognized_as
Relational: identified_as_competitor_of, spins_off_company, spins_off_division
Dict with events, the events at matching companies, each with its
company's verdict ('qualified', or 'unclear' when the event says too
little to judge) and verdict_reason, and a null verdict when the
check couldn't run; rejected, the companies the check turned down,
as {name, reason}; count, every event found, rejected ones included;
and created_identifiers, the domains of matching companies this call
newly added to the list. If every location-query failed with an
upstream error (timeout / 5xx) and nothing landed, also
predictleads_available: False plus a human-readable error — a source
outage, NOT a genuinely empty feed. Partial success (some locations
errored, others returned events) stays silent and reports only the
events.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | No | Optional — a specific agent to save the companies into. Omit it to use the running agent, which is the usual case. | |
| criteria | Yes | What makes a company a match for this search, in plain words: the event it must show and the kind of company (e.g. "A B2B software company that just raised a round"). Each event's company is checked against it, and the check reads nothing else of your instructions, so make it no narrower than what this call searches for. A role that is part of the signal belongs here; who you'll contact at a matching company doesn't, since the check reads that as a rule about which companies match. | |
| list_name | No | Short kebab slug naming the Output-tab list bucket. Absent, companies land in the 'default' list. | |
| categories | Yes | One or more category names from the enumerated list above (the parameter is constrained to exactly those values). Map common intents to the right name: "funding" → receives_financing, "IPO" → goes_public, "acquisition" → acquires, "layoffs" → decreases_headcount_by. Pass all relevant categories in a single call — they are fetched together at no extra cost. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| lookback_days | No | Days of news history to scan (default 30). A news scan passes the window from its trigger prompt. | |
| company_locations | No | Filter by country or US state. Pass a list to cover multiple locations (e.g. ["United States", "Canada"]). At most 10 API requests are made across all locations. Omit to search globally. Prefer country-level values (e.g. "United States") over individual cities/states unless the ICP is tightly city-scoped. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false / destructiveHint=false; the description adds substantially more: credit cost (0.3/event, up to 10 records per location, failed queries free), the dedup-by-domain behavior, the per-company criteria check with its latency cost, the side effect of writing companies to the Output tab as rows, and the source-outage signal (predictleads_available: False plus error). These are exactly the traits annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The warning and discovery-vs-lookup distinction are front-loaded before cost and mechanics, which is the right ordering. It is nonetheless verbose for its size, and the 37-value category list restates the schema enum (albeit grouped usefully by intent).
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, but the embedded <returns> block fully documents the shape (events with verdict/verdict_reason, rejected as {name, reason}, count, created_identifiers) including the null-verdict and partial-failure cases. For a 7-parameter discovery tool with real side effects, nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, and the description earns above it by mapping natural-language intents to category names ('funding' → receives_financing, 'layoffs' → decreases_headcount_by) and by grouping the enum into thematic families, plus restating location-level cost limits. It adds meaning beyond the raw enum but largely mirrors content already present in the categories schema description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb + resource + scope: 'Discover recent news events across ALL companies, filtered by category.' It explicitly names what it is NOT ('NOT a company lookup', 'takes NO company/domain/name input'), which lets an agent distinguish it from tavily_search and search_monitor_events without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('find-companies discovery'), when-not ('to research signals for a SPECIFIC named company... this is the WRONG tool'), and names two alternatives with the condition that selects each (tavily_search for company-scoped queries; search_monitor_events(company_domain=...) only for already-monitored companies). Also states the fallback trigger ('only fall back to tavily_search if this returns no results or the signal isn't covered').
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
send_emailSend EmailADestructiveInspect
The sending mailbox is resolved automatically from the draft log created by
draft_email or draft_reply — no mailbox arg needed.
Dict with success status. On a Superhuman mailbox it also carries
sent_at, up to a minute ahead of the call — Superhuman holds the
message for its Undo Send window and it leaves the outbox then. If the
user says a just-sent message hasn't arrived, tell them it lands at
sent_at rather than sending it again.
| Name | Required | Description | Default |
|---|---|---|---|
| draft_id | Yes | The draft_id returned by draft_email or draft_reply. A draft made with on_behalf_of sends with the same on_behalf_of. | |
| provider | No | Deprecated and ignored — the mailbox is resolved from the draft log. Omit it. | |
| on_behalf_of | No | A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructiveHint=true, and the description adds context beyond that: the mailbox is auto-resolved (no arg needed), and the returns block discloses the Superhuman Undo Send hold and the incremental sent_at, plus a directive not to re-send a message the user says hasn't arrived. That is meaningful behavioral disclosure the annotations do not carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and the confirmation gate, then structured with summary/returns tags. The returns prose is somewhat long, but it substitutes for a missing output schema, so most of it earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description takes on the burden of explaining the return value (success status, sent_at on Superhuman) and adds the confirmation, mailbox-resolution, and do-not-resend guidance. Nothing an agent needs to invoke this correctly appears to be missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds the important framing that the sending mailbox is resolved automatically from the draft log, which explains why provider is deprecated and why no mailbox argument is needed. It is a modest increment over the schema rather than a major one.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — 'Send a previously created email draft' — and ties it directly to the draft_email / draft_reply siblings that produced the artifact. An agent can distinguish it from those siblings without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit precondition: 'Only call this AFTER the user has explicitly confirmed they want to send the draft.' It also implies the required prior step (draft creation via draft_email/draft_reply), so both the entry condition and the workflow ordering are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
send_luma_invitesSend Luma InvitesADestructiveInspect
Luma emails each recipient an invite to the event and adds them to the
roster as invited. This is a real send with no approval step and no
undo — treat calling it as sending, and confirm the recipient list with
the user first unless they have already approved this exact wave.
Distinct from the LinkedIn/email outreach queues: those are Sliq-sent cold sequences with pacing and approval gating, whereas this is Luma's branded event invite from the calendar the user owns. Use the queues to pitch the event to people who don't know it exists; use this to invite people who should land directly on the event's guest list.
Anyone already on the roster — invited, going, pending approval, waitlisted, or declined — is skipped, so re-running with the same list invites only the genuinely new people. That makes retries safe.
Every invite is recorded against the matching person Sliq tracks (under
data['luma_invites'], one entry per event), so who-we-invited is
answerable from our own records and not only from Luma's roster.
Recipients Sliq isn't tracking are still invited — they just get no such
record, which means an RSVP from them won't appear in the funnel. That is normal (hand-typed addresses, people who were
never prospects), and most waves have some, but the user can't see it
from Luma's side. When not_tracked is non-empty, say so in the reply
— how many, and which addresses — instead of reporting a clean send.
A summary dict
{event_id, invited: [...], invited_count, skipped: [{email, reason}],
skipped_count, tracked_count, not_tracked: [...], not_tracked_count}
— plus note, user-facing wording for the untracked recipients,
present only when there are any. invited lists the addresses
actually sent to; a fully-deduplicated call returns invited_count 0
and is a success, not an error. tracked_count counts recipients
Sliq already knows; it is lower than invited_count whenever the wave
includes people who aren't tracked.
| Name | Required | Description | Default |
|---|---|---|---|
| emails | Yes | Recipient email addresses. Deduplicated case-insensitively, then filtered against the live roster. | |
| message | No | Optional personal note included in the invite email, max 200 characters. Written for the whole wave — Luma has no per-recipient placeholder substitution here, so don't try to personalize it with names. | |
| event_id | Yes | The Luma event id from get_luma_events (starts with "evt-"). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=true, but the description adds substantial behavioral context beyond them: no approval step, no undo, dedupe against every roster status (invited/going/pending/waitlisted/declined), retry safety, per-event recording under data['luma_invites'], and the requirement to surface not_tracked recipients instead of reporting a clean send.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well front-loaded with <summary> and <returns> sections, and nearly every sentence carries actionable content. It is on the long side and repeats the not_tracked/skip semantics in both summary and returns, but the density is justified for a destructive send tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, but the <returns> block fully documents the response dict, including the invited_count 0 success case and the tracked_count vs invited_count relationship — so the agent need not guess at return values. Combined with the destructive-send disclosure, nothing needed to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents all four parameters thoroughly (including the on_behalf_of account-switching behavior and the 200-char message limit). The description reinforces the whole-wave nature of `message` and the roster filtering of `emails`, but adds little parameter meaning beyond what the schema provides, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — 'Send Luma's own event invitations to a list of email addresses' — and immediately distinguishes the operation from the LinkedIn/email outreach queues by naming the sibling category and the scenario each serves. An agent can tell this apart from send_email, manage_linkedin_invite_queue, or register_manual_linkedin_invites without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when/when-not routing: use the queues to pitch people who don't know the event; use this for people who should land on the guest list. It also adds an approval precondition ('confirm the recipient list with the user first unless they have already approved this exact wave') and explains that re-running is safe due to roster dedupe.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_linkedin_daily_limitsSet Linkedin Daily LimitsAInspect
limits is a partial {action: cap} map — only the actions you name change, the
rest keep their current cap. Each cap is clamped to that action's tier-aware ceiling
(the ceiling in check_linkedin_daily_usage's configurable_limits); a cap of 0 or
below resets that action to its default. Action keys are the configurable_limits
keys (connection_request, message, inmail, …); an unknown key is rejected with the
full valid list.
The number the user sets is the day-to-day center, not a fixed count: like Sliq's defaults, each cap carries a small ±jitter so sending doesn't read as automation, so the actual daily number varies by a few around the set value (never above the tier ceiling). Tell the user the value you set, not that they'll send exactly that many.
Owner-only — these are account-level account-safety settings. Returns {success, custom_daily_limits}, the resulting stored cap map.
Dict with success and the stored custom_daily_limits map.
| Name | Required | Description | Default |
|---|---|---|---|
| limits | Yes | Partial map of action -> daily cap. 0 or below resets that action to default. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say it is a non-read-only, non-destructive write. The description goes well beyond that: caps are clamped to a tier-aware ceiling, 0-or-below resets to default, unknown keys are rejected with the full valid list, the set value is a jittered center rather than a fixed count, and it is owner-only account-level configuration. It even states the exact return shape ({success, custom_daily_limits}), which nothing else provides.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the summary and the trigger condition, and the jitter paragraph, though long, earns its place because it changes what the agent should tell the user. Some tightening is possible — the ceiling reference and the jitter caveat repeat the tier-ceiling idea — but there is no padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description compensates by naming the return shape and pointing at check_linkedin_daily_usage for post-write verification. With mutation semantics, reset/clamp behavior, the jitter caveat, permission scope, and error behavior all covered, an agent has everything needed to call this correctly and to report the result honestly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning the schema lacks: partial-merge semantics ('only the actions you name change'), clamping behavior, the 0-resets rule, the action-key vocabulary, and the unknown-key rejection path. The only gap is that on_behalf_of is never mentioned in the description, so agency-context behavior is left entirely to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Change the user's per-action LinkedIn daily caps') and ties it to the exact settings surface it maps to (the AI Preferences settings card). It is trivially distinguishable from siblings like check_linkedin_daily_usage (read) and set_linkedin_warmup (a different account setting).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit trigger ('when the user asks to raise or lower how many connection requests, messages, InMails...'), an explicit anti-pattern ('actually make the change, don't just describe the UI'), and a follow-up instruction pointing at the sibling check_linkedin_daily_usage for verification. It also states the owner-only precondition, which routes the agent away from using on_behalf_of carelessly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_linkedin_warmupSet Linkedin WarmupAInspect
disabled=True opts out of the ramp (full daily volume right away — only safe on an
already-warm account); disabled=False re-enables it (resumes any active ramp).
Owner-only. Returns {success, warmup_disabled, is_ramping_up}, the recomputed ramp
state.
Dict with success, warmup_disabled, and is_ramping_up.
| Name | Required | Description | Default |
|---|---|---|---|
| disabled | Yes | True to skip warmup (full volume now), False to re-enable the ramp. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations only declaring readOnlyHint=false and destructiveHint=false, the description adds substantial context: it warns that disabled=True is 'only safe on an already-warm account', notes the call is 'Owner-only' (an auth constraint not in annotations), and describes the recomputed ramp state returned. This is real behavioral disclosure beyond the structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action and polarity mapping followed by the safety constraint, in a tight summary block with a separate returns section. Every sentence carries load, including the anti-pattern warning about describing the UI instead of acting.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, yet the description spells out the return shape ({success, warmup_disabled, is_ramping_up}) and the auth requirement (owner-only). For a two-parameter mutation with full schema coverage, nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds a safety caveat the schema omits ('full daily volume right away — only safe on an already-warm account') and clarifies that disabled=False 'resumes any active ramp'. It does not expand on on_behalf_of, which the schema already documents fully.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Turn the new-account warmup ramp off or on for the user's LinkedIn account') and immediately maps the polarity. No sibling tool touches warmup ramp state, and the closest neighbor (set_linkedin_daily_limits) is clearly distinct, so an agent can select it without ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the triggering conditions ('when the user asks to skip/disable warmup ... or to re-enable it') and adds an execution directive ('actually make the change, don't just describe the UI'). It covers both direction cases but names no exclusion or alternative tool, keeping it at 4 rather than 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_outreach_approvalSet Outreach ApprovalAInspect
settings is a partial {subtype: requires_approval} map — True holds the
message for the user's approval, False auto-sends. Only the subtypes you name
change. Each subtype key names a message type (e.g. email_first_message); an
unknown key is rejected with the full valid list.
scope:
'this_agent' (default) — change approval for THIS agent only, leaving the account-wide setting and other agents untouched. Needs an agent in context (or an explicit agent_id). This pins the agent to its own approval settings that no longer follow account-wide changes — tell the user that.
'account' — change the account-wide default that governs every agent with no override of its own. An agent that has its own override is unaffected — warn the user that an account change won't reach any agent that has its own override. Available to the account owner only; when acting for a shared operator, use 'this_agent'.
'inherit_account' — clear THIS agent's own approval settings so it follows the account-wide setting again.
settingsis ignored.
Returns {success, scope, settings} — settings is the resulting resolved
map for the level written.
Dict with success, scope, and resulting settings.
| Name | Required | Description | Default |
|---|---|---|---|
| scope | No | Which level to change. Defaults to 'this_agent'. | this_agent |
| agent_id | No | The agent to change. Defaults to the agent in context. | |
| settings | No | Partial map of subtype -> requires_approval. Omit for scope='inherit_account'. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations only declaring readOnlyHint=false and destructiveHint=false, the description carries rich behavioral detail: 'this_agent' pins the agent so it no longer follows account-wide changes, 'account' won't reach agents with their own override, 'inherit_account' clears settings and ignores the `settings` arg, and unknown subtype keys are rejected with the full valid list. These are consequential side effects and warnings not derivable from annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded in the summary tag, and the scope bullets are cleanly organized. It is long, but the length is justified by three distinct modes each with non-obvious consequences; there is essentially no filler. Slightly more verbose than strictly necessary for the returns tag.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-param mutation tool with no output schema, the description covers everything an agent needs: the three scope modes and their side effects, parameter semantics, the ignored-arg edge case, and the return shape `{success, scope, settings}`. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description nevertheless adds real meaning beyond the schema: it defines `settings` as a partial {subtype: requires_approval} map, explains True=hold/False=auto-send, clarifies only named subtypes change, and expands each scope enum value with its consequences and prerequisites (agent in context or explicit agent_id).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Change what outbound outreach holds for the user's approval before it sends.' It clearly names the mutation target (outreach-approval settings) and distinguishes itself from the read sibling get_outreach_approval. An agent can tell exactly what this does without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to call it ('when the user asks to review or auto-send a message type — actually make the change, don't just describe the UI') and gives when-to-use for each scope mode: 'this_agent' for a single agent, 'account' for the account-wide default, 'inherit_account' to clear overrides. It also flags the owner-only restriction and the shared-operator alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
setup_email_sequenceSetup Email SequenceADestructiveInspect
For single personal emails, use draft_email + send_email.
A campaign already sending without a flow keeps calling this tool without
node_id. Every other campaign sends through its flow, even a single email:
create it with define_sequence and add the prospects with
track_prospects, and the flow's send steps call this tool with their
node_id. A call without node_id on an agent that has no flow and hasn't
sent any outreach yet is refused.
An agent is required — create one via create_agent if none exists. This tool creates tracking items for the recipients it queues, so don't also call track_prospects for them. Only call this AFTER the user has explicitly confirmed the outreach.
Follow-ups thread on their own. A recipient this agent has already emailed
gets a reply in that email's thread, sent from the mailbox that sent it
(whatever mailbox says), so one call can mix first emails and follow-ups
across mailboxes; write their body as a reply. Everyone else gets a new email
from mailbox. If the mailbox that last emailed someone is no longer
connected, they get a new email too.
Dict with queued_count, follow_up_recipients (the queued recipients
sent as replies in their existing thread), skipped items, a preview of
the first queued email,
first_send_at / last_send_at (see below), and tracking_skipped —
recipients already tracked in another campaign, so
nothing queued for them. Each entry names existing_agent_title /
existing_agent_id; surface these and let the user place them.
This tool QUEUES; it never sends. Nothing has been delivered when it
returns — a background beat drains each mailbox in derived order at a
human-like pace, minutes to days later. first_send_at / last_send_at
are ISO timestamps bounding the projected send window (from the read-time
forecast), and awaiting_approval counts rows held for the user's approval.
Gated rows are excluded from the window; when every row is gated both bounds
are null and nothing is projected until the user approves.
So report queued work as queued, with the window: "Queued 4, first lands 12:02pm, last 12:11pm." Do NOT tell the user these were sent.
Writing a marker into a campaign's own tracker (a sheet, a CRM field)
at queue time is fine and is usually what its dedup depends on — skip
it and the next run re-queues the same people. But that marker records
handoff, not delivery: don't cite it back as proof a send happened, and
don't let it turn into "sent" in your summary. For what actually went
out, read the queue (manage_email_outreach_queue(action='status'));
to act at real send time, the agent needs a sent_email trigger.
| Name | Required | Description | Default |
|---|---|---|---|
| enrich | No | When True, look up each recipient's LinkedIn URL via Apollo (using their `email` plus optional `first_name` / `last_name`) and stamp it on the tracking row. Costs 1 Sliq credit per verified hit; free with BYO Apollo. Recipients that already carry `linkedin_url`, those missing inputs, and those past the credit limit are silently skipped. Reverse of `setup_linkedin_sequence(enrich=True)`. | |
| mailbox | No | Email address of a connected mailbox (e.g. 'alice@acme.io') for new emails. Omit to use the user's default mailbox. When the user has multiple mailboxes connected, ask which to use rather than guessing — surfacing the choice is the agent's job, not a silent fallback. For segmented delivery ("first third from A, second from B, last from C"), call this tool once per segment with the segment's recipients and the segment's mailbox. The tool does not rotate across calls; per-call recipient lists are how segmentation is expressed. | |
| node_id | No | Optional. The id of the sequence-DAG node this batch enacts (a node in the agent's `sequence`, authored via `define_sequence`). Stamped on every queued row so the Campaign Flow view can place each prospect and count per node. Pass it whenever the agent has a sequence. Rejected with ModelRetry if it isn't a node in the agent's sequence. | |
| agent_id | Yes | Required. Agent ID to associate with the queued items. | |
| recipients | Yes | List of dicts. Required: `email`. Optional per-recipient `subject` / `body` (final literal text, apart from any [[slot]] the user asked to fill in themselves while approving; placeholder syntax like `{first_name}`, `[name]`, `<<name>>`, `{{name}}` is rejected with ModelRetry; overrides templates). Other fields (`first_name`, `last_name`, `company`, `title`) are stored on the tracking item for later lookup — NOT substituted into templates. Other extras are ignored, except the LinkedIn identifiers: `linkedin_url` is honored from either the top-level key OR a `data` sub-dict (validated / canonicalized before storage), while `linkedin_provider_id` is honored from the top-level key only. Both land on the prospect's LinkedIn fields. For mixed-channel campaigns, also pass `linkedin_url` (and `linkedin_provider_id` when known) on each recipient so this tool can dedup against any existing LinkedIn-keyed tracking row for the same person — both identifiers land on one row. When `find_email` returned `{email, linkedin_url}` for this recipient, forward both fields here verbatim. On a flow email node this is also how a just-discovered address binds to the prospect resting on the node: passing their `linkedin_url` resolves the send to that existing row instead of orphaning a new one. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| body_template | No | Batch fallback body for recipients without their own `body`. Final literal text, as for a per-recipient `body`. Bodies (this and per-recipient `body`) are Markdown, rendered to HTML at send time for every provider — write hyperlinks as [text](url) so the link reads as its anchor text rather than a raw URL. | |
| subject_template | No | Batch fallback subject for recipients without their own `subject`. Final literal text, as for a per-recipient `subject`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations correctly indicate this is a mutation and potentially destructive action, and the description adds substantial context beyond them: the tool only queues and never sends, sends are drained later by a background process, tracking rows are created automatically, approval gating affects projections, and follow-up recipients reply in existing threads. It also warns not to call track_prospects for queued recipients and explains that tracking markers are not proof of delivery.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loads the purpose and main usage contrast before diving into edge cases and return behavior. It is long, but most of the length is justified by the tool's complexity; some repetition around queueing versus sending could be tightened.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Complete for a complex mutation tool with no output schema: the embedded returns section explains queued_count, follow_up_recipients, skipped items, first_send_at/last_send_at, awaiting_approval, tracking_skipped, and how to report timing to users. It also covers prerequisites, approval gating, tracking side effects, and the distinction between queued work and actual sends.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter behavior not fully captured in the schema: node_id connects a batch to a defined sequence node, mailbox segmentation is expressed by separate calls, one call can mix first emails and follow-ups across mailboxes, and recipient linkedin_url can dedup against existing rows. These semantics materially help correct invocation beyond field-level schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: "Queue bulk email outreach with rate-limited staggered sending and automatic tracking." It explicitly distinguishes the campaign/batch tool from single personal emails handled by draft_email + send_email, so an agent can identify the right tool without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use and when-not-to-use guidance: use draft_email + send_email for single personal emails, use define_sequence and track_prospects when a flow exists, omit node_id only for a campaign already sending without a flow, and call only after the user explicitly confirms outreach. It also states the agent prerequisite and points to create_agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
setup_linkedin_monitoringSetup Linkedin MonitoringAInspect
An agent can host several named monitors. mode='network' watches the user's
whole 1st-degree feed; mode='list' watches an explicit set of people and
company pages — pass members as profile URLs or vanity slugs for people, and
the full linkedin.com/company/... URL for a company page (a bare company name
or slug is read as a person's slug); mode='topic'
searches all of LinkedIn for posts matching keywords, beyond the user's
network. Watching the user's own profile (their
URL in members) is how a user gets engager capture on their own posts.
writing_instructions steer the comment voice. An edit to an already-active
list monitor waits for its next scheduled run, because every list run bills each
watched profile; every other call kicks off a run immediately, so a new monitor or
an edit shows results within minutes. After that it runs on run_weekdays (default Mon/Thu, in the user's timezone). The first run covers
only the members present at creation, and edit_monitor_members starts no run — so create a list monitor with its
complete membership in this one call (assemble a large or query-derived list
in run_code and pass the whole variable), since members added afterward miss
the backlog their first scheduled run would have caught. Comments land in the
approval queue by default — they post without review only if the user has
enabled auto-approve for the linkedin_comment subtype.
list mode works even when the user hasn't connected LinkedIn: discovery is
cookieless and drafting never touches their session, so drafted comments hold
in the approval queue and post once they connect. network/topic discover
through the user's own LinkedIn session, so those two need it connected first.
Schedule and recency are per-monitor. run_weekdays is which days the
monitor runs — weekday ints Mon=0 … Sun=6 in the user's local timezone (e.g.
[3] = Thursdays only, [0,1,2,3,4] = every weekday). recency_days is how
fresh a post must be to earn a drafted comment: only posts published within
that many days are commented on (default 7, max 7). Set these when the user
asks for a cadence ("once a week on Thursdays") or freshness rule ("only
comment on posts from the last 3 days") — those are the knobs, not the agent
goal text. A recency_days shorter than the gap between runs deliberately
skips the older posts in that gap: narrow it for a user who wants only the
freshest posts, or to trim a list monitor's per-post cost (see Cost — a
shorter window pulls fewer posts, though the per-profile floor dominates a
large list's cost). Leave it at the default week to comment on
everything since the last run. Omitting run_weekdays, recency_days, or (on a
list monitor) members on a re-setup keeps the monitor's current value; the
other fields overwrite on every re-setup, so re-pass
writing_instructions/draft_comments/fetch_engagers/capture_authors/
engager_icp/post_filter/engager_instructions when
editing rather than dropping them. For membership tweaks (add or drop a few people) use
edit_monitor_members — it takes only the changes, so you never re-echo the
whole list (dropping a URL on the re-echo silently stops watching that person).
Cost: a queued drafted comment costs 1 credit (all modes). list mode
additionally bills for the paid cookieless scraper's per-event usage
(1 credit = $0.10): 0.04 credit per post it returns plus 0.02 credit per
watched profile that posted nothing in the window — every watched profile is
billed on every scheduled run whether or not it posted, so the per-profile
floor (not the per-post charge) dominates a large list. That floor is a hard
minimum: a run costs at least 0.02 × number of watched profiles (a silent
profile exactly that, a posting one more), so a 2,000-profile list is ≥ 40
credits every scheduled day. Because that recurs per run, recommend a reduced
cadence
for a large list: the default is Mon/Thu, and for lists in the high hundreds
or more, suggest weekly (run_weekdays=[0]). Other levers are trimming the
member list; narrowing recency_days only trims the smaller per-post component.
Flag this to the user when setting up a list monitor, especially a big one;
a zero-balance list monitor won't run at all (even for engager capture).
network/topic discovery is free (Unipile session).
The capture actions share two optional companions, both handed to the woken
new_post_engagers run on the event. engager_icp filters WHO is collected,
engagers and authors alike (ICP, titles, exclusions); blank collects everyone. engager_instructions
decides WHAT HAPPENS to them; blank means they are recorded into the agent's
Output list and nothing else is done. Capture never sends outreach on its own —
if the user wants a sequence or a connection request, it has to be spelled out
in engager_instructions.
keywords is a LinkedIn boolean query. Combine topics with OR and quote
multi-word phrases: "creator economy" OR "influencer marketing". AND narrows,
NOT excludes, and parentheses group: ("seed" OR "series a") AND fundraising.
Operators must be UPPERCASE (lowercase and/or/not are read as literal words). The
combined AND+OR count is capped by the user's LinkedIn plan (free/premium 5, Sales
Navigator 15, Recruiter unlimited; NOT doesn't count) — past the cap LinkedIn
silently returns nothing, so cover many topics with several monitors each within
the cap, not one giant OR. A single unquoted topic (fundraising) is fine. A
malformed query (unbalanced quotes/parens, dangling operators) is rejected.
For network, leave keywords empty in almost all cases — the feed is already
ranked and the per-post draft step skips anything not worth commenting on; set a
narrow query only when the user wants one specific slice. list mode ignores
keywords — it watches the named people's and pages' recent posts directly, and the draft
step is the relevance gate. For mode='topic', keywords is required and is the
only filter.
Reusing an existing name updates that monitor (and re-activates it if it
was stopped) — this is also how you edit one. Prefer edit_monitor_members for
a list monitor's membership; reserve setup's members for the monitor's full
initial membership or a deliberate full reset.
Dict with status ('active' or 'error'), name, message, and additional keys.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | Yes | 'network' (1st-degree feed), 'list' (explicit people and company pages), or 'topic' (keyword search across all of LinkedIn). | |
| name | Yes | short label for this monitor (e.g. 'founders-i-follow'). | |
| members | No | list mode only — LinkedIn profile URLs/slugs and company page URLs to watch. Required when creating a list monitor; omit on a re-setup of an existing list monitor to keep its current members (use `edit_monitor_members` to add or drop individuals). | |
| agent_id | Yes | The agent this monitor lives on. | |
| keywords | No | a LinkedIn boolean query — required for topic mode, an optional relevance narrowing for network, ignored for list. Combine topics with OR (`"a" OR "b"`), AND to narrow, NOT to exclude; the AND+OR count is capped by the user's plan (see the keyword note above). | |
| engager_icp | No | optional — targeting criteria the collected people are filtered by (ICP, titles, exclusions). Blank collects everyone. Only meaningful when fetch_engagers or capture_authors is on. | |
| post_filter | No | optional — which posts the user wants, in their words (roles, topics, post types). Blank keeps every post. On a topic monitor posts are checked minutes after they go up, so a minimum-engagement rule sets most of them aside. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| recency_days | No | only comment on posts published within this many days (1–7, default 7). Omit to keep the current value. | |
| run_weekdays | No | which weekdays the monitor runs — ints Mon=0 … Sun=6 in the user's timezone (e.g. [3] = Thursdays only). Omit to keep the current value (default Mon/Thu on a new monitor). | |
| draft_comments | No | queue a drafted comment per discovered post. | |
| fetch_engagers | No | capture each post's engagers and wake this agent when new people engage. | |
| capture_authors | No | topic mode only — capture the person who wrote each kept post and wake this agent when new authors are saved. | |
| engager_instructions | No | optional — what to do with the captured people. Blank means collect them into the agent's Output list and take no action. | |
| writing_instructions | No | optional voice/style guidance for the drafts. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the non-readonly, non-destructive annotations, it discloses the important behavioral traits: which actions wake the agent, how approvals work, what happens when post_filter rejects a post, billing behavior, scheduling semantics, and the fact that edits to active list monitors wait until the next run.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded and organized around modes, actions, scheduling, costs, and editing. It is very long, but most of that length is justified by the tool's complexity and billing implications; still, the cost and edit-safety points are repeated enough that some trimming could improve scanability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 15-parameter, multi-mode monitoring setup tool with no output schema and only basic annotations, the description is unusually complete. It covers creation, editing, scheduling, costs, prerequisites, post filtering, capture behavior, and keyword semantics.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is already 100%, the description adds substantial meaning beyond the schema: keyword boolean syntax and plan caps, recency_days interactions with run cadence, members as full initial membership, and which fields overwrite versus persist on re-setup. These details materially improve correct parameter usage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: setting up recurring LinkedIn post monitoring on an agent. It clearly distinguishes discovery modes, per-post actions, and edit semantics, and names related tools like edit_monitor_members and stop_linkedin_monitoring that handle adjacent operations.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use guidance across modes, explains when to prefer edit_monitor_members over resetting setup members, and clarifies how reusing a name edits or reactivates a monitor. It also covers prerequisites such as LinkedIn connection requirements for network/topic versus list mode.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
setup_linkedin_sequenceSetup Linkedin SequenceADestructiveInspect
A campaign already sending without a flow keeps calling this tool without
node_id, as do a one-off send outside a campaign and a resolve look-up.
Every other campaign sends through its flow, even a single message or
connection requests with nothing after: create it with define_sequence
and add the prospects with track_prospects, and the flow's steps call this
tool with their node_id. A call without node_id on an agent that has no
flow and hasn't sent any outreach yet is refused.
Only call this AFTER the user has explicitly confirmed the outreach.
BATCH: pass ALL recipients in a single call. The tool is designed to
batch — provider_id recipients queue directly (resolving before send
unless already verified) and URL/slug recipients route through the
background resolving queue with rate-limited, business-hours pacing.
Calling once per recipient multiplies prompt overhead by ~N and produces
no observable benefit. If you have 27 prospects to queue, that's ONE call
with identifiers of length 27, not 27 calls of length 1.
Dict with known_queued (provider_id recipients queued directly),
deferred_count (URL/slug recipients queued for background resolution),
tracking_skipped (recipients already tracked in another campaign, so
nothing queued for them — each entry names existing_agent_title /
existing_agent_id; surface these and let the user place them), skipped
(recipients the queue helper refused — each entry has target_name,
provider_id, reason, error, plus existing_agent_id /
existing_agent_title when a live row on another campaign is what
blocked the send; name that campaign to the user rather than calling it
a duplicate on this one), accept_followup (present only when connection
requests queued to this agent but no sequence node or accept trigger
will fire a message on acceptance — names how to wire one), and
rate_limit_estimate — an ETA for the batch covering expected profile
resolution and sending days given the user's current rate-limit budget.
| Name | Required | Description | Default |
|---|---|---|---|
| enrich | No | When True, look up each prospect's verified work email via Apollo and stamp it on the tracking row. Requires each recipient to have `target_name` plus a URL-shaped `linkedin_url`. Costs 1 Sliq credit per verified hit; free with BYO Apollo. Recipients missing inputs and recipients past the credit limit are silently skipped. | |
| notify | No | Only for action_type='resolve'. False (default) = a silent data lookup the person is NOT notified of. True = a visible "View Profile" visit that DOES notify them ("X viewed your profile") — a no-message warm-up touch, typically placed later in a sequence, not a data read. Same paced lookup and daily cap either way. Rejected on non-resolve actions. On a sequenced call the resolve node's own `notify` field is authoritative and is OR'd in, so a "View Profile" node always visits. | |
| node_id | No | Optional. The id of the sequence-DAG node this batch enacts (a node in the agent's `sequence`, authored via `define_sequence`). Stamped on every queued row so the Campaign Flow view can place each prospect and count per node. Pass it whenever the agent has a sequence. Rejected with ModelRetry if it isn't a node in the agent's sequence. | |
| agent_id | No | Optional. Pass for campaigns or when inside an existing agent. Omit for ad hoc sends — the tool auto-uses the "LinkedIn Quick Actions" default agent. Every send lands in the agent you name; recipients already tracked in a different one are reported in `tracking_skipped` rather than queued (see Returns). | |
| action_type | Yes | 'resolve', 'connection_request', 'message', 'inmail', or 'follow'. For 'resolve' and 'follow', the `message` field is unused and rejected with ModelRetry. Follow auto-sends by default (its own `linkedin_follow` approval subtype); gate it via outbound-approval settings. 'resolve' commits NO outreach — it queues a paced profile lookup that fires a `resolved_profile` trigger event per person; use it when the per-person action depends on looked-up data (see the trigger-code skill for the branching example). Pair with `notify=True` for a visible "View Profile" visit (see `notify`). The event carries the looked-up profile — `already_connected`, `network_distance`, `connections_count`, `follower_count`, `headline`, `title`, `company` — and the same fields land on the prospect's row under `data`, so a later turn reads them with `query_prospects` instead of resolving again. A connection-count rule can read `connections_count` from here; when the rule turns on the count alone, `enrich_linkedin_profiles` returns it for the whole list in one call without spending account budget, so filter there first and queue only the recipients that pass. | |
| identifiers | Yes | List of recipient dicts. Each dict must carry one of `linkedin_url` (a profile URL or slug), `provider_id`, or `chat_id`, copied verbatim from the tool result that surfaced the person — never a slug rebuilt from a display name, which enrolls the wrong person. Freeform attributes go under `data`; unknown top-level keys are rejected. If you have no identifier for someone, omit them rather than guess. When a recipient's `target_name` shares no tokens with any LinkedIn name already held for that `provider_id` (a verified prospect or a post engager), the whole batch is rejected with ModelRetry before anything queues — fix the pairing and re-send (use the person's LinkedIn URL/identifier if the pid is wrong; use one consistent name if it's the same person). A pid with no held name isn't gated here — it resolves before send and the resolver adjudicates the name then. | |
| linkedin_api | No | For inmail only. Leave unset — the tool bills the InMail against the pool for the LinkedIn plan Sliq recorded when LinkedIn was connected. Only pass an explicit 'classic', 'sales_navigator', or 'recruiter' to force a pool. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark this as non-read-only and destructive, and the description adds substantial behavioral context beyond them: batching expectations, provider-id versus URL/slug handling, background resolution, rate limiting, business-hours pacing, tracking-item creation, skipped-recipient reasons, and accepted follow-up wiring. The mutation/destructive nature is consistent with annotations, with no contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is front-loaded and the description is structured into purpose, usage rules, batching guidance, and returns. It is long, but the length is mostly justified for a complex, high-impact outreach tool with no output schema; some portions could be tightened, but it is not padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex 8-parameter, destructive outreach tool without an output schema, the description covers purpose, routing, prerequisites, batching, behavioral side effects, and return-value semantics thoroughly. It gives the agent enough context to invoke the tool correctly and interpret key outcomes.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so parameter definitions already carry much of the burden. The description adds meaningful operational semantics beyond the schema, especially for `identifiers` batching, `node_id` flow usage, and `action_type` behavior in one-off versus sequenced campaigns, though it does not need to repeat every field-level detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (queue LinkedIn outreach for connection requests, messages, or InMails) and distinguishes itself as the single LinkedIn outreach tool. It names related tools like `define_sequence`, `track_prospects`, and the underlying `linkedin_invite_queue`, so an agent can tell how it relates to siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing rules: use without `node_id` for one-off sends, `resolve` lookups, or campaigns already sending without a flow; use through a flow for every other campaign; create flows with `define_sequence` and prospects with `track_prospects`. It also states the call must happen only after explicit user confirmation and explains the refusal case when no flow exists.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
start_find_searchStart Find SearchAInspect
Kick off an Exa Websets search for a named list on an agent.
An agent can host many named lists. Each list spawns its own Exa webset, so distinct criteria buckets (e.g. 'oil & gas operators' vs 'midstream') don't share Exa's per-webset domain-dedup.
Costs 2 Sliq credits per ingested result (charged during ingest). Free when the user has their own Exa key connected.
Refuses to start when the balance can't afford even one result in
sliq_credits mode. When the balance covers fewer results than requested,
the count is clamped to what's affordable and the result carries a
clamp_notice — surface it to the user ("you have X credits, so I'll
find Y").
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | The agent to host the new list. | |
| criteria | Yes | what to search for (the Exa query body). | |
| excluded | No | company domains/URLs the webset must not ingest (matched by domain). Use to avoid re-ingesting rows already recorded in the list, e.g. seeds from find_companies_by_tech_stack. | |
| list_name | Yes | short kebab slug identifying the bucket (e.g. 'oil-gas-operators'). Pick a name distinct from any existing list on the agent. Re-using a finished list's name extends it; re-using a running list's name is refused. | |
| entity_type | Yes | 'person' or 'company'. Mixed people+company agents are supported — each list has its own row type. | |
| max_results | No | target count (capped at the per-list provider cap). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| people_query | No | For company lists, a positive-form query for the people to find at each new company that the check against `criteria` doesn't reject — controls which people come back. They're saved as person rows on the '<list_name>-people' list when the search finishes. Company lists only; skipped on person lists. | |
| people_titles | No | The roles `people_query` is asking for, as title keywords, e.g. ['Marketing', 'Growth'] or ['Operations', 'Chief']. Each is matched as a case-insensitive substring of a person's job title and the list is OR-ed, so use the distinctive word a title is built from rather than a whole title: companies word the same job many ways, and a full phrase finds only the ones that word it exactly as written here. Broader words recall more people and admit more adjacent roles, so pick the narrowest word that still covers the variants. Set it whenever `people_query` names roles — the people search runs unattended, so this is its only structured read of the roles you asked for. Company lists only. | |
| people_preferences | No | Soft preferences and exclusions for the people found. Each is checked against them, where only an exclusion rules someone out. Company lists only. | |
| people_exclude_titles | No | Title keywords to reject, e.g. ['Coordinator', 'Intern']. Same substring matching as `people_titles`, subtracted from it. Whenever `people_preferences` rules a role out, put that role here too — an exclusion written only there applies after the fetch, so the excluded people can still fill the fetched slots. Company lists only. | |
| people_include_alumni | No | Include past employees (alumni) alongside current ones in the people search (default false). Company lists only. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false/destructiveHint=false, and the description adds genuinely non-obvious behavior: a 2-credit-per-ingested-result cost, waiver when the user brings their own Exa key, a hard refusal when the balance can't afford one result, and clamping with a user-facing `clamp_notice`. What it omits is the asynchronous nature of a 'start' call and how to check progress, which matters for a long-running kickoff.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first sentence, followed by dedup rationale, pricing, and failure/clamp behavior in short paragraphs. Every sentence carries information, though the dedup explanation and the quoted clamp_notice script could be tightened without loss.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 12-parameter mutation-free kickoff tool with no output schema, the description covers purpose, cost, refusal, and partial-result clamping -- and even hints at the response via `clamp_notice`. The remaining gap is that it doesn't describe the async lifecycle or the identifier returned to poll, which is significant for a tool whose name is 'start'.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all 12 parameters (including the intricate people_* options and `excluded`) are already documented in the schema, which sets the baseline at 3. The description's webset-dedup and criteria-bucket explanation indirectly motivates `list_name` and `excluded` but adds no direct parameter syntax or format detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Kick off an Exa Websets search for a named list on an agent.' The scoping to a named list per agent is unambiguous. However, it never names its closest sibling (extend_find_search), so an agent must infer from the schema that re-using a finished list name extends rather than starts a search.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear rationale for when this tool is the right choice -- distinct criteria buckets should become separate lists because they don't share Exa's per-webset domain-dedup -- plus prerequisites (an existing agent, sufficient balance). It stops short of naming alternative tools, so the routing decision is contextual rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
stop_linkedin_monitoringStop Linkedin MonitoringAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | The monitor to stop (the label it was set up with). | |
| agent_id | Yes | The agent the monitor lives on. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false; the description adds the meaningful context that discovered posts and already-queued comments are preserved and that resumption is possible via setup_linkedin_monitoring. That preservation guarantee is exactly what an agent needs before taking an irreversible-sounding action, though permissions/on_behalf_of behavior and error conditions are left to the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core effect and the preservation/resume caveats are front-loaded in a tight summary, and the returns block is compact. The XML-style tag scaffolding is slightly heavier than plain prose would be, but no sentence is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description usefully supplies the return shape (status/name/message) and covers state effects and the resume path. Only minor gaps remain, such as what happens if the name doesn't match an existing monitor.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% (all three parameters, including on_behalf_of's agency-client semantics, are documented in the schema), so the description is not required to carry parameter meaning. It adds nothing beyond the schema's own wording, which is the baseline case for full coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource: 'Stop a LinkedIn post monitor — no more scheduled runs.' It immediately distinguishes itself from the sibling setup_linkedin_monitoring by describing the inverse operation, and the effect (scheduled runs stop) rules out read-only siblings like get_linkedin_monitors.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description tells the agent how to reverse the action ('Re-run setup_linkedin_monitoring with the same name to resume'), which implies the inverse use case, but it never states explicitly when to choose this tool over alternatives such as edit_monitor_members or leaving the monitor in place. Usage is inferable rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
theirstack_searchTheirstack SearchAInspect
Use to find companies hiring for a role / tech stack / headcount-band. The
return shape includes company.domain directly — feed that to
record_search_results as identifier without an Apollo round-trip.
For installed-base discovery — companies that already use a technology,
rather than hire around it — use find_companies_by_tech_stack instead.
Costs 0.5 Sliq credits per job posting returned; a failed search is free.
In chat, STATE THE COUNT AND THE COST in the same reply as the
results — every time, without stopping to ask first. Use the user's
number when they gave one, otherwise the default: "pulled 25
postings — 12.5 credits; say if you want more, max 100." Price the
postings actually returned, which count gives: fewer than limit
costs less.
On a scheduled scan there is no reply to state it in: pass the
number the user chose at setup, carried in the scan spec as
theirstack_limit.
Provide at least one of job_title_keywords, job_title_exact,
description_keywords, or industry — without some kind of scope filter,
searches return too many results. Empty results are normal; broaden
filters and call again.
Every call names its industry scope: industry, or the company_keywords
and company_description_keywords pair for a vertical the industry
catalog doesn't name; a call with none of the three raises ModelRetry.
When the target companies aren't defined by industry at all, pass
industry=[].
Each returned job's job_description is truncated to 250 chars (with an
ellipsis suffix). The full posting body is often 1-3KB; the agent only
needs a short excerpt for the signals[i].summary field, and the full
body would bloat the LLM context across multiple rounds.
Each company the postings belong to is then checked against criteria,
one company at a time, reading its name, firmographics and postings, and
only the postings at matching companies come back in jobs; the rest come
back in rejected as {name, reason}. It adds a few seconds per ten
companies.
When this runs in an agent, every company with a domain is saved to the
workspace Output tab as an agent_search_results company row (deduped by
domain, entity_type='company') with its verdict, carrying its hiring
postings in data.signals and its firmographics; the rejected ones are
hidden from the list's default view. Re-running updates rows in place.
Pass list_name to name their list; absent, they land in the 'default'
list.
A dict with jobs, the postings at matching companies; rejected,
the companies the check turned down, as {name, reason}; count,
every posting returned, rejected companies' included, which is the
number billed; and created_identifiers, the domains of the matching
companies this call newly added to the list, not ones already on it.
Each job carries job_title, job_description (truncated to 250 chars),
url, date_posted, a company dict (name, domain, industry,
employee_count, annual_revenue_usd, linkedin_url, country, city), and
its company's verdict ('qualified', or 'unclear' when the company's
details say too little to judge) and verdict_reason, with a null
verdict when the check couldn't run. On upstream failure (timeout /
5xx / auth / quota), it returns jobs=[], count=0, and
theirstack_available=False so the agent can degrade to other sources.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Job postings this ONE call returns, 1-100 (values outside are clamped), default 25. On a scan, the spec's `theirstack_limit` is the whole run's budget across every hiring signal — divide it by the number of signals to get this call's number, don't pass it whole to each. There is no pagination on this endpoint, so this is the whole ceiling for a given filter set. | |
| persist | No | Default True — in an agent, the companies are saved as company rows automatically (see above). Pass False only for a read-only lookup the user doesn't want on a list. | |
| agent_id | No | Optional — a specific agent to save the companies into. Omit it to use the running agent, which is the usual case. Only a throwaway lookup outside any agent returns postings without saving. | |
| criteria | Yes | What makes a company a match for this search, in plain words: the kind of company and the hiring that qualifies it (e.g. "B2B software companies hiring sales development reps"). Each company the postings belong to is checked against it, and the check reads nothing else of your instructions, so make it no narrower than what this call searches for. A role that is part of the signal belongs here; who you'll contact at a matching company doesn't, since the check reads that as a rule about which companies match. | |
| industry | No | Industries the company must match, picked from this parameter's list of TheirStack industry names (e.g. ["Real Estate", "Architecture and Planning"]). A name also matches its sub-industries: "Manufacturing" covers "Motor Vehicle Manufacturing". Some parents are broad ("Consumer Services" includes non-profits and religious institutions), so name a parent only when all of it fits and list specific industries otherwise. | |
| list_name | No | Short kebab slug naming the Output-tab list bucket (e.g. 'hiring-sdrs'). Absent, companies land in the 'default' list. | |
| company_city | No | City/state substrings the company HQ must match (e.g. ["Atlanta", "Chicago", "Washington"]). Case-insensitive substring match, OR-combined across the list. Do NOT include the `(?i)` flag — TheirStack rejects it on this field. Pair with `company_country_codes` to keep matches scoped. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| lookback_days | No | Days of job-posting history to scan (default 14). A news scan passes the window from its trigger prompt. | |
| job_title_exact | No | Exact-match titles (TheirStack-indexed; use for short terms like "Snowflake" or "Chief" where regex would over-match). | |
| min_revenue_usd | No | Minimum company annual revenue in USD (e.g. 10_000_000 for $10M+). | |
| company_keywords | No | Concepts the company hires around, drawn from TheirStack's keyword catalog (e.g. ["Computer Vision", "Large Language Model (LLM)"]; slugs like "computer-vision" also work). Matches a company when any of its job postings mention the keyword, so it describes what the company does. Use it for verticals the industry taxonomy has no entry for — artificial intelligence, machine learning, computer vision. Values are resolved against the catalog; an unknown one raises ModelRetry with close matches rather than silently matching nothing. | |
| industry_excludes | No | Industries to exclude, sub-industries included, from the same list as `industry`. | |
| job_country_codes | No | ISO alpha-2 — where the job is posted (e.g. ["US"]). | |
| job_title_excludes | No | Regex partial-match phrases to filter out of titles. | |
| job_title_keywords | No | Regex partial-match phrases on title (case-insensitive). | |
| max_employee_count | No | Maximum company headcount. | |
| min_employee_count | No | Minimum company headcount. | |
| description_excludes | No | Regex partial-match phrases to filter out of job bodies (e.g. ["intern", "contractor", "1099"]). | |
| description_keywords | No | Regex partial-match phrases on the job body. | |
| company_country_codes | No | ISO alpha-2 — where the company is HQ'd. | |
| company_description_keywords | No | Regex partial-match phrases on the company's own description — what the company says about itself, as opposed to `description_keywords`, which matches the job posting body. Already case-insensitive, so pass the phrase as-is. Pair it with `company_keywords` and an employee band: on its own each of the two admits staffing agencies and job boards, which both self-describe in the vertical's language and post jobs mentioning every technology. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations (readOnlyHint=false, destructiveHint=false) only say it mutates nothing dangerous; the description goes far beyond by disclosing credit cost (0.5/posting, free on failure), 250-char truncation of job_description with rationale, the per-company criteria check adding seconds per ten companies, auto-persistence to the Output tab, in-place updates on re-run, list bucketing, and the `theirstack_available=False` degradation path.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and the alternative-tool routing, then structured into summary/returns. It is long and the cost/compliance instructions are verbose, but for a 22-param, credit-charging, persisting tool most sentences earn their place. Some tightening is possible.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema, yet a full <returns> section documents jobs, rejected, count (billed number), created_identifiers, per-job fields, verdict semantics, and the upstream-failure shape. Combined with param, cost, and persistence coverage, nothing an agent needs to invoke correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (baseline 3), but the description adds real meaning: `count` drives billing and can be less than `limit`, `limit` is a whole-run budget on scans to be divided across signals, and it clarifies when to reach for `company_keywords` vs `industry`. These are semantic distinctions not obvious from the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Search), resource (recent job postings via TheirStack), and purpose (company discovery by hiring signal). It explicitly distinguishes itself from the sibling `find_companies_by_tech_stack` by naming it and contrasting installed-base vs hiring-signal discovery.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use (role/tech stack/headcount-band hiring), when-to-use-the-alternative (installed-base discovery → find_companies_by_tech_stack), and hard requirements (at least one of job_title_keywords/job_title_exact/description_keywords/industry; industry scope or ModelRetry). Also explains that empty results are normal and to broaden filters.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
track_monitored_companiesTrack Monitored CompaniesAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| items | Yes | List of dicts, each with: - identifier: The company's display name (canonical) or a TLD-shape domain (e.g. "stripe.com") — REQUIRED - display_name: Human-readable company name (optional; falls back to identifier) - stage: 'active' (default) or 'removed' - data: Dict of company attributes — website, raw_input, etc. (optional) | |
| agent_id | Yes | ID of the agent to add companies to | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false; the description adds real value beyond them by disclosing cross-agent dedup behavior, the nightly-sweep consumer of active rows, and that duplicates yield updated/skipped counts rather than fresh rows. It omits auth/credit requirements, but the mutation semantics are well covered.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short tagged sections, front-loaded with the summary and the key side effect. Slightly terse but every sentence carries weight; no redundancy against the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by sketching the return dict (created/updated/skipped counts plus skipped details), and parameters are fully documented in the schema. Only edge behavior on malformed identifiers or errors is left unspecified, which is a minor gap for a bulk-add tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents agent_id, items, the identifier/display_name/stage/data fields, and on_behalf_of. The description adds nothing about parameter syntax or formats, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb+resource ('bulk-add companies to an agent's monitoring list'), states the scoping behavior (cross-agent dedup), and explains the consequence (nightly sweep watches every active row). This clearly separates it from siblings like update_monitored_company and edit_monitor_members without needing to name them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It implies when to use it ('adding companies here turns monitoring on for the agent') but never states when to prefer it over query_monitored_companies, update_monitored_company, or edit_monitor_members, and gives no prerequisites or exclusions. Usage is inferable but not spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
track_prospectsTrack ProspectsAInspect
A person belongs to exactly one campaign, so someone already tracked in another
campaign is reported in skipped rather than added twice; pass move_existing=True
to move them into this agent instead. A durable contact left without a campaign (its
campaign was deleted) is instead adopted straight in and reported in adopted.
Someone already on THIS campaign whom the user stopped pursuing (a 'skipped' stage)
is reported in removed, not re-enrolled and not sent to — clear the skip with
update_prospect first if the user explicitly wants them back. Someone with no usable
identifier (no LinkedIn profile URL, username slug, provider ID or email — say, only
a company page URL) is reported in unrecognized and not added; the rest of the
batch is.
On an agent with a define_sequence flow, tracking IS enrollment — the flow fires
each prospect's first touch itself (see the returned flow field); never queue a
first send for prospects you just tracked.
Dict with created and updated counts; moved, adopted, removed, skipped and
unrecognized details, plus unrecognized_note naming anyone not added; and
total, the number of items.
| Name | Required | Description | Default |
|---|---|---|---|
| items | Yes | People to add (see ProspectItem). Each carries a `person` with the identifiers you already hold (LinkedIn URL / slug, email, or provider_id) copied verbatim from the tool result that surfaced them — never a slug rebuilt from a display name, which resolves to the wrong person. The display name goes on `person.display_name`, taken from the same row as the identifier — a name that shares no token with the LinkedIn name already on file for its ID rejects the whole call. Optional freeform `data` per item. | |
| agent_id | Yes | ID of the agent to add prospects to | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| move_existing | No | Move people tracked in another campaign into this one instead of skipping them. Destructive — it cancels whatever was still queued for them, so only pass it on an explicit user request. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations readOnlyHint=false and destructiveHint=false, the description still adds substantial behavioral context: cross-agent dedup, skipped/adopted/removed/unrecognized buckets, identifier requirements, move_existing behavior, and automatic enrollment on define_sequence flows. This tells the agent what happens to a batch beyond a simple write operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with a summary, then structured into compact paragraphs for dedup behavior, item requirements, and return shape. Despite its length, every sentence carries a distinct rule or edge case, and the returns block is necessary because there is no output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given four parameters, nested item objects, no output schema, and minimal annotations, the description covers the missing pieces: return buckets, dedup outcomes, identifier validity, move_existing effects, and sequence enrollment. An agent has enough information to invoke it correctly in complex scenarios.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds cross-cutting meaning for items and move_existing by explaining campaign membership, dedup outcomes, and adoption/removal behavior, though it does not add new syntax or formatting details for parameters already documented in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: "Bulk-add people (prospects) to an agent's outreach list with cross-agent dedup." It explicitly differentiates from the sibling tool by saying "For companies to monitor, call track_monitored_companies instead," so an agent can select it correctly without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It names when to use this tool versus the alternative, then gives explicit conditions for edge cases: when people are skipped, adopted, removed, or unrecognized, and when to pass move_existing=True. It also warns not to queue a first send after tracking on a define_sequence flow, which is actionable invocation guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_agentUpdate AgentAInspect
An agent's recurring runs are steered by each trigger's prompt and code; the goal is shared context, not the run script. A goal edit that adds, removes, or changes a recurring step therefore doesn't reach the runs on its own: after updating the goal, read the agent's triggers and bring each affected trigger's prompt or code in line via update_trigger — or confirm the existing triggers already cover the change. Narrowing a live outreach campaign's ICP is the same shape with more surfaces: segment-bound triggers, per-segment templates, and queued-unsent sends stay wired to the dropped segment; follow your channel's outreach skill to reconcile them.
Set priority=True to push this whole campaign's queued sends — email and LinkedIn alike — ahead of your other campaigns'. It starves the others until it drains — that's intended. priority=False clears it.
Dict with success status and updated agent details
| Name | Required | Description | Default |
|---|---|---|---|
| goal | No | New goal/instructions. Name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat. | |
| title | No | New title | |
| status | No | Set to 'active' or 'paused'. Pausing stops the whole agent — every trigger on it. | |
| agent_id | Yes | ID of the agent to update | |
| priority | No | True to prioritize this campaign's queued sends (email and LinkedIn) over other campaigns; False to clear. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare non-read-only/non-destructive, and the description adds substantial context beyond them: pausing stops every trigger on the agent, goal edits don't propagate to recurring runs on their own, and priority=True starves other campaigns until the queue drains. These are real behavioral consequences an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The first sentence front-loads the core action and fields, and each paragraph targets a distinct concern. The outreach-ICP reconciliation passage is somewhat long and campaign-specific for a general agent-update tool, but it earns its place as a warning about stale wired surfaces.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a six-parameter mutation tool with no output schema, the description covers the mutation semantics, side effects, ordering constraints with update_trigger, and cross-account behavior. The <returns> block covers the return shape, so nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline would be 3, but the description adds meaning the schema does not: the starvation effect of priority=True and its clearing via False, and the cross-account scope of on_behalf_of. The outreach-reconciliation note also clarifies what goal/segment edits leave stale.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence gives a specific verb (Update) plus the exact updatable fields (title, goal, active/paused status, priority) and adds 'Only provided fields are changed'. It also distinguishes this tool's role from background agent runs that refine trigger prompts instead of editing the goal.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states when to call (interactive edits, with a user confirmation step), when not to reach for this tool (background runs don't edit the goal), and the follow-up tool to use after a goal edit (update_trigger). It even routes the outreach-campaign variant to a channel skill.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_calendar_eventUpdate Calendar EventADestructiveInspect
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | New event description/notes (only if changing) | |
| subject | No | New event title (only if changing) | |
| location | No | New event location (only if changing) | |
| attendees | No | New attendee list. Each is an object with required "email" and optional "name". (only if changing) | |
| end_datetime | No | New end time as ISO 8601 in the user's LOCAL timezone, not UTC (e.g., "2026-03-16T12:00:00"). Do not include a timezone offset or Z suffix. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| start_datetime | No | New start time as ISO 8601 in the user's LOCAL timezone, not UTC (e.g., "2026-03-16T11:30:00"). Do not include a timezone offset or Z suffix. | |
| calendar_event_id | Yes | The database id of the calendar event (from search_calendar results) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, so the safety profile is known. The description adds meaningful context beyond that: a mandatory confirmation workflow, which is real behavioral guidance for a mutation tool. Return shape is covered by the <returns> tag, so this only falls short of 5 by not disclosing reversibility or side effects (e.g., notifications to attendees).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary is two front-loaded sentences with no filler, and the critical confirmation rule is stated directly. The <summary>/<returns> tag wrapper is slightly heavier than needed but not wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive update tool, the definition covers the routing prerequisite, the confirmation requirement, and the return summary, while the schema carries full parameter detail. It is essentially complete, though it could note what happens to omitted fields on the existing event.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema fully documents the eight parameters, including local-timezone ISO 8601 rules and the 'only if changing' semantics. The description adds no additional parameter meaning beyond what the schema already provides, so the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Update an existing calendar event') and clarifies backend scope ('Outlook or Google Calendar'). It is easily distinguished from siblings like create_calendar_event and search_calendar.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly names the prerequisite tool ('Use search_calendar first to find the event') and the parameter that requires it ('pass its id here'). It also provides an unambiguous behavioral rule ('ALWAYS present the proposed changes and ask for explicit confirmation'), so when-to-use and alternatives are fully covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_crm_taskUpdate Crm TaskAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| clear | No | Fields to empty: 'due_date', 'person', 'company', 'deal'. | |
| notes | No | Replace the notes entirely. To add to them, use notes_append instead. | |
| title | No | New title. | |
| status | No | 'done' to complete it, 'open' to reopen it. | |
| deal_id | No | Link this deal instead (query_deals `id`). | |
| task_id | Yes | The task's id (query_crm_tasks). | |
| archived | No | true deletes the task (reversible), false restores it. A deleted task can only be changed in a call that also restores it (archived=false). | |
| due_date | No | New due day, YYYY-MM-DD. | |
| person_id | No | Link this person instead (query_people `id`). | |
| company_id | No | Link this company instead (query_companies `id`). | |
| notes_append | No | Add this as a new paragraph at the end of the notes. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| assignee_email | No | Reassign to this active teammate's email from list_teammates (not an invited one). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=false and destructiveHint=false; the description earns its keep by explaining that the delete is a soft, reversible archive ("hides the task but keeps it, so archived=false brings it back"), which is exactly the reason destructiveHint is false and would otherwise be invisible. It also bounds scope by naming the mutation it cannot perform and describes the return payload's shape relative to a sibling query.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the operation list in the first sentence, then prerequisites, then the delete semantics, then the exclusions. The summary/returns structure is used purposefully and no sentence is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 13-parameter mutation with no output schema, the description is unusually complete: it names the id source, explains the archive flag, scopes out what it cannot change, and the returns block stands in for the missing output schema. The remaining gap is conflicting-parameter behavior, which matters for a tool with both notes and notes_append and a clear list.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already carries per-parameter meaning and the baseline is 3. The description adds conceptual framing above the schema — the delete/restore toggle and the fact that operations are all in-place changes to one existing task — but it does not address interaction between parameters (e.g., notes vs notes_append, or clear vs due_date), so it goes only slightly past baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Change a CRM task") and then enumerates the full operation set: complete/reopen, reschedule, rename, reassign, edit notes/links, delete and restore. That enumeration lets an agent distinguish it from create_crm_task, query_crm_tasks, and update_deal without opening any sibling schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives the prerequisite route for the required parameter ("Get the id from query_crm_tasks") and an explicit when-not with a redirect (follow-up suggestions not yet added by the user must be handled on the Tasks view). It does not contrast itself against create_crm_task or update_deal for adjacent intents, so it stops short of a full routing rule set.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_dealUpdate DealAInspect
Won and lost are statuses, not stages: use status='won' / 'lost', and status='open' to reopen. Deleting (archived=true) hides the deal everywhere but keeps it, so archived=false brings it back. The updated deal, same shape as a query_deals row.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | New name. | |
| clear | No | Fields to empty: 'amount', 'expected_close_date', 'person', 'company'. A deal must keep a person or a company. | |
| notes | No | Replace the notes entirely. To add to them, use notes_append instead. | |
| stage | No | Move to this stage, by name or id. | |
| amount | No | New value as a plain number, e.g. 15000. | |
| status | No | 'open', 'won', or 'lost'. | |
| deal_id | Yes | The deal's id (query_deals). | |
| archived | No | true deletes the deal (reversible), false restores it. A deleted deal can only be changed in a call that also restores it (archived=false). | |
| person_id | No | Link this person instead (query_people `id`). | |
| company_id | No | Link this company instead (query_companies `id`). | |
| new_person | No | A contact to add and link: name plus email or LinkedIn URL. Someone Sliq already has with that email or LinkedIn is reused. Used instead of person_id. | |
| new_company | No | A company to add and link: name plus domain. A company Sliq already has with that domain is reused. Used instead of company_id. | |
| notes_append | No | Add this as a new paragraph at the end of the notes. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| assignee_email | No | New owner, an active teammate's email from list_teammates (not an invited one). | |
| expected_close_date | No | YYYY-MM-DD. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare non-read-only and non-destructive, so the safety bar is partly covered, but the description adds real context beyond them: archived=true is a reversible soft-delete that hides the deal everywhere while keeping it, and a deleted deal can only be modified in a call that also sets archived=false. That is the kind of non-obvious constraint annotations cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the full mutation surface in one sentence, then adds the two highest-risk clarifications (status vs stage, soft-delete) immediately after. Dense but every clause carries information; the XML tag wrapper is slightly awkward but harmless.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 16-parameter mutation tool with no output schema, the definition covers purpose, id sourcing, the status/stage ambiguity, and reversible deletion. The return value is stated as 'same shape as a query_deals row', which is adequate given the missing output schema. Nested-object reuse semantics (new_person/new_company) are left to the schema, which is acceptable at 100% coverage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents each field and the baseline is 3. The description nevertheless adds cross-parameter meaning: the status-vs-stage distinction for the status/stage fields, and the notes-vs-notes_append relationship, which the schema confirms but the description frames as a decision rule.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence names a specific verb (change) and exhaustively enumerates the mutable surface: rename, move stage, mark won/lost/reopen, amount, close date, owner, notes, linked contact/company, delete/restore. It clearly distinguishes itself from query_deals (read) and create_deal (new record), so an agent can route without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete routing: get the id from query_deals, and clarifies that won/lost are statuses rather than stages (status='won' vs stage=...). It also distinguishes notes from notes_append via the schema text. It stops short of explicit when-not-to-use guidance versus other update tools like update_prospect or create_deal.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_message_tagUpdate Message TagAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | New name; omit to leave unchanged. | |
| tag_id | Yes | The tag to edit (from a group's tags list). | |
| description | No | New description; omit to leave unchanged. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false, so the description adds real value: partial-update semantics (unpassed fields unchanged), the side-effect dependency on re-tagging, and the returned object shape. It does not mention permission requirements or failure behavior, keeping it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary, one sentence of behavior, one sentence of required follow-up, then a compact returns block. No filler; every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a four-parameter partial-update tool with no output schema, the description covers the mutation semantics, the classifier dependency, and the return dict. The only omission is anything about authorization or the on_behalf_of agency behavior, which the schema already handles.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and each parameter (name, tag_id, description, on_behalf_of) is documented in the schema itself, so the baseline is 3. The description adds only the rename/description framing, no syntax or format detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Rename a tag or change its description') and immediately narrows scope with 'Only the fields you pass change'. This is distinguishable from sibling mutators like update_message_tag_group and update_segment_tag, which operate on groups/tags of other kinds.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit downstream workflow: since the tag description is what the classifier reads, you must re-classify with force=True after editing for the change to take effect, and it names the relevant classify tools in the sibling set. This is exactly the when/how guidance an agent needs for correct sequencing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_message_tag_groupUpdate Message Tag GroupAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | 'single' or 'multi'; omit to leave unchanged. | |
| name | No | New name; omit to leave unchanged. | |
| criteria | No | The classify scope to track — {channel, agent_ids, positions, senders}, each null = all (see create_message_tag_group). A provided object REPLACES every axis (an axis you leave out becomes all), so pass the current value of any axis you keep; omit the object to leave the stored criteria unchanged. classify reuses this scope, so change it to re-target what the question tracks. | |
| group_id | Yes | The tag group to edit (from list_message_tag_groups). | |
| description | No | New one-line description; omit to leave unchanged. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false and destructiveHint=false, and the description adds real value beyond that: partial-update semantics ('only the fields you pass change') and the important caveat that editing does not re-classify anything. This non-obvious side effect is exactly the kind of behavioral context annotations can't convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary, then a returns block that is justified because there is no output schema. No filler sentences; each statement (partial update, no re-classify, follow-up call) earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description supplies the return shape, covers partial-update behavior, and closes the loop with the required follow-up classification call. For a mutation tool whose annotations already cover the safety profile, nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, but the description adds the partial-update contract ('Only the fields you pass change') that governs how every optional param behaves, which the schema alone only states per-field. It also names the editable fields, though group_id and on_behalf_of are only documented in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource and enumerates exactly what can change: 'Rename a tag group or change its description, mode, or classify scope.' An agent can distinguish this from create/delete/list siblings by resource and verb alone, and the follow-up mention of classify_message_tag_group clarifies its position in the workflow.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly scopes behavior with 'Only the fields you pass change' and gives a concrete next step: follow with classify_message_tag_group(force=True) to re-tag. It doesn't state when to prefer this over create_message_tag_group or list_message_tag_groups, but the workflow context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_monitored_companyUpdate Monitored CompanyAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| stage | No | 'active' or 'removed' (optional) | |
| agent_id | Yes | ID of the agent | |
| identifier | Yes | The company's display name or domain. | |
| data_updates | No | Dict to merge into existing data (optional) | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false, so the description is not carrying the safety burden. It nevertheless adds real behavioral context beyond the annotations: the nightly-sweep side effect of setting stage='removed' versus resuming with 'active'. It does not disclose merge/replace semantics for data_updates, which is the remaining gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tightly-scoped sentences inside a <summary> tag, with the primary action front-loaded and the stage semantics immediately after. The <returns> block is slightly redundant given the trivial return type, but nothing is padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema at all, the description correctly supplies the return shape ('Dict with the updated company details'). Combined with schema-documented params and the sweep-behavior note, an agent has enough to call this correctly, though the authorizing context of on_behalf_of is left entirely to the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, but the description earns an extra point by explaining the consequence of each stage value rather than just echoing the enum. It still says nothing beyond the schema about data_updates or on_behalf_of.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence names a specific verb (Update) and resource (a single monitored company) plus the two mutable facets (stage and/or data), which separates it from query_monitored_companies and track_monitored_companies. It stops short of explicitly naming a sibling it is not, so a 4 rather than a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives concrete operational guidance for the stage parameter: 'removed' stops the nightly sweep, 'active' resumes it. That tells the agent when each value is appropriate, but it offers no guidance on when to use this tool versus track_monitored_companies or edit_monitor_members.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_nodeUpdate NodeAInspect
Because no node is added, dropped, or renamed, every prospect keeps its cursor and each
node keeps its on_enter enactment (prompt, code, run history). The edited sequence is
re-validated as a whole, so an edge repoint that would dangle a reference, orphan a node,
or introduce a cycle is rejected rather than saved. To add or remove nodes, change a
node's kind, or edit the start node, re-author the flow with define_sequence instead.
A forked-on condition is often restated in several places — a decision's rule, each
arm.case, a downstream terminal's label, and the node's on_enter prompt. When you
change one, change the others in the same turn (a further update_node for the node
fields, update_trigger for the prompt) so they agree.
Dict with success, agent_id, and node_id. Read the updated flow via
get_campaign_flow.
| Name | Required | Description | Default |
|---|---|---|---|
| node | Yes | The node's complete new definition — an omitted optional slot (a `follow_up`, a `timeout`) is dropped, not preserved, so carry forward every field the node keeps. A send or connection-request node's `message_templates` is the exception: omitted leaves the step's templates as they are. Its `id` must name an existing body node and its `kind` must match that node's current kind. | |
| agent_id | Yes | ID of the agent whose flow holds the node. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: it discloses that prospects keep their cursor, nodes keep their on_enter enactment, optional slots are dropped rather than preserved, message_templates is the exception, and edge repoints are whole-flow re-validated so dangling refs/orphans/cycles are rejected rather than saved. Annotations only declare non-read-only/non-destructive; this adds the real behavioral contract.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a one-line summary, then usage, then constraints, then a returns note. Dense but each paragraph earns its place; the closing restatement of a forked condition across rule/arm.case/label/on_enter prompt is genuinely useful coordination guidance. Slightly long, hence not a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with a large nested schema, the description covers scope, prerequisites, persistence/re-validation behavior, the associated prompt edit via update_trigger, and even the return shape in the returns section. An agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, but the description meaningfully reinforces the full-new-definition contract ('Pass the node's full new definition'), the id-selects-the-node rule, and the kind-must-match constraint even though the schema also states these. It ties the fields to semantics (which fields to change for which purpose) rather than just repeating the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (edit) and resource (one existing node of a campaign's flow), and immediately scopes it against the alternative: 'Edit one existing node ... leaving the rest of the DAG untouched.' An agent can distinguish this from define_sequence and update_trigger without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit selection rule ('Use this — not `define_sequence` — whenever the set of nodes isn't changing'), enumerates concrete use cases, and states the exclusions (adding/removing nodes, changing kind, editing the start node) with the tool to use instead. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_onboarding_progressUpdate Onboarding ProgressAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| milestone | Yes | one of 'tool_connected', 'task_executed', 'automation_suggested' | |
| current_task | No | description of what the user wants to accomplish (set when user first describes their task) | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false, so the mutation/safety profile is covered. The description adds the return shape ('Dict with success status') but discloses no side effects, prerequisites, or what current_task/on_behalf_of actually change behaviorally.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short, front-loaded sentences that each carry information (purpose then trigger). The XML wrapper tags add mild noise but no real bloat.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a low-risk three-parameter progress-tracking tool with full schema coverage and annotations, the description covers purpose, trigger, and return. Nothing critical is missing, though it could say more about how milestone relates to onboarding state.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the enum values and the two optional parameters are already fully documented in the schema. The description touches the 'milestone' idea but adds no syntax or meaning beyond what the schema provides, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Update the user's onboarding progress'), which is a distinct concept unlikely to be confused with the many CRM/outreach siblings. It names the resource clearly, though it doesn't explicitly contrast itself against any alternative tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides an explicit trigger condition: 'Call this when a milestone is reached.' That gives the agent a concrete when-to-use signal, though it offers no exclusions or alternatives for the case where a milestone has not been reached.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_prospectUpdate ProspectAInspect
Set priority=True to push this person's queued sends — email and LinkedIn alike —
ahead of everyone else's; it's account-wide and durable, carrying across the sequence's
later steps for this person. priority=False clears it. One priority flag per prospect,
independent of channel: both channels' derived send order reads it.
Most stage transitions — sent, connected, messaged, replied — stamp
automatically from send and reply detection; don't set those by hand. The three
reply-outcome rungs above replied — interested/meeting_booked (positive) and
not_interested (an explicit decline) — normally stamp from the reply classifier
too, so set one by hand only to fix a wrong call or record an off-platform outcome:
stage='interested' for clear interest, stage='meeting_booked' when they agree to or
book a meeting, stage='not_interested' when they've declined. The mark only raises a
rung — a decline can't override a live positive (to lower a false
meeting_booked/interested, correct that reply with correct_reply_outcome) — and it
counts the person as replied, so to drop someone who never engaged use stage='skipped'.
Setting a reply-outcome rung also cancels the person's still-pending queued outreach
on both channels — automation stops; messages you queue afterwards still send.
A later positive reply lifts not_interested back up on its own.
When you set stage='skipped', pass skip_note with the reason in a few words, so a later reader knows why this person was dropped without digging up the chat — a bare skip records no reason.
To reverse a skip and put the person back in the funnel, set stage to the rung they should resume at — normally stage='pending' to re-queue them from the top. That re-enters them into outreach (a still-skipped prospect is held out of every send) and clears the stale skip note; their flow position is preserved, so the sequence resumes from where they were rather than restarting a step already taken.
To store an address you found for a prospect that reached an email step without
one (recovering an email_not_found block), pass email; the stamped address makes
the step send when you retry it. For a blocked prospect,
retry_blocked_enactment(email=...) stamps and retries in one call instead.
To undo an accidental hand-set mark (e.g. a prospect wrongly marked meeting_booked),
pass clear_manual_mark=True on channel — it removes the mark and re-derives the
stage from actual send/reply activity, the only way to lower past a manual mark (which
the mark path deliberately floors demotions at). This differs from correct_reply_outcome,
which re-judges a specific reply; use clear_manual_mark when the wrong rung came from a
hand mark, not a misjudged reply. Pass it on its own (no stage/data/priority/email).
Dict with the updated item details. After a skip, skip_outcome says how many
queued sends were cancelled and lists any that had already gone out — a skip
can't undo those, so tell the user about them.
| Name | Required | Description | Default |
|---|---|---|---|
| No | An address to store for this prospect (optional). Fills in a prospect that reached an email step without one, so a retried step can send. Lands on the person's profile, the prospect's only address, overwriting an existing one — so it also changes the address any teammate's campaign for this person sends to. Refused if the address already belongs to another person in the workspace (likely the same person under two records). Leave unset for ordinary stage/priority updates. | ||
| stage | No | New stage value (optional) — e.g. 'skipped' to drop the person, 'pending' to reverse a skip and re-queue them. | |
| channel | Yes | 'linkedin' or 'email' — which funnel the update applies to: the stage column `stage` sets, the manual mark `clear_manual_mark` removes, and the queued sends a skip cancels. | |
| agent_id | Yes | ID of the agent | |
| priority | No | True to prioritize this prospect's queued sends (email and LinkedIn) over everyone else's; False to clear. Account-wide and channel-independent (optional). | |
| skip_note | No | Short free-text reason for a skip, e.g. "off-industry — gold mining, not supplements" (optional). Recorded only when stage='skipped'; ignored otherwise. | |
| prospect_id | Yes | The prospect's `id` — the outreach_prospects primary key, as returned on every query_prospects row and shown on the flow roster line. | |
| data_updates | No | Dict to merge into existing data (optional) | |
| linkedin_url | No | A corrected or missing LinkedIn profile URL/slug to bind to THIS prospect in place (optional) — an unverified claim the next send verifies through the profile resolver; any prior verification of the old URL is cleared with it. Refused when the URL already belongs to another of the user's prospects, or when a teammate's prospect verified the same person. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. | |
| clear_manual_mark | No | True to remove this channel's hand-set reply-outcome mark and re-derive the stage from send/reply activity — the undo for an accidental mark. Pass it alone; combining with stage/data_updates/priority/email is refused. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=false / destructiveHint=false; the description goes well beyond that, disclosing that priority is account-wide and durable, that a reply-outcome rung cancels both channels' pending sends, that the mark path floors demotions, that a skip preserves flow position and clears the stale note, and that clear_manual_mark must be passed alone. These are exactly the mutation side effects an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The opening sentence front-loads the core action and the length is largely justified by 11 interacting parameters and non-obvious state transitions. Some passages (the sequence of stage-transition paragraph) are dense and could be tightened, but there is little pure filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex mutation tool with no output schema, the returns section still explains the post-skip payload (skip_outcome with cancelled/already-sent sends and the warning that sent ones can't be recalled). Combined with the transition and priority semantics, an agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real cross-parameter meaning: the channel→stage-column mapping, priority being channel-independent, skip_note only applying with stage='skipped', and clear_manual_mark's mutual exclusion with other args. It stops short of documenting every parameter (data_updates is only named, not explained), so it does not fully rise above the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource and enumerates exactly what can be changed (stage, data, send priority, stored contact info), with the channel-scoped stage mapping called out. It also explicitly routes companies to update_monitored_company, which lets an agent separate it from a similarly-named sibling without reading schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when/when-not guidance throughout: don't hand-set sent/connected/messaged/replied, set reply-outcome rungs only to fix a misjudgement or record off-platform outcomes, use skipped to drop a never-engaged person, and use clear_manual_mark vs correct_reply_outcome depending on whether the wrong rung came from a hand mark or a misjudged reply. It also names the alternative path (retry_blocked_enactment) and forbids the duplicate-creating path (track_prospects).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_segment_groupUpdate Segment GroupAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | 'single' or 'multi'; omit to leave unchanged. | |
| name | No | New name; omit to leave unchanged. | |
| criteria | No | The classify scope to track — {agent_ids, senders}, the campaign (agent_tasks) ids and teammate emails, each null = all. A provided object REPLACES the whole stored scope (an axis you leave out becomes all), so pass the current value of any axis you keep; omit the object to leave it unchanged. New people in this scope are tagged automatically and classify reuses it, so change it to re-target what the segment tracks. | |
| group_id | Yes | The segment group to edit (from list_segment_groups). | |
| description | No | New one-line description; omit to leave unchanged. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false, so the mutation/safety profile is covered. The description adds genuinely useful behavior beyond that: partial-update semantics ('only the fields you pass change') and the non-obvious fact that editing does not trigger re-classification. It stops short of stating auth/permission requirements or whether an omitted axis reverting to 'all' can silently widen scope (that detail lives in the schema).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Organized into summary and returns blocks, with the core behavior front-loaded in the first sentence. The returns block enumerates the response dict fields, which is verbose but earns its place since no output schema exists. No filler sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with no output schema, the description usefully supplies the return shape (id, name, description, mode, updated_at, criteria, tags) and the classify follow-up workflow, which is the main non-obvious behavior. Remaining gaps — permission requirements and the multi-step consequence of overwriting criteria — are minor given the fully documented schema and clear annotations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so each parameter (mode, name, criteria, group_id, description, on_behalf_of) is already documented in the schema, including the replace semantics for criteria. The description's 'only the fields you pass change' and 'change its description, mode, or classify scope' largely restate what the schema says, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Rename a segment group or change its description, mode, or classify scope') and enumerates exactly which fields are editable. It also names the downstream sibling classify_segment_group, so an agent can tell it apart from creation and classification tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear operational context: 'Only the fields you pass change' and 'Editing does not re-classify anything — follow with classify_segment_group(force=True) to re-tag.' That is a real when-and-what-next statement routing to an alternative. It does not, however, distinguish itself from near-neighbors like update_segment_tag or state when an edit is preferred over delete+recreate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_segment_tagUpdate Segment TagAInspect
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | New name; omit to leave unchanged. | |
| tag_id | Yes | The tag to edit (from a group's tags list). | |
| description | No | New description; omit to leave unchanged. | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations establish this is a non-read-only but non-destructive update, and the description adds non-obvious behavioral details: only passed fields change, the description is what the classifier reads, and re-tagging with force=True is required for changes to take effect. It also states the return shape, which is useful context beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: it states the operation, the partial-update behavior, and the critical classifier caveat in a few sentences. The return-value section is brief and there is no wasted language.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple update tool with complete schema coverage and annotations, the description covers the operation, partial-update behavior, required follow-up classification step, and return shape. Nothing essential for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with name, tag_id, description, and on_behalf_of all documented in the schema. The description only adds the general partial-update semantics that omitted fields remain unchanged, so it does not meaningfully exceed the schema's parameter documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: rename a segment tag or change its description. It is clear what the tool does, but it does not explicitly distinguish this from siblings such as update_segment_group or update_message_tag.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It implies usage for renaming or editing a tag description and gives an important follow-up instruction to re-tag with force=True after changing the description. However, it does not say when to use this tool versus update_segment_group, delete_segment_tag, or the classify tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_triggerUpdate TriggerAInspect
A send step's message, or a connection request's note, lives in its message_templates, so
to change what a step sends,
edit the template with update_node; an on_enter prompt on that step carries only
extra instructions (tone, whether it may rephrase, what to write when a {slot} can't be
filled: wording the user could approve without changing a word, since the queue rejects
a leftover {slot}), never the message itself. A [[double-bracketed]] part of a template
is the user's to fill in while approving, so the run leaves it as written.
You MUST call get_skill_guide('trigger_code') before writing code — the
qualification rules, sandbox globals, tool surface, and out contract live
in the skill. New or changed code runs on the trigger's next firing.
The agent's goal is injected into every turn on this agent, so it steers later
runs and replies rather than merely describing them: a trigger edit that
changes what the agent does leaves the goal asserting the old behavior. After
this call, check the goal — when it describes what you just changed (a step
you rewrote, a daily volume or cadence it states, a trigger it says is
paused), bring it in line via update_agent(agent_id=..., goal=...) in the
same turn.
Dict with success, agent_id, trigger_id, next_run_at, and the edited trigger's
full dict under trigger, plus a warning when an active trigger sits on a paused
agent, since it doesn't fire until the agent is set back to active.
Read the agent's other triggers via get_agent_details.
| Name | Required | Description | Default |
|---|---|---|---|
| code | No | New trigger code. Runs on the trigger's next firing. | |
| label | No | New label. | |
| prompt | No | New per-run instructions for this trigger. Name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat. | |
| status | No | 'active' to resume firing, 'paused' to stop this trigger. | |
| trigger_id | Yes | ID of the trigger to edit (from the agent's trigger list). | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and destructiveHint=false. The description adds substantial context beyond that: a new prompt/code replaces the existing one, code runs on the next firing, paused triggers' fields are not in context, newly written code carries an out contract and sandbox rules, and an active trigger on a paused agent yields a warning. This is rich behavioral disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The action is front-loaded in the summary tag and the structure is clean (summary/returns). It is verbose, though, and the long message_templates paragraph is largely about an adjacent tool's behavior, which weighs slightly against strict conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although there is no output schema, the description embeds a returns section covering success, agent_id, trigger_id, next_run_at, the trigger dict, and the paused-agent warning. Combined with the guidance to read sibling triggers via get_agent_details, the agent has everything needed to call and interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description reinforces replacement semantics ('A new prompt or code replaces the current one') and the pause/resume meaning of status, but adds little beyond what the schema already documents for each parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states a specific verb and resource with scope: 'Edit one trigger by id, leaving its other fields untouched.' It immediately separates this tool from add_trigger/remove_trigger and, via the message_templates discussion, from update_node. An agent knows what this does without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use cases are enumerated (refine prompt, attach/replace code, rename, pause/resume) and alternatives are named: use update_node to change what a send step sends, and get_skill_guide before writing code. It also gives prerequisites (read paused triggers via get_agent_details) and follow-ups (update_agent for the goal).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_workspaceUpdate WorkspaceAInspect
Use this for structured data. To add an agent-specific operational learning, call
append_learning instead.
The app reads inputs: excluded, mailbox, entity_type, discovery_mode, watcher_context, content_calendar, destination; outputs: ideas, drafts, published, digests, insights. Values there must fit the app's shape (a rejected write shows it); for your own data, use another key.
operation='read': Name section and key to get back just that entry; section alone returns that section; neither returns the full workspace ({inputs, outputs}). An entry that was never written comes back as an empty section. The full form re-sends every entry, so name the entry when you know which one you want. operation='update': Requires section, key, and a non-null value. Sets workspace[section][key] = value. operation='append': Requires section, key, and a LIST value. Atomically extends the existing list at workspace[section][key] with value's items (creating it if absent). Use this to accumulate into a list — a digest entry, new calendar topics, freshly-created draft records — without reading, concatenating, and rewriting the whole array yourself (which races other writers). Errors if the current value isn't a list (use 'update' to replace it). operation='delete': Requires section and key. ALWAYS confirm with the user before calling — this is destructive. Shape depends on the operation. On read, {'success': True, 'agent_id': ..., 'workspace': {'inputs': {...}, 'outputs': {...}}}, with 'workspace' holding only the section — or only the one section/key entry — you named. On update or delete, {'success': True, 'agent_id': ..., 'workspace_keys': {'inputs': [...], 'outputs': [...]}}
| Name | Required | Description | Default |
|---|---|---|---|
| key | No | The key to set, extend, or delete (required for update/append/delete; optional narrowing for read, where it also needs section) | |
| value | No | Value to store (required for update; must not be None — use operation='delete' to remove a key). Name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat. For append, a list of items to add to the existing list. | |
| section | No | Which workspace section — 'inputs' or 'outputs' (required for update/append/delete; optional narrowing for read) | |
| agent_id | Yes | ID of the agent | |
| operation | Yes | 'read', 'update', 'append', or 'delete' | |
| on_behalf_of | No | An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description itself is unusually rich: it discloses atomicity of append, the list-type error condition, the non-null requirement for update, the shape of read/update/delete responses, and that delete is destructive. However, it explicitly states delete 'is destructive' while annotations declare destructiveHint=false, a direct inconsistency that misleads an agent about the tool's worst-case behavior. This conflict caps an otherwise excellent disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded summary followed by per-operation bullets, so it is scannable and information-dense. It is on the long side—the `record_search_results` digression is verbose—but nearly every sentence carries operational value for a four-operation tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a multi-operation tool with no output schema, the description closes the gaps: it documents the return shapes for read/update/delete, the section/key narrowing behavior, and the branch conditions. Schema coverage is 100%, so no parameter meaning is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning beyond the schema: `value` must fit the app's shape for known keys (a rejected write reveals it) and outside resources must be named by the identifier their tool takes (spreadsheet ID/tab, URL), not just a title. It also clarifies section/key narrowing semantics for read and the list constraint for append.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific resource (an agent's workspace entries) and the four verbs it supports (read/update/append/delete), and it distinguishes itself from siblings by naming `record_search_results` (for bulk entity sets) and `append_learning` (for operational learnings). The two-section model (inputs/outputs) is spelled out, so an agent knows exactly what it operates on.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: use `record_search_results` for any bulk people/company set (with reasons: dedup, pagination, lifecycle), use `append_learning` for operational learnings, and use this tool for structured data. Per-operation guidance (append for accumulation, update to replace, delete requires confirmation) leaves nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
24 tool updates
- Changed
draft_reply1 field changed- changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Draft the reply on a consented teammate's behalf — pass their\nemail. The draft is created in their mailbox; pass the same teammate to\nsend_email to send it. Gated on that teammate's act-on-behalf setting\n(stricter than conversation sharing); a teammate who hasn't granted it is\nrejected. Omit to draft from your own account.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
edit_monitor_members1 field changed- changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Edit a consented teammate's monitor instead of your own — pass\ntheir email; agent_id must be one of their agents. Gated on that teammate's\nact-on-behalf setting; a teammate who hasn't granted it is rejected. Omit\nfor your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
fetch_linkedin_messages_with_person1 field changed- changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Read a consented teammate's LinkedIn history instead of\nyour own — pass their email. Gated on that teammate's\nconversation-sharing setting; a teammate who hasn't shared is\nrejected. Reads via the teammate's own LinkedIn connection and\ncaches the result to their local store. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
generate_monitored_post_comment1 field changed- changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Draft on a consented teammate's monitored post instead of your\nown — pass their email; the 1-credit charge lands on their account. Gated\non that teammate's act-on-behalf setting; a teammate who hasn't granted it\nis rejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
get_emails2 fields changed- changed
Input schema / properties / ids / descriptionPrevious value: -"List of email IDs from search_emails results (max 20)."New value: +"List of email IDs from search_emails results (max 20). Pass the same\non_behalf_of the search used." - changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Read a consented teammate's emails instead of your own — pass their email. Gated on that teammate's conversation-sharing setting; a teammate who hasn't shared is rejected. Pass the same email you used with search_emails. Or pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
get_message_experiments1 field changed- changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Read a consented teammate's tests instead of your own — pass their email.\nGated on that teammate's conversation-sharing setting. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
get_pending_approvals1 field changed- changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Read a consented teammate's pending approvals instead of\nyour own — pass their email. Gated on that teammate's act-on-behalf\nsetting (stricter than conversation sharing); a teammate who hasn't\ngranted it is rejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Added
get_privacy_settings - Changed
get_writing_style_guide2 fields changed- added
Input schema / properties / channel / descriptionAdded value: +"A channel a user can set a writing style guide for." - added
Input schema / properties / channel / titleAdded value: +"WritingStyleChannel"
- Changed
list_prospect_events1 field changed- changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Read a consented teammate's activity instead of your own —\npass their email. Gated on that teammate's conversation-sharing\nsetting; a teammate who hasn't shared is rejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
manage_email_outreach_queue3 fields changed- changed
Input schema / properties / action / descriptionPrevious value: -"One of:\n- 'status': Get full queue details (pending/sent/failed items). The\n `pending_items` / `pending_approval_items` lists cap at 50 each, each\n with a `pending_truncated` / `pending_approval_truncated` flag — when a\n list's flag is True, pass `offset` (offset += 50) to read the next page.\n Each item carries the `mailbox` it sends from; the account-level\n `mailboxes` list gives each mailbox's remaining daily headroom. The\n top-level `cancelled_by_kind` maps each cancel cause to its count\n (`recipient_replied`, `task_deleted`, `auto_shelved`, …; pre-taxonomy\n rows under `unknown`) — read it to answer \"how much outreach was\n cancelled, and why?\".\n- 'update': Edit a queued email's subject and/or body. Requires recipient_email.\n Pass subject and/or body to change — no cancel-and-re-queue needed. Send order\n is derived, not per-row scheduled; to pause or resume the campaign use\n update_agent (see the note above). A draft on a step running an A/B test\n carries `ab_version` ('A', 'B', or 'left_out' when its send won't count) on its\n status row and in this action's result; updating one takes ab_version too — the\n version the edited draft reads as — or ab_version alone to re-file it unedited.\n- 'approve': Send authorization belongs to the user, so this action\n needs a message that arrived after the drafts were queued and\n explicitly says to approve or send them. A message from before the\n drafts existed cannot authorize them — a request to review them,\n to approve them later, or to be able to approve from chat means:\n report that the queue is awaiting approval and end your turn; the\n user's next message decides. Once authorized: if recipient_email\n is given, approve only that item, otherwise approve ALL\n pending_approval items. From account-level chat an approve-all is\n rejected unless agent_id names the campaign — it must not fire\n every campaign's drafts into sending at once.\n- 'cancel': Cancel a queued email. Pass recipient_email to cancel that one\n person. To cancel the ENTIRE pending queue — which drops drafts the user\n already approved — you must pass\n cancel_all=True; an unscoped cancel without it is rejected. From\n account-level chat a cancel_all is rejected unless agent_id names the campaign\n to cancel — it must not clear every campaign's queue at once."New value: +"One of:\n- 'status': Get full queue details (pending/sent/failed items). The\n `pending_items` / `pending_approval_items` lists cap at 50 each, each\n with a `pending_truncated` / `pending_approval_truncated` flag — when a\n list's flag is True, pass `offset` (offset += 50) to read the next page.\n Each item carries the `mailbox` it sends from; the account-level\n `mailboxes` list gives each mailbox's remaining daily headroom. The\n top-level `cancelled_by_kind` maps each cancel cause to its count\n (`recipient_replied`, `task_deleted`, `auto_shelved`, …; pre-taxonomy\n rows under `unknown`) — read it to answer \"how much outreach was\n cancelled, and why?\".\n- 'update': Edit a queued email's subject and/or body. Requires recipient_email.\n Pass subject and/or body to change — no cancel-and-re-queue needed. Send order\n is derived, not per-row scheduled; to pause or resume the campaign use\n update_agent (see the note above). A draft on a step running an A/B test\n carries `ab_version` ('A', 'B', or 'left_out' when its send won't count) on its\n status row and in this action's result; updating one takes ab_version too — the\n version the edited draft reads as — or ab_version alone to re-file it unedited.\n- 'approve': Send authorization belongs to the user, so this action\n needs a message that arrived after the drafts were queued and\n explicitly says to approve or send them. A message from before the\n drafts existed cannot authorize them — a request to review them,\n to approve them later, or to be able to approve from chat means:\n report that the queue is awaiting approval and end your turn; the\n user's next message decides. Once authorized: if recipient_email\n is given, approve only that item, otherwise approve ALL\n pending_approval items. An email whose subject or body still holds a\n [[slot]] for the user to fill in stays awaiting approval and comes back in\n `still_held` with the slot to fill: tell the user, or fill it with\n 'update' once they give you the wording. From account-level chat an\n approve-all is rejected unless agent_id names the campaign — it must not\n fire every campaign's drafts into sending at once.\n- 'cancel': Cancel a queued email. Pass recipient_email to cancel that one\n person. To cancel the ENTIRE pending queue — which drops drafts the user\n already approved — you must pass\n cancel_all=True; an unscoped cancel without it is rejected. From\n account-level chat a cancel_all is rejected unless agent_id names the campaign\n to cancel — it must not clear every campaign's queue at once." - changed
Input schema / properties / agent_id / descriptionPrevious value: -"For 'cancel' and 'approve' — scope a bulk cancel_all / approve-all to one\n campaign's queue. Required from account-level chat (no active agent), where an\n unscoped bulk cancel or approve is rejected; call list_agents to get the id."New value: +"For 'cancel' and 'approve' — scope a bulk cancel_all / approve-all to one\n campaign's queue. Required from account-level chat (no active agent) and in any\n call with on_behalf_of, where an unscoped bulk cancel or approve is rejected; call\n list_agents to get the id (list_team_shared_agents for a teammate's campaigns)." - changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Act on a consented teammate's email queue instead of your own —\npass their email. Covers approve / cancel / update / status; a bulk\napprove-all / cancel_all must name one of the teammate's campaigns via\nagent_id. Gated on that teammate's act-on-behalf setting; a teammate who\nhasn't granted it is rejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
manage_inbox1 field changed- changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Act on a consented teammate's inbox instead of your own — pass\ntheir email. Every action (label, archive, mark read, unsubscribe) runs\nagainst their mailbox. Gated on that teammate's act-on-behalf setting\n(stricter than conversation sharing); a teammate who hasn't granted it is\nrejected. Omit for your own inbox.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
manage_linkedin_invite_queue3 fields changed- changed
Input schema / properties / action / descriptionPrevious value: -"One of:\n- 'status': Get full queue details (pending/sent/failed items with names and times).\n The `pending_items` / `pending_approval_items` / `failed_items` lists cap at 50\n each, each with a matching `*_truncated` flag — when a list's flag is True, more\n rows exist past this page; pass `offset` (offset += 50) to read them. Each\n `failed_items` row carries `last_error`, the reason that send failed — read it to\n answer \"why wasn't X sent?\". `pending_approval_items` rows also carry `last_error`\n when the row was held for a reason beyond plain approval gating (e.g. the\n resolver's name-mismatch hold). Queued post comments and reactions carry `post_url`\n and a `post_text` excerpt of the post the action targets, so you can tell which\n post a queued comment is on; `post_text_truncated` True means the target post ran\n longer than the excerpt shown. All four lists share one field shape; a\n `resolving_items` row (profile not yet resolved) has a blank `target_provider_id`\n and its `message` is the copy that will send once it resolves — edit or cancel it\n by its `target_identifier`. Every `pending_items` / `resolving_items` row carries\n `projected_send_day` + `projected_send_time` (a resolving row's is its projected\n lookup time, or its projected send time once it's a resolve-then-send) and a\n `queue_position` — the row's place in the one ordered lookup+send timeline, so the\n two lists interleave into what actually fires next. The top-level `send_forecast`\n is account-wide: `days` counts queued sends per day and `eta_date` is the day the\n last one goes out. A `resolve` row in `resolving_items` queues no invite itself, so\n `estimated_invites` counts per day the connection request each is expected to lead\n to, and `invites_sent_by` is the day the last connection request goes out, queued\n or estimated (null when none is due or one won't fit in the forecast window) —\n read it to answer \"when will all my invites be sent?\". The top-level `cancelled_by_kind` maps each cancel\n cause to its count (`recipient_replied`, `task_deleted`, `auto_shelved`, …;\n pre-taxonomy rows under `unknown`) — read it to answer \"how much outreach was\n cancelled, and why?\".\n- 'update': Update a queued item's message text. Name the item by\n target_provider_id (a pending row's pid) OR, for a resolving row that has no\n pid yet, by target_identifier (its URL/slug, shown on the row). Use this to\n edit a queued message — including on resolving rows, which still hold the\n message that will send once their profile resolves. When a target has more\n than one queued action — a post with both a comment and a reaction, or a\n person with a connection request and a follow — also pass action_type to name\n which one to edit. A draft on a step running an A/B test carries `ab_version`\n ('A', 'B', or 'left_out' when its send won't count) on its status row and in\n this action's result; updating one takes ab_version too — the version the\n edited draft reads as — or ab_version alone to re-file it unedited.\n- 'approve': Send authorization belongs to the user, so this action needs a\n message that arrived after the drafts were queued and explicitly says to\n approve or send them. A message from before the drafts existed cannot\n authorize them — a request to review them, to approve them later, or to be\n able to approve from chat means: report that the queue is awaiting approval\n and end your turn; the user's next message decides. Once authorized, this\n moves pending_approval items to 'pending'; they then send in queue order at\n your account's pace. If\n target_provider_id is given, approve only that item. Otherwise approve ALL\n pending_approval items (optionally filtered by action_type).\n Only touches items already awaiting approval — it does NOT run pending profile\n lookups or pre-approve resolving rows (those aren't in the approvals UI, so\n nobody has reviewed them; they resolve on their own schedule and, if the\n user's settings gate the action, surface for approval once resolved).\n From account-level chat an approve-all is rejected unless agent_id names the\n campaign — it must not fire every campaign's drafts into sending at once.\n- 'cancel': Cancel a queued item. Pass target_provider_id to cancel that one\n person or post — every queued action for them — or target_identifier to cancel\n a resolving row that has no pid yet. To cancel the ENTIRE pending\n queue — which drops drafts the user\n already approved — you must pass\n cancel_all=True; an unscoped cancel without it is rejected (an action_type\n or node_id filter alone is still a bulk cancel and also needs cancel_all=True).\n Narrow a cancel_all to one flow stage with node_id (e.g. restart only the m2\n messages without touching m3) — the only way to separate two stages that share\n an action_type. From account-level chat a cancel_all is rejected unless agent_id\n names the campaign to cancel — it must not clear every campaign's queue at once.\n- 'pause': Pause the queue (stops sending, keeps items queued). Pass resume_at\n to schedule an automatic resume (\"pause while I'm on vacation, resume July 20\");\n without it the pause is indefinite and only an explicit 'resume' restarts sending.\n- 'resume': Resume the queue (pending items drain in queue order at your account's pace)"New value: +"One of:\n- 'status': Get full queue details (pending/sent/failed items with names and times).\n The `pending_items` / `pending_approval_items` / `failed_items` lists cap at 50\n each, each with a matching `*_truncated` flag — when a list's flag is True, more\n rows exist past this page; pass `offset` (offset += 50) to read them. Each\n `failed_items` row carries `last_error`, the reason that send failed — read it to\n answer \"why wasn't X sent?\". `pending_approval_items` rows carry `hold_reason`\n when the row was held for a reason beyond plain approval gating (e.g. the\n resolver's name-mismatch hold, or a [[slot]] the user has to fill in). Queued post\n comments and reactions carry `post_url` and a `post_text` excerpt of the post the\n action targets, so you can tell which\n post a queued comment is on; `post_text_truncated` True means the target post ran\n longer than the excerpt shown. All four lists share one field shape; a\n `resolving_items` row (profile not yet resolved) has a blank `target_provider_id`\n and its `message` is the copy that will send once it resolves — edit or cancel it\n by its `target_identifier`. Every `pending_items` / `resolving_items` row carries\n `projected_send_day` + `projected_send_time` (a resolving row's is its projected\n lookup time, or its projected send time once it's a resolve-then-send) and a\n `queue_position` — the row's place in the one ordered lookup+send timeline, so the\n two lists interleave into what actually fires next. The top-level `send_forecast`\n is account-wide: `days` counts queued sends per day and `eta_date` is the day the\n last one goes out. A `resolve` row in `resolving_items` queues no invite itself, so\n `estimated_invites` counts per day the connection request each is expected to lead\n to, and `invites_sent_by` is the day the last connection request goes out, queued\n or estimated (null when none is due or one won't fit in the forecast window) —\n read it to answer \"when will all my invites be sent?\". The top-level `cancelled_by_kind` maps each cancel\n cause to its count (`recipient_replied`, `task_deleted`, `auto_shelved`, …;\n pre-taxonomy rows under `unknown`) — read it to answer \"how much outreach was\n cancelled, and why?\".\n- 'update': Update a queued item's message text. Name the item by\n target_provider_id (a pending row's pid) OR, for a resolving row that has no\n pid yet, by target_identifier (its URL/slug, shown on the row). Use this to\n edit a queued message — including on resolving rows, which still hold the\n message that will send once their profile resolves. When a target has more\n than one queued action — a post with both a comment and a reaction, or a\n person with a connection request and a follow — also pass action_type to name\n which one to edit. A draft on a step running an A/B test carries `ab_version`\n ('A', 'B', or 'left_out' when its send won't count) on its status row and in\n this action's result; updating one takes ab_version too — the version the\n edited draft reads as — or ab_version alone to re-file it unedited.\n- 'approve': Send authorization belongs to the user, so this action needs a\n message that arrived after the drafts were queued and explicitly says to\n approve or send them. A message from before the drafts existed cannot\n authorize them — a request to review them, to approve them later, or to be\n able to approve from chat means: report that the queue is awaiting approval\n and end your turn; the user's next message decides. Once authorized, this\n moves pending_approval items to 'pending'; they then send in queue order at\n your account's pace. If\n target_provider_id is given, approve only that item. Otherwise approve ALL\n pending_approval items (optionally filtered by action_type). A connection\n request, message or InMail whose text still holds a [[slot]] for the user to fill\n in stays awaiting approval and comes back in `still_held` with the slot to fill:\n tell the user, or fill it with 'update' once they give you the wording.\n Only touches items already awaiting approval — it does NOT run pending profile\n lookups or pre-approve resolving rows (those aren't in the approvals UI, so\n nobody has reviewed them; they resolve on their own schedule and, if the\n user's settings gate the action, surface for approval once resolved).\n From account-level chat an approve-all is rejected unless agent_id names the\n campaign — it must not fire every campaign's drafts into sending at once.\n- 'cancel': Cancel a queued item. Pass target_provider_id to cancel that one\n person or post — every queued action for them — or target_identifier to cancel\n a resolving row that has no pid yet. To cancel the ENTIRE pending\n queue — which drops drafts the user\n already approved — you must pass\n cancel_all=True; an unscoped cancel without it is rejected (an action_type\n or node_id filter alone is still a bulk cancel and also needs cancel_all=True).\n Narrow a cancel_all to one flow stage with node_id (e.g. restart only the m2\n messages without touching m3) — the only way to separate two stages that share\n an action_type. From account-level chat a cancel_all is rejected unless agent_id\n names the campaign to cancel — it must not clear every campaign's queue at once.\n- 'pause': Pause the queue (stops sending, keeps items queued). Not available\n on a teammate's behalf; approve, cancel, or update their items instead. Pass\n resume_at to schedule an automatic resume (\"pause while I'm on vacation, resume\n July 20\"); without it the pause is indefinite and only an explicit 'resume'\n restarts sending.\n- 'resume': Resume the queue (pending items drain in queue order at your account's pace).\n Not available on a teammate's behalf; approve, cancel, or update their items instead." - changed
Input schema / properties / agent_id / descriptionPrevious value: -"For 'cancel' and 'approve' — scope a bulk cancel_all / approve-all to one\n campaign's queue. Required from account-level chat (no active agent), where an\n unscoped bulk cancel or approve is rejected; call list_agents to get the id."New value: +"For 'cancel' and 'approve' — scope a bulk cancel_all / approve-all to one\n campaign's queue. Required from account-level chat (no active agent) and in any\n call with on_behalf_of, where an unscoped bulk cancel or approve is rejected; call\n list_agents to get the id (list_team_shared_agents for a teammate's campaigns)." - changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Act on a consented teammate's queue instead of your own — pass\ntheir email. Covers approve / cancel / update / status; pause and resume\n(account-wide controls) are not available on a teammate's behalf. A bulk\napprove-all / cancel_all must name one of the teammate's campaigns via\nagent_id. Gated on that teammate's act-on-behalf setting; a teammate who\nhasn't granted it is rejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
query_agent_people1 field changed- changed
Input schema / properties / group_by / anyOfPrevious value: -[ - { - "enum": [ - "title", - "company", - "location", - "outreach_stage", - "list", - "source", - "degree", - "connector", - "segment" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "A dimension to group people by.", + "enum": [ + "title", + "company", + "location", + "outreach_stage", + "list", + "source", + "degree", + "connector", + "segment" + ], + "title": "PersonGroupBy", + "type": "string" + }, + { + "type": "null" + } +]
- Changed
query_monitored_companies1 field changed- added
Input schema / properties / mode / titleAdded value: +"QueryMode"
- Changed
query_monitored_posts1 field changed- added
Input schema / properties / mode / titleAdded value: +"QueryMode"
- Changed
query_prospects2 fields changed- added
Input schema / properties / mode / titleAdded value: +"QueryMode" - changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Read a consented teammate's prospects instead of your own —\npass their email. Gated on that teammate's conversation-sharing\nsetting; a teammate who hasn't shared is rejected. Any `agent_id` must\nbe one of that teammate's agents. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
query_search_results1 field changed- changed
Input schema / properties / group_by / anyOfPrevious value: -[ - { - "enum": [ - "list", - "company", - "source" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "list", + "source", + "company" + ], + "type": "string" + }, + { + "type": "null" + } +]
- Changed
queue_linkedin_post_engagement1 field changed- changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Queue the engagement on a consented teammate's account instead\nof your own — pass their email. It lands as a standalone (account-level)\nengagement on their queue, not tied to any of your agents. Gated on that\nteammate's act-on-behalf setting; a teammate who hasn't granted it is\nrejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
search_emails1 field changed- changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Read a consented teammate's emails instead of your own — pass their email. Gated on that teammate's conversation-sharing setting; a teammate who hasn't shared is rejected. Omit for your own. Or pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
search_linkedin_message_history2 fields changed- changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Read a consented teammate's LinkedIn message history\ninstead of your own — pass their email. Gated on that teammate's\nconversation-sharing setting; a teammate who hasn't shared is\nrejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with conversation sharing turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." - changed
Input schema / properties / where_clause / descriptionPrevious value: -"SQL WHERE clause (without WHERE keyword). Use ONLY these\ncolumns: provider, chat_id, sender_name, sender_provider_id,\nrecipient_name, recipient_provider_id, content, message_datetime,\nreply_outcome (inbound replies only; '' / 'interested' /\n'meeting_booked' / 'not_interested' — the reply's classified outcome).\nTo read your thread with a person, match their provider_id alone:\n`sender_provider_id = '<id>' OR recipient_provider_id = '<id>'`.\nKeep their name out of that clause: a name ILIKE is a substring\nmatch, so it pulls in other people's threads (`'%Ana%Li%'` also\nmatches \"Diana Lim\"). Match on a name only to find someone whose\nprovider_id you don't have; when a hit's counterpart name is their\nfull name, take it as theirs and re-read by that counterpart's\nprovider_id. An empty result isn't proof of no prior\ncontact: this local store holds only a bounded initial backfill\nfrom around when the account was connected, forward. When you need\ncertainty, call `fetch_linkedin_messages_with_person` with their\nprovider_id — it reads LinkedIn's full synced history live and\nwrites it back here."New value: +"SQL WHERE clause (without WHERE keyword). Use ONLY these\ncolumns: provider, chat_id, sender_name, sender_provider_id,\nrecipient_name, recipient_provider_id, content, message_datetime,\nreply_outcome (inbound replies only; '' / 'interested' /\n'meeting_booked' / 'not_interested' — the reply's classified outcome).\nTo read your thread with a person, match their provider_id alone:\n`sender_provider_id = '<id>' OR recipient_provider_id = '<id>'`.\nKeep their name out of that clause: a name ILIKE is a substring\nmatch, so it pulls in other people's threads (`'%Ana%Li%'` also\nmatches \"Diana Lim\"). Match on a name only to find someone whose\nprovider_id you don't have; when a hit's counterpart name is their\nfull name, take it as theirs and re-read by that counterpart's\nprovider_id. An empty result isn't proof of no prior\ncontact: this local store holds only a bounded initial backfill\nfrom around when the account was connected, forward. When you need\ncertainty, call `fetch_linkedin_messages_with_person` with their\nprovider_id — it reads LinkedIn's full synced history live."
- Changed
send_email2 fields changed- changed
Input schema / properties / draft_id / descriptionPrevious value: -"The draft_id returned by draft_email or draft_reply"New value: +"The draft_id returned by draft_email or draft_reply. A draft made with\non_behalf_of sends with the same on_behalf_of." - changed
Input schema / properties / on_behalf_of / descriptionPrevious value: -"Send a draft you created on a consented teammate's behalf —\npass the SAME email you passed to draft_reply. The send goes out from\ntheir mailbox. Gated on that teammate's act-on-behalf setting; a\nteammate who hasn't granted it is rejected. Omit to send your own draft.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with team actions turned on), or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
- Changed
setup_email_sequence3 fields changed- changed
Input schema / properties / body_template / descriptionPrevious value: -"Batch fallback body for recipients without their own\n`body`. Final literal text — placeholder syntax rejected.\nBodies (this and per-recipient `body`) are Markdown, rendered to\nHTML at send time for every provider — write hyperlinks as\n[text](url) so the link reads as its anchor text rather than a\nraw URL."New value: +"Batch fallback body for recipients without their own\n`body`. Final literal text, as for a per-recipient `body`.\nBodies (this and per-recipient `body`) are Markdown, rendered to\nHTML at send time for every provider — write hyperlinks as\n[text](url) so the link reads as its anchor text rather than a\nraw URL." - changed
Input schema / properties / recipients / descriptionPrevious value: -"List of dicts. Required: `email`. Optional per-recipient\n`subject` / `body` (final literal text; placeholder syntax\n`{first_name}`, `[name]`, `<<name>>`, `{{name}}` is rejected with\nModelRetry; overrides templates). Other fields (`first_name`,\n`last_name`, `company`, `title`) are stored on the tracking item\nfor later lookup — NOT substituted into templates. Other extras are\nignored, except the LinkedIn identifiers: `linkedin_url` is honored\nfrom either the top-level key OR a `data` sub-dict (validated /\ncanonicalized before storage), while `linkedin_provider_id` is\nhonored from the top-level key only. Both land on the prospect's\nLinkedIn fields.\nFor mixed-channel campaigns, also pass `linkedin_url` (and\n`linkedin_provider_id` when known) on each recipient so this\ntool can dedup against any existing LinkedIn-keyed tracking\nrow for the same person — both identifiers land on one row.\nWhen `find_email` returned `{email, linkedin_url}` for this\nrecipient, forward both fields here verbatim. On a flow email node\nthis is also how a just-discovered address binds to the prospect\nresting on the node: passing their `linkedin_url` resolves the send\nto that existing row instead of orphaning a new one."New value: +"List of dicts. Required: `email`. Optional per-recipient\n`subject` / `body` (final literal text, apart from any [[slot]] the user\nasked to fill in themselves while approving; placeholder syntax like\n`{first_name}`, `[name]`, `<<name>>`, `{{name}}` is rejected with ModelRetry;\noverrides templates). Other fields (`first_name`,\n`last_name`, `company`, `title`) are stored on the tracking item\nfor later lookup — NOT substituted into templates. Other extras are\nignored, except the LinkedIn identifiers: `linkedin_url` is honored\nfrom either the top-level key OR a `data` sub-dict (validated /\ncanonicalized before storage), while `linkedin_provider_id` is\nhonored from the top-level key only. Both land on the prospect's\nLinkedIn fields.\nFor mixed-channel campaigns, also pass `linkedin_url` (and\n`linkedin_provider_id` when known) on each recipient so this\ntool can dedup against any existing LinkedIn-keyed tracking\nrow for the same person — both identifiers land on one row.\nWhen `find_email` returned `{email, linkedin_url}` for this\nrecipient, forward both fields here verbatim. On a flow email node\nthis is also how a just-discovered address binds to the prospect\nresting on the node: passing their `linkedin_url` resolves the send\nto that existing row instead of orphaning a new one." - changed
Input schema / properties / subject_template / descriptionPrevious value: -"Batch fallback subject for recipients without their\nown `subject`. Final literal text — placeholder syntax rejected."New value: +"Batch fallback subject for recipients without their\nown `subject`. Final literal text, as for a per-recipient `subject`."
- Changed
setup_linkedin_sequence1 field changed- changed
Input schema / properties / identifiers / items / properties / message / descriptionPrevious value: -"The final personalized body text, already resolved to literal words. The tool substitutes nothing and rejects any merge-field or placeholder syntax."New value: +"The final personalized body text, already resolved to literal words. The tool substitutes nothing and rejects any merge-field or placeholder syntax, apart from a [[slot]] in double square brackets the user asked to fill in themselves while approving."
1 tool update
- Changed
manage_linkedin_invite_queue1 field changed- changed
Input schema / properties / action / descriptionPrevious value: -"One of:\n- 'status': Get full queue details (pending/sent/failed items with names and times).\n The `pending_items` / `pending_approval_items` / `failed_items` lists cap at 50\n each, each with a matching `*_truncated` flag — when a list's flag is True, more\n rows exist past this page; pass `offset` (offset += 50) to read them. Each\n `failed_items` row carries `last_error`, the reason that send failed — read it to\n answer \"why wasn't X sent?\". `pending_approval_items` rows also carry `last_error`\n when the row was held for a reason beyond plain approval gating (e.g. the\n resolver's name-mismatch hold). Queued post comments and reactions carry `post_url`\n and a `post_text` excerpt of the post the action targets, so you can tell which\n post a queued comment is on; `post_text_truncated` True means the target post ran\n longer than the excerpt shown. All four lists share one field shape; a\n `resolving_items` row (profile not yet resolved) has a blank `target_provider_id`\n and its `message` is the copy that will send once it resolves — edit or cancel it\n by its `target_identifier`. Every `pending_items` / `resolving_items` row carries\n `projected_send_day` + `projected_send_time` (a resolving row's is its projected\n lookup time, or its projected send time once it's a resolve-then-send) and a\n `queue_position` — the row's place in the one ordered lookup+send timeline, so the\n two lists interleave into what actually fires next. The top-level `cancelled_by_kind` maps each cancel\n cause to its count (`recipient_replied`, `task_deleted`, `auto_shelved`, …;\n pre-taxonomy rows under `unknown`) — read it to answer \"how much outreach was\n cancelled, and why?\".\n- 'update': Update a queued item's message text. Name the item by\n target_provider_id (a pending row's pid) OR, for a resolving row that has no\n pid yet, by target_identifier (its URL/slug, shown on the row). Use this to\n edit a queued message — including on resolving rows, which still hold the\n message that will send once their profile resolves. When a target has more\n than one queued action — a post with both a comment and a reaction, or a\n person with a connection request and a follow — also pass action_type to name\n which one to edit. A draft on a step running an A/B test carries `ab_version`\n ('A', 'B', or 'left_out' when its send won't count) on its status row and in\n this action's result; updating one takes ab_version too — the version the\n edited draft reads as — or ab_version alone to re-file it unedited.\n- 'approve': Send authorization belongs to the user, so this action needs a\n message that arrived after the drafts were queued and explicitly says to\n approve or send them. A message from before the drafts existed cannot\n authorize them — a request to review them, to approve them later, or to be\n able to approve from chat means: report that the queue is awaiting approval\n and end your turn; the user's next message decides. Once authorized, this\n moves pending_approval items to 'pending'; they then send in queue order at\n your account's pace. If\n target_provider_id is given, approve only that item. Otherwise approve ALL\n pending_approval items (optionally filtered by action_type).\n Only touches items already awaiting approval — it does NOT run pending profile\n lookups or pre-approve resolving rows (those aren't in the approvals UI, so\n nobody has reviewed them; they resolve on their own schedule and, if the\n user's settings gate the action, surface for approval once resolved).\n From account-level chat an approve-all is rejected unless agent_id names the\n campaign — it must not fire every campaign's drafts into sending at once.\n- 'cancel': Cancel a queued item. Pass target_provider_id to cancel that one\n person or post — every queued action for them — or target_identifier to cancel\n a resolving row that has no pid yet. To cancel the ENTIRE pending\n queue — which drops drafts the user\n already approved — you must pass\n cancel_all=True; an unscoped cancel without it is rejected (an action_type\n or node_id filter alone is still a bulk cancel and also needs cancel_all=True).\n Narrow a cancel_all to one flow stage with node_id (e.g. restart only the m2\n messages without touching m3) — the only way to separate two stages that share\n an action_type. From account-level chat a cancel_all is rejected unless agent_id\n names the campaign to cancel — it must not clear every campaign's queue at once.\n- 'pause': Pause the queue (stops sending, keeps items queued). Pass resume_at\n to schedule an automatic resume (\"pause while I'm on vacation, resume July 20\");\n without it the pause is indefinite and only an explicit 'resume' restarts sending.\n- 'resume': Resume the queue (pending items drain in queue order at your account's pace)"New value: +"One of:\n- 'status': Get full queue details (pending/sent/failed items with names and times).\n The `pending_items` / `pending_approval_items` / `failed_items` lists cap at 50\n each, each with a matching `*_truncated` flag — when a list's flag is True, more\n rows exist past this page; pass `offset` (offset += 50) to read them. Each\n `failed_items` row carries `last_error`, the reason that send failed — read it to\n answer \"why wasn't X sent?\". `pending_approval_items` rows also carry `last_error`\n when the row was held for a reason beyond plain approval gating (e.g. the\n resolver's name-mismatch hold). Queued post comments and reactions carry `post_url`\n and a `post_text` excerpt of the post the action targets, so you can tell which\n post a queued comment is on; `post_text_truncated` True means the target post ran\n longer than the excerpt shown. All four lists share one field shape; a\n `resolving_items` row (profile not yet resolved) has a blank `target_provider_id`\n and its `message` is the copy that will send once it resolves — edit or cancel it\n by its `target_identifier`. Every `pending_items` / `resolving_items` row carries\n `projected_send_day` + `projected_send_time` (a resolving row's is its projected\n lookup time, or its projected send time once it's a resolve-then-send) and a\n `queue_position` — the row's place in the one ordered lookup+send timeline, so the\n two lists interleave into what actually fires next. The top-level `send_forecast`\n is account-wide: `days` counts queued sends per day and `eta_date` is the day the\n last one goes out. A `resolve` row in `resolving_items` queues no invite itself, so\n `estimated_invites` counts per day the connection request each is expected to lead\n to, and `invites_sent_by` is the day the last connection request goes out, queued\n or estimated (null when none is due or one won't fit in the forecast window) —\n read it to answer \"when will all my invites be sent?\". The top-level `cancelled_by_kind` maps each cancel\n cause to its count (`recipient_replied`, `task_deleted`, `auto_shelved`, …;\n pre-taxonomy rows under `unknown`) — read it to answer \"how much outreach was\n cancelled, and why?\".\n- 'update': Update a queued item's message text. Name the item by\n target_provider_id (a pending row's pid) OR, for a resolving row that has no\n pid yet, by target_identifier (its URL/slug, shown on the row). Use this to\n edit a queued message — including on resolving rows, which still hold the\n message that will send once their profile resolves. When a target has more\n than one queued action — a post with both a comment and a reaction, or a\n person with a connection request and a follow — also pass action_type to name\n which one to edit. A draft on a step running an A/B test carries `ab_version`\n ('A', 'B', or 'left_out' when its send won't count) on its status row and in\n this action's result; updating one takes ab_version too — the version the\n edited draft reads as — or ab_version alone to re-file it unedited.\n- 'approve': Send authorization belongs to the user, so this action needs a\n message that arrived after the drafts were queued and explicitly says to\n approve or send them. A message from before the drafts existed cannot\n authorize them — a request to review them, to approve them later, or to be\n able to approve from chat means: report that the queue is awaiting approval\n and end your turn; the user's next message decides. Once authorized, this\n moves pending_approval items to 'pending'; they then send in queue order at\n your account's pace. If\n target_provider_id is given, approve only that item. Otherwise approve ALL\n pending_approval items (optionally filtered by action_type).\n Only touches items already awaiting approval — it does NOT run pending profile\n lookups or pre-approve resolving rows (those aren't in the approvals UI, so\n nobody has reviewed them; they resolve on their own schedule and, if the\n user's settings gate the action, surface for approval once resolved).\n From account-level chat an approve-all is rejected unless agent_id names the\n campaign — it must not fire every campaign's drafts into sending at once.\n- 'cancel': Cancel a queued item. Pass target_provider_id to cancel that one\n person or post — every queued action for them — or target_identifier to cancel\n a resolving row that has no pid yet. To cancel the ENTIRE pending\n queue — which drops drafts the user\n already approved — you must pass\n cancel_all=True; an unscoped cancel without it is rejected (an action_type\n or node_id filter alone is still a bulk cancel and also needs cancel_all=True).\n Narrow a cancel_all to one flow stage with node_id (e.g. restart only the m2\n messages without touching m3) — the only way to separate two stages that share\n an action_type. From account-level chat a cancel_all is rejected unless agent_id\n names the campaign to cancel — it must not clear every campaign's queue at once.\n- 'pause': Pause the queue (stops sending, keeps items queued). Pass resume_at\n to schedule an automatic resume (\"pause while I'm on vacation, resume July 20\");\n without it the pause is indefinite and only an explicit 'resume' restarts sending.\n- 'resume': Resume the queue (pending items drain in queue order at your account's pace)"
163 tool updates
- Changed
add_trigger1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
apollo_enrich1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
apollo_save1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
apollo_search1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
append_learning1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
attach_to_outreach1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
check_email_daily_usage1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
check_linkedin_daily_usage1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
classify_message_tag_group2 fields changed- changed
Input schema / properties / channel / anyOfPrevious value: -[ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "An outreach channel.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
classify_segment_group1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
correct_message_classification2 fields changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +} - added
Input schema / properties / source / titleAdded value: +"MessageSource"
- Changed
correct_reply_outcome3 fields changed- added
Input schema / properties / channel / titleAdded value: +"Channel" - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +} - added
Input schema / properties / outcome / titleAdded value: +"ReplyOutcome"
- Changed
correct_segment_classification1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
create_agent2 fields changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +} - changed
Input schema / properties / template_id / anyOfPrevious value: -[ - { - "enum": [ - "prospecting-agent", - "linkedin-engagement", - "thought-leadership-draft", - "seo-blog-post", - "weekly-sales-review", - "social-listening", - "company-monitor", - "landing-page-analytics" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "prospecting-agent", + "linkedin-engagement", + "thought-leadership-draft", + "seo-blog-post", + "weekly-sales-review", + "company-monitor", + "landing-page-analytics" + ], + "type": "string" + }, + { + "type": "null" + } +]
- Changed
create_calendar_event2 fields changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +} - changed
Input schema / properties / provider / anyOfPrevious value: -[ - { - "enum": [ - "outlook", - "google" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "outlook", + "google" + ], + "title": "Provider", + "type": "string" + }, + { + "type": "null" + } +]
- Changed
create_crm_task1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
create_deal1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
create_message_tag_group3 fields changed- changed
Input schema / properties / criteria / anyOfPrevious value: -[ - { - "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", - "properties": { - "agent_ids": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "channel": { - "anyOf": [ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "positions": { - "anyOf": [ - { - "items": { - "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the other person's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", - "enum": [ - "first", - "follow_up", - "reply" - ], - "title": "Position", - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "senders": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "title": "Criteria", - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", + "properties": { + "agent_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "channel": { + "anyOf": [ + { + "description": "An outreach channel.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "positions": { + "anyOf": [ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the other person's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "senders": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "Criteria", + "type": "object" + }, + { + "type": "null" + } +] - added
Input schema / properties / mode / titleAdded value: +"Mode" - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
create_segment_group2 fields changed- added
Input schema / properties / mode / titleAdded value: +"Mode" - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
define_sequence2 fields changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +} - changed
Input schema / properties / sequence / properties / nodes / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", - "properties": { - "accepted": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - }, - "already_connected": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "connection_request", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "maxLength": 300, - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "timeout": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "id", - "label", - "kind", - "accepted", - "already_connected" - ], - "title": "ConnectionRequestNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", - "properties": { - "action": { - "enum": [ - "message", - "inmail", - "email" - ], - "type": "string" - }, - "channel": { - "enum": [ - "linkedin", - "email" - ], - "type": "string" - }, - "follow_up": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "send", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "replied": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "replied" - ], - "title": "SendNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", - "properties": { - "action": { - "enum": [ - "follow", - "withdraw", - "resolve", - "comment", - "reaction" - ], - "type": "string" - }, - "channel": { - "const": "linkedin", - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "action", - "type": "string" - }, - "label": { - "type": "string" - }, - "notify": { - "default": false, - "type": "boolean" - }, - "then": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "then" - ], - "title": "SilentActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the order may cost, in whole US dollars) and `gift_note` (at\nmost 240 characters) are the step's optional defaults, pre-filled on every card. No\nchannel and no reply to route; `then` is its single advance once the gift is delivered:\nbare (`then.after=None`) to move on at delivery, or held to wait that long after it.", - "properties": { - "gift_note": { - "anyOf": [ - { - "maxLength": 240, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "gift", - "type": "string" - }, - "label": { - "type": "string" - }, - "max_price_usd": { - "anyOf": [ - { - "exclusiveMinimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "product_url": { - "anyOf": [ - { - "format": "uri", - "maxLength": 2083, - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "then": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "GiftNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", - "properties": { - "action_description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "manual", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "action_description", - "then" - ], - "title": "ManualActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "automation", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "AutomationNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", - "properties": { - "arms": { - "items": { - "additionalProperties": false, - "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", - "properties": { - "case": { - "minLength": 1, - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "case", - "to" - ], - "title": "Arm", - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "decision", - "type": "string" - }, - "label": { - "type": "string" - }, - "rule": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "rule", - "arms" - ], - "title": "DecisionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "terminal", - "type": "string" - }, - "label": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind" - ], - "title": "TerminalNode", - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", + "properties": { + "accepted": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + }, + "already_connected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "connection_request", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "maxLength": 300, + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "timeout": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "label", + "kind", + "accepted", + "already_connected" + ], + "title": "ConnectionRequestNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", + "properties": { + "action": { + "enum": [ + "message", + "inmail", + "email" + ], + "type": "string" + }, + "channel": { + "description": "An outreach channel.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + "follow_up": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "send", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "replied": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "replied" + ], + "title": "SendNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", + "properties": { + "action": { + "enum": [ + "follow", + "withdraw", + "resolve", + "comment", + "reaction" + ], + "type": "string" + }, + "channel": { + "const": "linkedin", + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "action", + "type": "string" + }, + "label": { + "type": "string" + }, + "notify": { + "default": false, + "type": "boolean" + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "then" + ], + "title": "SilentActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the item may cost including tax and shipping, in whole US\ndollars) and `gift_note` (at most 240 characters) are the step's optional defaults,\npre-filled on every card. No channel and no reply to route; `then` is its single advance\nonce the gift is delivered: bare (`then.after=None`) to move on at delivery, or held to\nwait that long after it.", + "properties": { + "gift_note": { + "anyOf": [ + { + "maxLength": 240, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "gift", + "type": "string" + }, + "label": { + "type": "string" + }, + "max_price_usd": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "product_url": { + "anyOf": [ + { + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "GiftNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", + "properties": { + "action_description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "manual", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "action_description", + "then" + ], + "title": "ManualActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "automation", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "AutomationNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", + "properties": { + "arms": { + "items": { + "additionalProperties": false, + "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", + "properties": { + "case": { + "minLength": 1, + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "case", + "to" + ], + "title": "Arm", + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "decision", + "type": "string" + }, + "label": { + "type": "string" + }, + "rule": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "rule", + "arms" + ], + "title": "DecisionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "terminal", + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind" + ], + "title": "TerminalNode", + "type": "object" + } +]
- Changed
delete_agent1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
delete_message_tag1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
delete_message_tag_group1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
delete_segment_group1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
delete_segment_tag1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
detach_from_outreach1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
draft_email1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
draft_reply2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Draft the reply on a consented teammate's behalf — pass their\nemail. The draft is created in their mailbox; pass the same teammate to\nsend_email to send it. Gated on that teammate's act-on-behalf setting\n(stricter than conversation sharing); a teammate who hasn't granted it is\nrejected. Omit to draft from your own account." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Draft the reply on a consented teammate's behalf — pass their\nemail. The draft is created in their mailbox; pass the same teammate to\nsend_email to send it. Gated on that teammate's act-on-behalf setting\n(stricter than conversation sharing); a teammate who hasn't granted it is\nrejected. Omit to draft from your own account.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
edit_monitor_members2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Edit a consented teammate's monitor instead of your own — pass\ntheir email; agent_id must be one of their agents. Gated on that teammate's\nact-on-behalf setting; a teammate who hasn't granted it is rejected. Omit\nfor your own." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Edit a consented teammate's monitor instead of your own — pass\ntheir email; agent_id must be one of their agents. Gated on that teammate's\nact-on-behalf setting; a teammate who hasn't granted it is rejected. Omit\nfor your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
enrich_linkedin_profiles1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
escalate_to_team1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
estimate_message_tag_classify_scope2 fields changed- changed
Input schema / properties / channel / anyOfPrevious value: -[ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "An outreach channel.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
estimate_segment_classify_scope1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
exa_find_people1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
exa_search_news1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
extend_find_search1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
fetch_linkedin_messages_with_person2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Read a consented teammate's LinkedIn history instead of\nyour own — pass their email. Gated on that teammate's\nconversation-sharing setting; a teammate who hasn't shared is\nrejected. Reads via the teammate's own LinkedIn connection and\ncaches the result to their local store. Omit for your own." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Read a consented teammate's LinkedIn history instead of\nyour own — pass their email. Gated on that teammate's\nconversation-sharing setting; a teammate who hasn't shared is\nrejected. Reads via the teammate's own LinkedIn connection and\ncaches the result to their local store. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
fetch_post_engagers1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
find_companies_by_tech_stack1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
find_email1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
find_linkedin_url1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
find_phone_number1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
find_warm_intro_paths1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
generate_monitored_post_comment2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Draft on a consented teammate's monitored post instead of your\nown — pass their email; the 1-credit charge lands on their account. Gated\non that teammate's act-on-behalf setting; a teammate who hasn't granted it\nis rejected. Omit for your own." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Draft on a consented teammate's monitored post instead of your\nown — pass their email; the 1-credit charge lands on their account. Gated\non that teammate's act-on-behalf setting; a teammate who hasn't granted it\nis rejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
get_agent_details1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_campaign_flow1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_chat_messages1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_credit_usage1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_email_attachment1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_emails2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Read a consented teammate's emails instead of your own — pass their email. Gated on that teammate's conversation-sharing setting; a teammate who hasn't shared is rejected. Pass the same email you used with search_emails." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Read a consented teammate's emails instead of your own — pass their email. Gated on that teammate's conversation-sharing setting; a teammate who hasn't shared is rejected. Pass the same email you used with search_emails. Or pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
get_integration_status1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_linkedin_monitors1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_linkedin_posts1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_luma_events1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_luma_guests1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_mailbox_deliverability1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_mcp_change_calls1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_meeting_transcript1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_message_experiments3 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Read a consented teammate's tests instead of your own — pass their email.\nGated on that teammate's conversation-sharing setting. Omit for your own." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Read a consented teammate's tests instead of your own — pass their email.\nGated on that teammate's conversation-sharing setting. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +} - changed
Input schema / properties / outcome / anyOfPrevious value: -[ - { - "enum": [ - "accepted", - "replied", - "interested", - "meeting" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "An outreach outcome a prospect can reach.", + "enum": [ + "accepted", + "replied", + "interested", + "meeting" + ], + "title": "Measure", + "type": "string" + }, + { + "type": "null" + } +]
- Changed
get_message_tag_rates2 fields changed- changed
Input schema / properties / channel / anyOfPrevious value: -[ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "An outreach channel.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_message_tag_unclassified_count1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_node_history1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_outreach_approval1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_outreach_window1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_pending_approvals4 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Read a consented teammate's pending approvals instead of\nyour own — pass their email. Gated on that teammate's act-on-behalf\nsetting (stricter than conversation sharing); a teammate who hasn't\ngranted it is rejected. Omit for your own." -} - changed
Input schema / properties / bucket / anyOfPrevious value: -[ - { - "enum": [ - "email", - "linkedin_invite", - "linkedin_message", - "linkedin_follow", - "engagement", - "reddit", - "gift", - "manual" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "email", + "linkedin_invite", + "linkedin_message", + "linkedin_follow", + "engagement", + "gift", + "manual" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / bucket / descriptionPrevious value: -"Drill into one bucket for its full-text items, paged. One of\n'email', 'linkedin_invite', 'linkedin_message', 'linkedin_follow',\n'engagement', 'reddit', 'gift', 'manual'. Omit for the bounded counts+preview."New value: +"Drill into one bucket for its full-text items, paged. One of\n'email', 'linkedin_invite', 'linkedin_message', 'linkedin_follow',\n'engagement', 'gift', 'manual'. Omit for the bounded counts+preview." - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Read a consented teammate's pending approvals instead of\nyour own — pass their email. Gated on that teammate's act-on-behalf\nsetting (stricter than conversation sharing); a teammate who hasn't\ngranted it is rejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
get_personalization_guide1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_run_transcript1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_segment_funnel1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_segment_rates2 fields changed- changed
Input schema / properties / channel / anyOfPrevious value: -[ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "An outreach channel.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_segment_unclassified_count1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_skill_guide2 fields changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +} - changed
Input schema / properties / skill / enumPrevious value: -[ - "blog_content_calendar", - "blog_post_writing", - "company_monitor", - "email_inbox_management", - "email_sequence_setup", - "exa_find_people", - "find_companies_news_scan", - "find_companies_news_scan_setup", - "find_companies_search_criteria", - "find_companies_webset_mode", - "find_people", - "find_warm_intro_paths", - "linkedin_flowless_campaigns", - "linkedin_outreach", - "linkedin_outreach_debugging", - "linkedin_outreach_finding_leads", - "linkedin_outreach_operations", - "linkedin_outreach_warmup", - "linkedin_post_monitoring", - "linkedin_post_writing", - "message_tagging", - "mixed_channel_outreach", - "news_scan_predictleads", - "news_scan_tavily", - "news_scan_theirstack", - "posthog_setup", - "prospecting_agent", - "scheduling_calendar_events", - "segment_tagging", - "social_listening", - "trigger_code", - "weekly_sales_review" -]New value: +[ + "blog_content_calendar", + "blog_post_writing", + "company_monitor", + "email_inbox_management", + "email_sequence_setup", + "exa_find_people", + "find_companies_news_scan", + "find_companies_news_scan_setup", + "find_companies_search_criteria", + "find_companies_webset_mode", + "find_people", + "find_warm_intro_paths", + "linkedin_flowless_campaigns", + "linkedin_outreach", + "linkedin_outreach_debugging", + "linkedin_outreach_finding_leads", + "linkedin_outreach_operations", + "linkedin_outreach_warmup", + "linkedin_post_monitoring", + "linkedin_post_writing", + "message_tagging", + "mixed_channel_outreach", + "news_scan_predictleads", + "news_scan_tavily", + "news_scan_theirstack", + "posthog_setup", + "prospecting_agent", + "scheduling_calendar_events", + "segment_tagging", + "trigger_code", + "weekly_sales_review" +]
- Removed
get_social_listening_config - Changed
get_tag_cross_tab2 fields changed- changed
Input schema / properties / channel / anyOfPrevious value: -[ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "An outreach channel.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_tool_connect_url1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_user_memory1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
get_writing_style_guide2 fields changed- changed
Input schema / properties / channel / enumPrevious value: -[ - "linkedin_dm", - "email", - "linkedin_comment", - "reddit_comment" -]New value: +[ + "linkedin_dm", + "email", + "linkedin_comment" +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
google_news_search1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
import_linkedin_history1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
list_agency_clients1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
list_agents1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
list_attachments1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
list_classifiable_campaigns1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
list_message_tag_groups1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
list_prospect_events2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Read a consented teammate's activity instead of your own —\npass their email. Gated on that teammate's conversation-sharing\nsetting; a teammate who hasn't shared is rejected. Omit for your own." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Read a consented teammate's activity instead of your own —\npass their email. Gated on that teammate's conversation-sharing\nsetting; a teammate who hasn't shared is rejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
list_segment_groups1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
list_sent_messages1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
list_team_shared_agents1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
list_teammates1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
look_up_imported_people1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
manage_email_outreach_queue2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Act on a consented teammate's email queue instead of your own —\npass their email. Covers approve / cancel / update / status; a bulk\napprove-all / cancel_all must name one of the teammate's campaigns via\nagent_id. Gated on that teammate's act-on-behalf setting; a teammate who\nhasn't granted it is rejected. Omit for your own." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Act on a consented teammate's email queue instead of your own —\npass their email. Covers approve / cancel / update / status; a bulk\napprove-all / cancel_all must name one of the teammate's campaigns via\nagent_id. Gated on that teammate's act-on-behalf setting; a teammate who\nhasn't granted it is rejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
manage_inbox2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Act on a consented teammate's inbox instead of your own — pass\ntheir email. Every action (label, archive, mark read, unsubscribe) runs\nagainst their mailbox. Gated on that teammate's act-on-behalf setting\n(stricter than conversation sharing); a teammate who hasn't granted it is\nrejected. Omit for your own inbox." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Act on a consented teammate's inbox instead of your own — pass\ntheir email. Every action (label, archive, mark read, unsubscribe) runs\nagainst their mailbox. Gated on that teammate's act-on-behalf setting\n(stricter than conversation sharing); a teammate who hasn't granted it is\nrejected. Omit for your own inbox.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
manage_linkedin_invite_queue3 fields changed- changed
Input schema / properties / action_type / anyOfPrevious value: -[ - { - "enum": [ - "resolve", - "connection_request", - "message", - "inmail", - "follow", - "comment", - "reaction", - "withdrawal" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "resolve", + "connection_request", + "message", + "inmail", + "follow", + "comment", + "reaction", + "withdrawal" + ], + "title": "LinkedInQueueActionType", + "type": "string" + }, + { + "type": "null" + } +] - removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Act on a consented teammate's queue instead of your own — pass\ntheir email. Covers approve / cancel / update / status; pause and resume\n(account-wide controls) are not available on a teammate's behalf. A bulk\napprove-all / cancel_all must name one of the teammate's campaigns via\nagent_id. Gated on that teammate's act-on-behalf setting; a teammate who\nhasn't granted it is rejected. Omit for your own." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Act on a consented teammate's queue instead of your own — pass\ntheir email. Covers approve / cancel / update / status; pause and resume\n(account-wide controls) are not available on a teammate's behalf. A bulk\napprove-all / cancel_all must name one of the teammate's campaigns via\nagent_id. Gated on that teammate's act-on-behalf setting; a teammate who\nhasn't granted it is rejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
mark_monitor_events_notified1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Removed
mark_social_posts_notified - Changed
matches_icp1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
move_prospect_to_node1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_agent_people1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_analytics1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_companies1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_crm_tasks1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_deals1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_linkedin_post_engagements1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_linkedin_posts1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_monitored_companies1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_monitored_posts1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_people1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_prospect_research1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_prospects2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Read a consented teammate's prospects instead of your own —\npass their email. Gated on that teammate's conversation-sharing\nsetting; a teammate who hasn't shared is rejected. Any `agent_id` must\nbe one of that teammate's agents. Omit for your own." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Read a consented teammate's prospects instead of your own —\npass their email. Gated on that teammate's conversation-sharing\nsetting; a teammate who hasn't shared is rejected. Any `agent_id` must\nbe one of that teammate's agents. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
query_search_results1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_search_runs2 fields changed- changed
Input schema / properties / entity_type / anyOfPrevious value: -[ - { - "enum": [ - "person", - "company" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "person", + "company" + ], + "title": "EntityType", + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_segment_people2 fields changed- changed
Input schema / properties / channel / anyOfPrevious value: -[ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "An outreach channel.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
query_tagged_messages2 fields changed- changed
Input schema / properties / channel / anyOfPrevious value: -[ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "An outreach channel.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
queue_gift2 fields changed- changed
Input schema / properties / max_price_usd / descriptionPrevious value: -"The most the order may cost, in whole US dollars."New value: +"The most the item may cost including tax and shipping, in whole US\ndollars. Pass a budget the user names for the gift as is, even one they call\nall-in, and tell them the fees come on top of it: a lower limit can make the order\nfail for the item they picked, so lowering it is their call." - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
queue_linkedin_post_engagement2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Queue the engagement on a consented teammate's account instead\nof your own — pass their email. It lands as a standalone (account-level)\nengagement on their queue, not tied to any of your agents. Gated on that\nteammate's act-on-behalf setting; a teammate who hasn't granted it is\nrejected. Omit for your own." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Queue the engagement on a consented teammate's account instead\nof your own — pass their email. It lands as a standalone (account-level)\nengagement on their queue, not tied to any of your agents. Gated on that\nteammate's act-on-behalf setting; a teammate who hasn't granted it is\nrejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
read_attachment1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
read_email_attachment1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
record_search_results3 fields changed- changed
Input schema / properties / entity_type / enumPrevious value: -[ - "company", - "person" -]New value: +[ + "person", + "company" +] - added
Input schema / properties / entity_type / titleAdded value: +"EntityType" - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
register_manual_linkedin_invites1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
remove_trigger1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
report_enactment_blocked1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
request_user_action1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
resolve_date1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
retry_blocked_enactment1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
save_memory1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
save_prospect_research2 fields changed- added
Input schema / properties / findings / items / properties / subject_kind / titleAdded value: +"EntityType" - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
search_calendar1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
search_chat_history1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
search_emails2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Read a consented teammate's emails instead of your own — pass their email. Gated on that teammate's conversation-sharing setting; a teammate who hasn't shared is rejected. Omit for your own." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Read a consented teammate's emails instead of your own — pass their email. Gated on that teammate's conversation-sharing setting; a teammate who hasn't shared is rejected. Omit for your own. Or pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
search_linkedin_connections1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
search_linkedin_message_history2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Read a consented teammate's LinkedIn message history\ninstead of your own — pass their email. Gated on that teammate's\nconversation-sharing setting; a teammate who hasn't shared is\nrejected. Omit for your own." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Read a consented teammate's LinkedIn message history\ninstead of your own — pass their email. Gated on that teammate's\nconversation-sharing setting; a teammate who hasn't shared is\nrejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
search_linkedin_people1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
search_meetings1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
search_monitor_events1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
search_news_events1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Removed
search_social_posts - Changed
send_email2 fields changed- removed
Input schema / properties / as_teammateRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Send a draft you created on a consented teammate's behalf —\npass the SAME email you passed to draft_reply. The send goes out from\ntheir mailbox. Gated on that teammate's act-on-behalf setting; a\nteammate who hasn't granted it is rejected. Omit to send your own draft." -} - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Send a draft you created on a consented teammate's behalf —\npass the SAME email you passed to draft_reply. The send goes out from\ntheir mailbox. Gated on that teammate's act-on-behalf setting; a\nteammate who hasn't granted it is rejected. Omit to send your own draft.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply." +}
- Changed
send_luma_invites1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
set_linkedin_daily_limits1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
set_linkedin_warmup1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
set_outreach_approval1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
setup_email_sequence1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
setup_linkedin_monitoring2 fields changed- added
Input schema / properties / mode / titleAdded value: +"DiscoveryMode" - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
setup_linkedin_sequence2 fields changed- changed
Input schema / properties / linkedin_api / anyOfPrevious value: -[ - { - "enum": [ - "classic", - "sales_navigator", - "recruiter" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "classic", + "sales_navigator", + "recruiter" + ], + "title": "LinkedInApi", + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Removed
setup_social_listening - Changed
start_find_search2 fields changed- added
Input schema / properties / entity_type / titleAdded value: +"EntityType" - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
stop_linkedin_monitoring1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
theirstack_search1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
track_monitored_companies1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
track_prospects1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
update_agent1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
update_calendar_event1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
update_crm_task1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
update_deal2 fields changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +} - changed
Input schema / properties / status / anyOfPrevious value: -[ - { - "enum": [ - "open", - "won", - "lost" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "open", + "won", + "lost" + ], + "title": "Status", + "type": "string" + }, + { + "type": "null" + } +]
- Changed
update_message_tag1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
update_message_tag_group3 fields changed- changed
Input schema / properties / criteria / anyOfPrevious value: -[ - { - "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", - "properties": { - "agent_ids": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "channel": { - "anyOf": [ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "positions": { - "anyOf": [ - { - "items": { - "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the other person's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", - "enum": [ - "first", - "follow_up", - "reply" - ], - "title": "Position", - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "senders": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "title": "Criteria", - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", + "properties": { + "agent_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "channel": { + "anyOf": [ + { + "description": "An outreach channel.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "positions": { + "anyOf": [ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the other person's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "senders": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "Criteria", + "type": "object" + }, + { + "type": "null" + } +] - changed
Input schema / properties / mode / anyOfPrevious value: -[ - { - "enum": [ - "single", - "multi" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "single", + "multi" + ], + "title": "Mode", + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
update_monitored_company2 fields changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +} - changed
Input schema / properties / stage / anyOfPrevious value: -[ - { - "enum": [ - "active", - "removed" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "active", + "removed" + ], + "title": "Stage", + "type": "string" + }, + { + "type": "null" + } +]
- Changed
update_node2 fields changed- changed
Input schema / properties / node / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", - "properties": { - "accepted": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - }, - "already_connected": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "connection_request", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "maxLength": 300, - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "timeout": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "id", - "label", - "kind", - "accepted", - "already_connected" - ], - "title": "ConnectionRequestNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", - "properties": { - "action": { - "enum": [ - "message", - "inmail", - "email" - ], - "type": "string" - }, - "channel": { - "enum": [ - "linkedin", - "email" - ], - "type": "string" - }, - "follow_up": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "send", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "replied": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "replied" - ], - "title": "SendNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", - "properties": { - "action": { - "enum": [ - "follow", - "withdraw", - "resolve", - "comment", - "reaction" - ], - "type": "string" - }, - "channel": { - "const": "linkedin", - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "action", - "type": "string" - }, - "label": { - "type": "string" - }, - "notify": { - "default": false, - "type": "boolean" - }, - "then": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "then" - ], - "title": "SilentActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the order may cost, in whole US dollars) and `gift_note` (at\nmost 240 characters) are the step's optional defaults, pre-filled on every card. No\nchannel and no reply to route; `then` is its single advance once the gift is delivered:\nbare (`then.after=None`) to move on at delivery, or held to wait that long after it.", - "properties": { - "gift_note": { - "anyOf": [ - { - "maxLength": 240, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "gift", - "type": "string" - }, - "label": { - "type": "string" - }, - "max_price_usd": { - "anyOf": [ - { - "exclusiveMinimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "product_url": { - "anyOf": [ - { - "format": "uri", - "maxLength": 2083, - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "then": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "GiftNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", - "properties": { - "action_description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "manual", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "action_description", - "then" - ], - "title": "ManualActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "automation", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "AutomationNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", - "properties": { - "arms": { - "items": { - "additionalProperties": false, - "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", - "properties": { - "case": { - "minLength": 1, - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "case", - "to" - ], - "title": "Arm", - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "decision", - "type": "string" - }, - "label": { - "type": "string" - }, - "rule": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "rule", - "arms" - ], - "title": "DecisionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "terminal", - "type": "string" - }, - "label": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind" - ], - "title": "TerminalNode", - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", + "properties": { + "accepted": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + }, + "already_connected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "connection_request", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "maxLength": 300, + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "timeout": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "label", + "kind", + "accepted", + "already_connected" + ], + "title": "ConnectionRequestNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", + "properties": { + "action": { + "enum": [ + "message", + "inmail", + "email" + ], + "type": "string" + }, + "channel": { + "description": "An outreach channel.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + "follow_up": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "send", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "replied": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "replied" + ], + "title": "SendNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", + "properties": { + "action": { + "enum": [ + "follow", + "withdraw", + "resolve", + "comment", + "reaction" + ], + "type": "string" + }, + "channel": { + "const": "linkedin", + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "action", + "type": "string" + }, + "label": { + "type": "string" + }, + "notify": { + "default": false, + "type": "boolean" + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "then" + ], + "title": "SilentActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the item may cost including tax and shipping, in whole US\ndollars) and `gift_note` (at most 240 characters) are the step's optional defaults,\npre-filled on every card. No channel and no reply to route; `then` is its single advance\nonce the gift is delivered: bare (`then.after=None`) to move on at delivery, or held to\nwait that long after it.", + "properties": { + "gift_note": { + "anyOf": [ + { + "maxLength": 240, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "gift", + "type": "string" + }, + "label": { + "type": "string" + }, + "max_price_usd": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "product_url": { + "anyOf": [ + { + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "GiftNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", + "properties": { + "action_description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "manual", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "action_description", + "then" + ], + "title": "ManualActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "automation", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "AutomationNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", + "properties": { + "arms": { + "items": { + "additionalProperties": false, + "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", + "properties": { + "case": { + "minLength": 1, + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "case", + "to" + ], + "title": "Arm", + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "decision", + "type": "string" + }, + "label": { + "type": "string" + }, + "rule": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "rule", + "arms" + ], + "title": "DecisionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "terminal", + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind" + ], + "title": "TerminalNode", + "type": "object" + } +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
update_onboarding_progress1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
update_prospect2 fields changed- added
Input schema / properties / channel / titleAdded value: +"Channel" - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
update_segment_group2 fields changed- changed
Input schema / properties / mode / anyOfPrevious value: -[ - { - "enum": [ - "single", - "multi" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "single", + "multi" + ], + "title": "Mode", + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
update_segment_tag1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
- Changed
update_trigger2 fields changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +} - changed
Input schema / properties / status / anyOfPrevious value: -[ - { - "enum": [ - "active", - "paused" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "active", + "paused" + ], + "title": "Status", + "type": "string" + }, + { + "type": "null" + } +]
- Changed
update_workspace1 field changed- added
Input schema / properties / on_behalf_ofAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account." +}
6 tool updates
- Changed
define_sequence2 fields changed- changed
Input schema / properties / sequence / properties / nodes / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", - "properties": { - "accepted": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - }, - "already_connected": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "connection_request", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "maxLength": 300, - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "timeout": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "id", - "label", - "kind", - "accepted", - "already_connected" - ], - "title": "ConnectionRequestNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", - "properties": { - "action": { - "enum": [ - "message", - "inmail", - "email" - ], - "type": "string" - }, - "channel": { - "enum": [ - "linkedin", - "email" - ], - "type": "string" - }, - "follow_up": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "send", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "replied": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "replied" - ], - "title": "SendNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", - "properties": { - "action": { - "enum": [ - "follow", - "withdraw", - "resolve", - "comment", - "reaction" - ], - "type": "string" - }, - "channel": { - "const": "linkedin", - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "action", - "type": "string" - }, - "label": { - "type": "string" - }, - "notify": { - "default": false, - "type": "boolean" - }, - "then": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "then" - ], - "title": "SilentActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the order may cost, in whole US dollars) and `gift_note` (at\nmost 240 characters) are the step's optional defaults, pre-filled on every card. No\nchannel and no reply to route; `then` is its single advance once the gift is delivered:\nbare (`then.after=None`) to move on at delivery, or held to wait that long after it.", - "properties": { - "gift_note": { - "anyOf": [ - { - "maxLength": 240, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "gift", - "type": "string" - }, - "label": { - "type": "string" - }, - "max_price_usd": { - "anyOf": [ - { - "exclusiveMinimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "product_url": { - "anyOf": [ - { - "format": "uri", - "maxLength": 2083, - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "then": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "GiftNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", - "properties": { - "action_description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "manual", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "action_description", - "then" - ], - "title": "ManualActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "automation", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "AutomationNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", - "properties": { - "arms": { - "items": { - "additionalProperties": false, - "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", - "properties": { - "case": { - "minLength": 1, - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "case", - "to" - ], - "title": "Arm", - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "decision", - "type": "string" - }, - "label": { - "type": "string" - }, - "rule": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "rule", - "arms" - ], - "title": "DecisionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "terminal", - "type": "string" - }, - "label": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind" - ], - "title": "TerminalNode", - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", + "properties": { + "accepted": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + }, + "already_connected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "connection_request", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "maxLength": 300, + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "timeout": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "label", + "kind", + "accepted", + "already_connected" + ], + "title": "ConnectionRequestNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", + "properties": { + "action": { + "enum": [ + "message", + "inmail", + "email" + ], + "type": "string" + }, + "channel": { + "enum": [ + "linkedin", + "email" + ], + "type": "string" + }, + "follow_up": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "send", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "replied": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "replied" + ], + "title": "SendNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", + "properties": { + "action": { + "enum": [ + "follow", + "withdraw", + "resolve", + "comment", + "reaction" + ], + "type": "string" + }, + "channel": { + "const": "linkedin", + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "action", + "type": "string" + }, + "label": { + "type": "string" + }, + "notify": { + "default": false, + "type": "boolean" + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "then" + ], + "title": "SilentActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the order may cost, in whole US dollars) and `gift_note` (at\nmost 240 characters) are the step's optional defaults, pre-filled on every card. No\nchannel and no reply to route; `then` is its single advance once the gift is delivered:\nbare (`then.after=None`) to move on at delivery, or held to wait that long after it.", + "properties": { + "gift_note": { + "anyOf": [ + { + "maxLength": 240, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "gift", + "type": "string" + }, + "label": { + "type": "string" + }, + "max_price_usd": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "product_url": { + "anyOf": [ + { + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "GiftNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", + "properties": { + "action_description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "manual", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "action_description", + "then" + ], + "title": "ManualActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "automation", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "AutomationNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", + "properties": { + "arms": { + "items": { + "additionalProperties": false, + "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", + "properties": { + "case": { + "minLength": 1, + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "case", + "to" + ], + "title": "Arm", + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "decision", + "type": "string" + }, + "label": { + "type": "string" + }, + "rule": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "rule", + "arms" + ], + "title": "DecisionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "terminal", + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind" + ], + "title": "TerminalNode", + "type": "object" + } +] - changed
Input schema / properties / sequence / properties / start / properties / entry / descriptionPrevious value: -"An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`."New value: +"An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node."
- Changed
get_pending_approvals1 field changed- added
Input schema / properties / agent_idAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Scope every section to one agent's items. Omit for every agent." +}
- Added
list_agency_clients - Removed
search_trends - Changed
start_find_search4 fields changed- changed
Input schema / properties / people_include_alumni / descriptionPrevious value: -"Include past employees (alumni) alongside current\nones in the attached people block (default false). Company lists only."New value: +"Include past employees (alumni) alongside current\nones in the people search (default false). Company lists only." - changed
Input schema / properties / people_preferences / descriptionPrevious value: -"Soft preferences and exclusions for the attached\npeople. Each is checked against them, where only an exclusion rules\nsomeone out; the kept people are ordered most senior first. Company\nlists only."New value: +"Soft preferences and exclusions for the people\nfound. Each is checked against them, where only an exclusion rules\nsomeone out. Company lists only." - changed
Input schema / properties / people_query / descriptionPrevious value: -"For company lists, a positive-form query for the people to\nattach as a `data.people[]` block on each company row — controls which\npeople come back. Company lists only; skipped on person lists."New value: +"For company lists, a positive-form query for the people to\nfind at each new company that the check against `criteria` doesn't\nreject — controls which people come back. They're saved as person rows\non the '<list_name>-people' list when the search finishes. Company lists\nonly; skipped on person lists." - changed
Input schema / properties / people_titles / descriptionPrevious value: -"The roles `people_query` is asking for, as title keywords,\ne.g. ['Marketing', 'Growth'] or ['Operations', 'Chief']. Each is matched\nas a case-insensitive substring of a person's job title and the list is\nOR-ed, so use the distinctive word a title is built from rather than a\nwhole title: companies word the same job many ways, and a full phrase\nfinds only the ones that word it exactly as written here. Broader words\nrecall more people and admit more adjacent roles, so pick the narrowest\nword that still covers the variants. Set it whenever `people_query`\nnames roles — the attachment runs unattended, so this is its only\nstructured read of the roles you asked for. Company lists only."New value: +"The roles `people_query` is asking for, as title keywords,\ne.g. ['Marketing', 'Growth'] or ['Operations', 'Chief']. Each is matched\nas a case-insensitive substring of a person's job title and the list is\nOR-ed, so use the distinctive word a title is built from rather than a\nwhole title: companies word the same job many ways, and a full phrase\nfinds only the ones that word it exactly as written here. Broader words\nrecall more people and admit more adjacent roles, so pick the narrowest\nword that still covers the variants. Set it whenever `people_query`\nnames roles — the people search runs unattended, so this is its only\nstructured read of the roles you asked for. Company lists only."
- Changed
update_node1 field changed- changed
Input schema / properties / node / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", - "properties": { - "accepted": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - }, - "already_connected": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "connection_request", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "maxLength": 300, - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "timeout": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "id", - "label", - "kind", - "accepted", - "already_connected" - ], - "title": "ConnectionRequestNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", - "properties": { - "action": { - "enum": [ - "message", - "inmail", - "email" - ], - "type": "string" - }, - "channel": { - "enum": [ - "linkedin", - "email" - ], - "type": "string" - }, - "follow_up": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "send", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "replied": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "replied" - ], - "title": "SendNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", - "properties": { - "action": { - "enum": [ - "follow", - "withdraw", - "resolve", - "comment", - "reaction" - ], - "type": "string" - }, - "channel": { - "const": "linkedin", - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "action", - "type": "string" - }, - "label": { - "type": "string" - }, - "notify": { - "default": false, - "type": "boolean" - }, - "then": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "then" - ], - "title": "SilentActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the order may cost, in whole US dollars) and `gift_note` (at\nmost 240 characters) are the step's optional defaults, pre-filled on every card. No\nchannel and no reply to route; `then` is its single advance once the gift is delivered:\nbare (`then.after=None`) to move on at delivery, or held to wait that long after it.", - "properties": { - "gift_note": { - "anyOf": [ - { - "maxLength": 240, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "gift", - "type": "string" - }, - "label": { - "type": "string" - }, - "max_price_usd": { - "anyOf": [ - { - "exclusiveMinimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "product_url": { - "anyOf": [ - { - "format": "uri", - "maxLength": 2083, - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "then": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "GiftNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", - "properties": { - "action_description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "manual", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "action_description", - "then" - ], - "title": "ManualActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "automation", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "AutomationNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", - "properties": { - "arms": { - "items": { - "additionalProperties": false, - "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", - "properties": { - "case": { - "minLength": 1, - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "case", - "to" - ], - "title": "Arm", - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "decision", - "type": "string" - }, - "label": { - "type": "string" - }, - "rule": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "rule", - "arms" - ], - "title": "DecisionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "terminal", - "type": "string" - }, - "label": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind" - ], - "title": "TerminalNode", - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", + "properties": { + "accepted": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + }, + "already_connected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "connection_request", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "maxLength": 300, + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "timeout": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "label", + "kind", + "accepted", + "already_connected" + ], + "title": "ConnectionRequestNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", + "properties": { + "action": { + "enum": [ + "message", + "inmail", + "email" + ], + "type": "string" + }, + "channel": { + "enum": [ + "linkedin", + "email" + ], + "type": "string" + }, + "follow_up": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "send", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "replied": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "replied" + ], + "title": "SendNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", + "properties": { + "action": { + "enum": [ + "follow", + "withdraw", + "resolve", + "comment", + "reaction" + ], + "type": "string" + }, + "channel": { + "const": "linkedin", + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "action", + "type": "string" + }, + "label": { + "type": "string" + }, + "notify": { + "default": false, + "type": "boolean" + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "then" + ], + "title": "SilentActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the order may cost, in whole US dollars) and `gift_note` (at\nmost 240 characters) are the step's optional defaults, pre-filled on every card. No\nchannel and no reply to route; `then` is its single advance once the gift is delivered:\nbare (`then.after=None`) to move on at delivery, or held to wait that long after it.", + "properties": { + "gift_note": { + "anyOf": [ + { + "maxLength": 240, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "gift", + "type": "string" + }, + "label": { + "type": "string" + }, + "max_price_usd": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "product_url": { + "anyOf": [ + { + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "GiftNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", + "properties": { + "action_description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "manual", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "action_description", + "then" + ], + "title": "ManualActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "automation", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "AutomationNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", + "properties": { + "arms": { + "items": { + "additionalProperties": false, + "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", + "properties": { + "case": { + "minLength": 1, + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "case", + "to" + ], + "title": "Arm", + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "decision", + "type": "string" + }, + "label": { + "type": "string" + }, + "rule": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "rule", + "arms" + ], + "title": "DecisionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "terminal", + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind" + ], + "title": "TerminalNode", + "type": "object" + } +]
4 tool updates
- Changed
exa_find_people1 field changed- changed
Input schema / properties / searches / items / properties / company_domain / descriptionPrevious value: -"The company's website domain, e.g. 'acme.com'. Pass it whenever you have it — a domain names one employer, where a company name matches every company written that way, and same-name companies are the main source of wrong-company results. A company row's `identifier` holds one; pass it as stored, whether that is a bare host or a full URL."New value: +"The company's website domain, e.g. 'acme.com'. Pass it whenever you have it — a domain names one employer, where a company name matches every company written that way, and same-name companies are the main source of wrong-company results. A company row's `identifier` holds its domain; pass it as stored."
- Changed
manage_email_outreach_queue2 fields changed- added
Input schema / properties / ab_versionAdded value: +{ + "anyOf": [ + { + "description": "Which version of a step's A/B test a queued draft counts under, or LEFT_OUT of the test.", + "enum": [ + "A", + "B", + "left_out" + ], + "title": "DraftVersion", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Required for 'update' of a draft in an A/B test — the version the edited\ndraft now reads as: 'A', 'B', or 'left_out' when it fits neither, so its send isn't\ncounted in the test; its current version when the edit doesn't change that. Omit\nfor a draft not in a test." +} - changed
Input schema / properties / action / descriptionPrevious value: -"One of:\n- 'status': Get full queue details (pending/sent/failed items). The\n `pending_items` / `pending_approval_items` lists cap at 50 each, each\n with a `pending_truncated` / `pending_approval_truncated` flag — when a\n list's flag is True, pass `offset` (offset += 50) to read the next page.\n Each item carries the `mailbox` it sends from; the account-level\n `mailboxes` list gives each mailbox's remaining daily headroom. The\n top-level `cancelled_by_kind` maps each cancel cause to its count\n (`recipient_replied`, `task_deleted`, `auto_shelved`, …; pre-taxonomy\n rows under `unknown`) — read it to answer \"how much outreach was\n cancelled, and why?\".\n- 'update': Edit a queued email's subject and/or body. Requires recipient_email.\n Pass subject and/or body to change — no cancel-and-re-queue needed. Send order\n is derived, not per-row scheduled; to pause or resume the campaign use\n update_agent (see the note above).\n- 'approve': Send authorization belongs to the user, so this action\n needs a message that arrived after the drafts were queued and\n explicitly says to approve or send them. A message from before the\n drafts existed cannot authorize them — a request to review them,\n to approve them later, or to be able to approve from chat means:\n report that the queue is awaiting approval and end your turn; the\n user's next message decides. Once authorized: if recipient_email\n is given, approve only that item, otherwise approve ALL\n pending_approval items. From account-level chat an approve-all is\n rejected unless agent_id names the campaign — it must not fire\n every campaign's drafts into sending at once.\n- 'cancel': Cancel a queued email. Pass recipient_email to cancel that one\n person. To cancel the ENTIRE pending queue — which drops drafts the user\n already approved — you must pass\n cancel_all=True; an unscoped cancel without it is rejected. From\n account-level chat a cancel_all is rejected unless agent_id names the campaign\n to cancel — it must not clear every campaign's queue at once."New value: +"One of:\n- 'status': Get full queue details (pending/sent/failed items). The\n `pending_items` / `pending_approval_items` lists cap at 50 each, each\n with a `pending_truncated` / `pending_approval_truncated` flag — when a\n list's flag is True, pass `offset` (offset += 50) to read the next page.\n Each item carries the `mailbox` it sends from; the account-level\n `mailboxes` list gives each mailbox's remaining daily headroom. The\n top-level `cancelled_by_kind` maps each cancel cause to its count\n (`recipient_replied`, `task_deleted`, `auto_shelved`, …; pre-taxonomy\n rows under `unknown`) — read it to answer \"how much outreach was\n cancelled, and why?\".\n- 'update': Edit a queued email's subject and/or body. Requires recipient_email.\n Pass subject and/or body to change — no cancel-and-re-queue needed. Send order\n is derived, not per-row scheduled; to pause or resume the campaign use\n update_agent (see the note above). A draft on a step running an A/B test\n carries `ab_version` ('A', 'B', or 'left_out' when its send won't count) on its\n status row and in this action's result; updating one takes ab_version too — the\n version the edited draft reads as — or ab_version alone to re-file it unedited.\n- 'approve': Send authorization belongs to the user, so this action\n needs a message that arrived after the drafts were queued and\n explicitly says to approve or send them. A message from before the\n drafts existed cannot authorize them — a request to review them,\n to approve them later, or to be able to approve from chat means:\n report that the queue is awaiting approval and end your turn; the\n user's next message decides. Once authorized: if recipient_email\n is given, approve only that item, otherwise approve ALL\n pending_approval items. From account-level chat an approve-all is\n rejected unless agent_id names the campaign — it must not fire\n every campaign's drafts into sending at once.\n- 'cancel': Cancel a queued email. Pass recipient_email to cancel that one\n person. To cancel the ENTIRE pending queue — which drops drafts the user\n already approved — you must pass\n cancel_all=True; an unscoped cancel without it is rejected. From\n account-level chat a cancel_all is rejected unless agent_id names the campaign\n to cancel — it must not clear every campaign's queue at once."
- Changed
manage_linkedin_invite_queue2 fields changed- added
Input schema / properties / ab_versionAdded value: +{ + "anyOf": [ + { + "description": "Which version of a step's A/B test a queued draft counts under, or LEFT_OUT of the test.", + "enum": [ + "A", + "B", + "left_out" + ], + "title": "DraftVersion", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Required for 'update' of a draft in an A/B test — the version the edited\ndraft now reads as: 'A', 'B', or 'left_out' when it fits neither, so its send isn't\ncounted in the test; its current version when the edit doesn't change that. Omit\nfor a draft not in a test." +} - changed
Input schema / properties / action / descriptionPrevious value: -"One of:\n- 'status': Get full queue details (pending/sent/failed items with names and times).\n The `pending_items` / `pending_approval_items` / `failed_items` lists cap at 50\n each, each with a matching `*_truncated` flag — when a list's flag is True, more\n rows exist past this page; pass `offset` (offset += 50) to read them. Each\n `failed_items` row carries `last_error`, the reason that send failed — read it to\n answer \"why wasn't X sent?\". `pending_approval_items` rows also carry `last_error`\n when the row was held for a reason beyond plain approval gating (e.g. the\n resolver's name-mismatch hold). Queued post comments and reactions carry `post_url`\n and a `post_text` excerpt of the post the action targets, so you can tell which\n post a queued comment is on; `post_text_truncated` True means the target post ran\n longer than the excerpt shown. All four lists share one field shape; a\n `resolving_items` row (profile not yet resolved) has a blank `target_provider_id`\n and its `message` is the copy that will send once it resolves — edit or cancel it\n by its `target_identifier`. Every `pending_items` / `resolving_items` row carries\n `projected_send_day` + `projected_send_time` (a resolving row's is its projected\n lookup time, or its projected send time once it's a resolve-then-send) and a\n `queue_position` — the row's place in the one ordered lookup+send timeline, so the\n two lists interleave into what actually fires next. The top-level `cancelled_by_kind` maps each cancel\n cause to its count (`recipient_replied`, `task_deleted`, `auto_shelved`, …;\n pre-taxonomy rows under `unknown`) — read it to answer \"how much outreach was\n cancelled, and why?\".\n- 'update': Update a queued item's message text. Name the item by\n target_provider_id (a pending row's pid) OR, for a resolving row that has no\n pid yet, by target_identifier (its URL/slug, shown on the row). Use this to\n edit a queued message — including on resolving rows, which still hold the\n message that will send once their profile resolves. When a target has more\n than one queued action — a post with both a comment and a reaction, or a\n person with a connection request and a follow — also pass action_type to name\n which one to edit.\n- 'approve': Send authorization belongs to the user, so this action needs a\n message that arrived after the drafts were queued and explicitly says to\n approve or send them. A message from before the drafts existed cannot\n authorize them — a request to review them, to approve them later, or to be\n able to approve from chat means: report that the queue is awaiting approval\n and end your turn; the user's next message decides. Once authorized, this\n moves pending_approval items to 'pending'; they then send in queue order at\n your account's pace. If\n target_provider_id is given, approve only that item. Otherwise approve ALL\n pending_approval items (optionally filtered by action_type).\n Only touches items already awaiting approval — it does NOT run pending profile\n lookups or pre-approve resolving rows (those aren't in the approvals UI, so\n nobody has reviewed them; they resolve on their own schedule and, if the\n user's settings gate the action, surface for approval once resolved).\n From account-level chat an approve-all is rejected unless agent_id names the\n campaign — it must not fire every campaign's drafts into sending at once.\n- 'cancel': Cancel a queued item. Pass target_provider_id to cancel that one\n person or post — every queued action for them — or target_identifier to cancel\n a resolving row that has no pid yet. To cancel the ENTIRE pending\n queue — which drops drafts the user\n already approved — you must pass\n cancel_all=True; an unscoped cancel without it is rejected (an action_type\n or node_id filter alone is still a bulk cancel and also needs cancel_all=True).\n Narrow a cancel_all to one flow stage with node_id (e.g. restart only the m2\n messages without touching m3) — the only way to separate two stages that share\n an action_type. From account-level chat a cancel_all is rejected unless agent_id\n names the campaign to cancel — it must not clear every campaign's queue at once.\n- 'pause': Pause the queue (stops sending, keeps items queued). Pass resume_at\n to schedule an automatic resume (\"pause while I'm on vacation, resume July 20\");\n without it the pause is indefinite and only an explicit 'resume' restarts sending.\n- 'resume': Resume the queue (pending items drain in queue order at your account's pace)"New value: +"One of:\n- 'status': Get full queue details (pending/sent/failed items with names and times).\n The `pending_items` / `pending_approval_items` / `failed_items` lists cap at 50\n each, each with a matching `*_truncated` flag — when a list's flag is True, more\n rows exist past this page; pass `offset` (offset += 50) to read them. Each\n `failed_items` row carries `last_error`, the reason that send failed — read it to\n answer \"why wasn't X sent?\". `pending_approval_items` rows also carry `last_error`\n when the row was held for a reason beyond plain approval gating (e.g. the\n resolver's name-mismatch hold). Queued post comments and reactions carry `post_url`\n and a `post_text` excerpt of the post the action targets, so you can tell which\n post a queued comment is on; `post_text_truncated` True means the target post ran\n longer than the excerpt shown. All four lists share one field shape; a\n `resolving_items` row (profile not yet resolved) has a blank `target_provider_id`\n and its `message` is the copy that will send once it resolves — edit or cancel it\n by its `target_identifier`. Every `pending_items` / `resolving_items` row carries\n `projected_send_day` + `projected_send_time` (a resolving row's is its projected\n lookup time, or its projected send time once it's a resolve-then-send) and a\n `queue_position` — the row's place in the one ordered lookup+send timeline, so the\n two lists interleave into what actually fires next. The top-level `cancelled_by_kind` maps each cancel\n cause to its count (`recipient_replied`, `task_deleted`, `auto_shelved`, …;\n pre-taxonomy rows under `unknown`) — read it to answer \"how much outreach was\n cancelled, and why?\".\n- 'update': Update a queued item's message text. Name the item by\n target_provider_id (a pending row's pid) OR, for a resolving row that has no\n pid yet, by target_identifier (its URL/slug, shown on the row). Use this to\n edit a queued message — including on resolving rows, which still hold the\n message that will send once their profile resolves. When a target has more\n than one queued action — a post with both a comment and a reaction, or a\n person with a connection request and a follow — also pass action_type to name\n which one to edit. A draft on a step running an A/B test carries `ab_version`\n ('A', 'B', or 'left_out' when its send won't count) on its status row and in\n this action's result; updating one takes ab_version too — the version the\n edited draft reads as — or ab_version alone to re-file it unedited.\n- 'approve': Send authorization belongs to the user, so this action needs a\n message that arrived after the drafts were queued and explicitly says to\n approve or send them. A message from before the drafts existed cannot\n authorize them — a request to review them, to approve them later, or to be\n able to approve from chat means: report that the queue is awaiting approval\n and end your turn; the user's next message decides. Once authorized, this\n moves pending_approval items to 'pending'; they then send in queue order at\n your account's pace. If\n target_provider_id is given, approve only that item. Otherwise approve ALL\n pending_approval items (optionally filtered by action_type).\n Only touches items already awaiting approval — it does NOT run pending profile\n lookups or pre-approve resolving rows (those aren't in the approvals UI, so\n nobody has reviewed them; they resolve on their own schedule and, if the\n user's settings gate the action, surface for approval once resolved).\n From account-level chat an approve-all is rejected unless agent_id names the\n campaign — it must not fire every campaign's drafts into sending at once.\n- 'cancel': Cancel a queued item. Pass target_provider_id to cancel that one\n person or post — every queued action for them — or target_identifier to cancel\n a resolving row that has no pid yet. To cancel the ENTIRE pending\n queue — which drops drafts the user\n already approved — you must pass\n cancel_all=True; an unscoped cancel without it is rejected (an action_type\n or node_id filter alone is still a bulk cancel and also needs cancel_all=True).\n Narrow a cancel_all to one flow stage with node_id (e.g. restart only the m2\n messages without touching m3) — the only way to separate two stages that share\n an action_type. From account-level chat a cancel_all is rejected unless agent_id\n names the campaign to cancel — it must not clear every campaign's queue at once.\n- 'pause': Pause the queue (stops sending, keeps items queued). Pass resume_at\n to schedule an automatic resume (\"pause while I'm on vacation, resume July 20\");\n without it the pause is indefinite and only an explicit 'resume' restarts sending.\n- 'resume': Resume the queue (pending items drain in queue order at your account's pace)"
- Changed
start_find_search1 field changed- changed
Input schema / properties / excluded / descriptionPrevious value: -"company domains/URLs the webset must not ingest (compared by\ncanonical hostname). Use to avoid re-ingesting rows already recorded in\nthe list, e.g. seeds from find_companies_by_tech_stack."New value: +"company domains/URLs the webset must not ingest (matched by\ndomain). Use to avoid re-ingesting rows already recorded in\nthe list, e.g. seeds from find_companies_by_tech_stack."
9 tool updates
- Changed
apollo_save1 field changed- changed
Input schema / properties / items / descriptionPrevious value: -"List of dicts. For contacts: {first_name, last_name, email (REQUIRED), title, organization_name}. For accounts: {name, domain}."New value: +"List of dicts. For contacts: {first_name, last_name, email (required), title, organization_name}. For accounts: {name, domain}."
- Changed
define_sequence2 fields changed- changed
Input schema / properties / sequence / properties / nodes / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", - "properties": { - "accepted": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - }, - "already_connected": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "connection_request", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "maxLength": 300, - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "timeout": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "id", - "label", - "kind", - "accepted", - "already_connected" - ], - "title": "ConnectionRequestNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", - "properties": { - "action": { - "enum": [ - "message", - "inmail", - "email" - ], - "type": "string" - }, - "channel": { - "enum": [ - "linkedin", - "email" - ], - "type": "string" - }, - "follow_up": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "send", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "replied": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "replied" - ], - "title": "SendNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", - "properties": { - "action": { - "enum": [ - "follow", - "withdraw", - "resolve", - "comment", - "reaction" - ], - "type": "string" - }, - "channel": { - "const": "linkedin", - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "action", - "type": "string" - }, - "label": { - "type": "string" - }, - "notify": { - "default": false, - "type": "boolean" - }, - "then": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "then" - ], - "title": "SilentActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", - "properties": { - "action_description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "manual", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "action_description", - "then" - ], - "title": "ManualActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "automation", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "AutomationNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", - "properties": { - "arms": { - "items": { - "additionalProperties": false, - "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", - "properties": { - "case": { - "minLength": 1, - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "case", - "to" - ], - "title": "Arm", - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "decision", - "type": "string" - }, - "label": { - "type": "string" - }, - "rule": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "rule", - "arms" - ], - "title": "DecisionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "terminal", - "type": "string" - }, - "label": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind" - ], - "title": "TerminalNode", - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", + "properties": { + "accepted": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + }, + "already_connected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "connection_request", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "maxLength": 300, + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "timeout": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "label", + "kind", + "accepted", + "already_connected" + ], + "title": "ConnectionRequestNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", + "properties": { + "action": { + "enum": [ + "message", + "inmail", + "email" + ], + "type": "string" + }, + "channel": { + "enum": [ + "linkedin", + "email" + ], + "type": "string" + }, + "follow_up": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "send", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "replied": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "replied" + ], + "title": "SendNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", + "properties": { + "action": { + "enum": [ + "follow", + "withdraw", + "resolve", + "comment", + "reaction" + ], + "type": "string" + }, + "channel": { + "const": "linkedin", + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "action", + "type": "string" + }, + "label": { + "type": "string" + }, + "notify": { + "default": false, + "type": "boolean" + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "then" + ], + "title": "SilentActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the order may cost, in whole US dollars) and `gift_note` (at\nmost 240 characters) are the step's optional defaults, pre-filled on every card. No\nchannel and no reply to route; `then` is its single advance once the gift is delivered:\nbare (`then.after=None`) to move on at delivery, or held to wait that long after it.", + "properties": { + "gift_note": { + "anyOf": [ + { + "maxLength": 240, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "gift", + "type": "string" + }, + "label": { + "type": "string" + }, + "max_price_usd": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "product_url": { + "anyOf": [ + { + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "GiftNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", + "properties": { + "action_description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "manual", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "action_description", + "then" + ], + "title": "ManualActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "automation", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "AutomationNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", + "properties": { + "arms": { + "items": { + "additionalProperties": false, + "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", + "properties": { + "case": { + "minLength": 1, + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "case", + "to" + ], + "title": "Arm", + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "decision", + "type": "string" + }, + "label": { + "type": "string" + }, + "rule": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "rule", + "arms" + ], + "title": "DecisionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "terminal", + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind" + ], + "title": "TerminalNode", + "type": "object" + } +] - changed
Input schema / properties / sequence / properties / start / properties / entry / descriptionPrevious value: -"An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`."New value: +"An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`."
- Changed
find_email3 fields changed- changed
Input schema / properties / contacts / descriptionPrevious value: -"People to look up. Each must have name plus at least one of\n linkedin_url, company_name, or company_domain."New value: +"People to look up. Each needs a name, plus either an\n apollo_id or at least one of linkedin_url, company_name, or\n company_domain." - changed
Input schema / properties / contacts / items / descriptionPrevious value: -"A person to look up in Apollo."New value: +"A person to look up." - added
Input schema / properties / contacts / items / properties / apollo_idAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The `apollo_id` from an `apollo_search` people result. With it, `name` can be the result's masked name and no other field is needed: Apollo matches the person by this ID." +}
- Changed
get_pending_approvals2 fields changed- changed
Input schema / properties / bucket / anyOfPrevious value: -[ - { - "enum": [ - "email", - "linkedin_invite", - "linkedin_message", - "linkedin_follow", - "engagement", - "reddit", - "manual" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "email", + "linkedin_invite", + "linkedin_message", + "linkedin_follow", + "engagement", + "reddit", + "gift", + "manual" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / bucket / descriptionPrevious value: -"Drill into one bucket for its full-text items, paged. One of\n'email', 'linkedin_invite', 'linkedin_message', 'linkedin_follow',\n'engagement', 'reddit', 'manual'. Omit for the bounded counts+preview."New value: +"Drill into one bucket for its full-text items, paged. One of\n'email', 'linkedin_invite', 'linkedin_message', 'linkedin_follow',\n'engagement', 'reddit', 'gift', 'manual'. Omit for the bounded counts+preview."
- Changed
move_prospect_to_node1 field changed- changed
Input schema / properties / node_id / descriptionPrevious value: -"A connection_request / send / action / manual / automation / decision /\nterminal node id in the agent's sequence (not start)."New value: +"A connection_request / send / action / manual / gift / automation /\ndecision / terminal node id in the agent's sequence (not start)."
- Changed
query_agent_people1 field changed- changed
Input schema / properties / filters / descriptionPrevious value: -"Clauses that must all match. For \"degree\", \"outreach_stage\", \"list\" or\n\"source\", use {\"column_key\": <dimension>, \"operator\": \"is_any_of\", \"values\": [keys]}\nwith keys as group_counts returns them ('' matches no value). For \"segment\", use the\nsame shape with the segment keys above. For \"verdict\", use the same shape with any\nof 'rejected' (the people the user's default view filters out), 'qualified' and\n'unclear'; it matches each person's `verdict`, or with group_by=\"list\" their verdict\non each list, and 'rejected' brings the rejected people in without\n`include_rejected`. For \"run\", use the same shape with run ids from\n`query_search_runs` as strings; it matches the people those search runs found,\nrejected ones included. When those runs all saved to one list and group_by isn't\n\"list\", a \"verdict\" filter reads that list alone, as on its group_by=\"list\" bucket page.\nFor \"name\", \"title\", \"headline\", \"company\" or \"location\", use\n{\"column_key\": <column>, \"operator\": \"contains\", \"text\": <substring>}."New value: +"Clauses that must all match. For \"degree\", \"outreach_stage\", \"list\" or\n\"source\", use {\"column_key\": <dimension>, \"operator\": \"is_any_of\", \"values\": [keys]}\nwith keys as group_counts returns them ('' matches no value). For \"segment\", use the\nsame shape with the segment keys above. For \"verdict\", use the same shape with any\nof 'rejected' (the people the user's default view filters out, labelled \"Didn't\nmatch\" in their table), 'qualified' and 'unclear'; it matches each person's\n`verdict`, or with group_by=\"list\" their verdict on each list, and 'rejected' brings\nthe rejected people in without\n`include_rejected`. For \"run\", use the same shape with run ids from\n`query_search_runs` as strings; it matches the people those search runs found,\nrejected ones included. When those runs all saved to one list and group_by isn't\n\"list\", a \"verdict\" filter reads that list alone, as on its group_by=\"list\" bucket page.\nFor \"name\", \"title\", \"headline\", \"company\" or \"location\", use\n{\"column_key\": <column>, \"operator\": \"contains\", \"text\": <substring>}."
- Added
queue_gift - Changed
request_user_action1 field changed- changed
Input schema / properties / bucket_name / descriptionPrevious value: -"Short label naming the kind of ask (e.g. \"voice-note\",\n\"founder-gift\"). Also the third dimension of the pending-dedup key — two different buckets can\ncoexist as pending rows on the same prospect."New value: +"Short label naming the kind of ask (e.g. \"voice-note\",\n\"intro-call\"). Also the third dimension of the pending-dedup key — two different buckets can\ncoexist as pending rows on the same prospect."
- Changed
update_node1 field changed- changed
Input schema / properties / node / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", - "properties": { - "accepted": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - }, - "already_connected": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "connection_request", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "maxLength": 300, - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "timeout": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "id", - "label", - "kind", - "accepted", - "already_connected" - ], - "title": "ConnectionRequestNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", - "properties": { - "action": { - "enum": [ - "message", - "inmail", - "email" - ], - "type": "string" - }, - "channel": { - "enum": [ - "linkedin", - "email" - ], - "type": "string" - }, - "follow_up": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "send", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "replied": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "replied" - ], - "title": "SendNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", - "properties": { - "action": { - "enum": [ - "follow", - "withdraw", - "resolve", - "comment", - "reaction" - ], - "type": "string" - }, - "channel": { - "const": "linkedin", - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "action", - "type": "string" - }, - "label": { - "type": "string" - }, - "notify": { - "default": false, - "type": "boolean" - }, - "then": { - "additionalProperties": false, - "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`.", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "then" - ], - "title": "SilentActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", - "properties": { - "action_description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "manual", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "action_description", - "then" - ], - "title": "ManualActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "automation", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "AutomationNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", - "properties": { - "arms": { - "items": { - "additionalProperties": false, - "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", - "properties": { - "case": { - "minLength": 1, - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "case", - "to" - ], - "title": "Arm", - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "decision", - "type": "string" - }, - "label": { - "type": "string" - }, - "rule": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "rule", - "arms" - ], - "title": "DecisionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "terminal", - "type": "string" - }, - "label": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind" - ], - "title": "TerminalNode", - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", + "properties": { + "accepted": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + }, + "already_connected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "connection_request", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "maxLength": 300, + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "timeout": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "label", + "kind", + "accepted", + "already_connected" + ], + "title": "ConnectionRequestNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", + "properties": { + "action": { + "enum": [ + "message", + "inmail", + "email" + ], + "type": "string" + }, + "channel": { + "enum": [ + "linkedin", + "email" + ], + "type": "string" + }, + "follow_up": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "send", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "replied": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "replied" + ], + "title": "SendNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", + "properties": { + "action": { + "enum": [ + "follow", + "withdraw", + "resolve", + "comment", + "reaction" + ], + "type": "string" + }, + "channel": { + "const": "linkedin", + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "action", + "type": "string" + }, + "label": { + "type": "string" + }, + "notify": { + "default": false, + "type": "boolean" + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "then" + ], + "title": "SilentActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the order may cost, in whole US dollars) and `gift_note` (at\nmost 240 characters) are the step's optional defaults, pre-filled on every card. No\nchannel and no reply to route; `then` is its single advance once the gift is delivered:\nbare (`then.after=None`) to move on at delivery, or held to wait that long after it.", + "properties": { + "gift_note": { + "anyOf": [ + { + "maxLength": 240, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "gift", + "type": "string" + }, + "label": { + "type": "string" + }, + "max_price_usd": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "product_url": { + "anyOf": [ + { + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "GiftNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", + "properties": { + "action_description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "manual", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "action_description", + "then" + ], + "title": "ManualActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "automation", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "AutomationNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", + "properties": { + "arms": { + "items": { + "additionalProperties": false, + "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", + "properties": { + "case": { + "minLength": 1, + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "case", + "to" + ], + "title": "Arm", + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "decision", + "type": "string" + }, + "label": { + "type": "string" + }, + "rule": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "rule", + "arms" + ], + "title": "DecisionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "terminal", + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind" + ], + "title": "TerminalNode", + "type": "object" + } +]
7 tool updates
- Changed
edit_monitor_members2 fields changed- changed
Input schema / properties / add / descriptionPrevious value: -"LinkedIn profile URLs/slugs to start watching."New value: +"LinkedIn profile URLs/slugs or company page URLs to start watching." - changed
Input schema / properties / remove / descriptionPrevious value: -"LinkedIn profile URLs/slugs to stop watching."New value: +"LinkedIn profile URLs/slugs or company page URLs to stop watching."
- Changed
exa_search_news1 field changed- changed
Input schema / properties / criteria / descriptionPrevious value: -"What makes a company a match for this search, in plain words:\nthe event it must show and the kind of company (e.g. \"A manufacturer\nthat itself announced a new US plant\"). Each company is checked\nagainst it, and the check reads nothing else of your instructions,\nso make it no narrower than what this call searches for. A role that\nis part of the signal belongs here; who you'll contact at a matching\ncompany doesn't, since the check reads that as a rule about which\ncompanies match."New value: +"What makes a company a match for this search, in plain words:\nthe event it must show and the kind of company (e.g. \"A manufacturer\nthat itself opened or is opening a new US plant\"). Each company is\nchecked against it, and the check reads nothing else of your\ninstructions, so make it no narrower than what this call searches\nfor. A role that is part of the signal belongs here; who you'll\ncontact at a matching company doesn't, since the check reads that as\na rule about which companies match."
- Changed
query_agent_people1 field changed- changed
Input schema / properties / filters / descriptionPrevious value: -"Clauses that must all match. For \"degree\", \"outreach_stage\", \"list\" or\n\"source\", use {\"column_key\": <dimension>, \"operator\": \"is_any_of\", \"values\": [keys]}\nwith keys as group_counts returns them ('' matches no value). For \"segment\", use the\nsame shape with the segment keys above. For \"verdict\", use the same shape with any\nof 'rejected' (the people the user's default view filters out), 'qualified' and\n'unclear'; it matches each person's `verdict`, or with group_by=\"list\" their verdict\non each list, and 'rejected' brings the rejected people in without\n`include_rejected`. For \"run\", use the same shape with run ids from\n`query_search_runs` as strings; it matches the people those search runs found,\nrejected ones included. For \"name\", \"title\", \"headline\", \"company\" or \"location\", use\n{\"column_key\": <column>, \"operator\": \"contains\", \"text\": <substring>}."New value: +"Clauses that must all match. For \"degree\", \"outreach_stage\", \"list\" or\n\"source\", use {\"column_key\": <dimension>, \"operator\": \"is_any_of\", \"values\": [keys]}\nwith keys as group_counts returns them ('' matches no value). For \"segment\", use the\nsame shape with the segment keys above. For \"verdict\", use the same shape with any\nof 'rejected' (the people the user's default view filters out), 'qualified' and\n'unclear'; it matches each person's `verdict`, or with group_by=\"list\" their verdict\non each list, and 'rejected' brings the rejected people in without\n`include_rejected`. For \"run\", use the same shape with run ids from\n`query_search_runs` as strings; it matches the people those search runs found,\nrejected ones included. When those runs all saved to one list and group_by isn't\n\"list\", a \"verdict\" filter reads that list alone, as on its group_by=\"list\" bucket page.\nFor \"name\", \"title\", \"headline\", \"company\" or \"location\", use\n{\"column_key\": <column>, \"operator\": \"contains\", \"text\": <substring>}."
- Changed
query_search_results1 field changed- changed
Input schema / properties / run_id / descriptionPrevious value: -"Only the rows whose person or company this search run returned, rejected\nones included, on any list (an `id` from `query_search_runs`). Omit for every row."New value: +"Only the rows whose person or company this search run returned, rejected\nones included, on the list it saved to (an `id` from `query_search_runs`). Omit for\nevery row."
- Changed
record_search_results2 fields changed- changed
Input schema / properties / results / items / properties / source / descriptionPrevious value: -"Where this row came from: the discovery tool that surfaced this candidate — 'theirstack' (theirstack_search), 'theirstack_tech_stack' (find_companies_by_tech_stack), 'tavily' (tavily_search), 'predictleads' (search_news_events), 'exa_news' (exa_search_news), 'google_news' (google_news_search), 'apollo' (apollo_search), 'exa_find_people', 'search_linkedin_people', 'post_engagers' (fetch_post_engagers, query_linkedin_post_engagements), 'luma_guests' (get_luma_guests). Use 'agent' for a row no discovery tool surfaced, such as a pasted list or a CRM read. Recorded only when the row is first created; an existing row keeps its original source."New value: +"Where this row came from: the discovery tool that surfaced this candidate — 'theirstack' (theirstack_search), 'theirstack_tech_stack' (find_companies_by_tech_stack), 'tavily' (tavily_search), 'predictleads' (search_news_events), 'exa_news' (exa_search_news), 'google_news' (google_news_search), 'apollo' (apollo_search), 'exa_find_people', 'search_linkedin_people', 'post_engagers' (fetch_post_engagers, query_linkedin_post_engagements), 'post_authors' (a LinkedIn monitor's author capture), 'luma_guests' (get_luma_guests). Use 'agent' for a row no discovery tool surfaced, such as a pasted list or a CRM read. Recorded only when the row is first created; an existing row keeps its original source." - changed
Input schema / properties / results / items / properties / source / enumPrevious value: -[ - "agent", - "exa_find_people", - "search_linkedin_people", - "theirstack", - "theirstack_tech_stack", - "luma_guests", - "post_engagers", - "exa_websets", - "warm_intro", - "tavily", - "predictleads", - "exa_news", - "google_news", - "apollo" -]New value: +[ + "agent", + "exa_find_people", + "search_linkedin_people", + "theirstack", + "theirstack_tech_stack", + "luma_guests", + "post_engagers", + "post_authors", + "exa_websets", + "warm_intro", + "tavily", + "predictleads", + "exa_news", + "google_news", + "apollo" +]
- Changed
search_linkedin_message_history1 field changed- changed
Input schema / properties / where_clause / descriptionPrevious value: -"SQL WHERE clause (without WHERE keyword). Use ONLY these\ncolumns: provider, chat_id, sender_name, sender_provider_id,\nrecipient_name, recipient_provider_id, content, message_datetime,\nreply_outcome (inbound replies only; '' / 'interested' /\n'meeting_booked' / 'not_interested' — the reply's classified outcome).\nTo check whether a prior conversation exists before firing a\ntemplated message, match on\n`sender_provider_id`/`recipient_provider_id` first; if that returns\nzero rows, run a second pass matching the person's full name (first\nAND last) as a substring, e.g. `sender_name ILIKE '%Jane%Doe%' OR\nrecipient_name ILIKE '%Jane%Doe%'`, before concluding there's no\nhistory — a stored provider_id is often blank or wrong, so an\nexact-match zero is indistinguishable from \"never messaged.\" Never\nmatch on first name alone (it pulls every same-first-name person in\nthe inbox and risks dropping a cold message onto a stranger's live\nthread). The full-name pass is a substring match, so if it spans\nmore than one person (different provider_ids), use only the thread\nwhose counterpart is your recipient — don't merge look-alike names\nor reuse a mismatched `chat_id`. Even both passes empty isn't proof\nof no prior contact: this local store holds only a bounded initial\nbackfill from around when the account was connected, forward, so an\nolder or pre-connection thread may never have been ingested — and\nthe same person may be stored under a variant name a substring match\nmisses. When you have the counterpart's `provider_id` and need\ncertainty, call `fetch_linkedin_messages_with_person` — it reads\nLinkedIn's full synced history live and writes it back here; if that\nalso returns empty, treat \"no history\" as confirmed, otherwise flag\nthe uncertainty when a cold-open template goes to approval."New value: +"SQL WHERE clause (without WHERE keyword). Use ONLY these\ncolumns: provider, chat_id, sender_name, sender_provider_id,\nrecipient_name, recipient_provider_id, content, message_datetime,\nreply_outcome (inbound replies only; '' / 'interested' /\n'meeting_booked' / 'not_interested' — the reply's classified outcome).\nTo read your thread with a person, match their provider_id alone:\n`sender_provider_id = '<id>' OR recipient_provider_id = '<id>'`.\nKeep their name out of that clause: a name ILIKE is a substring\nmatch, so it pulls in other people's threads (`'%Ana%Li%'` also\nmatches \"Diana Lim\"). Match on a name only to find someone whose\nprovider_id you don't have; when a hit's counterpart name is their\nfull name, take it as theirs and re-read by that counterpart's\nprovider_id. An empty result isn't proof of no prior\ncontact: this local store holds only a bounded initial backfill\nfrom around when the account was connected, forward. When you need\ncertainty, call `fetch_linkedin_messages_with_person` with their\nprovider_id — it reads LinkedIn's full synced history live and\nwrites it back here."
- Changed
setup_linkedin_monitoring5 fields changed- added
Input schema / properties / capture_authorsAdded value: +{ + "default": false, + "description": "topic mode only — capture the person who wrote each kept\npost and wake this agent when new authors are saved.", + "type": "boolean" +} - changed
Input schema / properties / engager_icp / descriptionPrevious value: -"optional — targeting criteria the collected engagers are\nfiltered by (ICP, titles, exclusions). Blank collects everyone. Only\nmeaningful when fetch_engagers is on."New value: +"optional — targeting criteria the collected people are\nfiltered by (ICP, titles, exclusions). Blank collects everyone. Only\nmeaningful when fetch_engagers or capture_authors is on." - changed
Input schema / properties / engager_instructions / descriptionPrevious value: -"optional — what to do with captured engagers. Blank\nmeans collect them into the agent's Output list and take no action."New value: +"optional — what to do with the captured people. Blank\nmeans collect them into the agent's Output list and take no action." - changed
Input schema / properties / members / descriptionPrevious value: -"list mode only — LinkedIn profile URLs/slugs to watch. Required\nwhen creating a list monitor; omit on a re-setup of an existing list\nmonitor to keep its current members (use `edit_monitor_members` to add\nor drop individuals)."New value: +"list mode only — LinkedIn profile URLs/slugs and company page\nURLs to watch. Required when creating a list monitor; omit on a re-setup\nof an existing list monitor to keep its current members (use\n`edit_monitor_members` to add or drop individuals)." - changed
Input schema / properties / mode / descriptionPrevious value: -"'network' (1st-degree feed), 'list' (explicit people), or 'topic'\n(keyword search across all of LinkedIn)."New value: +"'network' (1st-degree feed), 'list' (explicit people and company\npages), or 'topic' (keyword search across all of LinkedIn)."
6 tool updates
- Changed
exa_find_people1 field changed- removed
Input schema / properties / persistRemoved value: -{ - "default": true, - "description": "Default True — in an agent, checked people are saved and linked\nas person rows automatically (see `list_name`). Pass False only for a\nread-only lookup the user doesn't want on a list.", - "type": "boolean" -}
- Changed
fetch_post_engagers7 fields changed- removed
Input schema / properties / criteria / anyOfRemoved value: -[ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } -] - removed
Input schema / properties / criteria / defaultRemoved value: -null - changed
Input schema / properties / criteria / descriptionPrevious value: -"Optional — who counts as a match, in plain words (e.g.\n\"Heads of Sales at B2B software companies; not recruiters\"). Each\nperson saved is checked against it. Omit it to save everyone who\nengaged."New value: +"Who counts as a match, in plain words (e.g. \"Heads of Sales\nat B2B software companies; not recruiters\")." - added
Input schema / properties / criteria / minLengthAdded value: +1 - added
Input schema / properties / criteria / typeAdded value: +"string" - removed
Input schema / properties / persistRemoved value: -{ - "default": true, - "description": "Default True — in an agent, every person who engaged is saved as\na person row automatically (see `list_name`). Pass False only for a read-only\nlookup the user doesn't want on a list: the raw engagement rows still\nland and stay queryable via `query_linkedin_post_engagements`, but no\n`agent_search_results` list is written and nobody is checked.", - "type": "boolean" -} - changed
Input schema / requiredPrevious value: -[ - "post_url" -]New value: +[ + "post_url", + "criteria" +]
- Changed
query_agent_people1 field changed- changed
Input schema / properties / filters / descriptionPrevious value: -"Clauses that must all match. For \"degree\", \"outreach_stage\", \"list\" or\n\"source\", use {\"column_key\": <dimension>, \"operator\": \"is_any_of\", \"values\": [keys]}\nwith keys as group_counts returns them ('' matches no value). For \"segment\", use the\nsame shape with the segment keys above. For \"verdict\", use the same shape with any\nof 'rejected' (the people the user's default view filters out), 'qualified' and\n'unclear'; it matches each person's `verdict`, or with group_by=\"list\" their verdict\non each list, and 'rejected' brings the rejected people in without\n`include_rejected`. For \"name\", \"title\", \"headline\", \"company\" or \"location\", use\n{\"column_key\": <column>, \"operator\": \"contains\", \"text\": <substring>}."New value: +"Clauses that must all match. For \"degree\", \"outreach_stage\", \"list\" or\n\"source\", use {\"column_key\": <dimension>, \"operator\": \"is_any_of\", \"values\": [keys]}\nwith keys as group_counts returns them ('' matches no value). For \"segment\", use the\nsame shape with the segment keys above. For \"verdict\", use the same shape with any\nof 'rejected' (the people the user's default view filters out), 'qualified' and\n'unclear'; it matches each person's `verdict`, or with group_by=\"list\" their verdict\non each list, and 'rejected' brings the rejected people in without\n`include_rejected`. For \"run\", use the same shape with run ids from\n`query_search_runs` as strings; it matches the people those search runs found,\nrejected ones included. For \"name\", \"title\", \"headline\", \"company\" or \"location\", use\n{\"column_key\": <column>, \"operator\": \"contains\", \"text\": <substring>}."
- Changed
query_search_results1 field changed- added
Input schema / properties / run_idAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Only the rows whose person or company this search run returned, rejected\nones included, on any list (an `id` from `query_search_runs`). Omit for every row." +}
- Added
query_search_runs - Changed
setup_linkedin_monitoring2 fields changed- removed
Input schema / properties / comment_scopeRemoved value: -{ - "default": "", - "description": "optional — which post types are worth a drafted comment, in\nthe user's words. Blank keeps the built-in criteria only.", - "type": "string" -} - added
Input schema / properties / post_filterAdded value: +{ + "default": "", + "description": "optional — which posts the user wants, in their words (roles,\ntopics, post types). Blank keeps every post. On a topic monitor posts are\nchecked minutes after they go up, so a minimum-engagement rule sets most\nof them aside.", + "type": "string" +}
13 tool updates
- Changed
exa_find_people13 fields changed- changed
Input schema / properties / agent_id / descriptionPrevious value: -"Optional — a specific agent to save the matched people into.\nOmit it to use the running agent, which is the usual case. Only a\nthrowaway lookup outside any agent returns results without saving."New value: +"Optional — a specific agent to save the checked people into.\nOmit it to use the running agent, which is the usual case. Only a\nthrowaway lookup outside any agent returns results without saving." - changed
Input schema / properties / list_name / descriptionPrevious value: -"Short kebab slug naming the Output-tab list bucket (e.g.\n'acme-execs'). When this runs in an agent, matched people are saved\nand linked as `agent_search_results` person rows automatically —\ndeduped by profile, re-runs update in place; `list_name` names\ntheir list, and absent it they land in the 'default' list. Query\nthem by reference with `query_search_results` instead of re-typing\nURLs from `results`. The `results` return is unchanged either way."New value: +"Short kebab slug naming the Output-tab list bucket (e.g.\n'acme-execs'). When this runs in an agent, the people the check\njudged are saved and linked as `agent_search_results` person rows\nautomatically, each with its verdict (the rejected ones hidden from\nthe list's default view) — deduped by profile, re-runs update in\nplace; `list_name` names their list, and absent it they land in\nthe 'default' list. Query\nthem by reference with `query_search_results` instead of re-typing\nURLs from `results`. The `results` return is unchanged either way." - changed
Input schema / properties / num_results / descriptionPrevious value: -"Candidates Exa retrieves and ranks per search before\nthe match filter runs. Default 10; max 100. Matches cluster at\nthe top of Exa's ranking, so 10 usually holds the right people\nfor a well-formed query; the returned `results` are shorter\nafter filtering. If `results` is thin and `rejected` is long,\nreword the query (the reject reasons say whether and how)\nrather than raising this — the tail ranks are lower-relevance\nanyway."New value: +"Candidates Exa retrieves and ranks per search before\nthe check runs. Default 10; max 100. Matches cluster at\nthe top of Exa's ranking, so 10 usually holds the right people\nfor a well-formed query; the returned `results` are shorter\nafter the check. If `results` is thin and `rejected` is long,\nreword the query (the reject reasons say whether and how)\nrather than raising this — the tail ranks are lower-relevance\nanyway." - changed
Input schema / properties / persist / descriptionPrevious value: -"Default True — in an agent, matched people are saved and linked\nas person rows automatically (see `list_name`). Pass False only for a\nread-only lookup the user doesn't want on a list."New value: +"Default True — in an agent, checked people are saved and linked\nas person rows automatically (see `list_name`). Pass False only for a\nread-only lookup the user doesn't want on a list." - changed
Input schema / properties / searches / descriptionPrevious value: -"List of `ExaPeopleSearch` objects. Each has its own\n`query` + optional `system_prompt`. For a single-company\nlookup, pass a list of one."New value: +"List of `ExaPeopleSearch` objects. Each has its own\n`query` + `criteria`. For a single-company lookup, pass a list\nof one." - changed
Input schema / properties / searches / items / descriptionPrevious value: -"One people search: who to find, and at which company.\n\nMultiple of these can be batched into a single `exa_find_people`\ncall — the tool fans them out in parallel and returns results in\ninput order. Each search is independent (its own query +\nsystem_prompt); the shared `num_results` is passed as a kwarg on the\nparent tool call."New value: +"One people search: who to find, and at which company.\n\nMultiple of these can be batched into a single `exa_find_people`\ncall — the tool fans them out in parallel and returns results in\ninput order. Each search is independent (its own query +\ncriteria); the shared `num_results` is passed as a kwarg on the\nparent tool call." - changed
Input schema / properties / searches / items / properties / company / descriptionPrevious value: -"The company this search is aimed at, by name, on its own. Anyone placed at a differently-named employer is then dropped before you see them — a founder of 'Acmely' coming back for an 'Acme' search. The company name inside `query` / `system_prompt` steers ranking; this is what filters. Required, with no default, so a search running unfiltered is a decision rather than an omission: pass null for a cross-company search ('pricing leaders across chemical distributors'), and for an alumni search, where the person's current employer is a different company by definition."New value: +"The company this search is aimed at, by name, on its own; null for a cross-company search ('pricing leaders across chemical distributors') or an alumni search. When `company_domain` / `company_linkedin_url` pins the search, the check is also told this name and that identifier as the employer searched for, so it can tell that employer's staff from a same-named company's. Requiring that they work there now is still `criteria`'s job." - added
Input schema / properties / searches / items / properties / criteriaAdded value: +{ + "description": "What a match is for this search, in plain words: the requirements a person must meet, led by the employer and that they work there now (or the one person you're after), then any role, seniority, region or exclusion that decides it. The right place for exclusions. Each person returned is checked against all of it as requirements, so leave out a preference that only orders results (VPs ahead of Directors, the founder ahead of a first sales hire) and name everyone you'd accept. Example: 'Currently works at Microsoft, not a former employee. Product leader. Not a growth marketer.'", + "minLength": 1, + "type": "string" +} - changed
Input schema / properties / searches / items / properties / locations / descriptionPrevious value: -"Places the person must be based in, by country, state or city name, e.g. ['United States'] or ['Texas', 'Colorado']. Entries are OR-ed, and the filter applies whichever provider serves the search. Spell states out: a two-letter code is read as a country, so 'CT' does not mean Connecticut. At a multinational this is what keeps overseas staff out of the results — naming a place in `query` or `system_prompt` does not filter a search pinned by `company_domain` / `company_linkedin_url`."New value: +"Places the person must be based in, by country, state or city name, e.g. ['United States'] or ['Texas', 'Colorado']. Entries are OR-ed, and the filter applies whichever provider serves the search. Spell states out: a two-letter code is read as a country, so 'CT' does not mean Connecticut. At a multinational this is what keeps overseas staff out of what a search pinned by `company_domain` / `company_linkedin_url` fetches: `criteria` only rejects from what was fetched, and `query` doesn't reach that search at all." - changed
Input schema / properties / searches / items / properties / query / descriptionPrevious value: -"POSITIVE-FORM description of who you want, including the company name. Examples: 'VP of Product at Microsoft', 'senior marketing leaders at Exaforce'. Do NOT include exclusions here — vector retrieval has no concept of 'not', so naming an excluded term biases the ranker TOWARDS it."New value: +"POSITIVE-FORM description of who you want, including the company name. Examples: 'VP of Product at Microsoft', 'senior marketing leaders at Exaforce'. On a search not pinned by `company_domain` / `company_linkedin_url`, include the region the ask names ('VP of Product at Microsoft in the US'): that search has no location filter, so `locations` and `criteria` only reject from the people `query` retrieves. Do NOT include exclusions here — vector retrieval has no concept of 'not', so naming an excluded term biases the ranker TOWARDS it." - removed
Input schema / properties / searches / items / properties / system_promptRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Optional ranker strategy hint — the right place for preferences and exclusions. Example: 'MUST currently work at Microsoft. Prioritize VP+ over Director+. Exclude growth marketers.'" -} - changed
Input schema / properties / searches / items / properties / titles / descriptionPrevious value: -"Role keywords, e.g. ['Operations', 'Training']. Each is matched as a case-insensitive substring of the person's job title and the list is OR-ed, so short function or seniority words are what match: 'Marketing', 'Revenue', 'Director', 'Chief'. A full title phrase matches only where it appears verbatim, which misses every company that words that title differently or abbreviates it — so always include the broad function word on its own ('Marketing') before adding specific titles, and pass both spellings of a rank ('VP' and 'Vice President' match different people). Entries are OR-ed, so a title that matches nobody costs nothing. Narrowing also decides WHICH people come back at a big employer: results are unordered, so without it you get an arbitrary slice of everyone there rather than the roles you asked for."New value: +"Role keywords, e.g. ['Operations', 'Training']. Each is matched as a case-insensitive substring of the person's job title and the list is OR-ed, so short function or seniority words are what match: 'Marketing', 'Revenue', 'Director', 'Chief'. A full title phrase matches only where it appears verbatim, which misses every company that words that title differently or abbreviates it — so always include the broad function word on its own ('Marketing') before adding specific titles, and pass both spellings of a rank ('VP' and 'Vice President' match different people). Entries are OR-ed, so a title that matches nobody costs nothing. Narrowing also decides WHICH people come back at a big employer: the fetch is unordered, so without it you get an arbitrary slice of everyone there rather than the roles you asked for." - changed
Input schema / properties / searches / items / requiredPrevious value: -[ - "query", - "company" -]New value: +[ + "query", + "criteria", + "company" +]
- Changed
exa_search_news2 fields changed- added
Input schema / properties / criteriaAdded value: +{ + "description": "What makes a company a match for this search, in plain words:\nthe event it must show and the kind of company (e.g. \"A manufacturer\nthat itself announced a new US plant\"). Each company is checked\nagainst it, and the check reads nothing else of your instructions,\nso make it no narrower than what this call searches for. A role that\nis part of the signal belongs here; who you'll contact at a matching\ncompany doesn't, since the check reads that as a rule about which\ncompanies match.", + "minLength": 1, + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "queries" -]New value: +[ + "queries", + "criteria" +]
- Changed
extend_find_search1 field changed- changed
Input schema / properties / list_name / descriptionPrevious value: -"The slug the list was created with (visible in the Output\ntab sub-pill, or via `query_search_results`'s `list_name` column)."New value: +"The slug the list was created with (the `list_name` column\nof `query_search_results`)."
- Changed
fetch_post_engagers2 fields changed- added
Input schema / properties / criteriaAdded value: +{ + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional — who counts as a match, in plain words (e.g.\n\"Heads of Sales at B2B software companies; not recruiters\"). Each\nperson saved is checked against it. Omit it to save everyone who\nengaged." +} - changed
Input schema / properties / persist / descriptionPrevious value: -"Default True — in an agent, every person who engaged is saved as\na person row automatically (see `list_name`). Pass False only for a read-only\nlookup the user doesn't want on a list: the raw engagement rows still\nland and stay queryable via `query_linkedin_post_engagements`, but no\n`agent_search_results` list is written. To filter the saved engagers,\nrecord a `verdict` on them with `record_search_results` instead."New value: +"Default True — in an agent, every person who engaged is saved as\na person row automatically (see `list_name`). Pass False only for a read-only\nlookup the user doesn't want on a list: the raw engagement rows still\nland and stay queryable via `query_linkedin_post_engagements`, but no\n`agent_search_results` list is written and nobody is checked."
- Changed
find_companies_by_tech_stack2 fields changed- added
Input schema / properties / criteriaAdded value: +{ + "description": "What makes a company a match for this search, in plain words:\nthe kind of company you're looking for (e.g. \"Consumer brands that\nsell through their own Shopify store, not agencies or app\ndevelopers\"). Each company returned is checked against it, and the\ncheck reads nothing else of your instructions, so make it no\nnarrower than what this call searches for. A role that is part of\nthe signal belongs here; who you'll contact at a matching company\ndoesn't, since the check reads that as a rule about which companies\nmatch.", + "minLength": 1, + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "technologies" -]New value: +[ + "technologies", + "criteria" +]
- Changed
find_warm_intro_paths1 field changed- changed
Input schema / properties / list_name / descriptionPrevious value: -"short kebab slug naming this set of targets; distinct slugs\nrender as separate Output sub-pills. Required."New value: +"short kebab slug naming this set of targets. Required."
- Added
import_linkedin_history - Changed
retry_blocked_enactment1 field changed- changed
Input schema / properties / email / descriptionPrevious value: -"An address to stamp on the prospect before retrying (optional) — the\nsame write as update_prospect's `email`; refused if another prospect in\nthe account already owns it."New value: +"An address to stamp on the prospect before retrying (optional) — the\nsame write as update_prospect's `email`; refused if another person in the\nworkspace already carries it."
- Changed
search_linkedin_people2 fields changed- added
Input schema / properties / criteriaAdded value: +{ + "description": "What a match is for this search, in plain words: the person\n you're looking for (\"Jane Doe, currently at Acme Corp\"), or the\n roles, seniority, region and employer that qualify someone\n (\"Sales leaders, VP and above, based in the US\"). Each returned\n profile is checked against it.", + "minLength": 1, + "type": "string" +} - added
Input schema / requiredAdded value: +[ + "criteria" +]
- Changed
search_news_events2 fields changed- added
Input schema / properties / criteriaAdded value: +{ + "description": "What makes a company a match for this search, in plain words:\n the event it must show and the kind of company (e.g. \"A B2B\n software company that just raised a round\"). Each event's\n company is checked against it, and the check reads nothing\n else of your instructions, so make it no narrower than what\n this call searches for. A role that is part of the signal\n belongs here; who you'll contact at a matching company\n doesn't, since the check reads that as a rule about which\n companies match.", + "minLength": 1, + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "categories" -]New value: +[ + "categories", + "criteria" +]
- Changed
start_find_search2 fields changed- changed
Input schema / properties / people_exclude_titles / descriptionPrevious value: -"Title keywords to reject, e.g. ['Coordinator',\n'Intern']. Same substring matching as `people_titles`, subtracted from\nit. Whenever `people_preferences` rules a role out, put that role here\ntoo — the attachment cannot read the prose, so an exclusion written only\nthere is not applied. Company lists only."New value: +"Title keywords to reject, e.g. ['Coordinator',\n'Intern']. Same substring matching as `people_titles`, subtracted from\nit. Whenever `people_preferences` rules a role out, put that role here\ntoo — an exclusion written only there applies after the fetch, so the\nexcluded people can still fill the fetched slots. Company lists only." - changed
Input schema / properties / people_preferences / descriptionPrevious value: -"Soft ranking/filtering preferences (and exclusions)\nfor the attached people. Company lists only."New value: +"Soft preferences and exclusions for the attached\npeople. Each is checked against them, where only an exclusion rules\nsomeone out; the kept people are ordered most senior first. Company\nlists only."
- Changed
theirstack_search2 fields changed- added
Input schema / properties / criteriaAdded value: +{ + "description": "What makes a company a match for this search, in plain words:\nthe kind of company and the hiring that qualifies it (e.g. \"B2B\nsoftware companies hiring sales development reps\"). Each company the\npostings belong to is checked against it, and the check reads\nnothing else of your instructions, so make it no narrower than what\nthis call searches for. A role that is part of the signal belongs\nhere; who you'll contact at a matching company doesn't, since the\ncheck reads that as a rule about which companies match.", + "minLength": 1, + "type": "string" +} - added
Input schema / requiredAdded value: +[ + "criteria" +]
- Changed
update_prospect1 field changed- changed
Input schema / properties / email / descriptionPrevious value: -"An address to store for this prospect (optional). Fills in a prospect that\nreached an email step without one, so a retried step can send. Lands on the\ncampaign row and on the person's profile, overwriting either's existing\naddress; refused if the address already belongs to another prospect in the\naccount (reconcile the duplicate instead). Leave unset for ordinary\nstage/priority updates."New value: +"An address to store for this prospect (optional). Fills in a prospect that\nreached an email step without one, so a retried step can send. Lands on the\nperson's profile, the prospect's only address, overwriting an existing one —\nso it also changes the address any teammate's campaign for this person sends\nto. Refused if the address already belongs to another person in the\nworkspace (likely the same person under two records). Leave unset for\nordinary stage/priority updates."
18 tool updates
- Changed
add_trigger1 field changed- changed
Input schema / properties / trigger / anyOfPrevious value: -[ - { - "description": "Run on a recurring schedule.", - "properties": { - "code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "days_of_week": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Days to run: 0=Monday through 6=Sunday. Omit for every day." - }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "prompt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "time_local": { - "description": "Time of day in user's local timezone, e.g. '09:00'", - "type": "string" - }, - "type": { - "const": "scheduled", - "type": "string" - } - }, - "required": [ - "type", - "time_local" - ], - "title": "ScheduledTrigger", - "type": "object" - }, - { - "description": "Run once at a specific time.", - "properties": { - "code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "prompt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "trigger_at": { - "description": "ISO datetime string in UTC, e.g. '2026-03-25T18:00:00Z'", - "type": "string" - }, - "type": { - "const": "one_time", - "type": "string" - } - }, - "required": [ - "type", - "trigger_at" - ], - "title": "OneTimeTrigger", - "type": "object" - }, - { - "description": "Run before each calendar meeting.", - "properties": { - "code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "minutes_before": { - "default": 15, - "description": "Minutes before meeting start. 5-60.", - "type": "integer" - }, - "prompt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "type": { - "const": "before_meeting", - "type": "string" - } - }, - "required": [ - "type" - ], - "title": "BeforeMeetingTrigger", - "type": "object" - }, - { - "description": "Run after each calendar meeting ends.", - "properties": { - "code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "minutes_after": { - "default": 10, - "description": "Minutes after meeting end. 0-60.", - "type": "integer" - }, - "prompt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "type": { - "const": "after_meeting", - "type": "string" - } - }, - "required": [ - "type" - ], - "title": "AfterMeetingTrigger", - "type": "object" - }, - { - "description": "Run when a specific event occurs.", - "properties": { - "code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "prompt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "type": { - "enum": [ - "new_email", - "all_email", - "sent_email", - "new_calendar_event", - "new_meeting", - "new_linkedin_message", - "new_linkedin_message_from_prospect", - "accepted_linkedin_invitation", - "sent_linkedin_connection_request", - "sent_linkedin_message", - "sent_linkedin_inmail", - "sent_linkedin_comment", - "sent_linkedin_reaction", - "sent_linkedin_follow", - "resolved_profile", - "new_post_engagers", - "calendly_booking", - "calendly_cancellation", - "luma_event_created", - "luma_guest_registered", - "luma_guest_updated" - ], - "type": "string" - } - }, - "required": [ - "type" - ], - "title": "EventTrigger", - "type": "object" - }, - { - "description": "A sequence node's enactment trigger — fires when a prospect enters `node_id`.\nManaged on any node (including a terminal, as a hook) via add_trigger / update_trigger /\nremove_trigger; define_sequence auto-mints one per enactable node.", - "properties": { - "code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "node_id": { - "type": "string" - }, - "prompt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "type": { - "const": "on_enter", - "type": "string" - } - }, - "required": [ - "type", - "node_id" - ], - "title": "OnEnterTrigger", - "type": "object" - } -]New value: +[ + { + "description": "Run on a recurring schedule.", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "days_of_week": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Days to run: 0=Monday through 6=Sunday. Omit for every day." + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "time_local": { + "description": "Time of day in user's local timezone, e.g. '09:00'", + "type": "string" + }, + "type": { + "const": "scheduled", + "type": "string" + } + }, + "required": [ + "type", + "time_local" + ], + "title": "ScheduledTrigger", + "type": "object" + }, + { + "description": "Run once at a specific time.", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "trigger_at": { + "description": "ISO datetime string in UTC, e.g. '2026-03-25T18:00:00Z'", + "type": "string" + }, + "type": { + "const": "one_time", + "type": "string" + } + }, + "required": [ + "type", + "trigger_at" + ], + "title": "OneTimeTrigger", + "type": "object" + }, + { + "description": "Run before each calendar meeting.", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "minutes_before": { + "default": 15, + "description": "Minutes before meeting start. 5-60.", + "type": "integer" + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "type": { + "const": "before_meeting", + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "BeforeMeetingTrigger", + "type": "object" + }, + { + "description": "Run after each calendar meeting ends.", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "minutes_after": { + "default": 10, + "description": "Minutes after meeting end. 0-60.", + "type": "integer" + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "type": { + "const": "after_meeting", + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "AfterMeetingTrigger", + "type": "object" + }, + { + "description": "Run when a specific event occurs.", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "type": { + "enum": [ + "new_email", + "all_email", + "sent_email", + "new_calendar_event", + "new_meeting", + "new_linkedin_message", + "new_linkedin_message_from_prospect", + "accepted_linkedin_invitation", + "sent_linkedin_connection_request", + "sent_linkedin_message", + "sent_linkedin_inmail", + "sent_linkedin_comment", + "sent_linkedin_reaction", + "sent_linkedin_follow", + "resolved_profile", + "new_post_engagers", + "luma_event_created", + "luma_guest_registered", + "luma_guest_updated" + ], + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "EventTrigger", + "type": "object" + }, + { + "description": "A sequence node's enactment trigger — fires when a prospect enters `node_id`.\nManaged on any node (including a terminal, as a hook) via add_trigger / update_trigger /\nremove_trigger; define_sequence auto-mints one per enactable node.", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "node_id": { + "type": "string" + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "type": { + "const": "on_enter", + "type": "string" + } + }, + "required": [ + "type", + "node_id" + ], + "title": "OnEnterTrigger", + "type": "object" + } +]
- Changed
create_agent1 field changed- changed
Input schema / properties / triggers / items / anyOfPrevious value: -[ - { - "description": "Run on a recurring schedule.", - "properties": { - "code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "days_of_week": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Days to run: 0=Monday through 6=Sunday. Omit for every day." - }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "prompt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "time_local": { - "description": "Time of day in user's local timezone, e.g. '09:00'", - "type": "string" - }, - "type": { - "const": "scheduled", - "type": "string" - } - }, - "required": [ - "type", - "time_local" - ], - "title": "ScheduledTrigger", - "type": "object" - }, - { - "description": "Run once at a specific time.", - "properties": { - "code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "prompt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "trigger_at": { - "description": "ISO datetime string in UTC, e.g. '2026-03-25T18:00:00Z'", - "type": "string" - }, - "type": { - "const": "one_time", - "type": "string" - } - }, - "required": [ - "type", - "trigger_at" - ], - "title": "OneTimeTrigger", - "type": "object" - }, - { - "description": "Run before each calendar meeting.", - "properties": { - "code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "minutes_before": { - "default": 15, - "description": "Minutes before meeting start. 5-60.", - "type": "integer" - }, - "prompt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "type": { - "const": "before_meeting", - "type": "string" - } - }, - "required": [ - "type" - ], - "title": "BeforeMeetingTrigger", - "type": "object" - }, - { - "description": "Run after each calendar meeting ends.", - "properties": { - "code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "minutes_after": { - "default": 10, - "description": "Minutes after meeting end. 0-60.", - "type": "integer" - }, - "prompt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "type": { - "const": "after_meeting", - "type": "string" - } - }, - "required": [ - "type" - ], - "title": "AfterMeetingTrigger", - "type": "object" - }, - { - "description": "Run when a specific event occurs.", - "properties": { - "code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "prompt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "type": { - "enum": [ - "new_email", - "all_email", - "sent_email", - "new_calendar_event", - "new_meeting", - "new_linkedin_message", - "new_linkedin_message_from_prospect", - "accepted_linkedin_invitation", - "sent_linkedin_connection_request", - "sent_linkedin_message", - "sent_linkedin_inmail", - "sent_linkedin_comment", - "sent_linkedin_reaction", - "sent_linkedin_follow", - "resolved_profile", - "new_post_engagers", - "calendly_booking", - "calendly_cancellation", - "luma_event_created", - "luma_guest_registered", - "luma_guest_updated" - ], - "type": "string" - } - }, - "required": [ - "type" - ], - "title": "EventTrigger", - "type": "object" - }, - { - "description": "A sequence node's enactment trigger — fires when a prospect enters `node_id`.\nManaged on any node (including a terminal, as a hook) via add_trigger / update_trigger /\nremove_trigger; define_sequence auto-mints one per enactable node.", - "properties": { - "code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "node_id": { - "type": "string" - }, - "prompt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "type": { - "const": "on_enter", - "type": "string" - } - }, - "required": [ - "type", - "node_id" - ], - "title": "OnEnterTrigger", - "type": "object" - } -]New value: +[ + { + "description": "Run on a recurring schedule.", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "days_of_week": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Days to run: 0=Monday through 6=Sunday. Omit for every day." + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "time_local": { + "description": "Time of day in user's local timezone, e.g. '09:00'", + "type": "string" + }, + "type": { + "const": "scheduled", + "type": "string" + } + }, + "required": [ + "type", + "time_local" + ], + "title": "ScheduledTrigger", + "type": "object" + }, + { + "description": "Run once at a specific time.", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "trigger_at": { + "description": "ISO datetime string in UTC, e.g. '2026-03-25T18:00:00Z'", + "type": "string" + }, + "type": { + "const": "one_time", + "type": "string" + } + }, + "required": [ + "type", + "trigger_at" + ], + "title": "OneTimeTrigger", + "type": "object" + }, + { + "description": "Run before each calendar meeting.", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "minutes_before": { + "default": 15, + "description": "Minutes before meeting start. 5-60.", + "type": "integer" + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "type": { + "const": "before_meeting", + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "BeforeMeetingTrigger", + "type": "object" + }, + { + "description": "Run after each calendar meeting ends.", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "minutes_after": { + "default": 10, + "description": "Minutes after meeting end. 0-60.", + "type": "integer" + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "type": { + "const": "after_meeting", + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "AfterMeetingTrigger", + "type": "object" + }, + { + "description": "Run when a specific event occurs.", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "type": { + "enum": [ + "new_email", + "all_email", + "sent_email", + "new_calendar_event", + "new_meeting", + "new_linkedin_message", + "new_linkedin_message_from_prospect", + "accepted_linkedin_invitation", + "sent_linkedin_connection_request", + "sent_linkedin_message", + "sent_linkedin_inmail", + "sent_linkedin_comment", + "sent_linkedin_reaction", + "sent_linkedin_follow", + "resolved_profile", + "new_post_engagers", + "luma_event_created", + "luma_guest_registered", + "luma_guest_updated" + ], + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "EventTrigger", + "type": "object" + }, + { + "description": "A sequence node's enactment trigger — fires when a prospect enters `node_id`.\nManaged on any node (including a terminal, as a hook) via add_trigger / update_trigger /\nremove_trigger; define_sequence auto-mints one per enactable node.", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "node_id": { + "type": "string" + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "type": { + "const": "on_enter", + "type": "string" + } + }, + "required": [ + "type", + "node_id" + ], + "title": "OnEnterTrigger", + "type": "object" + } +]
- Changed
create_message_tag_group1 field changed- changed
Input schema / properties / criteria / anyOfPrevious value: -[ - { - "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", - "properties": { - "agent_ids": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "channel": { - "anyOf": [ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "positions": { - "anyOf": [ - { - "items": { - "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", - "enum": [ - "first", - "follow_up", - "reply" - ], - "title": "Position", - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "senders": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "title": "Criteria", - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", + "properties": { + "agent_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "channel": { + "anyOf": [ + { + "description": "The outreach channel a query is bounded to; None means both.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "positions": { + "anyOf": [ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the other person's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "senders": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "Criteria", + "type": "object" + }, + { + "type": "null" + } +]
- Changed
define_sequence2 fields changed- changed
Input schema / properties / sequence / properties / nodes / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", - "properties": { - "accepted": { - "type": "string" - }, - "already_connected": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "connection_request", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "maxLength": 300, - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "timeout": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "id", - "label", - "kind", - "accepted", - "already_connected" - ], - "title": "ConnectionRequestNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", - "properties": { - "action": { - "enum": [ - "message", - "inmail", - "email" - ], - "type": "string" - }, - "channel": { - "enum": [ - "linkedin", - "email" - ], - "type": "string" - }, - "follow_up": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "send", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "replied": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "replied" - ], - "title": "SendNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", - "properties": { - "action": { - "enum": [ - "follow", - "withdraw", - "resolve", - "comment", - "reaction" - ], - "type": "string" - }, - "channel": { - "const": "linkedin", - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "action", - "type": "string" - }, - "label": { - "type": "string" - }, - "notify": { - "default": false, - "type": "boolean" - }, - "then": { - "additionalProperties": false, - "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "then" - ], - "title": "SilentActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", - "properties": { - "action_description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "manual", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "action_description", - "then" - ], - "title": "ManualActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "automation", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "AutomationNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", - "properties": { - "arms": { - "items": { - "additionalProperties": false, - "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", - "properties": { - "case": { - "minLength": 1, - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "case", - "to" - ], - "title": "Arm", - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "decision", - "type": "string" - }, - "label": { - "type": "string" - }, - "rule": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "rule", - "arms" - ], - "title": "DecisionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "terminal", - "type": "string" - }, - "label": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind" - ], - "title": "TerminalNode", - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", + "properties": { + "accepted": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + }, + "already_connected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "connection_request", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "maxLength": 300, + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "timeout": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "label", + "kind", + "accepted", + "already_connected" + ], + "title": "ConnectionRequestNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", + "properties": { + "action": { + "enum": [ + "message", + "inmail", + "email" + ], + "type": "string" + }, + "channel": { + "enum": [ + "linkedin", + "email" + ], + "type": "string" + }, + "follow_up": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "send", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "replied": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "replied" + ], + "title": "SendNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", + "properties": { + "action": { + "enum": [ + "follow", + "withdraw", + "resolve", + "comment", + "reaction" + ], + "type": "string" + }, + "channel": { + "const": "linkedin", + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "action", + "type": "string" + }, + "label": { + "type": "string" + }, + "notify": { + "default": false, + "type": "boolean" + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "then" + ], + "title": "SilentActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", + "properties": { + "action_description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "manual", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "action_description", + "then" + ], + "title": "ManualActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "automation", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "AutomationNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", + "properties": { + "arms": { + "items": { + "additionalProperties": false, + "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", + "properties": { + "case": { + "minLength": 1, + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "case", + "to" + ], + "title": "Arm", + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "decision", + "type": "string" + }, + "label": { + "type": "string" + }, + "rule": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "rule", + "arms" + ], + "title": "DecisionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "terminal", + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind" + ], + "title": "TerminalNode", + "type": "object" + } +] - changed
Input schema / properties / sequence / properties / start / properties / entry / descriptionPrevious value: -"An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node)."New value: +"An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`."
- Changed
estimate_message_tag_classify_scope1 field changed- changed
Input schema / properties / positions / anyOfPrevious value: -[ - { - "items": { - "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", - "enum": [ - "first", - "follow_up", - "reply" - ], - "title": "Position", - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the other person's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } +]
- Changed
get_message_tag_rates2 fields changed- added
Input schema / properties / originAdded value: +{ + "anyOf": [ + { + "description": "Whose conversation a sent message belongs to: a Sliq prospect's, or an imported one — a\nLinkedIn conversation the user started with someone who has no prospect row. An origin filter\nis one of these, None for both.", + "enum": [ + "sliq", + "imported" + ], + "title": "Origin", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "'sliq' for conversations with prospects only, 'imported' for imported\nconversations only; omit for both. Imported conversations have no\nconnection-request notes, so `accepted` over them is empty." +} - changed
Input schema / properties / positions / anyOfPrevious value: -[ - { - "items": { - "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", - "enum": [ - "first", - "follow_up", - "reply" - ], - "title": "Position", - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the other person's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } +]
- Changed
get_tool_connect_url1 field changed- changed
Input schema / properties / integration_id / descriptionPrevious value: -"one of 'gmail', 'outlook', 'linkedin', 'slack', 'hubspot_mcp', 'attio', 'clarify',\n'salesforce', 'notion', 'linear', 'fathom', 'granola', 'grain', 'zoom',\n'otter', 'fireflies', 'circleback', 'superhuman', 'github', 'asana_mcp', 'atlassian_mcp',\n'calendly', 'googledrive', 'googledocs', 'googlesheets', 'reddit',\n'exa_websets', 'apollo'"New value: +"one of 'gmail', 'outlook', 'linkedin', 'slack', 'hubspot_mcp', 'attio', 'clarify',\n'salesforce', 'notion', 'linear', 'fathom', 'granola', 'grain', 'zoom',\n'otter', 'fireflies', 'circleback', 'superhuman', 'github', 'asana_mcp', 'atlassian_mcp',\n'googledrive', 'googledocs', 'googlesheets', 'reddit',\n'exa_websets', 'apollo'"
- Changed
list_sent_messages4 fields changed- added
Input schema / properties / originAdded value: +{ + "anyOf": [ + { + "description": "Whose conversation a sent message belongs to: a Sliq prospect's, or an imported one — a\nLinkedIn conversation the user started with someone who has no prospect row. An origin filter\nis one of these, None for both.", + "enum": [ + "sliq", + "imported" + ], + "title": "Origin", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "'sliq' for messages to prospects only, 'imported' for imported conversations\nonly; omit for both." +} - changed
Input schema / properties / positions / anyOfPrevious value: -[ - { - "items": { - "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", - "enum": [ - "first", - "follow_up", - "reply" - ], - "title": "Position", - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the other person's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } +] - added
Input schema / properties / provider_idAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Only LinkedIn messages sent to this LinkedIn provider id — how to list an\nimported conversation's messages, which have no prospect_id." +} - changed
Input schema / properties / q / descriptionPrevious value: -"Case-insensitive text to find in the message or the prospect's name."New value: +"Case-insensitive text to find in the message or the recipient's name."
- Added
look_up_imported_people - Added
query_agent_people - Changed
query_search_results2 fields changed- changed
Input schema / properties / verdict / anyOfPrevious value: -[ - { - "description": "The agent's curation call on an `agent_search_results` row. NULL (no verdict) means\nunreviewed; the default task views hide only `REJECTED` rows.", - "enum": [ - "qualified", - "rejected" - ], - "title": "SearchResultVerdict", - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The agent's curation call on an `agent_search_results` row. NULL (no verdict) means not yet\nscreened; `UNCLEAR` means screened but too thin to judge. The default task views hide only\n`REJECTED` rows.", + "enum": [ + "qualified", + "rejected", + "unclear" + ], + "title": "SearchResultVerdict", + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / verdict / descriptionPrevious value: -"Only the rows with this verdict, 'qualified' or 'rejected' ('rejected'\nreads the rows the user's default view filters out). Omit for every verdict."New value: +"Only the rows with this verdict, 'qualified', 'rejected' or 'unclear'\n('rejected' reads the rows the user's default view filters out). Omit for every\nverdict."
- Changed
query_tagged_messages2 fields changed- added
Input schema / properties / originAdded value: +{ + "anyOf": [ + { + "description": "Whose conversation a sent message belongs to: a Sliq prospect's, or an imported one — a\nLinkedIn conversation the user started with someone who has no prospect row. An origin filter\nis one of these, None for both.", + "enum": [ + "sliq", + "imported" + ], + "title": "Origin", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "'sliq' for conversations with prospects only, 'imported' for imported LinkedIn\nconversations (ones the user started with someone who isn't a prospect) only; omit\nfor both." +} - changed
Input schema / properties / positions / anyOfPrevious value: -[ - { - "items": { - "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", - "enum": [ - "first", - "follow_up", - "reply" - ], - "title": "Position", - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the other person's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } +]
- Removed
query_task_people - Changed
record_search_results2 fields changed- changed
Input schema / properties / results / items / properties / verdict / anyOfPrevious value: -[ - { - "description": "The agent's curation call on an `agent_search_results` row. NULL (no verdict) means\nunreviewed; the default task views hide only `REJECTED` rows.", - "enum": [ - "qualified", - "rejected" - ], - "title": "SearchResultVerdict", - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The agent's curation call on an `agent_search_results` row. NULL (no verdict) means not yet\nscreened; `UNCLEAR` means screened but too thin to judge. The default task views hide only\n`REJECTED` rows.", + "enum": [ + "qualified", + "rejected", + "unclear" + ], + "title": "SearchResultVerdict", + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / results / items / properties / verdict / descriptionPrevious value: -"Your judgment of this row after screening it: 'qualified' keeps it on the list; 'rejected' keeps it saved but out of the list the user sees by default, and they see it, with `verdict_reason`, when they filter the table on Verdict. Omit it to leave the row's current verdict as it is."New value: +"Your judgment of this row after screening it: 'qualified' keeps it on the list; 'rejected' keeps it saved but out of the list the user sees by default, and they see it, with `verdict_reason`, when they filter the table on Verdict; 'unclear' keeps it on the list, marked as one whose details were too thin to judge. Omit it to leave the row's current verdict as it is."
- Changed
update_deal2 fields changed- added
Input schema / properties / new_companyAdded value: +{ + "anyOf": [ + { + "description": "A company not yet in Sliq. Needs a domain; an existing company with that domain is\nreused.", + "properties": { + "domain": { + "default": "", + "type": "string" + }, + "name": { + "default": "", + "type": "string" + } + }, + "title": "NewCompany", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "A company to add and link: name plus domain. A company Sliq already has\nwith that domain is reused. Used instead of company_id." +} - added
Input schema / properties / new_personAdded value: +{ + "anyOf": [ + { + "description": "A contact not yet in Sliq. Needs an email or a LinkedIn URL; an existing person with the\nsame email/LinkedIn is reused, not duplicated.", + "properties": { + "email": { + "default": "", + "type": "string" + }, + "linkedin_url": { + "default": "", + "type": "string" + }, + "name": { + "default": "", + "type": "string" + } + }, + "title": "NewPerson", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "A contact to add and link: name plus email or LinkedIn URL. Someone Sliq\nalready has with that email or LinkedIn is reused. Used instead of person_id." +}
- Changed
update_message_tag_group1 field changed- changed
Input schema / properties / criteria / anyOfPrevious value: -[ - { - "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", - "properties": { - "agent_ids": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "channel": { - "anyOf": [ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "positions": { - "anyOf": [ - { - "items": { - "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", - "enum": [ - "first", - "follow_up", - "reply" - ], - "title": "Position", - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "senders": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "title": "Criteria", - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", + "properties": { + "agent_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "channel": { + "anyOf": [ + { + "description": "The outreach channel a query is bounded to; None means both.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "positions": { + "anyOf": [ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the other person's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "senders": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "Criteria", + "type": "object" + }, + { + "type": "null" + } +]
- Changed
update_node1 field changed- changed
Input schema / properties / node / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", - "properties": { - "accepted": { - "type": "string" - }, - "already_connected": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "connection_request", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "maxLength": 300, - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "timeout": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "id", - "label", - "kind", - "accepted", - "already_connected" - ], - "title": "ConnectionRequestNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", - "properties": { - "action": { - "enum": [ - "message", - "inmail", - "email" - ], - "type": "string" - }, - "channel": { - "enum": [ - "linkedin", - "email" - ], - "type": "string" - }, - "follow_up": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "send", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "replied": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "replied" - ], - "title": "SendNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", - "properties": { - "action": { - "enum": [ - "follow", - "withdraw", - "resolve", - "comment", - "reaction" - ], - "type": "string" - }, - "channel": { - "const": "linkedin", - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "action", - "type": "string" - }, - "label": { - "type": "string" - }, - "notify": { - "default": false, - "type": "boolean" - }, - "then": { - "additionalProperties": false, - "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "then" - ], - "title": "SilentActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", - "properties": { - "action_description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "manual", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "action_description", - "then" - ], - "title": "ManualActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "automation", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "AutomationNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", - "properties": { - "arms": { - "items": { - "additionalProperties": false, - "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", - "properties": { - "case": { - "minLength": 1, - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "case", - "to" - ], - "title": "Arm", - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "decision", - "type": "string" - }, - "label": { - "type": "string" - }, - "rule": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "rule", - "arms" - ], - "title": "DecisionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "terminal", - "type": "string" - }, - "label": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind" - ], - "title": "TerminalNode", - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", + "properties": { + "accepted": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + }, + "already_connected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "connection_request", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "maxLength": 300, + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "timeout": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "label", + "kind", + "accepted", + "already_connected" + ], + "title": "ConnectionRequestNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", + "properties": { + "action": { + "enum": [ + "message", + "inmail", + "email" + ], + "type": "string" + }, + "channel": { + "enum": [ + "linkedin", + "email" + ], + "type": "string" + }, + "follow_up": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "send", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "replied": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "replied" + ], + "title": "SendNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", + "properties": { + "action": { + "enum": [ + "follow", + "withdraw", + "resolve", + "comment", + "reaction" + ], + "type": "string" + }, + "channel": { + "const": "linkedin", + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "action", + "type": "string" + }, + "label": { + "type": "string" + }, + "notify": { + "default": false, + "type": "boolean" + }, + "then": { + "additionalProperties": false, + "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`.", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "then" + ], + "title": "SilentActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", + "properties": { + "action_description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "manual", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "action_description", + "then" + ], + "title": "ManualActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "automation", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "AutomationNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", + "properties": { + "arms": { + "items": { + "additionalProperties": false, + "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", + "properties": { + "case": { + "minLength": 1, + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "case", + "to" + ], + "title": "Arm", + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "decision", + "type": "string" + }, + "label": { + "type": "string" + }, + "rule": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "rule", + "arms" + ], + "title": "DecisionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "terminal", + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind" + ], + "title": "TerminalNode", + "type": "object" + } +]
- Changed
update_prospect4 fields changed- changed
Input schema / properties / channel / descriptionPrevious value: -"'linkedin' or 'email' — which funnel the update applies to and which\nhandle on `person` is matched (email against the email column; url/\nprovider_id against the LinkedIn columns)."New value: +"'linkedin' or 'email' — which funnel the update applies to: the stage\ncolumn `stage` sets, the manual mark `clear_manual_mark` removes, and the\nqueued sends a skip cancels." - removed
Input schema / properties / personRemoved value: -{ - "description": "A handle to the prospect — pass the LinkedIn URL / provider_id / email\nyou already hold; the tool resolves the row within `channel`.", - "properties": { - "display_name": { - "default": "", - "description": "Human-readable name for the UI label.", - "type": "string" - }, - "email": { - "default": "", - "description": "The lead's email, if that's the only identifier.", - "type": "string" - }, - "linkedin_provider_id": { - "default": "", - "description": "The lead's LinkedIn provider id (ACoAA…), if known.", - "type": "string" - }, - "linkedin_url": { - "default": "", - "description": "The lead's LinkedIn profile URL, if known.", - "type": "string" - } - }, - "title": "PersonRef", - "type": "object" -} - added
Input schema / properties / prospect_idAdded value: +{ + "description": "The prospect's `id` — the outreach_prospects primary key, as\nreturned on every query_prospects row and shown on the flow roster line.", + "type": "integer" +} - changed
Input schema / requiredPrevious value: -[ - "agent_id", - "person", - "channel" -]New value: +[ + "agent_id", + "prospect_id", + "channel" +]
7 tool updates
- Changed
create_agent1 field changed- added
Input schema / properties / title / maxLengthAdded value: +255
- Changed
exa_find_people1 field changed- changed
Input schema / properties / num_results / descriptionPrevious value: -"Candidates Exa retrieves and ranks per search before\nthe match filter runs. Default 10; max 100. Matches cluster at\nthe top of Exa's ranking, so 10 usually holds the right people\nfor a well-formed query; the returned `results` are shorter\nafter filtering. If `results` is thin and `rejected` is long,\nreword the query (the reject reasons say how) rather than\nraising this — the tail ranks are lower-relevance anyway."New value: +"Candidates Exa retrieves and ranks per search before\nthe match filter runs. Default 10; max 100. Matches cluster at\nthe top of Exa's ranking, so 10 usually holds the right people\nfor a well-formed query; the returned `results` are shorter\nafter filtering. If `results` is thin and `rejected` is long,\nreword the query (the reject reasons say whether and how)\nrather than raising this — the tail ranks are lower-relevance\nanyway."
- Changed
fetch_post_engagers1 field changed- changed
Input schema / properties / persist / descriptionPrevious value: -"Default True — in an agent, every engager is saved as a person\nrow automatically (see `list_name`). Pass False only for a read-only\nlookup the user doesn't want on a list: the raw engagement rows still\nland and stay queryable via `query_linkedin_post_engagements`, but no\n`agent_search_results` list is written. To filter the saved engagers,\nrecord a `verdict` on them with `record_search_results` instead."New value: +"Default True — in an agent, every person who engaged is saved as\na person row automatically (see `list_name`). Pass False only for a read-only\nlookup the user doesn't want on a list: the raw engagement rows still\nland and stay queryable via `query_linkedin_post_engagements`, but no\n`agent_search_results` list is written. To filter the saved engagers,\nrecord a `verdict` on them with `record_search_results` instead."
- Changed
setup_linkedin_monitoring1 field changed- added
Input schema / properties / name / maxLengthAdded value: +200
- Changed
setup_social_listening1 field changed- changed
Input schema / properties / search_queries / anyOfPrevious value: -[ - { - "additionalProperties": { - "items": { - "type": "string" - }, - "type": "array" - }, - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "additionalProperties": { + "items": { + "maxLength": 50, + "minLength": 3, + "type": "string" + }, + "type": "array" + }, + "type": "object" + }, + { + "type": "null" + } +]
- Changed
track_prospects2 fields changed- changed
Input schema / properties / items / descriptionPrevious value: -"People to add (see ProspectItem). Each carries a `person` with the\nidentifiers you already hold (LinkedIn URL / slug, email, or\nprovider_id) copied verbatim from the tool result that surfaced them —\nnever a slug rebuilt from a display name, which resolves to the wrong\nperson. Junk like a company URL or 'N/A' is rejected. The display name\ngoes on `person.display_name`, taken from the same row as the identifier\n— a name that shares no token with the LinkedIn name already on file for\nits ID rejects the whole call. Optional freeform `data` per item."New value: +"People to add (see ProspectItem). Each carries a `person` with the\nidentifiers you already hold (LinkedIn URL / slug, email, or\nprovider_id) copied verbatim from the tool result that surfaced them —\nnever a slug rebuilt from a display name, which resolves to the wrong\nperson. The display name goes on `person.display_name`, taken from the\nsame row as the identifier — a name that shares no token with the\nLinkedIn name already on file for its ID rejects the whole call.\nOptional freeform `data` per item." - changed
Input schema / properties / items / items / properties / person / descriptionPrevious value: -"The person to track. Pass the identifiers you already hold (LinkedIn URL / slug, provider_id, email) copied verbatim from the tool result that surfaced them — never a slug rebuilt from a display name, which resolves to a different real person. A company URL, domain, free text, or 'N/A' is rejected."New value: +"The person to track. Pass the identifiers you already hold (LinkedIn URL / slug, provider_id, email) copied verbatim from the tool result that surfaced them — never a slug rebuilt from a display name, which resolves to a different real person. A company page URL, free text, or 'N/A' isn't an identifier: a person with nothing else isn't added and comes back in `unrecognized`. A bare company domain such as acme.com, by contrast, rejects the whole call."
- Changed
update_agent1 field changed- changed
Input schema / properties / title / anyOfPrevious value: -[ - { - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "maxLength": 255, + "type": "string" + }, + { + "type": "null" + } +]
5 tool updates
- Changed
find_companies_by_tech_stack4 fields changed- changed
Input schema / properties / industry / anyOfPrevious value: -[ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "items": { + "enum": [ + "Abrasives and Nonmetallic Minerals Manufacturing", + "Accessible Architecture and Design", + "Accessible Hardware Manufacturing", + "Accommodation Services", + "Accounting", + "Administration of Justice", + "Administrative and Support Services", + "Advertising Services", + "Agricultural Chemical Manufacturing", + "Agriculture, Construction, Mining Machinery Manufacturing", + "Air, Water, and Waste Program Management", + "Airlines and Aviation", + "Alternative Dispute Resolution", + "Alternative Fuel Vehicle Manufacturing", + "Alternative Medicine", + "Ambulance Services", + "Amusement Parks and Arcades", + "Animal Feed Manufacturing", + "Animation and Post-production", + "Apparel Manufacturing", + "Appliances, Electrical, and Electronics Manufacturing", + "Architectural and Structural Metal Manufacturing", + "Architecture and Planning", + "Armed Forces", + "Artificial Rubber and Synthetic Fiber Manufacturing", + "Artists and Writers", + "Audio and Video Equipment Manufacturing", + "Automation Machinery Manufacturing", + "Aviation and Aerospace Component Manufacturing", + "Baked Goods Manufacturing", + "Banking", + "Bars, Taverns, and Nightclubs", + "Bed-and-Breakfasts, Hostels, Homestays", + "Beverage Manufacturing", + "Biomass Electric Power Generation", + "Biotechnology Research", + "Blockchain Services", + "Blogs", + "Boilers, Tanks, and Shipping Container Manufacturing", + "Book Publishing", + "Book and Periodical Publishing", + "Breweries", + "Broadcast Media Production and Distribution", + "Building Construction", + "Building Equipment Contractors", + "Building Finishing Contractors", + "Building Structure and Exterior Contractors", + "Business Consulting and Services", + "Business Content", + "Business Intelligence Platforms", + "Cable and Satellite Programming", + "Capital Markets", + "Caterers", + "Chemical Manufacturing", + "Chemical Raw Materials Manufacturing", + "Child Day Care Services", + "Chiropractors", + "Circuses and Magic Shows", + "Civic and Social Organizations", + "Civil Engineering", + "Claims Adjusting, Actuarial Services", + "Clay and Refractory Products Manufacturing", + "Climate Data and Analytics", + "Climate Technology Product Manufacturing", + "Coal Mining", + "Collection Agencies", + "Commercial and Industrial Equipment Rental", + "Commercial and Industrial Machinery Maintenance", + "Commercial and Service Industry Machinery Manufacturing", + "Communications Equipment Manufacturing", + "Community Development and Urban Planning", + "Community Services", + "Computer Games", + "Computer Hardware Manufacturing", + "Computer Networking Products", + "Computer and Network Security", + "Computers and Electronics Manufacturing", + "Conservation Programs", + "Construction", + "Construction Hardware Manufacturing", + "Consumer Goods Rental", + "Consumer Services", + "Correctional Institutions", + "Cosmetology and Barber Schools", + "Courts of Law", + "Credit Intermediation", + "Cutlery and Handtool Manufacturing", + "Dairy Product Manufacturing", + "Dance Companies", + "Data Infrastructure and Analytics", + "Data Security Software Products", + "Defense and Space Manufacturing", + "Dentists", + "Design Services", + "Desktop Computing Software Products", + "Digital Accessibility Services", + "Distilleries", + "E-Learning Providers", + "Economic Programs", + "Education", + "Education Administration Programs", + "Electric Lighting Equipment Manufacturing", + "Electric Power Generation", + "Electric Power Transmission, Control, and Distribution", + "Electrical Equipment Manufacturing", + "Electronic and Precision Equipment Maintenance", + "Embedded Software Products", + "Emergency and Relief Services", + "Engineering Services", + "Engines and Power Transmission Equipment Manufacturing", + "Entertainment Providers", + "Environmental Quality Programs", + "Environmental Services", + "Equipment Rental Services", + "Events Services", + "Executive Offices", + "Executive Search Services", + "Fabricated Metal Products", + "Facilities Services", + "Family Planning Centers", + "Farming", + "Farming, Ranching, Forestry", + "Fashion Accessories Manufacturing", + "Financial Services", + "Fine Arts Schools", + "Fire Protection", + "Fisheries", + "Flight Training", + "Food and Beverage Manufacturing", + "Food and Beverage Retail", + "Food and Beverage Services", + "Footwear Manufacturing", + "Footwear and Leather Goods Repair", + "Forestry and Logging", + "Fossil Fuel Electric Power Generation", + "Freight and Package Transportation", + "Fruit and Vegetable Preserves Manufacturing", + "Fuel Cell Manufacturing", + "Fundraising", + "Funds and Trusts", + "Furniture and Home Furnishings Manufacturing", + "Gambling Facilities and Casinos", + "Geothermal Electric Power Generation", + "Glass Product Manufacturing", + "Glass, Ceramics and Concrete Manufacturing", + "Golf Courses and Country Clubs", + "Government Administration", + "Government Relations Services", + "Graphic Design", + "Ground Passenger Transportation", + "HVAC and Refrigeration Equipment Manufacturing", + "Health and Human Services", + "Higher Education", + "Highway, Street, and Bridge Construction", + "Historical Sites", + "Holding Companies", + "Home Health Care Services", + "Horticulture", + "Hospitality", + "Hospitals", + "Hospitals and Health Care", + "Hotels and Motels", + "Household Appliance Manufacturing", + "Household Services", + "Household and Institutional Furniture Manufacturing", + "Housing Programs", + "Housing and Community Development", + "Human Resources Services", + "Hydroelectric Power Generation", + "IT Services and IT Consulting", + "IT System Custom Software Development", + "IT System Data Services", + "IT System Design Services", + "IT System Installation and Disposal", + "IT System Operations and Maintenance", + "IT System Testing and Evaluation", + "IT System Training and Support", + "Individual and Family Services", + "Industrial Machinery Manufacturing", + "Industry Associations", + "Information Services", + "Insurance", + "Insurance Agencies and Brokerages", + "Insurance Carriers", + "Insurance and Employee Benefit Funds", + "Interior Design", + "International Affairs", + "International Trade and Development", + "Internet Marketplace Platforms", + "Internet News", + "Internet Publishing", + "Interurban and Rural Bus Services", + "Investment Advice", + "Investment Banking", + "Investment Management", + "Janitorial Services", + "Landscaping Services", + "Language Schools", + "Laundry and Drycleaning Services", + "Law Enforcement", + "Law Practice", + "Leasing Non-residential Real Estate", + "Leasing Residential Real Estate", + "Leather Product Manufacturing", + "Legal Services", + "Legislative Offices", + "Libraries", + "Lime and Gypsum Products Manufacturing", + "Loan Brokers", + "Machinery Manufacturing", + "Magnetic and Optical Media Manufacturing", + "Manufacturing", + "Maritime Transportation", + "Market Research", + "Marketing Services", + "Mattress and Blinds Manufacturing", + "Measuring and Control Instrument Manufacturing", + "Meat Products Manufacturing", + "Media & Telecommunications", + "Media Production", + "Medical Equipment Manufacturing", + "Medical Practices", + "Medical and Diagnostic Laboratories", + "Mental Health Care", + "Metal Ore Mining", + "Metal Treatments", + "Metal Valve, Ball, and Roller Manufacturing", + "Metalworking Machinery Manufacturing", + "Military and International Affairs", + "Mining", + "Mobile Computing Software Products", + "Mobile Food Services", + "Mobile Gaming Apps", + "Motor Vehicle Manufacturing", + "Motor Vehicle Parts Manufacturing", + "Movies and Sound Recording", + "Movies, Videos and Sound", + "Museums", + "Museums, Historical Sites, and Zoos", + "Musicians", + "Nanotechnology Research", + "Natural Gas Distribution", + "Natural Gas Extraction", + "Newspaper Publishing", + "Non-profit Organizations", + "Nonmetallic Mineral Mining", + "Nonresidential Building Construction", + "Nuclear Electric Power Generation", + "Nursing Homes and Residential Care Facilities", + "Office Administration", + "Office Furniture and Fixtures Manufacturing", + "Oil Extraction", + "Oil and Coal Product Manufacturing", + "Oil and Gas", + "Oil, Gas, and Mining", + "Online Audio and Video Media", + "Online and Mail Order Retail", + "Operations Consulting", + "Optometrists", + "Outpatient Care Centers", + "Outsourcing and Offshoring Consulting", + "Packaging and Containers Manufacturing", + "Paint, Coating, and Adhesive Manufacturing", + "Paper and Forest Product Manufacturing", + "Pension Funds", + "Performing Arts", + "Performing Arts and Spectator Sports", + "Periodical Publishing", + "Personal Care Product Manufacturing", + "Personal Care Services", + "Personal and Laundry Services", + "Pet Services", + "Pharmaceutical Manufacturing", + "Philanthropic Fundraising Services", + "Photography", + "Physical, Occupational and Speech Therapists", + "Physicians", + "Pipeline Transportation", + "Plastics Manufacturing", + "Plastics and Rubber Product Manufacturing", + "Political Organizations", + "Postal Services", + "Primary Metal Manufacturing", + "Primary and Secondary Education", + "Printing Services", + "Professional Organizations", + "Professional Services", + "Professional Training and Coaching", + "Public Assistance Programs", + "Public Health", + "Public Policy Offices", + "Public Relations and Communications Services", + "Public Safety", + "Racetracks", + "Radio and Television Broadcasting", + "Rail Transportation", + "Railroad Equipment Manufacturing", + "Ranching", + "Ranching and Fisheries", + "Real Estate", + "Real Estate Agents and Brokers", + "Real Estate and Equipment Rental Services", + "Recreational Facilities", + "Regenerative Design", + "Religious Institutions", + "Renewable Energy Equipment Manufacturing", + "Renewable Energy Power Generation", + "Renewable Energy Semiconductor Manufacturing", + "Repair and Maintenance", + "Research Services", + "Residential Building Construction", + "Restaurants", + "Retail", + "Retail Apparel and Fashion", + "Retail Appliances, Electrical, and Electronic Equipment", + "Retail Art Dealers", + "Retail Art Supplies", + "Retail Books and Printed News", + "Retail Building Materials and Garden Equipment", + "Retail Florists", + "Retail Furniture and Home Furnishings", + "Retail Gasoline", + "Retail Groceries", + "Retail Health and Personal Care Products", + "Retail Luxury Goods and Jewelry", + "Retail Motor Vehicles", + "Retail Musical Instruments", + "Retail Office Equipment", + "Retail Office Supplies and Gifts", + "Retail Pharmacies", + "Retail Recyclable Materials & Used Merchandise", + "Reupholstery and Furniture Repair", + "Robot Manufacturing", + "Robotics Engineering", + "Rubber Products Manufacturing", + "Satellite Telecommunications", + "Savings Institutions", + "School and Employee Bus Services", + "Seafood Product Manufacturing", + "Secretarial Schools", + "Securities and Commodity Exchanges", + "Security Guards and Patrol Services", + "Security Systems Services", + "Security and Investigations", + "Semiconductor Manufacturing", + "Services for Renewable Energy", + "Services for the Elderly and Disabled", + "Sheet Music Publishing", + "Shipbuilding", + "Shuttles and Special Needs Transportation Services", + "Sightseeing Transportation", + "Skiing Facilities", + "Smart Meter Manufacturing", + "Soap and Cleaning Product Manufacturing", + "Social Networking Platforms", + "Software Development", + "Solar Electric Power Generation", + "Sound Recording", + "Space Research and Technology", + "Specialty Trade Contractors", + "Spectator Sports", + "Sporting Goods Manufacturing", + "Sports Teams and Clubs", + "Sports and Recreation Instruction", + "Spring and Wire Product Manufacturing", + "Staffing and Recruiting", + "Steam and Air-Conditioning Supply", + "Strategic Management Services", + "Subdivision of Land", + "Sugar and Confectionery Product Manufacturing", + "Surveying and Mapping Services", + "Taxi and Limousine Services", + "Technical and Vocational Training", + "Technology, Information and Internet", + "Technology, Information and Media", + "Telecommunications", + "Telecommunications Carriers", + "Telephone Call Centers", + "Temporary Help Services", + "Textile Manufacturing", + "Theater Companies", + "Think Tanks", + "Tobacco Manufacturing", + "Translation and Localization", + "Transportation Equipment Manufacturing", + "Transportation Programs", + "Transportation, Logistics, Supply Chain and Storage", + "Travel Arrangements", + "Truck Transportation", + "Trusts and Estates", + "Turned Products and Fastener Manufacturing", + "Urban Transit Services", + "Utilities", + "Utilities Administration", + "Utility System Construction", + "Vehicle Repair and Maintenance", + "Venture Capital and Private Equity Principals", + "Veterinary Services", + "Vocational Rehabilitation Services", + "Warehousing and Storage", + "Waste Collection", + "Waste Treatment and Disposal", + "Water Supply and Irrigation Systems", + "Water, Waste, Steam, and Air Conditioning Services", + "Wellness and Fitness Services", + "Wholesale", + "Wholesale Alcoholic Beverages", + "Wholesale Apparel and Sewing Supplies", + "Wholesale Appliances, Electrical, and Electronics", + "Wholesale Building Materials", + "Wholesale Chemical and Allied Products", + "Wholesale Computer Equipment", + "Wholesale Drugs and Sundries", + "Wholesale Food and Beverage", + "Wholesale Footwear", + "Wholesale Furniture and Home Furnishings", + "Wholesale Hardware, Plumbing, Heating Equipment", + "Wholesale Import and Export", + "Wholesale Luxury Goods and Jewelry", + "Wholesale Machinery", + "Wholesale Metals and Minerals", + "Wholesale Motor Vehicles and Parts", + "Wholesale Paper Products", + "Wholesale Petroleum and Petroleum Products", + "Wholesale Photography Equipment and Supplies", + "Wholesale Raw Farm Products", + "Wholesale Recyclable Materials", + "Wind Electric Power Generation", + "Wineries", + "Wireless Services", + "Women's Handbag Manufacturing", + "Wood Product Manufacturing", + "Writing and Editing", + "Zoos and Botanical Gardens" + ], + "title": "TheirStackIndustry", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } +] - changed
Input schema / properties / industry / descriptionPrevious value: -"LinkedIn-style industry names the company must match\n(e.g. [\"Retail\", \"Higher Education\"]). Values are validated\nagainst TheirStack's canonical catalog (431 industries) —\nwrong variants like \"Architecture & Planning\" raise ModelRetry\nwith close-match suggestions."New value: +"Industries the company must match, picked from this\nparameter's list of TheirStack industry names (e.g. [\"Retail\",\n\"Higher Education\"]). A name also matches its sub-industries:\n\"Manufacturing\" covers \"Motor Vehicle Manufacturing\". Some\nparents are broad (\"Consumer Services\" includes non-profits and\nreligious institutions), so name a parent only when all of it\nfits and list specific industries otherwise." - changed
Input schema / properties / industry_excludes / anyOfPrevious value: -[ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "items": { + "enum": [ + "Abrasives and Nonmetallic Minerals Manufacturing", + "Accessible Architecture and Design", + "Accessible Hardware Manufacturing", + "Accommodation Services", + "Accounting", + "Administration of Justice", + "Administrative and Support Services", + "Advertising Services", + "Agricultural Chemical Manufacturing", + "Agriculture, Construction, Mining Machinery Manufacturing", + "Air, Water, and Waste Program Management", + "Airlines and Aviation", + "Alternative Dispute Resolution", + "Alternative Fuel Vehicle Manufacturing", + "Alternative Medicine", + "Ambulance Services", + "Amusement Parks and Arcades", + "Animal Feed Manufacturing", + "Animation and Post-production", + "Apparel Manufacturing", + "Appliances, Electrical, and Electronics Manufacturing", + "Architectural and Structural Metal Manufacturing", + "Architecture and Planning", + "Armed Forces", + "Artificial Rubber and Synthetic Fiber Manufacturing", + "Artists and Writers", + "Audio and Video Equipment Manufacturing", + "Automation Machinery Manufacturing", + "Aviation and Aerospace Component Manufacturing", + "Baked Goods Manufacturing", + "Banking", + "Bars, Taverns, and Nightclubs", + "Bed-and-Breakfasts, Hostels, Homestays", + "Beverage Manufacturing", + "Biomass Electric Power Generation", + "Biotechnology Research", + "Blockchain Services", + "Blogs", + "Boilers, Tanks, and Shipping Container Manufacturing", + "Book Publishing", + "Book and Periodical Publishing", + "Breweries", + "Broadcast Media Production and Distribution", + "Building Construction", + "Building Equipment Contractors", + "Building Finishing Contractors", + "Building Structure and Exterior Contractors", + "Business Consulting and Services", + "Business Content", + "Business Intelligence Platforms", + "Cable and Satellite Programming", + "Capital Markets", + "Caterers", + "Chemical Manufacturing", + "Chemical Raw Materials Manufacturing", + "Child Day Care Services", + "Chiropractors", + "Circuses and Magic Shows", + "Civic and Social Organizations", + "Civil Engineering", + "Claims Adjusting, Actuarial Services", + "Clay and Refractory Products Manufacturing", + "Climate Data and Analytics", + "Climate Technology Product Manufacturing", + "Coal Mining", + "Collection Agencies", + "Commercial and Industrial Equipment Rental", + "Commercial and Industrial Machinery Maintenance", + "Commercial and Service Industry Machinery Manufacturing", + "Communications Equipment Manufacturing", + "Community Development and Urban Planning", + "Community Services", + "Computer Games", + "Computer Hardware Manufacturing", + "Computer Networking Products", + "Computer and Network Security", + "Computers and Electronics Manufacturing", + "Conservation Programs", + "Construction", + "Construction Hardware Manufacturing", + "Consumer Goods Rental", + "Consumer Services", + "Correctional Institutions", + "Cosmetology and Barber Schools", + "Courts of Law", + "Credit Intermediation", + "Cutlery and Handtool Manufacturing", + "Dairy Product Manufacturing", + "Dance Companies", + "Data Infrastructure and Analytics", + "Data Security Software Products", + "Defense and Space Manufacturing", + "Dentists", + "Design Services", + "Desktop Computing Software Products", + "Digital Accessibility Services", + "Distilleries", + "E-Learning Providers", + "Economic Programs", + "Education", + "Education Administration Programs", + "Electric Lighting Equipment Manufacturing", + "Electric Power Generation", + "Electric Power Transmission, Control, and Distribution", + "Electrical Equipment Manufacturing", + "Electronic and Precision Equipment Maintenance", + "Embedded Software Products", + "Emergency and Relief Services", + "Engineering Services", + "Engines and Power Transmission Equipment Manufacturing", + "Entertainment Providers", + "Environmental Quality Programs", + "Environmental Services", + "Equipment Rental Services", + "Events Services", + "Executive Offices", + "Executive Search Services", + "Fabricated Metal Products", + "Facilities Services", + "Family Planning Centers", + "Farming", + "Farming, Ranching, Forestry", + "Fashion Accessories Manufacturing", + "Financial Services", + "Fine Arts Schools", + "Fire Protection", + "Fisheries", + "Flight Training", + "Food and Beverage Manufacturing", + "Food and Beverage Retail", + "Food and Beverage Services", + "Footwear Manufacturing", + "Footwear and Leather Goods Repair", + "Forestry and Logging", + "Fossil Fuel Electric Power Generation", + "Freight and Package Transportation", + "Fruit and Vegetable Preserves Manufacturing", + "Fuel Cell Manufacturing", + "Fundraising", + "Funds and Trusts", + "Furniture and Home Furnishings Manufacturing", + "Gambling Facilities and Casinos", + "Geothermal Electric Power Generation", + "Glass Product Manufacturing", + "Glass, Ceramics and Concrete Manufacturing", + "Golf Courses and Country Clubs", + "Government Administration", + "Government Relations Services", + "Graphic Design", + "Ground Passenger Transportation", + "HVAC and Refrigeration Equipment Manufacturing", + "Health and Human Services", + "Higher Education", + "Highway, Street, and Bridge Construction", + "Historical Sites", + "Holding Companies", + "Home Health Care Services", + "Horticulture", + "Hospitality", + "Hospitals", + "Hospitals and Health Care", + "Hotels and Motels", + "Household Appliance Manufacturing", + "Household Services", + "Household and Institutional Furniture Manufacturing", + "Housing Programs", + "Housing and Community Development", + "Human Resources Services", + "Hydroelectric Power Generation", + "IT Services and IT Consulting", + "IT System Custom Software Development", + "IT System Data Services", + "IT System Design Services", + "IT System Installation and Disposal", + "IT System Operations and Maintenance", + "IT System Testing and Evaluation", + "IT System Training and Support", + "Individual and Family Services", + "Industrial Machinery Manufacturing", + "Industry Associations", + "Information Services", + "Insurance", + "Insurance Agencies and Brokerages", + "Insurance Carriers", + "Insurance and Employee Benefit Funds", + "Interior Design", + "International Affairs", + "International Trade and Development", + "Internet Marketplace Platforms", + "Internet News", + "Internet Publishing", + "Interurban and Rural Bus Services", + "Investment Advice", + "Investment Banking", + "Investment Management", + "Janitorial Services", + "Landscaping Services", + "Language Schools", + "Laundry and Drycleaning Services", + "Law Enforcement", + "Law Practice", + "Leasing Non-residential Real Estate", + "Leasing Residential Real Estate", + "Leather Product Manufacturing", + "Legal Services", + "Legislative Offices", + "Libraries", + "Lime and Gypsum Products Manufacturing", + "Loan Brokers", + "Machinery Manufacturing", + "Magnetic and Optical Media Manufacturing", + "Manufacturing", + "Maritime Transportation", + "Market Research", + "Marketing Services", + "Mattress and Blinds Manufacturing", + "Measuring and Control Instrument Manufacturing", + "Meat Products Manufacturing", + "Media & Telecommunications", + "Media Production", + "Medical Equipment Manufacturing", + "Medical Practices", + "Medical and Diagnostic Laboratories", + "Mental Health Care", + "Metal Ore Mining", + "Metal Treatments", + "Metal Valve, Ball, and Roller Manufacturing", + "Metalworking Machinery Manufacturing", + "Military and International Affairs", + "Mining", + "Mobile Computing Software Products", + "Mobile Food Services", + "Mobile Gaming Apps", + "Motor Vehicle Manufacturing", + "Motor Vehicle Parts Manufacturing", + "Movies and Sound Recording", + "Movies, Videos and Sound", + "Museums", + "Museums, Historical Sites, and Zoos", + "Musicians", + "Nanotechnology Research", + "Natural Gas Distribution", + "Natural Gas Extraction", + "Newspaper Publishing", + "Non-profit Organizations", + "Nonmetallic Mineral Mining", + "Nonresidential Building Construction", + "Nuclear Electric Power Generation", + "Nursing Homes and Residential Care Facilities", + "Office Administration", + "Office Furniture and Fixtures Manufacturing", + "Oil Extraction", + "Oil and Coal Product Manufacturing", + "Oil and Gas", + "Oil, Gas, and Mining", + "Online Audio and Video Media", + "Online and Mail Order Retail", + "Operations Consulting", + "Optometrists", + "Outpatient Care Centers", + "Outsourcing and Offshoring Consulting", + "Packaging and Containers Manufacturing", + "Paint, Coating, and Adhesive Manufacturing", + "Paper and Forest Product Manufacturing", + "Pension Funds", + "Performing Arts", + "Performing Arts and Spectator Sports", + "Periodical Publishing", + "Personal Care Product Manufacturing", + "Personal Care Services", + "Personal and Laundry Services", + "Pet Services", + "Pharmaceutical Manufacturing", + "Philanthropic Fundraising Services", + "Photography", + "Physical, Occupational and Speech Therapists", + "Physicians", + "Pipeline Transportation", + "Plastics Manufacturing", + "Plastics and Rubber Product Manufacturing", + "Political Organizations", + "Postal Services", + "Primary Metal Manufacturing", + "Primary and Secondary Education", + "Printing Services", + "Professional Organizations", + "Professional Services", + "Professional Training and Coaching", + "Public Assistance Programs", + "Public Health", + "Public Policy Offices", + "Public Relations and Communications Services", + "Public Safety", + "Racetracks", + "Radio and Television Broadcasting", + "Rail Transportation", + "Railroad Equipment Manufacturing", + "Ranching", + "Ranching and Fisheries", + "Real Estate", + "Real Estate Agents and Brokers", + "Real Estate and Equipment Rental Services", + "Recreational Facilities", + "Regenerative Design", + "Religious Institutions", + "Renewable Energy Equipment Manufacturing", + "Renewable Energy Power Generation", + "Renewable Energy Semiconductor Manufacturing", + "Repair and Maintenance", + "Research Services", + "Residential Building Construction", + "Restaurants", + "Retail", + "Retail Apparel and Fashion", + "Retail Appliances, Electrical, and Electronic Equipment", + "Retail Art Dealers", + "Retail Art Supplies", + "Retail Books and Printed News", + "Retail Building Materials and Garden Equipment", + "Retail Florists", + "Retail Furniture and Home Furnishings", + "Retail Gasoline", + "Retail Groceries", + "Retail Health and Personal Care Products", + "Retail Luxury Goods and Jewelry", + "Retail Motor Vehicles", + "Retail Musical Instruments", + "Retail Office Equipment", + "Retail Office Supplies and Gifts", + "Retail Pharmacies", + "Retail Recyclable Materials & Used Merchandise", + "Reupholstery and Furniture Repair", + "Robot Manufacturing", + "Robotics Engineering", + "Rubber Products Manufacturing", + "Satellite Telecommunications", + "Savings Institutions", + "School and Employee Bus Services", + "Seafood Product Manufacturing", + "Secretarial Schools", + "Securities and Commodity Exchanges", + "Security Guards and Patrol Services", + "Security Systems Services", + "Security and Investigations", + "Semiconductor Manufacturing", + "Services for Renewable Energy", + "Services for the Elderly and Disabled", + "Sheet Music Publishing", + "Shipbuilding", + "Shuttles and Special Needs Transportation Services", + "Sightseeing Transportation", + "Skiing Facilities", + "Smart Meter Manufacturing", + "Soap and Cleaning Product Manufacturing", + "Social Networking Platforms", + "Software Development", + "Solar Electric Power Generation", + "Sound Recording", + "Space Research and Technology", + "Specialty Trade Contractors", + "Spectator Sports", + "Sporting Goods Manufacturing", + "Sports Teams and Clubs", + "Sports and Recreation Instruction", + "Spring and Wire Product Manufacturing", + "Staffing and Recruiting", + "Steam and Air-Conditioning Supply", + "Strategic Management Services", + "Subdivision of Land", + "Sugar and Confectionery Product Manufacturing", + "Surveying and Mapping Services", + "Taxi and Limousine Services", + "Technical and Vocational Training", + "Technology, Information and Internet", + "Technology, Information and Media", + "Telecommunications", + "Telecommunications Carriers", + "Telephone Call Centers", + "Temporary Help Services", + "Textile Manufacturing", + "Theater Companies", + "Think Tanks", + "Tobacco Manufacturing", + "Translation and Localization", + "Transportation Equipment Manufacturing", + "Transportation Programs", + "Transportation, Logistics, Supply Chain and Storage", + "Travel Arrangements", + "Truck Transportation", + "Trusts and Estates", + "Turned Products and Fastener Manufacturing", + "Urban Transit Services", + "Utilities", + "Utilities Administration", + "Utility System Construction", + "Vehicle Repair and Maintenance", + "Venture Capital and Private Equity Principals", + "Veterinary Services", + "Vocational Rehabilitation Services", + "Warehousing and Storage", + "Waste Collection", + "Waste Treatment and Disposal", + "Water Supply and Irrigation Systems", + "Water, Waste, Steam, and Air Conditioning Services", + "Wellness and Fitness Services", + "Wholesale", + "Wholesale Alcoholic Beverages", + "Wholesale Apparel and Sewing Supplies", + "Wholesale Appliances, Electrical, and Electronics", + "Wholesale Building Materials", + "Wholesale Chemical and Allied Products", + "Wholesale Computer Equipment", + "Wholesale Drugs and Sundries", + "Wholesale Food and Beverage", + "Wholesale Footwear", + "Wholesale Furniture and Home Furnishings", + "Wholesale Hardware, Plumbing, Heating Equipment", + "Wholesale Import and Export", + "Wholesale Luxury Goods and Jewelry", + "Wholesale Machinery", + "Wholesale Metals and Minerals", + "Wholesale Motor Vehicles and Parts", + "Wholesale Paper Products", + "Wholesale Petroleum and Petroleum Products", + "Wholesale Photography Equipment and Supplies", + "Wholesale Raw Farm Products", + "Wholesale Recyclable Materials", + "Wind Electric Power Generation", + "Wineries", + "Wireless Services", + "Women's Handbag Manufacturing", + "Wood Product Manufacturing", + "Writing and Editing", + "Zoos and Botanical Gardens" + ], + "title": "TheirStackIndustry", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } +] - changed
Input schema / properties / industry_excludes / descriptionPrevious value: -"Industry names to exclude. Same canonical-name\nvalidation as `industry`."New value: +"Industries to exclude, sub-industries included,\nfrom the same list as `industry`."
- Changed
request_user_action1 field changed- changed
Input schema / properties / bucket_name / descriptionPrevious value: -"Short user-facing label naming the ask (e.g. \"voice-note\",\n\"founder-gift\"). Shown in the Slack DM and home-page row. Also the\nthird dimension of the pending-dedup key — two different buckets can\ncoexist as pending rows on the same prospect."New value: +"Short label naming the kind of ask (e.g. \"voice-note\",\n\"founder-gift\"). Also the third dimension of the pending-dedup key — two different buckets can\ncoexist as pending rows on the same prospect."
- Changed
setup_email_sequence2 fields changed- removed
Input schema / properties / is_follow_upRemoved value: -{ - "default": false, - "description": "If True, send as a threaded reply to the original outreach\nemail instead of a new email. Requires recipients to have been\npreviously emailed via this agent.", - "type": "boolean" -} - changed
Input schema / properties / mailbox / descriptionPrevious value: -"Email address of a connected mailbox (e.g. 'alice@acme.io').\nOmit to use the user's default mailbox. When the user has\nmultiple mailboxes connected, ask which to use rather than\nguessing — surfacing the choice is the agent's job, not a\nsilent fallback.\n\nFor segmented delivery (\"first third from A, second from B,\nlast from C\"), call this tool once per segment with the\nsegment's recipients and the segment's mailbox. The tool does\nnot rotate across calls; per-call recipient lists are how\nsegmentation is expressed."New value: +"Email address of a connected mailbox (e.g. 'alice@acme.io')\nfor new emails. Omit to use the user's default mailbox. When the user has\nmultiple mailboxes connected, ask which to use rather than\nguessing — surfacing the choice is the agent's job, not a\nsilent fallback.\n\nFor segmented delivery (\"first third from A, second from B,\nlast from C\"), call this tool once per segment with the\nsegment's recipients and the segment's mailbox. The tool does\nnot rotate across calls; per-call recipient lists are how\nsegmentation is expressed."
- Changed
setup_linkedin_sequence1 field changed- changed
Input schema / properties / linkedin_api / descriptionPrevious value: -"For inmail only. Leave unset — the tool bills the InMail\nagainst the pool the account's LinkedIn plan provides. Only pass an\nexplicit 'classic', 'sales_navigator', or 'recruiter' to force a pool."New value: +"For inmail only. Leave unset — the tool bills the InMail\nagainst the pool for the LinkedIn plan Sliq recorded when LinkedIn\nwas connected. Only pass an explicit 'classic', 'sales_navigator',\nor 'recruiter' to force a pool."
- Changed
theirstack_search4 fields changed- changed
Input schema / properties / industry / anyOfPrevious value: -[ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "items": { + "enum": [ + "Abrasives and Nonmetallic Minerals Manufacturing", + "Accessible Architecture and Design", + "Accessible Hardware Manufacturing", + "Accommodation Services", + "Accounting", + "Administration of Justice", + "Administrative and Support Services", + "Advertising Services", + "Agricultural Chemical Manufacturing", + "Agriculture, Construction, Mining Machinery Manufacturing", + "Air, Water, and Waste Program Management", + "Airlines and Aviation", + "Alternative Dispute Resolution", + "Alternative Fuel Vehicle Manufacturing", + "Alternative Medicine", + "Ambulance Services", + "Amusement Parks and Arcades", + "Animal Feed Manufacturing", + "Animation and Post-production", + "Apparel Manufacturing", + "Appliances, Electrical, and Electronics Manufacturing", + "Architectural and Structural Metal Manufacturing", + "Architecture and Planning", + "Armed Forces", + "Artificial Rubber and Synthetic Fiber Manufacturing", + "Artists and Writers", + "Audio and Video Equipment Manufacturing", + "Automation Machinery Manufacturing", + "Aviation and Aerospace Component Manufacturing", + "Baked Goods Manufacturing", + "Banking", + "Bars, Taverns, and Nightclubs", + "Bed-and-Breakfasts, Hostels, Homestays", + "Beverage Manufacturing", + "Biomass Electric Power Generation", + "Biotechnology Research", + "Blockchain Services", + "Blogs", + "Boilers, Tanks, and Shipping Container Manufacturing", + "Book Publishing", + "Book and Periodical Publishing", + "Breweries", + "Broadcast Media Production and Distribution", + "Building Construction", + "Building Equipment Contractors", + "Building Finishing Contractors", + "Building Structure and Exterior Contractors", + "Business Consulting and Services", + "Business Content", + "Business Intelligence Platforms", + "Cable and Satellite Programming", + "Capital Markets", + "Caterers", + "Chemical Manufacturing", + "Chemical Raw Materials Manufacturing", + "Child Day Care Services", + "Chiropractors", + "Circuses and Magic Shows", + "Civic and Social Organizations", + "Civil Engineering", + "Claims Adjusting, Actuarial Services", + "Clay and Refractory Products Manufacturing", + "Climate Data and Analytics", + "Climate Technology Product Manufacturing", + "Coal Mining", + "Collection Agencies", + "Commercial and Industrial Equipment Rental", + "Commercial and Industrial Machinery Maintenance", + "Commercial and Service Industry Machinery Manufacturing", + "Communications Equipment Manufacturing", + "Community Development and Urban Planning", + "Community Services", + "Computer Games", + "Computer Hardware Manufacturing", + "Computer Networking Products", + "Computer and Network Security", + "Computers and Electronics Manufacturing", + "Conservation Programs", + "Construction", + "Construction Hardware Manufacturing", + "Consumer Goods Rental", + "Consumer Services", + "Correctional Institutions", + "Cosmetology and Barber Schools", + "Courts of Law", + "Credit Intermediation", + "Cutlery and Handtool Manufacturing", + "Dairy Product Manufacturing", + "Dance Companies", + "Data Infrastructure and Analytics", + "Data Security Software Products", + "Defense and Space Manufacturing", + "Dentists", + "Design Services", + "Desktop Computing Software Products", + "Digital Accessibility Services", + "Distilleries", + "E-Learning Providers", + "Economic Programs", + "Education", + "Education Administration Programs", + "Electric Lighting Equipment Manufacturing", + "Electric Power Generation", + "Electric Power Transmission, Control, and Distribution", + "Electrical Equipment Manufacturing", + "Electronic and Precision Equipment Maintenance", + "Embedded Software Products", + "Emergency and Relief Services", + "Engineering Services", + "Engines and Power Transmission Equipment Manufacturing", + "Entertainment Providers", + "Environmental Quality Programs", + "Environmental Services", + "Equipment Rental Services", + "Events Services", + "Executive Offices", + "Executive Search Services", + "Fabricated Metal Products", + "Facilities Services", + "Family Planning Centers", + "Farming", + "Farming, Ranching, Forestry", + "Fashion Accessories Manufacturing", + "Financial Services", + "Fine Arts Schools", + "Fire Protection", + "Fisheries", + "Flight Training", + "Food and Beverage Manufacturing", + "Food and Beverage Retail", + "Food and Beverage Services", + "Footwear Manufacturing", + "Footwear and Leather Goods Repair", + "Forestry and Logging", + "Fossil Fuel Electric Power Generation", + "Freight and Package Transportation", + "Fruit and Vegetable Preserves Manufacturing", + "Fuel Cell Manufacturing", + "Fundraising", + "Funds and Trusts", + "Furniture and Home Furnishings Manufacturing", + "Gambling Facilities and Casinos", + "Geothermal Electric Power Generation", + "Glass Product Manufacturing", + "Glass, Ceramics and Concrete Manufacturing", + "Golf Courses and Country Clubs", + "Government Administration", + "Government Relations Services", + "Graphic Design", + "Ground Passenger Transportation", + "HVAC and Refrigeration Equipment Manufacturing", + "Health and Human Services", + "Higher Education", + "Highway, Street, and Bridge Construction", + "Historical Sites", + "Holding Companies", + "Home Health Care Services", + "Horticulture", + "Hospitality", + "Hospitals", + "Hospitals and Health Care", + "Hotels and Motels", + "Household Appliance Manufacturing", + "Household Services", + "Household and Institutional Furniture Manufacturing", + "Housing Programs", + "Housing and Community Development", + "Human Resources Services", + "Hydroelectric Power Generation", + "IT Services and IT Consulting", + "IT System Custom Software Development", + "IT System Data Services", + "IT System Design Services", + "IT System Installation and Disposal", + "IT System Operations and Maintenance", + "IT System Testing and Evaluation", + "IT System Training and Support", + "Individual and Family Services", + "Industrial Machinery Manufacturing", + "Industry Associations", + "Information Services", + "Insurance", + "Insurance Agencies and Brokerages", + "Insurance Carriers", + "Insurance and Employee Benefit Funds", + "Interior Design", + "International Affairs", + "International Trade and Development", + "Internet Marketplace Platforms", + "Internet News", + "Internet Publishing", + "Interurban and Rural Bus Services", + "Investment Advice", + "Investment Banking", + "Investment Management", + "Janitorial Services", + "Landscaping Services", + "Language Schools", + "Laundry and Drycleaning Services", + "Law Enforcement", + "Law Practice", + "Leasing Non-residential Real Estate", + "Leasing Residential Real Estate", + "Leather Product Manufacturing", + "Legal Services", + "Legislative Offices", + "Libraries", + "Lime and Gypsum Products Manufacturing", + "Loan Brokers", + "Machinery Manufacturing", + "Magnetic and Optical Media Manufacturing", + "Manufacturing", + "Maritime Transportation", + "Market Research", + "Marketing Services", + "Mattress and Blinds Manufacturing", + "Measuring and Control Instrument Manufacturing", + "Meat Products Manufacturing", + "Media & Telecommunications", + "Media Production", + "Medical Equipment Manufacturing", + "Medical Practices", + "Medical and Diagnostic Laboratories", + "Mental Health Care", + "Metal Ore Mining", + "Metal Treatments", + "Metal Valve, Ball, and Roller Manufacturing", + "Metalworking Machinery Manufacturing", + "Military and International Affairs", + "Mining", + "Mobile Computing Software Products", + "Mobile Food Services", + "Mobile Gaming Apps", + "Motor Vehicle Manufacturing", + "Motor Vehicle Parts Manufacturing", + "Movies and Sound Recording", + "Movies, Videos and Sound", + "Museums", + "Museums, Historical Sites, and Zoos", + "Musicians", + "Nanotechnology Research", + "Natural Gas Distribution", + "Natural Gas Extraction", + "Newspaper Publishing", + "Non-profit Organizations", + "Nonmetallic Mineral Mining", + "Nonresidential Building Construction", + "Nuclear Electric Power Generation", + "Nursing Homes and Residential Care Facilities", + "Office Administration", + "Office Furniture and Fixtures Manufacturing", + "Oil Extraction", + "Oil and Coal Product Manufacturing", + "Oil and Gas", + "Oil, Gas, and Mining", + "Online Audio and Video Media", + "Online and Mail Order Retail", + "Operations Consulting", + "Optometrists", + "Outpatient Care Centers", + "Outsourcing and Offshoring Consulting", + "Packaging and Containers Manufacturing", + "Paint, Coating, and Adhesive Manufacturing", + "Paper and Forest Product Manufacturing", + "Pension Funds", + "Performing Arts", + "Performing Arts and Spectator Sports", + "Periodical Publishing", + "Personal Care Product Manufacturing", + "Personal Care Services", + "Personal and Laundry Services", + "Pet Services", + "Pharmaceutical Manufacturing", + "Philanthropic Fundraising Services", + "Photography", + "Physical, Occupational and Speech Therapists", + "Physicians", + "Pipeline Transportation", + "Plastics Manufacturing", + "Plastics and Rubber Product Manufacturing", + "Political Organizations", + "Postal Services", + "Primary Metal Manufacturing", + "Primary and Secondary Education", + "Printing Services", + "Professional Organizations", + "Professional Services", + "Professional Training and Coaching", + "Public Assistance Programs", + "Public Health", + "Public Policy Offices", + "Public Relations and Communications Services", + "Public Safety", + "Racetracks", + "Radio and Television Broadcasting", + "Rail Transportation", + "Railroad Equipment Manufacturing", + "Ranching", + "Ranching and Fisheries", + "Real Estate", + "Real Estate Agents and Brokers", + "Real Estate and Equipment Rental Services", + "Recreational Facilities", + "Regenerative Design", + "Religious Institutions", + "Renewable Energy Equipment Manufacturing", + "Renewable Energy Power Generation", + "Renewable Energy Semiconductor Manufacturing", + "Repair and Maintenance", + "Research Services", + "Residential Building Construction", + "Restaurants", + "Retail", + "Retail Apparel and Fashion", + "Retail Appliances, Electrical, and Electronic Equipment", + "Retail Art Dealers", + "Retail Art Supplies", + "Retail Books and Printed News", + "Retail Building Materials and Garden Equipment", + "Retail Florists", + "Retail Furniture and Home Furnishings", + "Retail Gasoline", + "Retail Groceries", + "Retail Health and Personal Care Products", + "Retail Luxury Goods and Jewelry", + "Retail Motor Vehicles", + "Retail Musical Instruments", + "Retail Office Equipment", + "Retail Office Supplies and Gifts", + "Retail Pharmacies", + "Retail Recyclable Materials & Used Merchandise", + "Reupholstery and Furniture Repair", + "Robot Manufacturing", + "Robotics Engineering", + "Rubber Products Manufacturing", + "Satellite Telecommunications", + "Savings Institutions", + "School and Employee Bus Services", + "Seafood Product Manufacturing", + "Secretarial Schools", + "Securities and Commodity Exchanges", + "Security Guards and Patrol Services", + "Security Systems Services", + "Security and Investigations", + "Semiconductor Manufacturing", + "Services for Renewable Energy", + "Services for the Elderly and Disabled", + "Sheet Music Publishing", + "Shipbuilding", + "Shuttles and Special Needs Transportation Services", + "Sightseeing Transportation", + "Skiing Facilities", + "Smart Meter Manufacturing", + "Soap and Cleaning Product Manufacturing", + "Social Networking Platforms", + "Software Development", + "Solar Electric Power Generation", + "Sound Recording", + "Space Research and Technology", + "Specialty Trade Contractors", + "Spectator Sports", + "Sporting Goods Manufacturing", + "Sports Teams and Clubs", + "Sports and Recreation Instruction", + "Spring and Wire Product Manufacturing", + "Staffing and Recruiting", + "Steam and Air-Conditioning Supply", + "Strategic Management Services", + "Subdivision of Land", + "Sugar and Confectionery Product Manufacturing", + "Surveying and Mapping Services", + "Taxi and Limousine Services", + "Technical and Vocational Training", + "Technology, Information and Internet", + "Technology, Information and Media", + "Telecommunications", + "Telecommunications Carriers", + "Telephone Call Centers", + "Temporary Help Services", + "Textile Manufacturing", + "Theater Companies", + "Think Tanks", + "Tobacco Manufacturing", + "Translation and Localization", + "Transportation Equipment Manufacturing", + "Transportation Programs", + "Transportation, Logistics, Supply Chain and Storage", + "Travel Arrangements", + "Truck Transportation", + "Trusts and Estates", + "Turned Products and Fastener Manufacturing", + "Urban Transit Services", + "Utilities", + "Utilities Administration", + "Utility System Construction", + "Vehicle Repair and Maintenance", + "Venture Capital and Private Equity Principals", + "Veterinary Services", + "Vocational Rehabilitation Services", + "Warehousing and Storage", + "Waste Collection", + "Waste Treatment and Disposal", + "Water Supply and Irrigation Systems", + "Water, Waste, Steam, and Air Conditioning Services", + "Wellness and Fitness Services", + "Wholesale", + "Wholesale Alcoholic Beverages", + "Wholesale Apparel and Sewing Supplies", + "Wholesale Appliances, Electrical, and Electronics", + "Wholesale Building Materials", + "Wholesale Chemical and Allied Products", + "Wholesale Computer Equipment", + "Wholesale Drugs and Sundries", + "Wholesale Food and Beverage", + "Wholesale Footwear", + "Wholesale Furniture and Home Furnishings", + "Wholesale Hardware, Plumbing, Heating Equipment", + "Wholesale Import and Export", + "Wholesale Luxury Goods and Jewelry", + "Wholesale Machinery", + "Wholesale Metals and Minerals", + "Wholesale Motor Vehicles and Parts", + "Wholesale Paper Products", + "Wholesale Petroleum and Petroleum Products", + "Wholesale Photography Equipment and Supplies", + "Wholesale Raw Farm Products", + "Wholesale Recyclable Materials", + "Wind Electric Power Generation", + "Wineries", + "Wireless Services", + "Women's Handbag Manufacturing", + "Wood Product Manufacturing", + "Writing and Editing", + "Zoos and Botanical Gardens" + ], + "title": "TheirStackIndustry", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } +] - changed
Input schema / properties / industry / descriptionPrevious value: -"LinkedIn-style industry names the company must match\n(e.g. [\"Real Estate\", \"Architecture and Planning\"]). Values are\nvalidated against TheirStack's canonical catalog (431 industries) —\nwrong variants like \"Architecture & Planning\" raise ModelRetry\nwith close-match suggestions."New value: +"Industries the company must match, picked from this\nparameter's list of TheirStack industry names (e.g. [\"Real\nEstate\", \"Architecture and Planning\"]). A name also matches its\nsub-industries: \"Manufacturing\" covers \"Motor Vehicle\nManufacturing\". Some parents are broad (\"Consumer Services\"\nincludes non-profits and religious institutions), so name a\nparent only when all of it fits and list specific industries\notherwise." - changed
Input schema / properties / industry_excludes / anyOfPrevious value: -[ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "items": { + "enum": [ + "Abrasives and Nonmetallic Minerals Manufacturing", + "Accessible Architecture and Design", + "Accessible Hardware Manufacturing", + "Accommodation Services", + "Accounting", + "Administration of Justice", + "Administrative and Support Services", + "Advertising Services", + "Agricultural Chemical Manufacturing", + "Agriculture, Construction, Mining Machinery Manufacturing", + "Air, Water, and Waste Program Management", + "Airlines and Aviation", + "Alternative Dispute Resolution", + "Alternative Fuel Vehicle Manufacturing", + "Alternative Medicine", + "Ambulance Services", + "Amusement Parks and Arcades", + "Animal Feed Manufacturing", + "Animation and Post-production", + "Apparel Manufacturing", + "Appliances, Electrical, and Electronics Manufacturing", + "Architectural and Structural Metal Manufacturing", + "Architecture and Planning", + "Armed Forces", + "Artificial Rubber and Synthetic Fiber Manufacturing", + "Artists and Writers", + "Audio and Video Equipment Manufacturing", + "Automation Machinery Manufacturing", + "Aviation and Aerospace Component Manufacturing", + "Baked Goods Manufacturing", + "Banking", + "Bars, Taverns, and Nightclubs", + "Bed-and-Breakfasts, Hostels, Homestays", + "Beverage Manufacturing", + "Biomass Electric Power Generation", + "Biotechnology Research", + "Blockchain Services", + "Blogs", + "Boilers, Tanks, and Shipping Container Manufacturing", + "Book Publishing", + "Book and Periodical Publishing", + "Breweries", + "Broadcast Media Production and Distribution", + "Building Construction", + "Building Equipment Contractors", + "Building Finishing Contractors", + "Building Structure and Exterior Contractors", + "Business Consulting and Services", + "Business Content", + "Business Intelligence Platforms", + "Cable and Satellite Programming", + "Capital Markets", + "Caterers", + "Chemical Manufacturing", + "Chemical Raw Materials Manufacturing", + "Child Day Care Services", + "Chiropractors", + "Circuses and Magic Shows", + "Civic and Social Organizations", + "Civil Engineering", + "Claims Adjusting, Actuarial Services", + "Clay and Refractory Products Manufacturing", + "Climate Data and Analytics", + "Climate Technology Product Manufacturing", + "Coal Mining", + "Collection Agencies", + "Commercial and Industrial Equipment Rental", + "Commercial and Industrial Machinery Maintenance", + "Commercial and Service Industry Machinery Manufacturing", + "Communications Equipment Manufacturing", + "Community Development and Urban Planning", + "Community Services", + "Computer Games", + "Computer Hardware Manufacturing", + "Computer Networking Products", + "Computer and Network Security", + "Computers and Electronics Manufacturing", + "Conservation Programs", + "Construction", + "Construction Hardware Manufacturing", + "Consumer Goods Rental", + "Consumer Services", + "Correctional Institutions", + "Cosmetology and Barber Schools", + "Courts of Law", + "Credit Intermediation", + "Cutlery and Handtool Manufacturing", + "Dairy Product Manufacturing", + "Dance Companies", + "Data Infrastructure and Analytics", + "Data Security Software Products", + "Defense and Space Manufacturing", + "Dentists", + "Design Services", + "Desktop Computing Software Products", + "Digital Accessibility Services", + "Distilleries", + "E-Learning Providers", + "Economic Programs", + "Education", + "Education Administration Programs", + "Electric Lighting Equipment Manufacturing", + "Electric Power Generation", + "Electric Power Transmission, Control, and Distribution", + "Electrical Equipment Manufacturing", + "Electronic and Precision Equipment Maintenance", + "Embedded Software Products", + "Emergency and Relief Services", + "Engineering Services", + "Engines and Power Transmission Equipment Manufacturing", + "Entertainment Providers", + "Environmental Quality Programs", + "Environmental Services", + "Equipment Rental Services", + "Events Services", + "Executive Offices", + "Executive Search Services", + "Fabricated Metal Products", + "Facilities Services", + "Family Planning Centers", + "Farming", + "Farming, Ranching, Forestry", + "Fashion Accessories Manufacturing", + "Financial Services", + "Fine Arts Schools", + "Fire Protection", + "Fisheries", + "Flight Training", + "Food and Beverage Manufacturing", + "Food and Beverage Retail", + "Food and Beverage Services", + "Footwear Manufacturing", + "Footwear and Leather Goods Repair", + "Forestry and Logging", + "Fossil Fuel Electric Power Generation", + "Freight and Package Transportation", + "Fruit and Vegetable Preserves Manufacturing", + "Fuel Cell Manufacturing", + "Fundraising", + "Funds and Trusts", + "Furniture and Home Furnishings Manufacturing", + "Gambling Facilities and Casinos", + "Geothermal Electric Power Generation", + "Glass Product Manufacturing", + "Glass, Ceramics and Concrete Manufacturing", + "Golf Courses and Country Clubs", + "Government Administration", + "Government Relations Services", + "Graphic Design", + "Ground Passenger Transportation", + "HVAC and Refrigeration Equipment Manufacturing", + "Health and Human Services", + "Higher Education", + "Highway, Street, and Bridge Construction", + "Historical Sites", + "Holding Companies", + "Home Health Care Services", + "Horticulture", + "Hospitality", + "Hospitals", + "Hospitals and Health Care", + "Hotels and Motels", + "Household Appliance Manufacturing", + "Household Services", + "Household and Institutional Furniture Manufacturing", + "Housing Programs", + "Housing and Community Development", + "Human Resources Services", + "Hydroelectric Power Generation", + "IT Services and IT Consulting", + "IT System Custom Software Development", + "IT System Data Services", + "IT System Design Services", + "IT System Installation and Disposal", + "IT System Operations and Maintenance", + "IT System Testing and Evaluation", + "IT System Training and Support", + "Individual and Family Services", + "Industrial Machinery Manufacturing", + "Industry Associations", + "Information Services", + "Insurance", + "Insurance Agencies and Brokerages", + "Insurance Carriers", + "Insurance and Employee Benefit Funds", + "Interior Design", + "International Affairs", + "International Trade and Development", + "Internet Marketplace Platforms", + "Internet News", + "Internet Publishing", + "Interurban and Rural Bus Services", + "Investment Advice", + "Investment Banking", + "Investment Management", + "Janitorial Services", + "Landscaping Services", + "Language Schools", + "Laundry and Drycleaning Services", + "Law Enforcement", + "Law Practice", + "Leasing Non-residential Real Estate", + "Leasing Residential Real Estate", + "Leather Product Manufacturing", + "Legal Services", + "Legislative Offices", + "Libraries", + "Lime and Gypsum Products Manufacturing", + "Loan Brokers", + "Machinery Manufacturing", + "Magnetic and Optical Media Manufacturing", + "Manufacturing", + "Maritime Transportation", + "Market Research", + "Marketing Services", + "Mattress and Blinds Manufacturing", + "Measuring and Control Instrument Manufacturing", + "Meat Products Manufacturing", + "Media & Telecommunications", + "Media Production", + "Medical Equipment Manufacturing", + "Medical Practices", + "Medical and Diagnostic Laboratories", + "Mental Health Care", + "Metal Ore Mining", + "Metal Treatments", + "Metal Valve, Ball, and Roller Manufacturing", + "Metalworking Machinery Manufacturing", + "Military and International Affairs", + "Mining", + "Mobile Computing Software Products", + "Mobile Food Services", + "Mobile Gaming Apps", + "Motor Vehicle Manufacturing", + "Motor Vehicle Parts Manufacturing", + "Movies and Sound Recording", + "Movies, Videos and Sound", + "Museums", + "Museums, Historical Sites, and Zoos", + "Musicians", + "Nanotechnology Research", + "Natural Gas Distribution", + "Natural Gas Extraction", + "Newspaper Publishing", + "Non-profit Organizations", + "Nonmetallic Mineral Mining", + "Nonresidential Building Construction", + "Nuclear Electric Power Generation", + "Nursing Homes and Residential Care Facilities", + "Office Administration", + "Office Furniture and Fixtures Manufacturing", + "Oil Extraction", + "Oil and Coal Product Manufacturing", + "Oil and Gas", + "Oil, Gas, and Mining", + "Online Audio and Video Media", + "Online and Mail Order Retail", + "Operations Consulting", + "Optometrists", + "Outpatient Care Centers", + "Outsourcing and Offshoring Consulting", + "Packaging and Containers Manufacturing", + "Paint, Coating, and Adhesive Manufacturing", + "Paper and Forest Product Manufacturing", + "Pension Funds", + "Performing Arts", + "Performing Arts and Spectator Sports", + "Periodical Publishing", + "Personal Care Product Manufacturing", + "Personal Care Services", + "Personal and Laundry Services", + "Pet Services", + "Pharmaceutical Manufacturing", + "Philanthropic Fundraising Services", + "Photography", + "Physical, Occupational and Speech Therapists", + "Physicians", + "Pipeline Transportation", + "Plastics Manufacturing", + "Plastics and Rubber Product Manufacturing", + "Political Organizations", + "Postal Services", + "Primary Metal Manufacturing", + "Primary and Secondary Education", + "Printing Services", + "Professional Organizations", + "Professional Services", + "Professional Training and Coaching", + "Public Assistance Programs", + "Public Health", + "Public Policy Offices", + "Public Relations and Communications Services", + "Public Safety", + "Racetracks", + "Radio and Television Broadcasting", + "Rail Transportation", + "Railroad Equipment Manufacturing", + "Ranching", + "Ranching and Fisheries", + "Real Estate", + "Real Estate Agents and Brokers", + "Real Estate and Equipment Rental Services", + "Recreational Facilities", + "Regenerative Design", + "Religious Institutions", + "Renewable Energy Equipment Manufacturing", + "Renewable Energy Power Generation", + "Renewable Energy Semiconductor Manufacturing", + "Repair and Maintenance", + "Research Services", + "Residential Building Construction", + "Restaurants", + "Retail", + "Retail Apparel and Fashion", + "Retail Appliances, Electrical, and Electronic Equipment", + "Retail Art Dealers", + "Retail Art Supplies", + "Retail Books and Printed News", + "Retail Building Materials and Garden Equipment", + "Retail Florists", + "Retail Furniture and Home Furnishings", + "Retail Gasoline", + "Retail Groceries", + "Retail Health and Personal Care Products", + "Retail Luxury Goods and Jewelry", + "Retail Motor Vehicles", + "Retail Musical Instruments", + "Retail Office Equipment", + "Retail Office Supplies and Gifts", + "Retail Pharmacies", + "Retail Recyclable Materials & Used Merchandise", + "Reupholstery and Furniture Repair", + "Robot Manufacturing", + "Robotics Engineering", + "Rubber Products Manufacturing", + "Satellite Telecommunications", + "Savings Institutions", + "School and Employee Bus Services", + "Seafood Product Manufacturing", + "Secretarial Schools", + "Securities and Commodity Exchanges", + "Security Guards and Patrol Services", + "Security Systems Services", + "Security and Investigations", + "Semiconductor Manufacturing", + "Services for Renewable Energy", + "Services for the Elderly and Disabled", + "Sheet Music Publishing", + "Shipbuilding", + "Shuttles and Special Needs Transportation Services", + "Sightseeing Transportation", + "Skiing Facilities", + "Smart Meter Manufacturing", + "Soap and Cleaning Product Manufacturing", + "Social Networking Platforms", + "Software Development", + "Solar Electric Power Generation", + "Sound Recording", + "Space Research and Technology", + "Specialty Trade Contractors", + "Spectator Sports", + "Sporting Goods Manufacturing", + "Sports Teams and Clubs", + "Sports and Recreation Instruction", + "Spring and Wire Product Manufacturing", + "Staffing and Recruiting", + "Steam and Air-Conditioning Supply", + "Strategic Management Services", + "Subdivision of Land", + "Sugar and Confectionery Product Manufacturing", + "Surveying and Mapping Services", + "Taxi and Limousine Services", + "Technical and Vocational Training", + "Technology, Information and Internet", + "Technology, Information and Media", + "Telecommunications", + "Telecommunications Carriers", + "Telephone Call Centers", + "Temporary Help Services", + "Textile Manufacturing", + "Theater Companies", + "Think Tanks", + "Tobacco Manufacturing", + "Translation and Localization", + "Transportation Equipment Manufacturing", + "Transportation Programs", + "Transportation, Logistics, Supply Chain and Storage", + "Travel Arrangements", + "Truck Transportation", + "Trusts and Estates", + "Turned Products and Fastener Manufacturing", + "Urban Transit Services", + "Utilities", + "Utilities Administration", + "Utility System Construction", + "Vehicle Repair and Maintenance", + "Venture Capital and Private Equity Principals", + "Veterinary Services", + "Vocational Rehabilitation Services", + "Warehousing and Storage", + "Waste Collection", + "Waste Treatment and Disposal", + "Water Supply and Irrigation Systems", + "Water, Waste, Steam, and Air Conditioning Services", + "Wellness and Fitness Services", + "Wholesale", + "Wholesale Alcoholic Beverages", + "Wholesale Apparel and Sewing Supplies", + "Wholesale Appliances, Electrical, and Electronics", + "Wholesale Building Materials", + "Wholesale Chemical and Allied Products", + "Wholesale Computer Equipment", + "Wholesale Drugs and Sundries", + "Wholesale Food and Beverage", + "Wholesale Footwear", + "Wholesale Furniture and Home Furnishings", + "Wholesale Hardware, Plumbing, Heating Equipment", + "Wholesale Import and Export", + "Wholesale Luxury Goods and Jewelry", + "Wholesale Machinery", + "Wholesale Metals and Minerals", + "Wholesale Motor Vehicles and Parts", + "Wholesale Paper Products", + "Wholesale Petroleum and Petroleum Products", + "Wholesale Photography Equipment and Supplies", + "Wholesale Raw Farm Products", + "Wholesale Recyclable Materials", + "Wind Electric Power Generation", + "Wineries", + "Wireless Services", + "Women's Handbag Manufacturing", + "Wood Product Manufacturing", + "Writing and Editing", + "Zoos and Botanical Gardens" + ], + "title": "TheirStackIndustry", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } +] - changed
Input schema / properties / industry_excludes / descriptionPrevious value: -"Industry names to exclude. Same canonical-name\nvalidation as `industry`."New value: +"Industries to exclude, sub-industries included,\nfrom the same list as `industry`."
7 tool updates
- Changed
exa_find_people2 fields changed- changed
Input schema / properties / persist / descriptionPrevious value: -"Default True — in an agent, matched people are saved and linked\nas person rows automatically (see `list_name`). Pass False to return\nresults without saving, for a flow that folds these people into\nanother row instead — e.g. a company-find that nests them under each\ncompany row via `record_search_results`, where a standalone person\nlist would duplicate people already shown under their company."New value: +"Default True — in an agent, matched people are saved and linked\nas person rows automatically (see `list_name`). Pass False only for a\nread-only lookup the user doesn't want on a list." - added
Input schema / properties / searches / items / properties / locationsAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Places the person must be based in, by country, state or city name, e.g. ['United States'] or ['Texas', 'Colorado']. Entries are OR-ed, and the filter applies whichever provider serves the search. Spell states out: a two-letter code is read as a country, so 'CT' does not mean Connecticut. At a multinational this is what keeps overseas staff out of the results — naming a place in `query` or `system_prompt` does not filter a search pinned by `company_domain` / `company_linkedin_url`." +}
- Changed
exa_search_news3 fields changed- added
Input schema / properties / agent_idAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional — a specific agent to save the companies into.\nOmit it to use the running agent, which is the usual case." +} - added
Input schema / properties / list_nameAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Short kebab slug naming the Output-tab list bucket.\nAbsent, companies land in the 'default' list." +} - added
Input schema / properties / lookback_days / descriptionAdded value: +"Days of news history to scan (default 30)."
- Changed
fetch_post_engagers1 field changed- changed
Input schema / properties / persist / descriptionPrevious value: -"Default True — in an agent, every engager is saved as a person\nrow automatically (see `list_name`). Pass False to fetch the\nengagers without saving them as person rows: the raw engagement rows\nstill land and stay queryable via `query_linkedin_post_engagements`,\nbut no `agent_search_results` list is written — for a flow that\nrecords only a filtered subset itself, so the full unfiltered list\nwouldn't also clutter the Output tab."New value: +"Default True — in an agent, every engager is saved as a person\nrow automatically (see `list_name`). Pass False only for a read-only\nlookup the user doesn't want on a list: the raw engagement rows still\nland and stay queryable via `query_linkedin_post_engagements`, but no\n`agent_search_results` list is written. To filter the saved engagers,\nrecord a `verdict` on them with `record_search_results` instead."
- Changed
find_companies_by_tech_stack2 fields changed- added
Input schema / properties / agent_idAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional — a specific agent to save the companies into.\nOmit it to use the running agent, which is the usual case." +} - added
Input schema / properties / list_nameAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Short kebab slug naming the Output-tab list bucket (e.g.\n'shopify-brands'). Absent, companies land in the 'default' list." +}
- Changed
record_search_results2 fields changed- changed
Input schema / properties / results / items / properties / source / descriptionPrevious value: -"Where this row came from: the discovery tool that surfaced this candidate — 'theirstack' (theirstack_search, find_companies_by_tech_stack), 'tavily' (tavily_search), 'predictleads' (search_news_events), 'exa_news' (exa_search_news), 'google_news' (google_news_search), 'apollo' (apollo_search), 'exa_find_people', 'search_linkedin_people', 'post_engagers' (fetch_post_engagers, query_linkedin_post_engagements), 'luma_guests' (get_luma_guests). Use 'agent' for a row no discovery tool surfaced, such as a pasted list or a CRM read. Recorded only when the row is first created; an existing row keeps its original source."New value: +"Where this row came from: the discovery tool that surfaced this candidate — 'theirstack' (theirstack_search), 'theirstack_tech_stack' (find_companies_by_tech_stack), 'tavily' (tavily_search), 'predictleads' (search_news_events), 'exa_news' (exa_search_news), 'google_news' (google_news_search), 'apollo' (apollo_search), 'exa_find_people', 'search_linkedin_people', 'post_engagers' (fetch_post_engagers, query_linkedin_post_engagements), 'luma_guests' (get_luma_guests). Use 'agent' for a row no discovery tool surfaced, such as a pasted list or a CRM read. Recorded only when the row is first created; an existing row keeps its original source." - changed
Input schema / properties / results / items / properties / source / enumPrevious value: -[ - "agent", - "exa_find_people", - "search_linkedin_people", - "theirstack", - "luma_guests", - "post_engagers", - "exa_websets", - "warm_intro", - "tavily", - "predictleads", - "exa_news", - "google_news", - "apollo" -]New value: +[ + "agent", + "exa_find_people", + "search_linkedin_people", + "theirstack", + "theirstack_tech_stack", + "luma_guests", + "post_engagers", + "exa_websets", + "warm_intro", + "tavily", + "predictleads", + "exa_news", + "google_news", + "apollo" +]
- Changed
search_news_events2 fields changed- added
Input schema / properties / agent_idAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional — a specific agent to save the companies into. Omit\nit to use the running agent, which is the usual case." +} - added
Input schema / properties / list_nameAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Short kebab slug naming the Output-tab list bucket. Absent,\ncompanies land in the 'default' list." +}
- Changed
theirstack_search1 field changed- changed
Input schema / properties / persist / descriptionPrevious value: -"Default True — in an agent, the companies are saved as company\nrows automatically (see above). Pass False to return the postings\nwithout saving, for a read-only query or a flow that records a\nvalidated/filtered subset itself."New value: +"Default True — in an agent, the companies are saved as company\nrows automatically (see above). Pass False only for a read-only\nlookup the user doesn't want on a list."
1 tool update
- Added
get_mcp_change_calls
8 tool updates
- Changed
define_sequence1 field changed- changed
Input schema / properties / sequence / properties / nodes / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).", - "properties": { - "accepted": { - "type": "string" - }, - "already_connected": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "connection_request", - "type": "string" - }, - "label": { - "type": "string" - }, - "timeout": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "id", - "label", - "kind", - "accepted", - "already_connected" - ], - "title": "ConnectionRequestNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", - "properties": { - "action": { - "enum": [ - "message", - "inmail", - "email" - ], - "type": "string" - }, - "channel": { - "enum": [ - "linkedin", - "email" - ], - "type": "string" - }, - "follow_up": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "send", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "replied": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "replied" - ], - "title": "SendNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", - "properties": { - "action": { - "enum": [ - "follow", - "withdraw", - "resolve", - "comment", - "reaction" - ], - "type": "string" - }, - "channel": { - "const": "linkedin", - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "action", - "type": "string" - }, - "label": { - "type": "string" - }, - "notify": { - "default": false, - "type": "boolean" - }, - "then": { - "additionalProperties": false, - "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "then" - ], - "title": "SilentActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", - "properties": { - "action_description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "manual", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "action_description", - "then" - ], - "title": "ManualActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "automation", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "AutomationNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", - "properties": { - "arms": { - "items": { - "additionalProperties": false, - "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", - "properties": { - "case": { - "minLength": 1, - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "case", - "to" - ], - "title": "Arm", - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "decision", - "type": "string" - }, - "label": { - "type": "string" - }, - "rule": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "rule", - "arms" - ], - "title": "DecisionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "terminal", - "type": "string" - }, - "label": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind" - ], - "title": "TerminalNode", - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", + "properties": { + "accepted": { + "type": "string" + }, + "already_connected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "connection_request", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "maxLength": 300, + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "timeout": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "label", + "kind", + "accepted", + "already_connected" + ], + "title": "ConnectionRequestNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", + "properties": { + "action": { + "enum": [ + "message", + "inmail", + "email" + ], + "type": "string" + }, + "channel": { + "enum": [ + "linkedin", + "email" + ], + "type": "string" + }, + "follow_up": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "send", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "replied": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "replied" + ], + "title": "SendNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", + "properties": { + "action": { + "enum": [ + "follow", + "withdraw", + "resolve", + "comment", + "reaction" + ], + "type": "string" + }, + "channel": { + "const": "linkedin", + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "action", + "type": "string" + }, + "label": { + "type": "string" + }, + "notify": { + "default": false, + "type": "boolean" + }, + "then": { + "additionalProperties": false, + "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "then" + ], + "title": "SilentActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", + "properties": { + "action_description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "manual", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "action_description", + "then" + ], + "title": "ManualActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "automation", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "AutomationNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", + "properties": { + "arms": { + "items": { + "additionalProperties": false, + "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", + "properties": { + "case": { + "minLength": 1, + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "case", + "to" + ], + "title": "Arm", + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "decision", + "type": "string" + }, + "label": { + "type": "string" + }, + "rule": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "rule", + "arms" + ], + "title": "DecisionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "terminal", + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind" + ], + "title": "TerminalNode", + "type": "object" + } +]
- Changed
get_message_experiments3 fields changed- changed
Input schema / properties / node_id / descriptionPrevious value: -"A send step's node id, to scope to that step. Needs `agent_id`."New value: +"A send or connection-request step's node id, to scope to that step. Needs\n`agent_id`." - changed
Input schema / properties / outcome / anyOfPrevious value: -[ - { - "enum": [ - "replied", - "interested", - "meeting" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "accepted", + "replied", + "interested", + "meeting" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / outcome / descriptionPrevious value: -"Only messages whose prospect reached this outcome (meeting counts as interested\nand replied; interested counts as replied). None = every sent message."New value: +"Only messages whose prospect reached this outcome (meeting counts as interested\nand replied; interested counts as replied; on a connection request, replied counts as\naccepted). None = every sent message."
- Changed
query_search_results2 fields changed- added
Input schema / properties / include_rejectedAdded value: +{ + "default": false, + "description": "Also return (and count) the rows marked rejected. Default\nFalse.", + "type": "boolean" +} - added
Input schema / properties / verdictAdded value: +{ + "anyOf": [ + { + "description": "The agent's curation call on an `agent_search_results` row. NULL (no verdict) means\nunreviewed; the default task views hide only `REJECTED` rows.", + "enum": [ + "qualified", + "rejected" + ], + "title": "SearchResultVerdict", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Only the rows with this verdict, 'qualified' or 'rejected' ('rejected'\nreads the rows the user's default view filters out). Omit for every verdict." +}
- Changed
query_task_people2 fields changed- changed
Input schema / properties / filters / descriptionPrevious value: -"Clauses that must all match. For \"degree\", \"outreach_stage\", \"list\" or\n\"source\", use {\"column_key\": <dimension>, \"operator\": \"is_any_of\", \"values\": [keys]}\nwith keys as group_counts returns them ('' matches no value). For \"segment\", use the\nsame shape with the segment keys above.\nFor \"name\", \"title\", \"headline\", \"company\" or \"location\", use {\"column_key\":\n<column>, \"operator\": \"contains\", \"text\": <substring>}."New value: +"Clauses that must all match. For \"degree\", \"outreach_stage\", \"list\" or\n\"source\", use {\"column_key\": <dimension>, \"operator\": \"is_any_of\", \"values\": [keys]}\nwith keys as group_counts returns them ('' matches no value). For \"segment\", use the\nsame shape with the segment keys above. For \"verdict\", use the same shape with\n'rejected' (the people the user's default view filters out) and/or 'qualified'; it\nmatches each person's `verdict`, or with group_by=\"list\" their verdict on each list,\nand 'rejected' brings the rejected people in without `include_rejected`. For \"name\",\n\"title\", \"headline\", \"company\" or \"location\", use {\"column_key\": <column>,\n\"operator\": \"contains\", \"text\": <substring>}." - added
Input schema / properties / include_rejectedAdded value: +{ + "default": false, + "description": "Also return (and count) the people left out as rejected. Default False.", + "type": "boolean" +}
- Changed
record_search_results4 fields changed- changed
Input schema / properties / list_name / descriptionPrevious value: -"short kebab slug naming the bucket these rows belong to\n(e.g. 'oil-gas-operators', 'fintech', 'companies'). Required.\nDistinct slugs render as separate sub-pills in the Output tab; reuse\nthe same slug across calls to accumulate into one list. For\nuncategorized runs, pick a single descriptive slug (e.g. the\nentity_type plural — 'companies' / 'people') and use it consistently."New value: +"short kebab slug naming the bucket these rows belong to\n(e.g. 'oil-gas-operators', 'fintech', 'companies'). Required.\nDistinct slugs are separate lists on the agent's People and Companies\ntabs; reuse the same slug across calls to accumulate into one list. For\nuncategorized runs, pick a single descriptive slug (e.g. the\nentity_type plural — 'companies' / 'people') and use it consistently." - changed
Input schema / properties / results / descriptionPrevious value: -"The people/company rows to upsert; each SearchResult carries its\nidentifier, display_name, source, and any signals/columns to store."New value: +"The people/company rows to upsert; each SearchResult carries its\nidentifier, display_name, source, any signals/columns to store, and\noptionally its verdict and verdict_reason." - added
Input schema / properties / results / items / properties / verdictAdded value: +{ + "anyOf": [ + { + "description": "The agent's curation call on an `agent_search_results` row. NULL (no verdict) means\nunreviewed; the default task views hide only `REJECTED` rows.", + "enum": [ + "qualified", + "rejected" + ], + "title": "SearchResultVerdict", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Your judgment of this row after screening it: 'qualified' keeps it on the list; 'rejected' keeps it saved but out of the list the user sees by default, and they see it, with `verdict_reason`, when they filter the table on Verdict. Omit it to leave the row's current verdict as it is." +} - added
Input schema / properties / results / items / properties / verdict_reasonAdded value: +{ + "default": "", + "description": "Why, in one short sentence the user reads in the row's Verdict column (e.g. 'HQ in Toronto — outside the US location filter'). Required with 'rejected'; set it only with a verdict.", + "type": "string" +}
- Changed
setup_email_sequence1 field changed- changed
Input schema / properties / node_id / descriptionPrevious value: -"Optional. The id of the sequence-DAG node this batch enacts\n(a node in the agent's `sequence`, authored via `define_sequence`).\nStamped on every queued row so the Campaign Flow view can place\neach prospect and count per node. Pass it whenever the agent has a\nsequence; omit for flowless/ad-hoc sends. Rejected with ModelRetry\nif it isn't a node in the agent's sequence."New value: +"Optional. The id of the sequence-DAG node this batch enacts\n(a node in the agent's `sequence`, authored via `define_sequence`).\nStamped on every queued row so the Campaign Flow view can place\neach prospect and count per node. Pass it whenever the agent has a\nsequence. Rejected with ModelRetry if it isn't a node in the agent's\nsequence."
- Changed
setup_linkedin_sequence1 field changed- changed
Input schema / properties / node_id / descriptionPrevious value: -"Optional. The id of the sequence-DAG node this batch enacts\n(a node in the agent's `sequence`, authored via `define_sequence`).\nStamped on every queued row so the Campaign Flow view can place\neach prospect and count per node. Pass it whenever the agent has a\nsequence; omit for flowless/ad-hoc sends. Rejected with ModelRetry\nif it isn't a node in the agent's sequence."New value: +"Optional. The id of the sequence-DAG node this batch enacts\n(a node in the agent's `sequence`, authored via `define_sequence`).\nStamped on every queued row so the Campaign Flow view can place\neach prospect and count per node. Pass it whenever the agent has a\nsequence. Rejected with ModelRetry if it isn't a node in the agent's\nsequence."
- Changed
update_node2 fields changed- changed
Input schema / properties / node / descriptionPrevious value: -"The node's complete new definition — an omitted optional slot (a `follow_up`, a\n`timeout`) is dropped, not preserved, so carry forward every field the node keeps.\nA send node's `message_templates` is the exception: omitted leaves the step's\ntemplates as they are.\nIts `id` must name an existing body node and its `kind` must match that node's\ncurrent kind."New value: +"The node's complete new definition — an omitted optional slot (a `follow_up`, a\n`timeout`) is dropped, not preserved, so carry forward every field the node keeps.\nA send or connection-request node's `message_templates` is the exception: omitted\nleaves the step's templates as they are.\nIts `id` must name an existing body node and its `kind` must match that node's\ncurrent kind." - changed
Input schema / properties / node / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).", - "properties": { - "accepted": { - "type": "string" - }, - "already_connected": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "connection_request", - "type": "string" - }, - "label": { - "type": "string" - }, - "timeout": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "id", - "label", - "kind", - "accepted", - "already_connected" - ], - "title": "ConnectionRequestNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", - "properties": { - "action": { - "enum": [ - "message", - "inmail", - "email" - ], - "type": "string" - }, - "channel": { - "enum": [ - "linkedin", - "email" - ], - "type": "string" - }, - "follow_up": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "send", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_templates": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "maxItems": 2, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "replied": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "replied" - ], - "title": "SendNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", - "properties": { - "action": { - "enum": [ - "follow", - "withdraw", - "resolve", - "comment", - "reaction" - ], - "type": "string" - }, - "channel": { - "const": "linkedin", - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "action", - "type": "string" - }, - "label": { - "type": "string" - }, - "notify": { - "default": false, - "type": "boolean" - }, - "then": { - "additionalProperties": false, - "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "then" - ], - "title": "SilentActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", - "properties": { - "action_description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "manual", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "action_description", - "then" - ], - "title": "ManualActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "automation", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "AutomationNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", - "properties": { - "arms": { - "items": { - "additionalProperties": false, - "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", - "properties": { - "case": { - "minLength": 1, - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "case", - "to" - ], - "title": "Arm", - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "decision", - "type": "string" - }, - "label": { - "type": "string" - }, - "rule": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "rule", - "arms" - ], - "title": "DecisionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "terminal", - "type": "string" - }, - "label": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind" - ], - "title": "TerminalNode", - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.", + "properties": { + "accepted": { + "type": "string" + }, + "already_connected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "connection_request", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "maxLength": 300, + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "timeout": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "label", + "kind", + "accepted", + "already_connected" + ], + "title": "ConnectionRequestNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", + "properties": { + "action": { + "enum": [ + "message", + "inmail", + "email" + ], + "type": "string" + }, + "channel": { + "enum": [ + "linkedin", + "email" + ], + "type": "string" + }, + "follow_up": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "send", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "replied": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "replied" + ], + "title": "SendNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", + "properties": { + "action": { + "enum": [ + "follow", + "withdraw", + "resolve", + "comment", + "reaction" + ], + "type": "string" + }, + "channel": { + "const": "linkedin", + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "action", + "type": "string" + }, + "label": { + "type": "string" + }, + "notify": { + "default": false, + "type": "boolean" + }, + "then": { + "additionalProperties": false, + "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "then" + ], + "title": "SilentActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", + "properties": { + "action_description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "manual", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "action_description", + "then" + ], + "title": "ManualActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "automation", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "AutomationNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", + "properties": { + "arms": { + "items": { + "additionalProperties": false, + "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", + "properties": { + "case": { + "minLength": 1, + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "case", + "to" + ], + "title": "Arm", + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "decision", + "type": "string" + }, + "label": { + "type": "string" + }, + "rule": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "rule", + "arms" + ], + "title": "DecisionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "terminal", + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind" + ], + "title": "TerminalNode", + "type": "object" + } +]
5 tool updates
- Changed
correct_message_classification1 field changed- changed
Input schema / properties / source / descriptionPrevious value: -"Which table the message is in — 'linkedin_data' (a LinkedIn DM), 'email_data'\n(a sent email), or 'linkedin_queue' (a connection-request note). This is a message's\n`source` field from query_tagged_messages."New value: +"Which table the message is in — 'linkedin_data' (a LinkedIn DM), 'email_data'\n(a sent email), or 'linkedin_queue' (a connection-request note). This is a message's\n`source` field from list_sent_messages or query_tagged_messages."
- Added
get_segment_funnel - Added
list_sent_messages - Changed
query_task_people4 fields changed- changed
Input schema / properties / filters / descriptionPrevious value: -"Clauses that must all match. For \"degree\", \"outreach_stage\", \"list\" or\n\"source\", use {\"column_key\": <dimension>, \"operator\": \"is_any_of\", \"values\": [keys]}\nwith keys as group_counts returns them ('' matches no value). For \"name\", \"title\",\n\"headline\", \"company\" or \"location\", use {\"column_key\": <column>, \"operator\":\n\"contains\", \"text\": <substring>}."New value: +"Clauses that must all match. For \"degree\", \"outreach_stage\", \"list\" or\n\"source\", use {\"column_key\": <dimension>, \"operator\": \"is_any_of\", \"values\": [keys]}\nwith keys as group_counts returns them ('' matches no value). For \"segment\", use the\nsame shape with the segment keys above.\nFor \"name\", \"title\", \"headline\", \"company\" or \"location\", use {\"column_key\":\n<column>, \"operator\": \"contains\", \"text\": <substring>}." - changed
Input schema / properties / group_by / anyOfPrevious value: -[ - { - "enum": [ - "title", - "company", - "location", - "outreach_stage", - "list", - "source", - "degree", - "connector" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "title", + "company", + "location", + "outreach_stage", + "list", + "source", + "degree", + "connector", + "segment" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / group_by / descriptionPrevious value: -"The dimension to count or to pick a bucket from. \"degree\" keys are '1', '2',\n'3', 'out_of_network'; \"connector\" keys are a connector's provider_id (else their\nidentifier or name) and carry the connector's name as `label`; \"outreach_stage\"\nkeys are funnel buckets; \"list\" and \"source\" keys are list names and discovery tools; \"title\", \"company\" and \"location\"\nkey on the person's own value. A '' key is the group with no value. A person with\nseveral lists, sources or connectors counts in each."New value: +"The dimension to count or to pick a bucket from. \"degree\" keys are '1', '2',\n'3', 'out_of_network'; \"connector\" keys are a connector's provider_id (else their\nidentifier or name) and carry the connector's name as `label`; \"outreach_stage\"\nkeys are funnel buckets; \"list\" and \"source\" keys are list names and discovery tools;\n\"segment\" keys are tag names in `segment_group_id`'s group, plus 'No match' for people\nthe classifier couldn't place; \"title\", \"company\" and \"location\" key on the person's\nown value. A '' key is the group with no value (for \"segment\", people the group hasn't\nclassified). A person with several lists, sources, connectors or tags counts in each." - added
Input schema / properties / segment_group_idAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The segment group (from list_segment_groups or get_segment_funnel) a\n\"segment\" filter or group_by reads. With it, each row also carries `segment`, its tag\nnames in that group." +}
- Changed
update_segment_group1 field changed- changed
Input schema / properties / criteria / descriptionPrevious value: -"The classify scope to track — {agent_ids, senders}, the campaign (agent_tasks)\nids and teammate emails, each null = all. A provided object REPLACES the whole stored\nscope (an axis you leave out becomes all), so pass the current value of any axis you\nkeep; omit the object to leave it unchanged. classify reuses this scope, so change it\nto re-target what the segment tracks."New value: +"The classify scope to track — {agent_ids, senders}, the campaign (agent_tasks)\nids and teammate emails, each null = all. A provided object REPLACES the whole stored\nscope (an axis you leave out becomes all), so pass the current value of any axis you\nkeep; omit the object to leave it unchanged. New people in this scope are tagged\nautomatically and classify reuses it, so change it to re-target what the segment\ntracks."
12 tool updates
- Changed
add_trigger1 field changed- changed
Input schema / properties / trigger / descriptionPrevious value: -"The trigger to add"New value: +"The trigger to add. In its prompt, name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat."
- Changed
create_agent3 fields changed- changed
Input schema / properties / goal / descriptionPrevious value: -"Complete instructions for what the agent should accomplish. Write as if instructing another assistant."New value: +"Complete instructions for what the agent should accomplish. Write as if instructing another assistant. Name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat." - changed
Input schema / properties / triggers / descriptionPrevious value: -"List of triggers that determine when the agent runs. An agent can have multiple triggers of different types (e.g. one schedule plus one event handler) — they fire independently. At least one trigger is required."New value: +"List of triggers that determine when the agent runs. An agent can have multiple triggers of different types (e.g. one schedule plus one event handler) — they fire independently. At least one trigger is required. In each trigger's prompt, name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat." - changed
Input schema / properties / workspace / descriptionPrevious value: -"Optional `{'inputs': {...}, 'outputs': {...}}` dict that seeds the agent's workspace."New value: +"Optional `{'inputs': {...}, 'outputs': {...}}` dict that seeds the agent's workspace. Name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat."
- Changed
create_message_tag_group1 field changed- changed
Input schema / properties / criteria / anyOfPrevious value: -[ - { - "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", - "properties": { - "agent_ids": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "channel": { - "anyOf": [ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "positions": { - "anyOf": [ - { - "items": { - "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", - "enum": [ - "first", - "follow_up", - "reply" - ], - "title": "Position", - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "senders": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "title": "CriteriaInput", - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", + "properties": { + "agent_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "channel": { + "anyOf": [ + { + "description": "The outreach channel a query is bounded to; None means both.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "positions": { + "anyOf": [ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "senders": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "Criteria", + "type": "object" + }, + { + "type": "null" + } +]
- Changed
create_segment_group1 field changed- changed
Input schema / properties / criteria / anyOfPrevious value: -[ - { - "description": "The persisted classify scope a segment tracks — campaigns and teammates (a person has no\nintrinsic channel, so channel is a view filter, never a classify criterion). Each axis null =\nall (every campaign / every teammate). Omit the whole object to leave criteria unset (create)\nor unchanged (edit).", - "properties": { - "agent_ids": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "senders": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "title": "CriteriaInput", - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The persisted classify scope a segment tracks — campaigns and teammates (a person has no\nintrinsic channel, so channel is a view filter, never a classify criterion). Each axis null =\nall (every campaign / every teammate). Omit the whole object to leave criteria unset (create)\nor unchanged (edit).", + "properties": { + "agent_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "senders": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "Criteria", + "type": "object" + }, + { + "type": "null" + } +]
- Removed
get_message_tag_cross_tab - Added
get_tag_cross_tab - Changed
query_task_people1 field changed- added
Input schema / properties / include_criteriaAdded value: +{ + "default": false, + "description": "Also return each row's `criteria`. Default False.", + "type": "boolean" +}
- Changed
update_agent1 field changed- changed
Input schema / properties / goal / descriptionPrevious value: -"New goal/instructions"New value: +"New goal/instructions. Name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat."
- Changed
update_message_tag_group1 field changed- changed
Input schema / properties / criteria / anyOfPrevious value: -[ - { - "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", - "properties": { - "agent_ids": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "channel": { - "anyOf": [ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "positions": { - "anyOf": [ - { - "items": { - "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", - "enum": [ - "first", - "follow_up", - "reply" - ], - "title": "Position", - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "senders": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "title": "CriteriaInput", - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", + "properties": { + "agent_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "channel": { + "anyOf": [ + { + "description": "The outreach channel a query is bounded to; None means both.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "positions": { + "anyOf": [ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "senders": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "Criteria", + "type": "object" + }, + { + "type": "null" + } +]
- Changed
update_segment_group1 field changed- changed
Input schema / properties / criteria / anyOfPrevious value: -[ - { - "description": "The persisted classify scope a segment tracks — campaigns and teammates (a person has no\nintrinsic channel, so channel is a view filter, never a classify criterion). Each axis null =\nall (every campaign / every teammate). Omit the whole object to leave criteria unset (create)\nor unchanged (edit).", - "properties": { - "agent_ids": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "senders": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "title": "CriteriaInput", - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The persisted classify scope a segment tracks — campaigns and teammates (a person has no\nintrinsic channel, so channel is a view filter, never a classify criterion). Each axis null =\nall (every campaign / every teammate). Omit the whole object to leave criteria unset (create)\nor unchanged (edit).", + "properties": { + "agent_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "senders": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "Criteria", + "type": "object" + }, + { + "type": "null" + } +]
- Changed
update_trigger1 field changed- changed
Input schema / properties / prompt / descriptionPrevious value: -"New per-run instructions for this trigger."New value: +"New per-run instructions for this trigger. Name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat."
- Changed
update_workspace1 field changed- changed
Input schema / properties / value / descriptionPrevious value: -"Value to store (required for update; must not be None — use operation='delete' to remove a key).\nFor append, a list of items to add to the existing list."New value: +"Value to store (required for update; must not be None — use operation='delete' to remove a key). Name any outside resource a run reads (a Google Sheet, doc, file, URL) by the identifier its tool takes (the spreadsheet ID and tab, the URL), not only by its title — a later run doesn't see this chat.\nFor append, a list of items to add to the existing list."
14 tool updates
- Added
create_crm_task - Added
create_deal - Changed
define_sequence1 field changed- changed
Input schema / properties / sequence / properties / nodes / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).", - "properties": { - "accepted": { - "type": "string" - }, - "already_connected": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "connection_request", - "type": "string" - }, - "label": { - "type": "string" - }, - "timeout": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "id", - "label", - "kind", - "accepted", - "already_connected" - ], - "title": "ConnectionRequestNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_template` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's template lives in its own rows and is merged back in on read.", - "properties": { - "action": { - "enum": [ - "message", - "inmail", - "email" - ], - "type": "string" - }, - "channel": { - "enum": [ - "linkedin", - "email" - ], - "type": "string" - }, - "follow_up": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "send", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_template": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "replied": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "replied" - ], - "title": "SendNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", - "properties": { - "action": { - "enum": [ - "follow", - "withdraw", - "resolve", - "comment", - "reaction" - ], - "type": "string" - }, - "channel": { - "const": "linkedin", - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "action", - "type": "string" - }, - "label": { - "type": "string" - }, - "notify": { - "default": false, - "type": "boolean" - }, - "then": { - "additionalProperties": false, - "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "then" - ], - "title": "SilentActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", - "properties": { - "action_description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "manual", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "action_description", - "then" - ], - "title": "ManualActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "automation", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "AutomationNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", - "properties": { - "arms": { - "items": { - "additionalProperties": false, - "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", - "properties": { - "case": { - "minLength": 1, - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "case", - "to" - ], - "title": "Arm", - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "decision", - "type": "string" - }, - "label": { - "type": "string" - }, - "rule": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "rule", - "arms" - ], - "title": "DecisionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "terminal", - "type": "string" - }, - "label": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind" - ], - "title": "TerminalNode", - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).", + "properties": { + "accepted": { + "type": "string" + }, + "already_connected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "connection_request", + "type": "string" + }, + "label": { + "type": "string" + }, + "timeout": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "label", + "kind", + "accepted", + "already_connected" + ], + "title": "ConnectionRequestNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", + "properties": { + "action": { + "enum": [ + "message", + "inmail", + "email" + ], + "type": "string" + }, + "channel": { + "enum": [ + "linkedin", + "email" + ], + "type": "string" + }, + "follow_up": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "send", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "replied": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "replied" + ], + "title": "SendNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", + "properties": { + "action": { + "enum": [ + "follow", + "withdraw", + "resolve", + "comment", + "reaction" + ], + "type": "string" + }, + "channel": { + "const": "linkedin", + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "action", + "type": "string" + }, + "label": { + "type": "string" + }, + "notify": { + "default": false, + "type": "boolean" + }, + "then": { + "additionalProperties": false, + "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "then" + ], + "title": "SilentActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", + "properties": { + "action_description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "manual", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "action_description", + "then" + ], + "title": "ManualActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "automation", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "AutomationNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", + "properties": { + "arms": { + "items": { + "additionalProperties": false, + "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", + "properties": { + "case": { + "minLength": 1, + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "case", + "to" + ], + "title": "Arm", + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "decision", + "type": "string" + }, + "label": { + "type": "string" + }, + "rule": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "rule", + "arms" + ], + "title": "DecisionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "terminal", + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind" + ], + "title": "TerminalNode", + "type": "object" + } +]
- Added
get_message_experiments - Changed
matches_icp4 fields changed- removed
Input schema / properties / icp_locationsRemoved value: -{ - "description": "Configured ICP location strings.", - "items": { - "type": "string" - }, - "type": "array" -} - removed
Input schema / properties / locationRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Candidate location (Apollo enriched.location, or TheirStack\ncity/country). None when enrichment had none." -} - added
Input schema / properties / location_verdictAdded value: +{ + "description": "The agent's judgment of whether the candidate's HQ lies\nwithin the configured ICP locations — 'in', 'uncertain' (plausibly\nin, but the location data is partial or it sits just outside a listed\nplace — kept and flagged), or 'out' (drop).", + "enum": [ + "in", + "uncertain", + "out" + ], + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "industry_verdict", - "icp_locations", - "icp_employee_count_ranges" -]New value: +[ + "industry_verdict", + "location_verdict", + "icp_employee_count_ranges" +]
- Added
query_crm_tasks - Added
query_deals - Changed
query_people1 field changed- changed
Input schema / properties / group_by / descriptionPrevious value: -"Aggregate mode, returned instead of the row list — whole-set per-bucket counts\nover all your people (not capped by `limit`). \"title\" / \"company\" / \"location\" bucket\nby that firmographic — \"title\" falls back to the headline when the title is blank, so\nlist a title bucket's people with \"COALESCE(NULLIF(title, ''), headline) = '<key>'\";\n\"outreach_stage\" by the person's collapsed lead-funnel bucket;\n\"segment\" by the person's tag in the `segment_group_id` group. Omit for the row list."New value: +"Aggregate mode, returned instead of the row list — whole-set per-bucket counts\nover all your people (not capped by `limit`). \"title\" / \"company\" / \"location\" bucket\nby that column's value; \"outreach_stage\" by the person's collapsed lead-funnel bucket;\n\"segment\" by the person's tag in the `segment_group_id` group. Omit for the row list."
- Changed
query_task_people2 fields changed- changed
Input schema / properties / filters / descriptionPrevious value: -"Clauses that must all match. For \"degree\", \"outreach_stage\", \"list\" or\n\"source\", use {\"column_key\": <dimension>, \"operator\": \"is_any_of\", \"values\": [keys]}\nwith keys as group_counts returns them ('' matches no value). For \"name\", \"title\",\n\"company\" or \"location\", use {\"column_key\": <column>, \"operator\": \"contains\",\n\"text\": <substring>}."New value: +"Clauses that must all match. For \"degree\", \"outreach_stage\", \"list\" or\n\"source\", use {\"column_key\": <dimension>, \"operator\": \"is_any_of\", \"values\": [keys]}\nwith keys as group_counts returns them ('' matches no value). For \"name\", \"title\",\n\"headline\", \"company\" or \"location\", use {\"column_key\": <column>, \"operator\":\n\"contains\", \"text\": <substring>}." - changed
Input schema / properties / q / descriptionPrevious value: -"A substring matched against name, title and company."New value: +"A substring matched against name, title, headline and company."
- Changed
setup_linkedin_sequence1 field changed- changed
Input schema / properties / identifiers / descriptionPrevious value: -"List of recipient dicts. Each dict must carry one of\n`linkedin_url` (a profile URL or slug), `provider_id`, or `chat_id`,\ncopied verbatim from the tool result that surfaced the person — never a\nslug rebuilt from a display name, which enrolls the wrong person.\nFreeform attributes go under `data`; unknown top-level keys are\nrejected. If you have no identifier for someone, omit them rather than\nguess. When a recipient's\n`target_name` shares no tokens with the verified (resolved) name\nalready tracked for that `provider_id`, the whole batch is\nrejected with ModelRetry before anything queues — fix the pairing\nand re-send (use the person's LinkedIn URL/identifier if the pid\nis wrong; use one consistent name if it's the same person). An\nunverified pid isn't gated here — it resolves before send and the\nresolver adjudicates the name then."New value: +"List of recipient dicts. Each dict must carry one of\n`linkedin_url` (a profile URL or slug), `provider_id`, or `chat_id`,\ncopied verbatim from the tool result that surfaced the person — never a\nslug rebuilt from a display name, which enrolls the wrong person.\nFreeform attributes go under `data`; unknown top-level keys are\nrejected. If you have no identifier for someone, omit them rather than\nguess. When a recipient's\n`target_name` shares no tokens with any LinkedIn name already held\nfor that `provider_id` (a verified prospect or a post engager), the\nwhole batch is rejected with ModelRetry before anything queues —\nfix the pairing and re-send (use the person's LinkedIn\nURL/identifier if the pid is wrong; use one consistent name if it's\nthe same person). A pid with no held name isn't gated here — it\nresolves before send and the resolver adjudicates the name then."
- Changed
track_prospects1 field changed- changed
Input schema / properties / items / descriptionPrevious value: -"People to add (see ProspectItem). Each carries a `person` with the\nidentifiers you already hold (LinkedIn URL / slug, email, or\nprovider_id) copied verbatim from the tool result that surfaced them —\nnever a slug rebuilt from a display name, which resolves to the wrong\nperson. Junk like a company URL or 'N/A' is rejected. The display name\ngoes on `person.display_name`; optional freeform `data` per item."New value: +"People to add (see ProspectItem). Each carries a `person` with the\nidentifiers you already hold (LinkedIn URL / slug, email, or\nprovider_id) copied verbatim from the tool result that surfaced them —\nnever a slug rebuilt from a display name, which resolves to the wrong\nperson. Junk like a company URL or 'N/A' is rejected. The display name\ngoes on `person.display_name`, taken from the same row as the identifier\n— a name that shares no token with the LinkedIn name already on file for\nits ID rejects the whole call. Optional freeform `data` per item."
- Added
update_crm_task - Added
update_deal - Changed
update_node2 fields changed- changed
Input schema / properties / node / descriptionPrevious value: -"The node's complete new definition — an omitted optional slot (a `follow_up`, a\n`timeout`) is dropped, not preserved, so carry forward every field the node keeps.\nA send node's `message_template` is the exception: omitted leaves its template as it is.\nIts `id` must name an existing body node and its `kind` must match that node's\ncurrent kind."New value: +"The node's complete new definition — an omitted optional slot (a `follow_up`, a\n`timeout`) is dropped, not preserved, so carry forward every field the node keeps.\nA send node's `message_templates` is the exception: omitted leaves the step's\ntemplates as they are.\nIts `id` must name an existing body node and its `kind` must match that node's\ncurrent kind." - changed
Input schema / properties / node / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).", - "properties": { - "accepted": { - "type": "string" - }, - "already_connected": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "connection_request", - "type": "string" - }, - "label": { - "type": "string" - }, - "timeout": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "id", - "label", - "kind", - "accepted", - "already_connected" - ], - "title": "ConnectionRequestNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_template` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's template lives in its own rows and is merged back in on read.", - "properties": { - "action": { - "enum": [ - "message", - "inmail", - "email" - ], - "type": "string" - }, - "channel": { - "enum": [ - "linkedin", - "email" - ], - "type": "string" - }, - "follow_up": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", - "properties": { - "delay": { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - "to": { - "type": "string" - } - }, - "required": [ - "delay", - "to" - ], - "title": "TimedAdvance", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "id": { - "type": "string" - }, - "kind": { - "const": "send", - "type": "string" - }, - "label": { - "type": "string" - }, - "message_template": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "replied": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "replied" - ], - "title": "SendNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", - "properties": { - "action": { - "enum": [ - "follow", - "withdraw", - "resolve", - "comment", - "reaction" - ], - "type": "string" - }, - "channel": { - "const": "linkedin", - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "action", - "type": "string" - }, - "label": { - "type": "string" - }, - "notify": { - "default": false, - "type": "boolean" - }, - "then": { - "additionalProperties": false, - "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).", - "properties": { - "after": { - "anyOf": [ - { - "additionalProperties": false, - "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", - "properties": { - "unit": { - "enum": [ - "w", - "cd", - "d", - "h", - "m" - ], - "type": "string" - }, - "value": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "value", - "unit" - ], - "title": "EdgeDelay", - "type": "object" - }, - { - "type": "null" - } - ], - "default": null - }, - "to": { - "type": "string" - } - }, - "required": [ - "to" - ], - "title": "Advance", - "type": "object" - } - }, - "required": [ - "id", - "label", - "kind", - "channel", - "action", - "then" - ], - "title": "SilentActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", - "properties": { - "action_description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "manual", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "action_description", - "then" - ], - "title": "ManualActionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "automation", - "type": "string" - }, - "label": { - "type": "string" - }, - "then": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "then" - ], - "title": "AutomationNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", - "properties": { - "arms": { - "items": { - "additionalProperties": false, - "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", - "properties": { - "case": { - "minLength": 1, - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "case", - "to" - ], - "title": "Arm", - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "id": { - "type": "string" - }, - "kind": { - "const": "decision", - "type": "string" - }, - "label": { - "type": "string" - }, - "rule": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind", - "rule", - "arms" - ], - "title": "DecisionNode", - "type": "object" - }, - { - "additionalProperties": false, - "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "const": "terminal", - "type": "string" - }, - "label": { - "type": "string" - } - }, - "required": [ - "id", - "label", - "kind" - ], - "title": "TerminalNode", - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).", + "properties": { + "accepted": { + "type": "string" + }, + "already_connected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "connection_request", + "type": "string" + }, + "label": { + "type": "string" + }, + "timeout": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "label", + "kind", + "accepted", + "already_connected" + ], + "title": "ConnectionRequestNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.", + "properties": { + "action": { + "enum": [ + "message", + "inmail", + "email" + ], + "type": "string" + }, + "channel": { + "enum": [ + "linkedin", + "email" + ], + "type": "string" + }, + "follow_up": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n ", + "properties": { + "delay": { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + "to": { + "type": "string" + } + }, + "required": [ + "delay", + "to" + ], + "title": "TimedAdvance", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "id": { + "type": "string" + }, + "kind": { + "const": "send", + "type": "string" + }, + "label": { + "type": "string" + }, + "message_templates": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "maxItems": 2, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "replied": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "replied" + ], + "title": "SendNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.", + "properties": { + "action": { + "enum": [ + "follow", + "withdraw", + "resolve", + "comment", + "reaction" + ], + "type": "string" + }, + "channel": { + "const": "linkedin", + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "action", + "type": "string" + }, + "label": { + "type": "string" + }, + "notify": { + "default": false, + "type": "boolean" + }, + "then": { + "additionalProperties": false, + "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).", + "properties": { + "after": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.", + "properties": { + "unit": { + "enum": [ + "w", + "cd", + "d", + "h", + "m" + ], + "type": "string" + }, + "value": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "value", + "unit" + ], + "title": "EdgeDelay", + "type": "object" + }, + { + "type": "null" + } + ], + "default": null + }, + "to": { + "type": "string" + } + }, + "required": [ + "to" + ], + "title": "Advance", + "type": "object" + } + }, + "required": [ + "id", + "label", + "kind", + "channel", + "action", + "then" + ], + "title": "SilentActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.", + "properties": { + "action_description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "manual", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "action_description", + "then" + ], + "title": "ManualActionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "automation", + "type": "string" + }, + "label": { + "type": "string" + }, + "then": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "then" + ], + "title": "AutomationNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').", + "properties": { + "arms": { + "items": { + "additionalProperties": false, + "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.", + "properties": { + "case": { + "minLength": 1, + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "case", + "to" + ], + "title": "Arm", + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "id": { + "type": "string" + }, + "kind": { + "const": "decision", + "type": "string" + }, + "label": { + "type": "string" + }, + "rule": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind", + "rule", + "arms" + ], + "title": "DecisionNode", + "type": "object" + }, + { + "additionalProperties": false, + "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "const": "terminal", + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "id", + "label", + "kind" + ], + "title": "TerminalNode", + "type": "object" + } +]
11 tool updates
- Changed
classify_message_tag_group1 field changed- removed
Input schema / properties / sendersRemoved value: -{ - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "In a shared workspace, the teammate email addresses whose sent messages to\ntag (and charge for). Omit to tag every sender in the workspace. Any email not in\nthe workspace is dropped; if that leaves no valid teammate, nothing is tagged — it\ndoes NOT fall back to the whole workspace." -}
- Changed
classify_segment_group1 field changed- removed
Input schema / properties / sendersRemoved value: -{ - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "In a shared workspace, the teammate email addresses whose people to tag — people\nwith a prospect owned by one of them. Omit to tag everyone in scope (including people\nno teammate has a prospect for). Addresses outside the workspace match nobody." -}
- Changed
create_message_tag_group2 fields changed- changed
Input schema / properties / criteria / anyOfPrevious value: -[ - { - "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / first messages and follow-ups). Omit the whole object to leave criteria unset\n(create) or unchanged (edit).", - "properties": { - "agent_ids": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "channel": { - "anyOf": [ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "position": { - "anyOf": [ - { - "enum": [ - "first", - "follow_up" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "title": "CriteriaInput", - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", + "properties": { + "agent_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "channel": { + "anyOf": [ + { + "description": "The outreach channel a query is bounded to; None means both.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "positions": { + "anyOf": [ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "senders": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "CriteriaInput", + "type": "object" + }, + { + "type": "null" + } +] - changed
Input schema / properties / criteria / descriptionPrevious value: -"The classify scope this question tracks — {channel, agent_ids, position}, each\nnull = all. `channel` is 'linkedin' or 'email' (null = both); `agent_ids` are campaign\n(agent_tasks) ids from classifiable_campaigns (null = every campaign); `position` is\n'first' (each conversation's first message) or 'follow_up' (later messages sent before\nthe prospect replied), null = both. classify reuses this scope, so set it to what the\nquestion should track. Omit for all."New value: +"The classify scope this question tracks — {channel, agent_ids, positions},\neach null = all. `channel` is 'linkedin' or 'email' (null = both); `agent_ids` are\ncampaign (agent_tasks) ids from classifiable_campaigns (null = every campaign);\n`positions` lists the conversation positions to tag — 'first' (each conversation's\nfirst message), 'follow_up' (later messages sent before the prospect replied), and/or\n'reply' (messages sent after the prospect replied); null = all. `senders` are the\nteammate emails whose sent messages to tag (null = every teammate); in a shared\nworkspace, default it to the user's own email unless they ask for teammates. classify\nreuses this scope, so set it to what the question should track. Omit for all."
- Changed
create_segment_group2 fields changed- changed
Input schema / properties / criteria / anyOfPrevious value: -[ - { - "description": "The persisted classify scope a segment tracks — campaigns only (a person has no intrinsic\nchannel, so channel is a view filter, never a classify criterion). `agent_ids` null = every\ncampaign. Omit the whole object to leave criteria unset (create) or unchanged (edit).", - "properties": { - "agent_ids": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "title": "CriteriaInput", - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The persisted classify scope a segment tracks — campaigns and teammates (a person has no\nintrinsic channel, so channel is a view filter, never a classify criterion). Each axis null =\nall (every campaign / every teammate). Omit the whole object to leave criteria unset (create)\nor unchanged (edit).", + "properties": { + "agent_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "senders": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "CriteriaInput", + "type": "object" + }, + { + "type": "null" + } +] - changed
Input schema / properties / criteria / descriptionPrevious value: -"The classify scope this segment tracks — {agent_ids}, the campaign (agent_tasks)\nids from classifiable_campaigns (null = every campaign). classify reuses this scope, so\nset it to what the segment should track. Omit for every campaign."New value: +"The classify scope this segment tracks — {agent_ids, senders}: the campaign\n(agent_tasks) ids from classifiable_campaigns and the teammate emails whose prospects\nto tag, each null = all. classify reuses this scope, so set it to what the segment\nshould track; in a shared workspace, default `senders` to the user's own email unless\nthey ask for teammates. Omit for every campaign and teammate."
- Changed
estimate_message_tag_classify_scope2 fields changed- removed
Input schema / properties / positionRemoved value: -{ - "anyOf": [ - { - "enum": [ - "first", - "follow_up" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "'first' (each conversation's first message) or 'follow_up' (later messages sent\nbefore the prospect replied) — the value you'd pass as `criteria.position`. Omit for\nboth." -} - added
Input schema / properties / positionsAdded value: +{ + "anyOf": [ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The conversation positions to count — 'first' (each conversation's first\nmessage), 'follow_up' (later messages sent before the prospect replied), and/or 'reply'\n(messages sent after the prospect replied) — the value you'd pass as\n`criteria.positions`. Omit for all." +}
- Changed
estimate_segment_classify_scope1 field changed- added
Input schema / properties / sendersAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The teammate emails whose people the first run would tag, as you'd pass to\nclassify_segment_group. Omit for everyone." +}
- Changed
get_message_tag_rates2 fields changed- removed
Input schema / properties / positionRemoved value: -{ - "anyOf": [ - { - "enum": [ - "first", - "follow_up" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "'first' (each conversation's first message) or 'follow_up' (later messages\nsent before the prospect replied); omit for both." -} - added
Input schema / properties / positionsAdded value: +{ + "anyOf": [ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The conversation positions to score — 'first' (each conversation's first\nmessage), 'follow_up' (later messages sent before the prospect replied), and/or 'reply'\n(messages sent after the prospect replied); omit for all." +}
- Changed
query_tagged_messages2 fields changed- removed
Input schema / properties / positionRemoved value: -{ - "anyOf": [ - { - "enum": [ - "first", - "follow_up" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "'first' (each conversation's first message) or 'follow_up' (later messages\nsent before the prospect replied); omit for both." -} - added
Input schema / properties / positionsAdded value: +{ + "anyOf": [ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The conversation positions to return — 'first' (each conversation's first\nmessage), 'follow_up' (later messages sent before the prospect replied), and/or 'reply'\n(messages sent after the prospect replied); omit for all." +}
- Added
query_task_people - Changed
update_message_tag_group2 fields changed- changed
Input schema / properties / criteria / anyOfPrevious value: -[ - { - "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / first messages and follow-ups). Omit the whole object to leave criteria unset\n(create) or unchanged (edit).", - "properties": { - "agent_ids": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - }, - "channel": { - "anyOf": [ - { - "description": "The outreach channel a query is bounded to; None means both.", - "enum": [ - "linkedin", - "email" - ], - "title": "Channel", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "position": { - "anyOf": [ - { - "enum": [ - "first", - "follow_up" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "title": "CriteriaInput", - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The persisted classify scope a question tracks. Each axis null = all (every channel /\nevery campaign / every conversation position / every teammate). Omit the whole object to leave\ncriteria unset (create) or unchanged (edit).", + "properties": { + "agent_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "channel": { + "anyOf": [ + { + "description": "The outreach channel a query is bounded to; None means both.", + "enum": [ + "linkedin", + "email" + ], + "title": "Channel", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "positions": { + "anyOf": [ + { + "items": { + "description": "A send's spot in its conversation; a positions filter is a list of these, None for all.\nThe conversation's first outbound send is 'first', a later one sent before the prospect's\nfirst reply is 'follow_up', and any send after that reply is 'reply'. Derived, never stored\nper message.", + "enum": [ + "first", + "follow_up", + "reply" + ], + "title": "Position", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "senders": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "CriteriaInput", + "type": "object" + }, + { + "type": "null" + } +] - changed
Input schema / properties / criteria / descriptionPrevious value: -"The classify scope to track — {channel, agent_ids, position}, each null = all\n(see create_message_tag_group). A provided object REPLACES every axis; omit to leave\nthe stored criteria unchanged. classify reuses this scope, so change it to re-target\nwhat the question tracks."New value: +"The classify scope to track — {channel, agent_ids, positions, senders}, each\nnull = all (see create_message_tag_group). A provided object REPLACES every axis (an\naxis you leave out becomes all), so pass the current value of any axis you keep; omit\nthe object to leave the stored criteria unchanged. classify reuses this scope, so change it to re-target\nwhat the question tracks."
- Changed
update_segment_group2 fields changed- changed
Input schema / properties / criteria / anyOfPrevious value: -[ - { - "description": "The persisted classify scope a segment tracks — campaigns only (a person has no intrinsic\nchannel, so channel is a view filter, never a classify criterion). `agent_ids` null = every\ncampaign. Omit the whole object to leave criteria unset (create) or unchanged (edit).", - "properties": { - "agent_ids": { - "anyOf": [ - { - "items": { - "type": "integer" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "title": "CriteriaInput", - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "description": "The persisted classify scope a segment tracks — campaigns and teammates (a person has no\nintrinsic channel, so channel is a view filter, never a classify criterion). Each axis null =\nall (every campaign / every teammate). Omit the whole object to leave criteria unset (create)\nor unchanged (edit).", + "properties": { + "agent_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + }, + "senders": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "CriteriaInput", + "type": "object" + }, + { + "type": "null" + } +] - changed
Input schema / properties / criteria / descriptionPrevious value: -"The classify scope to track — {agent_ids}, the campaign (agent_tasks) ids (null =\nevery campaign). A provided object REPLACES the stored scope; omit to leave it unchanged.\nclassify reuses this scope, so change it to re-target what the segment tracks."New value: +"The classify scope to track — {agent_ids, senders}, the campaign (agent_tasks)\nids and teammate emails, each null = all. A provided object REPLACES the whole stored\nscope (an axis you leave out becomes all), so pass the current value of any axis you\nkeep; omit the object to leave it unchanged. classify reuses this scope, so change it\nto re-target what the segment tracks."
Related MCP Connectors
- Gigi AIOAuthai.usegigi
LinkedIn outreach from Claude with a human veto: review, approve and send drafts, triage replies.
LinkedIn outreach, commenting, scheduling, and data via Claude and human approval gates.
Find LinkedIn prospects, draft outreach, and run human-paced campaigns from your AI agent.
LinkedIn outreach from Claude: invites under safe caps, follow-ups, replies and inbox triage.
Related MCP Servers
- AlicenseAqualityAmaintenanceAI agent for LinkedIn outreach: finds the right people, writes in your voice, follows up, replies. Six campaign goals: sell a product or service, find a job, hire people, find partners or investors, find a vendor, research interviews. Campaigns stay drafts until you launch them.223,733 PyPI11MIT
- AlicenseNot gradedqualityDmaintenanceEnables running the entire LinkedIn sales motion inside Claude Code, including content creation, audience warming, outreach, and booking calls.9MIT

GTM Bud MCP connectorofficial
AlicenseNot gradedqualityCmaintenanceEnables AI assistants to build, preview, launch, and monitor LinkedIn outbound campaigns, draft replies for approval, manage campaign settings, and upgrade profiles.MIT
PumpGTM MCP serverofficial
AlicenseNot gradedqualityBmaintenanceEnables AI agents to find intent-signaling buyers and run LinkedIn, email, and X outreach from connected accounts while respecting platform limits. Every reply is handed back to a human for approval or decision.2MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.