Appearance
Sales tools reference
For a short task-to-domain index, use Sales tools by domain. This page remains the canonical parameter-level reference for every Sales-Management MCP tool and is intentionally complete for Claude, Codex, and other MCP clients.
Every tool the Sales-Management MCP exposes, with full parameters. These are the same tools an agent gets from the From your sales platform connectors (or the single Sales Platform tools toggle), and the same tools an external MCP client like Claude Code, Codex, or Cursor sees when you connect a ?domain= server. See Sales Platform MCP for client setup and Connectors for the app walkthrough.
Each section below is one connector (?domain= value). Drop the domain query parameter to get every tool on one server.
Conventions
- Auth — every external call needs the
Authorization: Bearer mcp_…access key from Connect AI tools. Theaccount_idin the URL selects the account but never authorizes the request. title— every tool also accepts an optionaltitlestring (a short human-readable label like "Create WhatsApp channel"). Agents fill it in so the app can show what a tool call is doing; you can ignore it from your own MCP client.- IDs — a client_id is the lead's UUID from
list_clients/scan_clients/get_client. A chat_id is the small integer fromlist_chats. A chat also carries a per-channel external client id (client_idon the chat row) and the unifiedcommon_client_id(= the lead's UUID). These are different values — the tools' parameter descriptions say which one they want. - Dates — where a tool takes
since_date/to_date, RFC3339 (2026-07-01T00:00:00Z) is always safe. Some filters silently ignore plainYYYY-MM-DDvalues (noted per tool). - Errors — validation problems come back as MCP tool errors with a plain-English message. A few read tools instead return a JSON body like
{"error": "Order not found"}(noted per tool). - Pagination — list tools default to modest page sizes and cap the maximum (noted per tool). Offset tools return live interactive pages; use
scan_clientsandexport_chatsimmutable keyset cursors for exhaustive batches.list_chatsalso pages by keyset for recent-chat browsing.
Two custom-data buckets: custom_lead_fields vs client_context
The lead card has two separate places for structured data, and the client tools accept them as two separate parameters. Don't mix them up:
custom_lead_fields | client_context | |
|---|---|---|
| What it is | Free-form business data you (or your systems) put on a lead | Facts the AI collected from the lead in conversation |
| Where it renders | The lead card's editable "Additional info" panel | The lead card's read-only "Collected Context" panel |
| Shape | Flat key → string map ({"invoice_number": "INV-7"}); non-string values are JSON-encoded, never dropped | Nested objects allowed, mirroring the assistant's client_context_schema; invoices[] entries merge by invoice_number |
| Schema | None — any keys (new keys are auto-registered so the panel shows them) | Defined per assistant: enable the feature and set the field list with assistant_toggle_client_context / assistant_update_client_context_schema |
| Merge semantics | Key-by-key overlay — an incoming key replaces that key, other keys survive | Recursive deep merge — nested objects merge, invoices[] merges by invoice_number, unrelated keys survive |
| Storage | metadata.custom_lead_fields (in Go code this bucket is called custom client fields) | metadata.client_context |
Three tools accept both buckets, with identical semantics everywhere: client_create and client_update persist them inline, and client_context_merge merges them with an optional before/after preview. get_client returns both under metadata. Cross-reference client_context keys with the assistant's client_context_schema (from get_assistant) to render human-readable labels. export_clients exports neither bucket.
The related assistant-side settings live in the Assistants connector: assistant_toggle_client_context turns collection on/off and assistant_update_client_context_schema defines which fields the AI collects — the schema lives on the assistant, the collected values live on each lead.
Assistants (sales_assistants)
Create and configure sales assistants, their skills and toolkit toggles, and run synchronous test chats.
To change part of a long instruction or skill, use the view and patch tools rather than a full replacement: read the outline, patch with dry_run, save with expected_version, test, and restore an earlier version if needed. See Edit long instructions and skills safely.
Behaviour and hand-off settings, Work Hours, Flight Control, control numbers, and live notes have their own read and write tools at the end of this section, followed by assistant_get_model. See Assistant settings, hours, and Flight Control from Claude or Codex.
list_assistants
List all active sales assistants (bots) for this account. Returns id, name, status, language, model. No parameters.
get_assistant
Get detailed info about a specific assistant including instructions, welcome message, integrations, follow-ups, metadata, and skills. The metadata surfaces the toolkit settings — notably enable_contact_details, enable_client_context, and client_context_schema (the {key, label, description} fields the bot collects from clients). Use it to render collected client_context values from get_client responses.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
assistant_create
Create a new blank assistant (sales bot) for this account. It is created switched on and attached to the account's default pipeline when one exists, but it does not copy skills, integrations, knowledge, or follow-ups.
| Param | Type | Required | Description |
|---|---|---|---|
| bot_name | string | yes | Name of the assistant |
| instructions | string | no | System prompt / instructions |
| welcome_message | string | no | Welcome message sent to new conversations |
| language | number | no | Language code (0 = auto) |
assistant_clone
Create a full independent copy of an existing assistant: instructions, welcome message, language/style, knowledge-base attachment, skills, follow-ups, integrations, and toolkit settings. Integration and follow-up rows receive new IDs; channels are not copied.
| Param | Type | Required | Description |
|---|---|---|---|
| source_assistant_id | string | yes | ID of the assistant to copy |
| bot_name | string | no | Name for the copy; omit to derive it from the source |
assistant_update
Update assistant fields: name, welcome message, language, communication style, status, model.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| bot_name | string | no | New name |
| welcome_message | string | no | New welcome message |
| language | number | no | Language code |
| communication_style | number | no | Communication style enum |
| status | number | no | 0 = off, 1 = on |
| model | string | no | Model override |
Notes: empty strings are ignored — you can't clear a text field with this tool. At least one field must be provided. status must be a JSON number.
assistant_update_instructions
Update assistant instructions (system prompt). Runs a moderation check before saving; the saved version is tagged and the tag returned.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| instructions | string | yes | New instructions text |
assistant_view_instructions
Read an assistant's live instructions in parts. With no selector it returns the outline: every markdown heading with its line range. Choose outline, section, start_line/end_line, or search. search can be added to section or to start_line/end_line to search only that part: the answer then carries within (the section, if any, and the lines searched), and matches keep their line numbers in the whole text. outline goes alone, and section with start_line/end_line is refused.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| outline | boolean | no | Return the outline (the default when no other selector is given) |
| section | string | no | A heading exactly as the outline lists it, e.g. ## Delivery; returns the section with its subsections |
| start_line | integer | no | First line (1-based) |
| end_line | integer | no | Last line; needs start_line |
| search | string | no | Text to find; returns matching lines with context |
| case_sensitive | boolean | no | Default false |
| context_lines | integer | no | Lines around each match (default 2, max 20) |
| max_results | integer | no | Most matches returned (default 20, max 50); total_matches counts them all |
Every answer includes version, total_lines, total_chars, and draft_pending (a builder draft exists). A view returns at most 400 lines or 40,000 characters; when it stops early it sets truncated and next_start_line. A heading that appears more than once returns the first and a warning with the other line numbers.
assistant_patch_instructions
Change an assistant's live instructions with exact edits, applied in order, each to the text the earlier ones left.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| edits | array | yes | 1–20 edits: {op: "replace", find, replace} (an empty replace deletes), {op: "insert_after", find, text} (text goes right after find), or {op: "append", text} (text goes on a new line at the end) |
| expected_version | string | no | The version a view returned; the call is refused if the instructions changed since |
| dry_run | boolean | no | Return the diff without saving |
Notes:
- Exactly once. Every
findmust occur exactly once in the current text, character for character. Afindwith no match, or with several, refuses the whole call and nothing changes. The error names the edit (edit_index) and, for several matches, their line numbers (match_lines). There is no fuzzy matching; when a match differs only in letter case, quotes, or whitespace, the error says so innear_miss. - Same save as a full replacement. A saved patch goes through moderation and instruction history like
assistant_update_instructions, and re-syncs voice agents. If the live text is not yet a saved version, it is saved first, so the patch can be undone. A flagged text is not saved and, as with any flagged save, the assistant is switched off. - Versions. A saved patch gets a new
version(mcp-patch-…) and reportsprevious_version. The write is a compare-and-set: a save that lands between the read and the write returnsversion_conflictwith the current version. - Answer.
diff_stat(lines added and removed, characters before and after), a unifieddiff(bounded;diff_truncatedwhen cut),saved, andwarnings. - Builder drafts. The patch changes the live text and keeps a pending builder draft. A warning says that accepting the draft later in the dashboard would replace the live text, this change included.
assistant_list_instruction_versions
List saved versions of an assistant's instructions, newest first.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| limit | integer | no | Default 20, max 100 |
| diff_history_id | string | no | Also return the diff from that version to the live text |
Each version has history_id, version, created_at, chars, lines, source (dashboard, sales_mcp_replace, sales_mcp_patch, initial, or unknown, read from the version string), and is_live (its text equals the live text). The same versions appear in the dashboard's instruction history.
assistant_restore_instructions
Put a saved version back as the assistant's live instructions.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| history_id | string | yes | The version to restore (from assistant_list_instruction_versions) |
| dry_run | boolean | no | Return the diff without saving |
The live text is saved as a version first unless it already is one, so a restore can be undone. The restored text keeps its own version string, as a dashboard restore does, and goes through moderation.
assistant_list_skills
List all skills for an assistant with name, description, is_always_active, and sort order.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
assistant_create_skill
Create a new skill for an assistant. Skills are loadable instruction modules; the slug is generated from the name.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| name | string | yes | Skill name |
| description | string | no | Short description of what the skill does |
| instructions | string | no | Skill instructions content |
| is_always_active | boolean | no | If true, the skill is always injected into the system prompt |
assistant_update_skill
Update an existing skill's name, description, instructions, or active status.
| Param | Type | Required | Description |
|---|---|---|---|
| skill_id | string | yes | The skill ID |
| name | string | no | New name |
| description | string | no | New description |
| instructions | string | no | New instructions |
| is_always_active | boolean | no | Always-active flag |
| sort_order | number | no | Display order |
Notes: empty strings are ignored; at least one field must be provided.
assistant_delete_skill
Delete a skill by ID.
| Param | Type | Required | Description |
|---|---|---|---|
| skill_id | string | yes | The skill ID to delete |
A deleted skill stays in its version history and can be brought back with assistant_restore_skill.
assistant_view_skill
Read one skill in parts: its name, slug, description, is_always_active, sort_order, version, and draft_pending, and its live instructions.
| Param | Type | Required | Description |
|---|---|---|---|
| skill_id | string | yes | The skill ID |
| outline, section, start_line, end_line, search, case_sensitive, context_lines, max_results | no | As in assistant_view_instructions |
With no selector, a short skill (up to 200 lines and 12,000 characters) comes back whole; a longer one as its outline. search combines with section or start_line/end_line as in assistant_view_instructions, with within in the answer. A skill's version is a hash of its name, description, instructions, and always-active flag.
assistant_patch_skill
Change a skill's live instructions with the same exact edits as assistant_patch_instructions.
| Param | Type | Required | Description |
|---|---|---|---|
| skill_id | string | yes | The skill ID |
| edits | array | yes | 1–20 edits, as in assistant_patch_instructions |
| expected_version | string | no | The version assistant_view_skill returned |
| dry_run | boolean | no | Return the diff without saving |
The skill's previous state is kept in its version history. If skill version history isn't set up yet, a saved patch is refused (skill_history_unavailable) and nothing changes; a dry_run still works. A pending skill draft is kept, with the same warning as for instructions.
assistant_list_skill_versions
List earlier versions of an assistant's skills, newest first, deleted skills included.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| skill_id | string | no | Only this skill's versions |
| include_deleted | boolean | no | Include skills that no longer exist (default true) |
| limit | integer | no | Default 20, max 100 |
Each row has history_id, skill_id, op, changed_at, name, slug, is_always_active, sort_order, chars, lines, version, skill_exists, and is_live (restoring it would change nothing). op: "update" is the skill as it was before an edit of its name, slug, description, instructions, or always-active flag; op: "delete" is the skill as it was when deleted (not kept when the skill goes with its deleted assistant). Draft-only edits and reordering keep no version. The live skill itself is listed by assistant_list_skills.
assistant_restore_skill
Restore a skill version.
| Param | Type | Required | Description |
|---|---|---|---|
| history_id | string | yes | The version (from assistant_list_skill_versions) |
| dry_run | boolean | no | Show what would change without saving |
If the skill still exists, its name, slug, description, instructions, and always-active flag are set back, and its current state is kept as a version. If it was deleted, it is recreated on its assistant, under its old ID when that ID is free (same_id), and the answer carries its skill_id; if that version was already recreated since the delete, the answer names that skill instead of making a copy. A restored skill keeps its old slug unless another skill of the assistant took it. Like a patch, a restore is refused with version_conflict when the skill changed while it was being written.
assistant_get_config_link
Generate a link to the web UI assistant configuration page.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
get_assistant_available_tools
List all tools (functions) available to a specific assistant based on its connected integrations, knowledge bases, and feature toggles. Returns tool names and descriptions by default — useful when writing or reviewing the assistant's instructions, which should reference these tool names.
To inspect what inputs a tool expects, pass tool_name to get one tool's full parameters JSON schema (parameter names, types, descriptions, required/optional status, enum values), or include_schemas: true to include the schema for every tool in the listing.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| tool_name | string | no | Return only this tool, with its full parameters JSON schema. An unknown name errors with the list of available tool names |
| include_schemas | boolean | no | Include each tool's parameters JSON schema in the listing (default false — the listing can be large for assistants with many connected tools) |
Example single-tool response:
json
{
"assistant_id": "42",
"tools_count": 1,
"tools": [
{
"name": "search_product",
"description": "Search products in the knowledge base",
"parameters": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "search query for product name" },
"filter": { "type": "string", "enum": ["startswith", "contains", "exact"] },
"limit": { "type": "number", "description": "max results to return" }
},
"required": ["query"]
}
}
]
}Tools that take no arguments omit the parameters key.
test_chat_start
Start a test chat session with an assistant. Clears any previous test chat history; the assistant_id is the session key for subsequent messages.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID to test |
test_chat_send
Send a message in an active test chat session and get the assistant's AI response. Uses the assistant's current live instructions, skills, and toolkit toggles — the same pipeline as the web test chat. Synchronous: waits for the full response.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID (same as from test_chat_start) |
| message | string | conditional | The client's text. Required unless images is given — a client often sends a photo with no text |
| images | array of string | no | Photos the client sends with this message: public http(s) image URLs, max 4. The assistant sees them as pictures, exactly like a photo arriving from Telegram or Instagram |
Rehearsing a photo: pass the URL in images, not in message. A URL pasted into message reaches the assistant as text only — it cannot see the picture. With images, the assistant gets the picture and its URL, so it can hand the photo to an image search tool (for example search_product with image_url) the way the live assistant does. To replay a photo a real client sent, copy the images URLs from get_chat_messages (normally https://static.mychatbot.app/chat-images/…) as they are — no re-upload. Use links that open without a login. If a photo cannot be fetched, the platform usually removes it from the turn and the assistant answers from the text alone; with some models the send fails instead. Either way the turn stays in the test chat history, so run test_chat_start to clear it before you retry with a working link.
json
// test_chat_send — the client sends only a photo
{
"assistant_id": "42",
"images": ["https://static.mychatbot.app/chat-images/….jpg"]
}Returns JSON (not plain text):
json
{
"assistant_id": "42",
"response": "the assistant's reply text",
"tool_calls": [
{
"name": "get_order",
"arguments": "{\"order_id\":55}",
"result_preview": "first 500 chars of the tool result…",
"result_bytes": 4120
}
]
}Notes: tool_calls traces every tool the assistant invoked while answering. result_preview is capped at 500 characters — the full tool results stay in the stored history; read them with test_chat_get_history. Test-chat traffic is free: test replies are recorded as test-chat usage, not billed as messages.
test_chat_get_history
Read the stored test chat history for an assistant, including the assistant's internal tool calls — use it to debug which tools ran and what they returned.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID (same as from test_chat_start) |
| limit | number | no | Number of recent messages to return (default 50, max 200) |
| include_tool_details | boolean | no | false (default): tool calls listed by name only, tool-result content omitted. true: each tool call as {id, name, arguments} plus the tool-result message content |
| include_business_context | boolean | no | false (default): platform-injected business-knowledge (RAG) context messages are stripped from the history. true: keep them — role tool messages named __business_knowledge__, content capped by max_tool_result_chars. Business context is the knowledge the platform fed the assistant while it answered, not a tool the assistant called |
| max_tool_result_chars | number | no | Max characters of each included tool-result or business-context message's content (default 2000, 0 = full stored content) |
Notes: the session is per-assistant — one stored history per assistant, keyed by assistant_id. History persists until test_chat_start or test_chat_end clears it. A user turn sent with photos carries images (their URLs) next to its content; a photo-only turn has images and no content. Tool results truncated by max_tool_result_chars carry content_truncated: true and content_full_chars alongside the capped content. Platform-injected context notes are excluded by default, so the tool calls you see are the assistant's own — pass include_business_context=true to see them too.
test_chat_end
End an active test chat session and clear the chat history.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID to end the test chat for |
assistant_toggle_contact_details
Enable or disable the Contact Details collection toolkit feature. When enabled, the assistant proactively collects contact info (name, email, phone) from clients during conversations. Maps to the Contact Details toggle on the configuration page.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | integer | yes | The assistant ID (number or numeric string) |
| enabled | boolean | yes | True to enable, false to disable |
assistant_toggle_client_context
Enable or disable the Client Context toolkit feature — the AI-collected bucket described above. When enabling you may pass a schema array defining the fields to collect. Enabling also creates an always-active Client Context Collection skill that teaches the bot when to save context, and syncs the voice agent; disabling removes that skill.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | integer | yes | The assistant ID (number or numeric string) |
| enabled | boolean | yes | True to enable, false to disable |
| schema | array | no | Fields to collect; omit to keep the existing schema. Each item is {key, label, description} |
| schema[].key | string | yes (per item) | Stable machine-readable key (e.g. company_size) |
| schema[].label | string | yes (per item) | Human-readable label (e.g. Company Size) |
| schema[].description | string | no | Hint describing what to collect / when |
assistant_update_client_context_schema
Replace the list of Client Context fields without toggling the feature. The provided array fully replaces the stored schema — use it to add, remove, rename, or re-describe fields after Client Context is already enabled.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | integer | yes | The assistant ID (number or numeric string) |
| schema | array | yes | Full replacement schema; each item is {key, label, description}, key and label required |
assistant_toggle_strip_kb_history
Enable or disable Strip knowledge base results from history across all knowledge-base integrations attached to an assistant. When enabled, KB tool results are removed from conversation history before the next LLM call, reducing input tokens (full results remain in chat logs and the agent can re-search). Applied to every KB integration sharing the assistant's knowledge base; the response reports how many integrations were updated.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | integer | yes | The assistant ID (number or numeric string) |
| enabled | boolean | yes | True to enable, false to disable |
assistant_get_settings
Read an assistant's behaviour and hand-off settings: the values it runs on, for every key assistant_update_settings accepts.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
Returns settings (every key below) and read_only.auto_return_from_manual_handoff: when true, a chat a person switched off by hand stays off and auto-return skips it. That one is changed in the dashboard only: it is the Keep chats switched off by hand in operator mode checkbox under Settings → Handoff Control, and a checked box stores true. A soft-deleted assistant, or one of another account, is refused.
assistant_update_settings
Change an assistant's behaviour and hand-off settings, checked the way the dashboard checks them. Pass only the keys to change in settings; every other setting, and every other value the assistant stores, stays as it is.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| settings | object | yes | The keys to change (below) |
| dry_run | boolean | no | Check and show the change without saving |
| Setting | Type | Dashboard | Meaning |
|---|---|---|---|
manageLeads | boolean | Toolkit → Lead Management (Manage leads locally) | The assistant sets lead statuses and labels in chats (its set_client_status and label tools) |
switch_stop_word | string | Settings → Handoff Control (Stop word for manual handoff) | An operator who types this word in a chat switches it to operator mode and back. Other messages an operator sends from the channel's own app leave the chat with the AI; a reply sent from the dashboard to a chat the AI is handling switches it to operator mode regardless of this setting. Empty turns it off. At most 100 characters |
implicit_operator_handoff | boolean | Handoff Control (Implicit operator handoff) | Any operator message, including one sent from the channel's own app, switches the chat to operator mode. A reply sent from the dashboard to a chat the AI is handling switches it whether this is on or off |
silent_switch_to_operator | boolean | Handoff Control (Silent handoff) | Don't tell the customer when the chat switches |
auto_return_from_handoff | boolean | Handoff Control (Auto return from handoff) | Give a handed-off chat back to the assistant after the operator is silent |
auto_return_from_handoff_interval_minutes | integer or null | Handoff Control (Custom return interval) | Minutes of silence, 1–10080; null means 1440 |
end_conversation | boolean | Settings → Reply & Tools (User can have last word in a chat) | The assistant may end a finished conversation (its end_conversation tool) |
remove_links / remove_video_links / remove_file_links | boolean | Reply & Tools | Strip image, video, or file links from replies |
send_links_as_separate_messages | boolean | Reply & Tools | Send links as separate messages |
split_reply_by_paragraphs | boolean | Reply & Tools | Send each paragraph as its own message |
primary_language | string or null | Reply & Tools | Reply language when a chat has none of its own; one of the dashboard's languages by name (English, Ukrainian, Persian (Farsi), …); null clears it |
allow_language_switch | boolean | Reply & Tools | The assistant may switch a chat's language |
web_search | boolean | Toolkit → Web Search | The assistant may search the web |
web_search_model | string | Web Search | perplexity/sonar or perplexity/sonar-pro-search |
communication_guidelines | string | Role, Notes, Skills & Communication | Tone and style rules applied to every reply. Empty: none. At most 20,000 characters |
The dashboard's rules apply:
- A stop word and implicit hand-off exclude each other. Setting a stop word turns implicit hand-off off; turning implicit hand-off on clears the stop word; both at once is refused.
- The interval applies only while auto-return is on. Turning auto-return off also clears the interval and turns
auto_return_from_manual_handoffoff. - Choosing a
web_search_modelturns web search on; turning web search off storesperplexity/sonar.
Returns before and after for every key that changes, including keys a rule changed, and saved. A change to the values the assistant already has saves nothing. If the dashboard saved the assistant between the read and the write, the change is applied again to the fresher copy; after three tries it answers version_conflict and changes nothing. A dashboard tab opened before the change can still show the old values until it is reloaded.
assistant_get_working_hours
Read an assistant's Work Hours: timezone, timezone_in_effect (the stored one, or Europe/Kiev when none is set), each weekday's start, end, off (breaks), and on (extra hours), and has_shifts.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
assistant_set_working_hours
Set an assistant's Work Hours. Only the weekdays given change.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| timezone | string | no | A timezone the dashboard offers, e.g. Europe/London; Europe/Kyiv, UTC, and GMT are stored as the dashboard's Europe/Kiev and Etc/GMT |
| days | object | no | Weekday (Monday…Sunday) → {start, end, off?, on?}, or null for a closed day |
| dry_run | boolean | no | Check and show the change without saving |
At least one of timezone and days is required, and a timezone is required when the assistant has none. Times are whole hours from 00:00 to 23:00, as the dashboard's picker offers them. A day's start and end are both set or both empty. off and on are lists of {start, end} pairs (at most 20 each). An end that is not after its start runs past midnight. A timezone the dashboard does not offer is refused, with the dashboard's timezones at the same UTC offset.
With shifts set, the assistant is switched on inside them and off outside them, within a minute of the save, while the plan and balance allow. A week with no shifts leaves its on/off status alone and only sets its clock. Returns before, after (the timezone and the days that change), has_shifts, and saved. If the assistant was saved between the read and the write, the change is applied again to the fresher copy; after three tries it answers version_conflict and changes nothing.
assistant_get_flight_control
Flight Control status of an assistant: telegram_group_connected, connect_link (the Telegram link that adds the Flight Control bot to a group), dashboard_link, and the number of control numbers and live notes in force.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
Connecting a Telegram group needs a person: they open connect_link in Telegram and pick or create the group, and the bot confirms in it. It cannot be done over MCP.
assistant_list_control_numbers
List an assistant's control numbers: phones whose WhatsApp messages to it are instructions from the business, not a customer's. Returns numbers (id, label, phone, access, enabled), the assistant's WhatsApp channels and whether each can take control messages, tool_groups, timezone, plan_allowed, and limits.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
assistant_add_control_number
Add a control number to an assistant. Pro plan or higher; at most 5 per assistant.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| phone | string | yes | International format, e.g. +43 660 0000000 (00 works for +) |
| label | string | no | Whose number it is, at most 60 characters |
| access | object | no | Tool group → level, e.g. {"orders": "view"}: the groups and levels assistant_list_control_numbers returns in tool_groups — today clients, orders, bookings, setup, and usage, each view. Omitted: live notes only, no tool group |
| dry_run | boolean | no | Check without adding |
Returns the number and its number_id. The same number twice, a number without its country code, or a sixth number is refused.
assistant_remove_control_number
Remove a control number. Its chat becomes a customer chat again; nothing it said while it was a control chat is shown to the assistant as customer history.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| number_id | string | yes | The control number's id |
| dry_run | boolean | no | Show the number without removing it |
assistant_list_live_notes
List an assistant's live notes in force: note_id, text, source, created_at, expires_at (UTC) and ends_local, and the timezone they are read in.
A note's times are in the assistant's timezone for notes: its Work Hours timezone, else its WhatsApp number's country, else UTC (see When a note ends). This is not always timezone_in_effect from assistant_get_working_hours. When it would come from the WhatsApp numbers and they cannot be read right now, timezone is null and ends_local is left out.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
assistant_add_live_note
Add a live note: temporary context every reply sees, such as "the kitchen closes at 20:00 today". Pro plan or higher; at most 500 characters and 50 notes in force.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| text | string | yes | The note |
| until | string | no | +30m or +2h (1 minute to 7 days), end_of_today, end_of_tomorrow, closing (today's Work Hours end), or a local date and time YYYY-MM-DD HH:MM, all in the assistant's timezone for notes (above). Omit to keep the note until removed |
| dry_run | boolean | no | Check and show the end without adding |
A local time the clocks skip when daylight saving starts is refused, as is a time already past. closing is refused when Work Hours have no closing time left today.
assistant_remove_live_note
End a live note now.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| note_id | string | yes | The note's id |
| dry_run | boolean | no | Show the note without ending it |
assistant_get_model
The AI model an assistant answers with, where it comes from, and its price.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
Returns effective_model with its display_name, source — assistant (its own model), account (the account's model), or default (neither is set) — and account_model. A model stored on the assistant that replies do not use, because it is not a model the platform knows, is shown as ignored_assistant_override. price_per_100_messages is the model's price in US dollars per 100 messages; a model the price catalog does not list has null there and price_note: "not in the price catalog". retired marks a model that can no longer be chosen, and same_price_models lists the models that can be chosen at the same price. Change an assistant's own model with assistant_update (model); the account's model is set in the dashboard. A deleted assistant is refused.
Clients, Leads & Pipelines (sales_clients)
Leads with their notes, tasks, and attachment records; labels; pipelines and funnel stages; CSV/JSON export.
The team, custom lead fields, duplicate leads and their merge, bulk changes, and renaming, recoloring, or moving a status have their own tools at the end of this section.
list_clients
List one bounded, live page of clients (leads/customers), with filters. The response is {clients, count, total, limit, offset}. Start with offset=0, add count to the offset after each call, and stop when offset + count >= total (or when count < limit). An explicit zero or negative limit does not mean "all"; it is normalized to the default 20. The maximum is 100.
Offset paging is appropriate for interactive lookup, not an exhaustive batch: creates, deletes, changing filters, and activity-based reordering can shift rows between calls. Use scan_clients for complete large-data traversal.
| Param | Type | Required | Description |
|---|---|---|---|
| funnel_status | string | no | Filter by funnel status |
| labels | string | no | Comma-separated labels to filter by |
| search | string | no | Search across name, email, phone |
| pipeline_id | string | no | Filter by pipeline ID |
| has_phone_number | boolean | no | Only clients with a phone number (false is ignored, it does not filter for absence) |
| has_email | boolean | no | Only clients with an email (false is ignored) |
| order_by | string | no | last_active_desc, last_active_asc, created_at_desc, created_at_asc |
| limit | number | no | Max results (default 20, max 100) |
| offset | number | no | Offset for pagination |
scan_clients
Read one bounded, account-wide page of clients for exhaustive caller-side batch processing. Rows are ordered by immutable client UUID ascending. The response is {clients, count, limit, next_after_id}. Pass next_after_id back as after_id and stop when count < limit.
Each row is a compact batch projection: id, name, email, phone, company, pipeline/status, labels, manager, created time, and last activity. Use get_client only for selected records that require full metadata.
The exact row fields are id, full_name, email, phone_number, company_name, pipeline_id, funnel_status, labels, manager_id, created_at, and last_active. next_after_id is present on every non-empty page and absent on the terminal empty page.
There is no offset, server-computed total, sort, search, or server-side filter. Apply pipeline, status, labels, contact details, and other mutable conditions in the caller as each row arrives. New or deleted records cannot shift an existing unvisited row behind an offset.
| Param | Type | Required | Description |
|---|---|---|---|
| limit | number | no | Max results (default 20, max 100) |
| after_id | string | no | Immutable client UUID cursor. Pass next_after_id from the prior response; omit on the first call |
get_client
Get detailed info about a specific client including contact info, funnel status, labels, and metadata. The response includes metadata.client_context (only populated when the assistant has Client Context enabled) and metadata.custom_lead_fields — see the two buckets. Cross-reference client_context keys with the assistant's client_context_schema from get_assistant for human-readable labels.
| Param | Type | Required | Description |
|---|---|---|---|
| client_id | string | yes | The client ID (UUID) = clients.id. Same value as a chat's common_client_id, not a chat's per-channel client_id |
client_create
Create a new client (lead/customer) in a validated pipeline. Pass extra data inline via custom_lead_fields and/or client_context — both persist with the client. Never put structured data in a note; notes are free text and are not displayed as fields.
| Param | Type | Required | Description |
|---|---|---|---|
| full_name | string | yes | Client full name |
| string | no | Email address (normalized) | |
| phone_number | string | no | Phone number (normalized) |
| company_name | string | no | Company name |
| funnel_status | string | no | Initial funnel status; must exist in the client's effective pipeline (matched case-insensitively, stored with the pipeline's casing) |
| labels | array of string | no | Labels to assign |
| pipeline_id | string | no | Pipeline ID; when omitted, new clients use the account default |
| custom_lead_fields | object | no | Flat key/value custom fields for the lead card's editable "Additional info" panel, e.g. {"invoice_number":"INV-7"}. Values stored as strings; non-string values JSON-encoded. Merged key-by-key |
| client_context | object | no | Structured context for the read-only "Collected Context" panel, mirroring the assistant's client_context_schema. Nested objects supported; invoices[] entries merge by invoice_number |
| assistant_id | number | no | The Sales assistant that will work this lead. Shown on the lead as its Sales agent |
| channel | string | no | The channel that assistant will use, as the platform is named on the Channels page (Instagram, Messenger, TelegramAccount, EChatViber, Email, Calls, …). Only accepted together with assistant_id |
Notes: if a client with the same normalized email or phone already exists, returns {"error":"DUPLICATE_CLIENT","existing_client_id":"…"} (plus match_reason) and does not modify the existing client — use client_update or client_context_merge instead.
client_update
Update client fields: name, contact info, funnel status, labels, manager. Also accepts custom_lead_fields and client_context, which are merged into the client's existing metadata — unrelated keys are preserved.
| Param | Type | Required | Description |
|---|---|---|---|
| client_id | string | yes | The client ID (UUID) |
| full_name | string | no | New name |
| string | no | New email | |
| phone_number | string | no | New phone |
| company_name | string | no | New company |
| funnel_status | string | no | New funnel status (not validated on this path) |
| labels | array of string | no | Replaces the label set (not additive) |
| manager_id | string | no | Assigned manager ID |
| custom_lead_fields | object | no | Same contract as on client_create |
| client_context | object | no | Same contract as on client_create |
| assistant_id | integer | no | The lead's default Sales assistant, its numeric id from list_assistants: the AI assistant that talks to this lead, not the human manager (manager_id). send_one_off_message uses it when no assistant is named. 0 clears the stored default assistant and channel |
| channel | string | no | The default channel for that assistant, as the communication_channel value from list_channels (for example Instagram, TelegramAccount, ViberPhone, Email, Calls). It must be connected for that assistant. Requires assistant_id |
| if_funnel_status | string | no | Apply the update only if the lead's funnel status is still this one (case-insensitive). If the lead has moved, nothing is written and the call fails with FUNNEL_STATUS_CONFLICT naming the current status: re-read the lead and decide again rather than retrying |
Notes: empty strings are ignored — you can't clear a field with this tool.
Changing funnel_status or labels arms the follow-up configured for the new status or label set, exactly as a manual change in the dashboard does; the answer's follow_ups array says what was scheduled for each of the lead's chats. When funnel_status is sent, the answer also has previous_funnel_status. Pass if_funnel_status whenever the change is based on the status you last saw (marking a lead invited after messaging it, or any pass over many leads): the lead may have replied and been moved by the assistant in the meantime, and a plain write would move it back.
client_context_merge
Merge data into a client's metadata.client_context and/or metadata.custom_lead_fields without wiping unrelated fields. Deep-merges nested objects; invoices[] entries merge by invoice_number (existing updated in place, new appended). Idempotent. With confirm=false (default) it returns a before/after preview without writing; with confirm=true it applies the merge and returns the updated client. Prefer client_create/client_update when creating or editing a client; use this when you want a preview or are only touching metadata.
| Param | Type | Required | Description |
|---|---|---|---|
| client_id | string | yes | The client ID (UUID) |
| custom_lead_fields | object | no* | Same contract as on client_create |
| client_context | object | no* | Same contract as on client_create |
| confirm | boolean | no | false (default) previews; true applies |
*At least one of the two buckets must be non-empty.
client_delete
Delete a client and all associated chats.
| Param | Type | Required | Description |
|---|---|---|---|
| client_id | string | yes | The client ID (UUID) |
client_list_notes / client_get_note / client_create_note / client_update_note / client_delete_note
Free-text notes on the lead card.
| Tool | Params |
|---|---|
| client_list_notes | client_id (required), limit (default 20, max 100), offset |
| client_get_note | client_id, note_id (both required) |
| client_create_note | client_id, content (both required) |
| client_update_note | client_id, note_id, content (all required; content replaces the note) |
| client_delete_note | client_id, note_id (both required) |
client_list_notes returns {notes, count, total, limit, offset}. This is a live interactive page; inserts or deletes between offset calls can shift rows.
client_list_tasks / client_create_task / client_update_task / client_delete_task
To-dos on the lead card.
| Tool | Params |
|---|---|
| client_list_tasks | client_id (required), limit (default 20, max 100), offset |
| client_create_task | client_id, title (required); description; priority (low, medium, high, urgent); due_date (RFC3339) |
| client_update_task | client_id, task_id (required); title; description; status (pending, in_progress, completed, cancelled); priority |
| client_delete_task | client_id, task_id (both required) |
client_list_tasks returns {tasks, count, total, limit, offset}. This is a live interactive page; inserts or deletes between offset calls can shift rows.
client_list_attachments / client_get_attachment
Read a client's attachment records (files, documents).
| Tool | Params |
|---|---|
| client_list_attachments | client_id (required), limit (default 20, max 100), offset |
| client_get_attachment | client_id, attachment_id (both required) |
client_list_attachments returns {attachments, count, total, limit, offset}. This is a live interactive page; inserts or deletes between offset calls can shift rows.
client_create_attachment
Attach an existing file URL to a client. This creates an attachment record (metadata); it does not upload file bytes.
| Param | Type | Required | Description |
|---|---|---|---|
| client_id | string | yes | The client ID (UUID) |
| file_name | string | yes | Display file name |
| file_url | string | yes | Existing URL of the file; must be an absolute http(s) URL |
| file_size | integer | no | File size in bytes (non-negative) |
| file_type | string | no | MIME type |
| description | string | no | Description |
| folders | array of string | no | Document sections, e.g. internal, sent, received |
| metadata | object | no | Document context: source (origin, defaults to ai_agent), invoice_number, order_number, custom (string key/value map) |
Notes: the server-owned origin fields (channel, chat, received time) and the send history are recorded automatically and cannot be set by the caller.
client_update_attachment
Update a client attachment's file metadata or referenced URL.
| Param | Type | Required | Description |
|---|---|---|---|
| client_id | string | yes | The client ID (UUID) |
| attachment_id | string | yes | The attachment ID |
| file_name | string | no | New display name (cannot be blanked) |
| file_url | string | no | New URL; absolute http(s) only |
| file_size | integer | no | New size in bytes; 0 resets the stored size |
| file_type | string | no | New MIME type (empty clears it) |
| description | string | no | New description (empty clears it) |
| folders | array of string | no | Replacement sections (empty array clears them) |
| metadata | object | no | Merged onto stored metadata; server-owned origin fields and send history are preserved |
Notes: at least one field must be provided.
client_delete_attachment
Delete a client attachment record. Does not delete the file at its external URL.
| Param | Type | Required | Description |
|---|---|---|---|
| client_id | string | yes | The client ID (UUID) |
| attachment_id | string | yes | The attachment ID |
export_clients
Export clients (leads) to a CSV or JSON file uploaded to the static.mychatbot.app CDN. Returns a public download_url plus row_count. Use whenever the user asks to download, export, or back up leads — never paste large lists into chat. Hard cap: 200 000 rows.
| Param | Type | Required | Description |
|---|---|---|---|
| format | string | no | csv (default) or json |
| columns | array of string | no | Defaults to id, full_name, email, phone_number, company_name, funnel_status, labels, pipeline_id, manager_id, created_at, updated_at, last_active. Also allowed: ad_id, ad_post_id, ad_title, crm_deal_id, crm_pipelines_card_id. Unknown columns error |
| funnel_status | string | no | Filter by funnel status |
| labels | string | no | Comma-separated labels |
| search | string | no | Search across name, email, phone |
| pipeline_id | string | no | Filter by pipeline ID |
| has_phone_number | boolean | no | Only clients with a phone (false ignored) |
| has_email | boolean | no | Only clients with an email (false ignored) |
| created_from / created_to | string | no | Bounds on created_at. RFC3339 or date-only YYYY-MM-DD (UTC start of day); a date-only created_to covers that entire day (UTC) |
| last_active_from / last_active_to | string | no | Bounds on the client's own last activity (clients.last_active), not chat activity. RFC3339 or date-only YYYY-MM-DD (UTC); a date-only last_active_to covers that entire day, so from=2026-07-25 to=2026-07-31 is the full week inclusive |
Notes: the metadata blob (both custom-data buckets) and normalized email/phone are deliberately not exportable. Labels in CSV are joined with ;. The date bounds apply directly to the client rows, so filtered exports work on accounts of any size.
list_labels / label_create / label_delete
Labels (tags) for leads.
| Tool | Params |
|---|---|
| list_labels | none |
| label_create | label_name (required) |
| label_delete | label_name (required) — also removes the label from every client that carries it |
list_pipelines / get_pipeline
| Tool | Params |
|---|---|
| list_pipelines | none — returns name, description, and default flag per pipeline |
| get_pipeline | pipeline_id (required) — returns the pipeline plus its funnel statuses (stages) |
pipeline_create / pipeline_update / pipeline_delete
| Tool | Params |
|---|---|
| pipeline_create | name (required), description — created non-default |
| pipeline_update | pipeline_id (required); name and/or description (at least one; empty strings can't clear) |
| pipeline_delete | pipeline_id (required) — rejected for the default pipeline, for pipelines that still have statuses, and for pipelines with assistants assigned |
funnel_status_create / funnel_status_delete
| Tool | Params |
|---|---|
| funnel_status_create | pipeline_id, status_name (required); color_index (optional UI color) |
| funnel_status_delete | pipeline_id, status_name (required) — no guard against deleting a status clients currently hold |
list_managers
List the account's team: the people a lead can be assigned to. No parameters.
Returns managers (manager_id, name, email, role, status, is_owner, and assignable) and count. A deactivated member is listed with assignable: false. The manager_id is the one client_update and client_bulk_update take.
list_lead_fields
List the account's custom lead fields: the fields of the lead card's Additional info panel. No parameters.
Returns fields (key, type — text or dropdown — a dropdown's options, and position, 1 = first) and count. A lead's values are set with custom_lead_fields; see the two buckets.
lead_field_create
Add a custom lead field to the account.
| Param | Type | Required | Description |
|---|---|---|---|
| key | string | yes | The field's name, e.g. budget |
| type | string | yes | text or dropdown |
| options | array of string | no | A dropdown's choices: at least one. A text field takes none |
| dry_run | boolean | no | Check without saving |
The key is saved as the dashboard saves it: trimmed, each run of whitespace an underscore, and lowercase (Delivery date becomes delivery_date), at most 100 characters. A reserved key (date_of_birth, avatar_url, ad_title, leadgen_id, and a few internal ones) is refused, as is a key the account already has: change that field with lead_field_update. Blank and repeated options are dropped; at most 100 options of up to 200 characters each. Returns the field and saved.
lead_field_update
Change a custom lead field's type or dropdown options. Values already on leads are kept.
| Param | Type | Required | Description |
|---|---|---|---|
| key | string | yes | The key as list_lead_fields shows it (a name like Delivery date also finds delivery_date) |
| type | string | no | text or dropdown |
| options | array of string | no | The dropdown's full new list of choices |
| dry_run | boolean | no | Show the change without saving |
Pass type, options, or both. A text field made a dropdown needs options. A dropdown made a text field loses its options; a lead saved with its own copy of the dropdown shows it until that lead is edited. Returns before, after, and saved; a change to what the field already is saves nothing.
lead_field_delete
Remove a custom lead field from the account's list.
| Param | Type | Required | Description |
|---|---|---|---|
| key | string | yes | The key as list_lead_fields shows it (a name like Delivery date also finds delivery_date) |
| dry_run | boolean | no | Show the field without removing it |
| confirm | boolean | no | true to remove it, after the dry run |
Run it with dry_run: true first, then with confirm: true; a call with neither is refused. Values already on leads stay in their records, and saving a lead that still has a value for the key adds the field back.
list_duplicate_clients
List pairs of leads that are one person: a chat of one lead carries a phone number that another lead holds. This is how a person who wrote first on a messaging channel can end up as two leads.
| Param | Type | Required | Description |
|---|---|---|---|
| limit | integer | no | Most pairs (default 50, max 200) |
Each pair has the phone, the phone_owner (the lead that holds the number), and the chat_lead (the lead whose chat carries it), each with client_id, full_name, phone_number, email, funnel_status, and last_active where set. Pairs come newest activity first; total counts them all. Only phone numbers are compared, not emails, and the scan reads the account's oldest 20,000 chats that carry a phone number, so on a very large account the newest duplicates may not be listed. Merge a pair with client_merge, usually with the phone owner as the target.
client_merge
Merge one lead into another. A merge cannot be undone.
| Param | Type | Required | Description |
|---|---|---|---|
| target_client_id | string | yes | The lead that stays (UUID) |
| source_client_id | string | yes | The lead merged in and deleted (UUID) |
| dry_run | boolean | no | Show the result without merging |
| preview_token | string | no | Returned by the dry run. Send it back with the same arguments on the live call when the answer asks for it; otherwise optional |
The source's chats, orders, notes, tasks, and attachments move to the target. Blank target fields (name, company, manager, pipeline, status, and lead source) are filled from the source, the labels are combined, and the source's phone and email stay on the target: as its own when it has none, else as additional contacts. Custom lead fields and collected context are combined too, keeping the target's value where both have one. Then the source lead is deleted. Both leads must be the account's, and different.
A dry_run changes nothing and returns target, source, fills_blank, labels_after, and a preview_token. The merge answers moved_chats, moved_orders, moved_notes, moved_tasks, moved_attachments, filled_fields, and added_phones/added_emails (how many contacts were added), and is noted on the target's chat timelines, as a dashboard merge is.
client_bulk_update
Change the status, labels, or manager of many leads at once, chosen by id or by filter.
| Param | Type | Required | Description |
|---|---|---|---|
| client_ids | array of string | no* | Lead UUIDs, at most 1000 |
| filter | object | no* | The leads to change, by the keys below; {} selects every lead |
| patch | object | yes | What to change (below) |
| expected_count | integer | conditional | Required on a live call with filter: the count the dry run returned. Optional with client_ids, and held to when given |
| dry_run | boolean | no | Count and sample the leads without changing them |
| preview_token | string | no | Returned by the dry run. Send it back with the same arguments on the live call when the answer asks for it; otherwise optional |
*Exactly one of client_ids and filter.
patch takes one or more of:
funnel_statuswithpipeline_id(a number or numeric string): puts the leads in that pipeline at that status, asget_pipelinelists it, matched ignoring letter case and spacing and saved with the pipeline's spelling.""clears the status.pipeline_idalone is refused, and it must be one of the account's pipelines.labelswithlabels_op:replace,add, orremove. Forreplaceandadd, every label must be one of the account's (list_labels;label_createadds one);removealso takes labels that are no longer in that list.manager_id: a team member who can take leads (assignableinlist_managers). Leads cannot be unassigned here.
Nothing else can be changed: no delete, and no names, phones, emails, or custom data.
filter keys: statuses, no_funnel_status, labels, manager_ids, pipeline_ids, assistant_ids (leads with a chat with one of these assistants), client_types, channels, campaigns, source_ids, created_from, created_to, last_active_from, last_active_to (YYYY-MM-DD or RFC3339; a date-only end covers that whole day, UTC), needs_operator, is_spam, has_phone_number, has_email, has_chats, and exclude_ids (leads to leave out). pipeline_ids and assistant_ids take numbers or numeric strings. These are the Leads page's filters; search is not accepted, and an unknown key is refused, because a key nobody reads would widen the selection. A filter may match at most 10,000 leads (too_many_matches).
Run a dry run first. It returns count, a sample of up to 25 leads, not_found (how many of the named ids are not the account's leads; the live call lists them in errors with code not_found), and a preview_token. With a filter, the live call needs expected_count: when the filter matches a different number of leads by then, it answers count_mismatch and changes nothing. With client_ids, an expected_count is optional and held to the same way.
The answer has requested, succeeded, failed, errors (at most 100 listed; errors_truncated when there were more), and succeeded_ids (left out, with ids_truncated: true, when more than 1000 leads succeeded). A lead whose status moved gets the change on its chat timelines. Moving leads into a status set to Automatically enable agent on this status hands their chats held by an operator back to the AI, except chats marked as spam and hand-offs the assistant asked for. Follow-ups are not re-evaluated for leads changed in bulk.
funnel_status_update
Rename, recolor, or move one status of a pipeline.
| Param | Type | Required | Description |
|---|---|---|---|
| pipeline_id | string | yes | The pipeline ID |
| status_name | string | yes | The status, as get_pipeline lists it |
| new_name | string | no | New name, at most 100 characters |
| color_index | integer | no | A color of the dashboard's palette, 0–16 |
| position | integer | no | New place in the pipeline, 1 = first |
| dry_run | boolean | no | Show the change without saving |
Pass at least one of new_name, color_index, and position. A new name that differs only in letter case or spacing is refused, as is the name of another status of the pipeline. A custom color is set in the dashboard.
A rename renames the status on every lead of the pipeline that has it, as the dashboard does, and notes it on their chat timelines; with more than 10,000 such leads it is refused (rename it in the dashboard). Those leads do not count as entering a new status: a status set to Automatically enable agent on this status does not hand their chats back to the AI. Follow-ups, automations, and assistants that name a status keep the old name: the answer lists them in still_name_old_status, to update by hand.
Returns before and after (status_name, position, color_index), changed, and saved; with a rename, leads_with_status, and after a saved rename leads_renamed and leads_failed, with a client_bulk_update call in finish that moves leads the rename missed (that call is an ordinary status move, so Automatically enable agent on this status applies to it). A move is saved first: if it fails, nothing is renamed. A rename or recolor is a compare-and-set: if the status was renamed since it was read, the answer is version_conflict and its name and color stay as they were (a move in the same call is already saved).
Conversations (sales_conversations)
Read-only access to chats and their message history.
list_chats
List recent chats sorted by last_active descending (most recent first), max 100 per call: client name, channel, last active, unread count, operator-needed flag. A chat with communication_channel="Calls" is a phone call — its message history is the call transcript. Every filter is optional.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | no | Filter by assistant ID |
| needs_operator | string | no | yes = only chats flagged for operator attention, no = only chats not needing one, any/omit = all |
| common_client_id | string | no | Filter by the unified client UUID (chats.common_client_id, = the lead's client_id from list_clients). NOT the per-channel id |
| external_client_id | string | no | Filter by the per-channel external client id (chats.client_id, e.g. a Telegram user id). In an app URL /chats/<external_client_id>/<page_id> this is the FIRST path segment |
| page_id | string | no | Filter by the channel page id — the SECOND segment of a pasted /chats/... app URL. Pair with external_client_id to resolve a chat from a URL |
| last_active_before | string | no | Only chats with last_active strictly before this time. RFC3339 (fractional seconds allowed) or date-only YYYY-MM-DD (= UTC start of day). Half of the pagination cursor — see below |
| last_active_after | string | no | Only chats with last_active strictly after this time. RFC3339 or date-only YYYY-MM-DD (= UTC start of day). Lower bound of a date window |
| before_id | number | no | The id half of the pagination cursor; only valid together with last_active_before. When both are set the bound is "older than that timestamp, or same timestamp with a smaller id" |
| limit | number | no | Max results (default 20, max 100) |
Returns {chats: [...], count, next_last_active_before, next_before_id}. next_last_active_before is a full-precision RFC3339 timestamp (sub-second fraction included); next_before_id is the last row's chat id. Both are present whenever the page contains at least one chat with a known last_active.
Pagination: there is no offset — page by keyset. Pass both cursor fields from the previous response — next_last_active_before as last_active_before and next_before_id as before_id — and call again; repeat until a page comes back with fewer than limit rows. Always use the returned cursor fields — do not rebuild the cursor from a chat row's displayed last_active: that string is truncated to whole seconds, and feeding it back into the strict before-bound would skip same-second chats. Passing last_active_before alone (without before_id) also works for plain date-window queries. Both bounds are strict and a date-only value always means UTC start of day, so for the window 2026-07-25…2026-07-31 inclusive pass last_active_after=2026-07-25 and last_active_before=2026-08-01 (day + 1). Combine both bounds to fetch a specific date window.
get_chat
Get detailed info about a specific chat including metadata, follow-up state, and channel info. The response distinguishes three ids: id (the canonical chat id), client_id (per-channel external id), and common_client_id (the lead's UUID) — do not conflate them.
| Param | Type | Required | Description |
|---|---|---|---|
| chat_id | string | yes | The chats.id integer returned by list_chats (e.g. 568507). NOT page_id or any channel-external id |
get_chat_messages
Read a chat's message history one page at a time, newest page first: the last N messages with role, content, and timestamp, oldest first within the page. Older messages are reachable with the before cursor, so any part of a long conversation can be read. For Calls chats this is the call transcript — there is no separate transcript endpoint. Tool calls are collapsed to a list of tool names unless you ask for details.
| Param | Type | Required | Description |
|---|---|---|---|
| chat_id | string | yes | The chats.id integer returned by list_chats |
| limit | number | no | Number of messages per page (default 50, max 200) |
| before | string | no | Page cursor: the next_before value from the previous response, passed back as-is, to read the messages older than that page. Omit to read the latest messages. A number is accepted too; any other value returns an error |
| include_tool_details | boolean | no | false (default): tool calls listed by name only. true: each tool call as {id, name, arguments} — what you need to work out why the assistant answered as it did. Tool-result content is returned either way |
| max_tool_result_chars | number | no | Max characters of each tool-result message's content (default 2000, 0 = full stored content) |
Returns {chat_id, total_messages, messages, has_more, next_before}. total_messages counts the whole chat, not the page. has_more is true when older messages exist; next_before (a string) is present only then. Each message carries role, created_at and, when present, content, images, name, tool_calls, and tool_call_id. A tool result shortened by max_tool_result_chars carries content_truncated: true and content_full_chars.
Paging: call without before to get the latest page. While has_more is true, pass next_before as before and call again; each call returns the next older page. A page can hold a few messages more than limit, because messages written together are never split across two pages.
Photos: a message the client sent with photos carries images — the photo URLs — next to its text in content. A photo-only message has images and no content. Pass those URLs to test_chat_send images (Assistants connector) to rehearse the same photo, or save the chat as an eval scenario with eval_scenario_save from_chat_id, which keeps the photos with their turns.
json
{
"chat_id": "8412",
"total_messages": 2,
"has_more": false,
"messages": [
{
"role": "user",
"created_at": "2026-09-02 13:47:30.792325+00",
"images": ["https://static.mychatbot.app/chat-images/….jpg"]
},
{
"role": "assistant",
"created_at": "2026-09-02 13:47:41.118204+00",
"tool_calls": ["search_product"]
}
]
}export_chats
Read one bounded page of conversations plus a bounded message window for each chat. This is the primitive for caller-side export and deterministic batch processing: it returns structured data, not a job id, download URL, CSV, or uploaded file. The Claude/Codex session or routine skill owns the page loop, checkpointing, transformations, chosen total, file format, and final upload.
Each call selects at most 100 chats, ordered by immutable chat_id desc, and performs one bulk message read for exactly those conversation keys. Per-chat counters cover the complete conversation even when only the first or last N messages are included. Human operator replies are distinguished from bot replies using the platform's stored Manager: tag.
| Param | Type | Required | Description |
|---|---|---|---|
| limit | number | no | Chats in this page (default 20, max 100) |
| messages_per_chat | number | no | Messages included per chat (default 10, max 50, 0 = counters only) |
| max_message_chars | number | no | Unicode characters returned per message (default 1,000, max 4,000). A larger value may require a smaller page/window |
| message_selection | string | no | last (default) or first. Messages are oldest-to-newest inside the selected window |
| before_id | string or number | no | Immutable chat-id page cursor. Pass next_before_id from the prior response; omit on the first call |
The response is {chats, count, limit, messages_per_chat, max_message_chars, message_selection, next_before_id}. next_before_id is present on every non-empty page and absent on the terminal empty page. Each chat carries chat_id, assistant_id, common_client_id, external_client_id, page_id, name, username, phone, channel, chat_link, last_active, needs_operator, total_messages, operator_messages, operators, first_message_at, last_message_at, and a messages array. Optional identifiers and display fields can be absent when the stored chat has no value. Timestamps retain full RFC3339 precision.
Each message carries at, id, role, operator, and text; id and operator are omitted when unavailable or inapplicable. A shortened message also carries text_truncated: true and text_full_chars. Message text defaults to at most 1,000 Unicode characters per value and can be raised to 4,000. To prevent a row-bounded request from becoming an unbounded payload, limit × messages_per_chat × max_message_chars must not exceed 1,000,000. Reduce page size or message window when raising the text cap. Image/media turns count as messages: their caption is preserved and the text contains a safe [image], [N images], or [media] placeholder. Private or signed media URLs are deliberately not returned.
There is intentionally no offset. A chat's activity time can change while a large export is running, so export_chats does not order or page by activity. It uses immutable descending chat ids: a chat that receives a new message during the traversal cannot jump across the page cursor and disappear. For every next call, copy the returned next_before_id unchanged:
text
cursor = {}
rows = []
repeat:
page = export_chats(limit=100, messages_per_chat=10,
message_selection="first", ...cursor)
append page.chats to rows (or write/checkpoint this page immediately)
if page.count < page.limit: stop
cursor = {before_id: page.next_before_id}The sweep is deliberately account-wide. Assistant/client assignment, page, channel, full-precision RFC3339 last_active, and needs_operator are returned on every chat but are not server-side filters on this multi-call tool: each can change while a large sweep runs, and reapplying membership predicates to every database page could make a matching unvisited chat disappear. Apply conditions in the caller as each row arrives. Passing one of those old filter arguments fails with a teaching error instead of being silently ignored. Use list_chats when live server-side filtering is more important than exhaustive batch membership.
For 7,000 chats this is 70 bounded data pages plus one terminal empty page when the total is an exact multiple of 100—not one 7,000-chat server job and not 7,000 get_chat_messages calls. Use common_client_id to join richer client fields from separately keyset-paginated scan_clients results. The caller must still escape spreadsheet formulas when it writes CSV and choose where to upload the finished file; export_chats itself creates no public object.
Channels (sales_channels)
Connected messaging channels and their setup.
list_channels
List all connected channels (Telegram, WhatsApp, Instagram, …) with their status. Credentials are stripped; rows include communication_channel, page_id, description, assistant_id, is_on, created_at, and connection_type. The boolean immediate_outreach_capable is true only for an enabled channel type accepted by immediate_outreach_create; construct its channel ID as <communication_channel>__<assistant_id>. An enabled official WhatsApp row carries outreach_existing_chats_only: true instead: it can be a campaign channel, but it reaches only leads who already have a chat on it, and Meta refuses the message when the lead has not written in the last 24 hours.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | no | Filter by assistant ID |
channel_toggle
Turn a channel on or off.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| page_id | string | yes | The channel page ID |
| communication_channel | string | yes | Channel type (telegram, whatsapp, …) |
| is_on | boolean | yes | True to enable, false to disable |
set_channel_options
Set one channel's options, as on the dashboard's channel pages (Reply Delay (s), Ignore Reactions, Ignore Story Reactions).
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant the channel belongs to |
| communication_channel | string | yes | The channel's platform, as list_channels shows it (e.g. Instagram; the Webhooks API is MCBWebhooks) |
| page_id | string | yes | The channel's page_id, as list_channels shows it |
| reply_delay | integer | no | Seconds the assistant waits for the customer to finish typing, 0–180 |
| ignore_reactions | boolean | no | Ignore message reactions (Instagram, Messenger, TikTok, Webhooks API) |
| ignore_story_reactions | boolean | no | Ignore story reactions (Instagram) |
| dry_run | boolean | no | Show the current and new values without saving |
At least one option is required. Only the options given change. The answer has before (the values now; a reply_delay of null means none saved, so the platform default applies) and set. list_channels does not show these options: a dry_run reads them. A channel of a deleted assistant is refused.
channel_get_config_link
Generate a link to the web UI channel configuration page. Channels need the web UI for auth setup.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| channel_type | string | yes | telegram, whatsapp, instagram, facebook, email, sms, widget, calls, olx, tiktok, chat2desk, personal_whatsapp |
create_calls_channel_sdk
Create a new voice Calls channel with a WebSDK connection for an assistant. Returns the agent_id and embed snippets (HTML and npm) for a website. Created with sensible defaults (10-minute max call duration). One WebSDK Calls channel per assistant. Note: WebSDK channels can't place outbound calls — outbound_call needs a Twilio/SIP-trunk Calls channel.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID to attach the calls channel to |
| first_message | string | no | Greeting the assistant says when a call starts; defaults to the assistant's welcome message |
| language | string | no | Language code (e.g. en, ru, uk); default en |
| voice_id | string | no | Voice ID; defaults to the platform default voice |
| site_context | string | no | What the business's website says about itself (title, description, topics). When present, the voice, accent, and opening line are picked to match it; an explicit voice_id still wins |
| first_message_auto | boolean | no | Treat first_message as a fallback: with site_context, the opener picked for the site, in the site's language, replaces it. A first_message without this flag is never changed |
| demo | boolean | no | Signup demo builds only. Marks the channel as the account's voice demo, with a hosted demo page (hosted_page_url in the answer) that previews the assistant over the owner's site |
| website_url | string | no | Demo builds only: the owner's website, shown behind the call button on the demo page; http(s) only |
| starters | array of string | no | Demo builds only: up to 3 questions from the owner's website, shown on the demo page (learn_website returns them) |
delete_channel
Disconnect a channel type from an assistant: every page of that type it holds, listed in page_ids. The assistant stops receiving new messages there; existing conversations remain.
Only this assistant is detached, as with Revoke token on the dashboard's Instagram, Messenger and WhatsApp tabs. Another assistant on the same page keeps its connection and keeps answering there. The platform webhook is removed only with the page's last connection in the account.
On an Instagram, Messenger or WhatsApp page shared with Share by schedule, this assistant leaves the rotation and the page passes to another assistant in it that still exists; from then on the schedule decides who holds it. This works only while this assistant holds the page: outside its shift, list_channels does not show the page for it, and delete_channel reports that no channel is connected.
For Instagram, ask the owner before disconnecting. When the page is left with no connection, the product catalogs built from its posts (Instagram Products, in any knowledge base) are kept unless instagram_catalogs is remove. A kept catalog is still searched but no longer updates. A page that stays connected to another assistant keeps its catalogs even with remove: they are not listed, and the result's note says they were left as they are.
The result has page_ids, status (disconnected) and a note to relay; the note says when another assistant kept the page. It lists every catalog of a page left with no connection in instagram_catalogs with its integration_id, knowledge_base, name, posts and status: kept, removed or failed. When a requested removal did not happen, error says why. If the catalogs cannot be checked at all, the result has instagram_catalogs_error instead and nothing is removed. A catalog that cannot be removed never undoes the disconnect, and a catalog whose page is connected again before it is removed is kept.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | Assistant that owns the channel |
| channel_type | string | yes | Exact channel type reported by list_channels |
| instagram_catalogs | string | no | Instagram only: keep (default) or remove the product catalogs built from the page. Pass remove only after the owner agreed. Any other value is refused and nothing is disconnected |
| remove_number | boolean | no | Personal WhatsApp only: true unlinks the number from the account. Unset or false pauses it instead |
Personal WhatsApp is paused by default rather than disconnected: the number is switched off but stays linked to the account, and its conversations are kept. The answer has status: "paused", page_ids, a dashboard_url, and a message to relay: the owner switches the number back on with one switch on the Channels page, with no new scan. A paused number keeps its line, so where the line is charged it still is. A number that was already paused answers already_paused with its reason: balance (it comes back by itself within four hours of a top-up) or logged_out (WhatsApp logged it out on the phone; the owner scans the code again). When an assistant's numbers come back different ways, numbers lists each with its own status and reason. Pass remove_number: true only when the owner wants the number unlinked; coming back then needs a new connection and scan.
delete_assistant
Permanently delete a Sales assistant, including channels, skills, tasks, and—if explicitly allowed—its customer conversations. The tool refuses an assistant with conversations unless delete_conversations is true.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | Assistant to delete |
| delete_conversations | boolean | no | Must be true to delete an assistant that has customer conversations |
connect_telegram
Validate a BotFather token, connect that Telegram bot to an assistant, register the webhook, and turn the channel on. Reconnecting the same bot to the same assistant refreshes its token. Treat bot_token as a secret.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | Assistant that should answer on the bot |
| bot_token | string | yes | Full Telegram bot token from BotFather |
| drop_pending_updates | boolean | no | Ignore messages accumulated while offline; default false |
create_website_widget
Create a Website Widget (chat bubble) channel for an assistant and turn it on. Returns the ready-to-paste embed snippet plus a hosted HTML page URL. Idempotent: reusing the same widget_id for the same assistant refreshes the config and returns the embed; a widget_id owned by a different assistant errors.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID to connect the widget to |
| widget_id | string | no | Short unique identifier, no spaces (e.g. main-site); omit for the assistant's default widget |
| welcome_text | string | no | Heading above the contact options in the widget home tab |
| pop_up_text | string | no | Text shown in the bubble a few seconds after page load |
| website_url | string | no | Your site URL, used for previewing the widget in context |
| logo | string | no | Public URL to a brand logo image (~45×45) |
| color | string | no | Primary widget color hex (default #6F52E0) |
| language | string | no | Widget UI language: en, ru, or ua (default en) |
| ad_oriented | boolean | no | The teaser-card look: a titled card with rotating popup messages instead of the bare bubble. On by default for a new widget; false gives the plain bubble. Re-configuring an existing widget keeps its look unless this is set |
| ad_title | string | no | Teaser card only: the card's title; defaults to Chat with <assistant name> |
| ad_description | string | no | Teaser card only: a one-line subtitle |
| ad_popup_message | string | no | Teaser card only: the rotating message inviting the visitor to chat |
| reply_delay_seconds | integer | no | Seconds the widget waits for the visitor to finish typing before the assistant answers, 0–300. A widget with no delay saved (every new one) answers after 1 second; an existing delay is kept unless this is set |
| starters | array of string | no | Demo pages only: up to 3 questions from the owner's website, shown on the demo page (learn_website returns them) |
Omit widget_id for the assistant's default widget: if signup already built a demo widget for this assistant, that one is returned (is_demo: true, preview_url set) instead of a second one. The widget has more settings than this tool takes (which contact details the form asks for, button position and size, popup timing, margins); for those, give the owner the config_link from the answer, which opens that widget's settings page and signs them in.
Outreach & Follow-ups (sales_outreach)
Follow-up automations, immediate outreach campaigns, and single one-off calls and messages.
Which outreach tool?
- One lead, one message →
send_one_off_messagewith a messaging channel - One lead, one voice call →
outbound_call - One lead, one operator call →
send_one_off_messagewithRingostat,UniTalk, orBinotel(legacy tool naming; itsmessageis not delivered) - Many leads (campaign) →
immediate_outreach_preview_audience, thenimmediate_outreach_create - Recurring, condition-based chasing → create a complete postponed sequence with
follow_up_*, attach it withassistant_attach_follow_up, then publish it
Postponed and immediate outreach have deliberately different safety models. A postponed sequence is created as a disabled draft; inspect it with get_follow_up, attach it to its assistant with assistant_attach_follow_up, then publish it explicitly. immediate_outreach_create schedules real messages or calls immediately and has no draft stage. Preview and obtain explicit approval before invoking it.
The tables below mirror the MCP JSON schemas. integer means a whole JSON number; numeric database IDs documented as string must be positive decimal strings such as "42". Unknown fields and values of the wrong JSON type are rejected. The connected MCP tool schema remains authoritative if it is newer than this page.
Postponed and immediate follow-up validation: names and targeting strings are trimmed, must be valid UTF-8, and reject unsafe control characters; IDs and list entries must not be duplicated. Their public-file inputs accept only HTTP(S) URLs up to 4,096 characters, without embedded credentials or local/private hosts. Their text fields reject NUL characters, and email subjects reject line breaks. Dates use the formats stated per field rather than locale-formatted dates.
list_follow_ups
List follow-up automations (outreach campaigns).
| Param | Type | Required | Description |
|---|---|---|---|
| type | string | no | postponed, immediate, or trigger |
| status | string | no | Exact stored status, maximum 50 characters. Common values are draft/live for postponed and scheduled/running/user_paused/completed/failed for immediate |
| limit | integer | no | Page size, 1–100; default 20 |
| offset | integer | no | Rows to skip, minimum 0. Pass next_offset from the previous response |
Returns {follow_ups, count, offset} and includes next_offset only when another page exists. follow_ups is always an array, including on an empty page.
get_follow_up
Get detailed info about a follow-up including steps, filters, and execution stats.
| Param | Type | Required | Description |
|---|---|---|---|
| follow_up_id | string | yes | Positive decimal follow-up ID, for example "42" |
Returns the complete stored record, including canonical steps, targeting, schedule, metadata, status, and execution fields.
follow_up_create
Create a complete postponed follow-up automation. It always starts as a disabled draft, even when every step is supplied. The tool accepts an ordered step list—not raw node IDs, edges, positions, or conditions—and the server generates the editor/runtime graph.
| Param | Type | Required | Description |
|---|---|---|---|
| name | string | yes | Follow-up name, 1–200 characters |
| target_status_name | string | no | Target funnel status for the audience, maximum 200 characters; empty means every status |
| target_labels | array of string | no | Up to 100 unique, non-empty labels; each is at most 200 characters |
| target_labels_operator | string | no | or (default) or and |
| steps | array of object | no | Complete ordered sequence (maximum 100). See Ordered step object |
| schedule | object | no | Optional working-hours schedule. See Working-hours object |
| generate_email_subject | boolean | no | Generate subjects with AI instead of using each step's email_subject |
Returns the complete stored draft record and its generated ID. Postponed pipeline targeting is not accepted by this MCP schema: do not pass target_pipeline_ids or pipeline_ids. Use status/label targeting, or configure any unsupported condition in the app.
Ordered step object
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | yes | Builder label (1–200 characters) |
| delay_seconds | integer | no | Continued silence before this step, 0–31,622,400 seconds. On the first step this is the entry delay; later it is measured after the preceding step |
| chat_depth | integer | no | Conversation history depth: 1–10, 20, or 100; default 10 |
| instructions | string | conditional | AI composition instruction, maximum 20,000 characters. Required before publish when use_custom_message=false |
| use_custom_message | boolean | no | Send custom_message verbatim instead of composing with AI |
| custom_message | string | conditional | Static message, maximum 20,000 characters. Required before publish when use_custom_message=true |
| email_subject | string | no | Email subject, maximum 998 characters; line breaks are rejected |
| attachments | array of string | no | Up to 10 unique public HTTP(S) URLs for the AI-composed message; each is at most 4,096 characters |
| custom_message_attachments | array of string | no | Up to 10 unique public HTTP(S) URLs for the custom message; each is at most 4,096 characters |
| trigger_on_delivery_failed | boolean | no | Run an alternative AI instruction if every delivery channel fails |
| delivery_failed_prompt | string | conditional | Alternative instruction, maximum 20,000 characters; required when trigger_on_delivery_failed=true |
| outreach_channels | array of string | no | Channels this step sends on, up to 50 unique ChannelType__AssistantID ids from list_channels or get_follow_up (for example EChatViber__2261). Empty: the step uses the follow-up channels of the client's channel, or else the chat's own channel. Official WhatsApp is honoured only on the lead's own WhatsApp chat. On follow_up_update, omitting it keeps the value of the existing step at the same position; [] clears it |
| delivery_failed_channels | array of string | no | Channels the delivery-failed pass may send on, same form and limits. On follow_up_update, omitting it keeps the existing step's value; [] clears it |
Drafts may have no steps or incomplete message content. Publishing validates that the sequence has at least one step and that every step has the content required by its message mode.
Working-hours object
schedule is {timezone, days}. timezone is an IANA name such as Europe/Kyiv. days must contain all seven case-sensitive keys—Monday through Sunday—and each value is {start, end}. Use HH:MM 24-hour values for an active day and empty strings for a day off. At least one day must be active; overnight windows such as 22:00 → 06:00 are accepted.
| Field | Type | Required | Description |
|---|---|---|---|
| timezone | string | yes | IANA timezone, 1–100 characters, for example Europe/Kyiv |
| days | object | yes | Exactly seven weekday objects with no extra keys |
| days.<Weekday>.start | string | yes | HH:MM or an empty string for a day off |
| days.<Weekday>.end | string | yes | HH:MM or an empty string for a day off; both start and end must be empty or populated |
json
{
"timezone": "Europe/Kyiv",
"days": {
"Monday": { "start": "09:00", "end": "18:00" },
"Tuesday": { "start": "09:00", "end": "18:00" },
"Wednesday": { "start": "09:00", "end": "18:00" },
"Thursday": { "start": "09:00", "end": "18:00" },
"Friday": { "start": "09:00", "end": "18:00" },
"Saturday": { "start": "", "end": "" },
"Sunday": { "start": "", "end": "" }
}
}follow_up_update
Update a postponed follow-up. Supplying steps atomically replaces the complete ordered sequence; use an empty array to clear a draft. Configuration on a live sequence can only change while it is being moved to draft in the same call, or after a prior unpublish. Updates use optimistic status matching, so a concurrent publish/unpublish returns an error instead of overwriting the other change.
| Param | Type | Required | Description |
|---|---|---|---|
| follow_up_id | string | yes | Positive decimal postponed follow-up ID |
| name | string | no | New name, 1–200 characters |
| status | string | no | draft to disable or live to publish after full validation |
| target_status_name | string | no | Target funnel status, maximum 200 characters; "" clears it |
| target_labels | array of string | no | Full replacement of up to 100 unique, non-empty labels; [] clears label targeting |
| target_labels_operator | string | no | or or and; defaults to or when targeting is first created |
| steps | array of object | no | Full ordered replacement; same object as follow_up_create |
| schedule | object | no | Full working-hours replacement |
| clear_schedule | boolean | no | Remove working-hours restrictions. Cannot be combined with schedule |
| generate_email_subject | boolean | no | Enable or disable AI-generated email subjects |
Provide at least one field to change in addition to follow_up_id.
Returns the complete updated record. Because updates use optimistic status matching, a concurrent lifecycle change fails instead of silently overwriting it; read the record again before retrying.
follow_up_delete
Delete a follow-up automation.
| Param | Type | Required | Description |
|---|---|---|---|
| follow_up_id | string | yes | Positive decimal follow-up ID |
Returns {message: "follow-up deleted", follow_up_id}. Deletion is permanent and can target any follow-up type; inspect the record first.
assistant_attach_follow_up
Attach a postponed follow-up sequence to an assistant — the assistant's Toolkit → Follow ups in the app. A postponed sequence starts for an assistant's chats only while it is attached to that assistant.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | Positive decimal assistant ID (list_assistants) |
| follow_up_id | integer | yes | The postponed follow-up (list_follow_ups with type: "postponed") |
| confirm | boolean | no | Absent/false = preview only. true = apply |
Unlike get_follow_up, follow_up_update, and follow_up_delete, which take the follow-up ID as a string, this tool and assistant_detach_follow_up take it as an integer.
A live sequence then arms for that assistant's chats whose status and labels match it, on their next message or status change, and messages those leads on its schedule — treat attaching it like activating it. A draft sends nothing until it is published (follow_up_update with status: "live"), so attaching the draft and then publishing it keeps activation to one step. Only postponed sequences are attached; immediate outreach runs on its own audience and is refused. Returns follow_ups_before, follow_ups_after, the follow-up, and the effect; attaching one that is already attached returns changed: false without follow_ups_after.
assistant_detach_follow_up
Detach a follow-up sequence from an assistant: it stops arming for that assistant's chats. Chats already armed or part-way through keep their queued steps; to stop those too, unpublish the sequence (follow_up_update with status: "draft"). The sequence itself is kept — follow_up_delete removes it.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | Positive decimal assistant ID |
| follow_up_id | integer | yes | The attached follow-up |
| confirm | boolean | no | Absent/false = preview only. true = apply |
Detaching one that is not attached returns changed: false.
immediate_outreach_create
Create and schedule an immediate outreach campaign. This can send messages or place calls. Use immediate_outreach_preview_audience, inspect sample leads, confirm the channel, content, schedule, audience count, and pacing with the user, and only then call this tool. It creates a scheduled campaign immediately—there is no draft stage on this path.
| Param | Type | Required | Description |
|---|---|---|---|
| name | string | yes | Campaign name, 1–200 characters |
| client_source | string | yes | Audience source: withChats, allClients, or withoutChats |
| outreach_channels | array of string | yes | 1–50 unique, enabled, account-owned channels in ChannelType__AssistantID form, attempted in order; at most one assistant per channel type. Use list_channels rows with immediate_outreach_capable=true. Supported types: PersonalWhatsApp, TelegramAccount, EChatViber, ViberPhone, EChatTelegram, Email, Calls, Ringostat, UniTalk, Binotel, SMS. Official WhatsApp (WhatsApp, a row with outreach_existing_chats_only=true) is accepted too, but it reaches only leads who already have a chat on it, and Meta refuses the message when the lead has not written in the last 24 hours |
| schedule_mode | string | yes | now (runs in ~3 min) or later (requires schedule_run_at) |
| schedule_run_at | string | no | Future RFC3339 instant for later, or a naive YYYY-MM-DDTHH:MM[:SS] wall time when paired with timezone_field; omit for now |
| timezone_field | string | no | Custom lead field, maximum 200 characters, containing an IANA timezone or UTC offset for per-lead scheduling; requires a naive schedule_run_at |
| send_window_start | string | no | Optional daily delivery-window start in HH:MM; requires send_window_end |
| send_window_end | string | no | Optional daily delivery-window end in HH:MM; supply both and make end later than start |
| send_timezone | string | no | IANA timezone for the daily window, maximum 100 characters; default UTC |
| dynamic_audience | boolean | no | false (default) freezes the audience when creating; true resolves the filters again at send time |
| instructions | string | conditional | AI prompt, maximum 20,000 characters; required when use_custom_message=false |
| custom_message | string | conditional | Static message, maximum 20,000 characters; required when use_custom_message=true |
| use_custom_message | boolean | no | If true, send custom_message verbatim; if false, compose with AI from instructions |
| email_subject | string | no | Email-only subject, maximum 998 characters and no line breaks |
| email_attachments | array | no | Up to 20 public HTTP(S) files; each entry is a URL or {url,name}. Requires an Email channel |
| instruction_attachments | array of string | no | Up to 20 public HTTP(S) URLs for AI-composed messages |
| custom_message_attachments | array of string | no | Up to 20 public HTTP(S) URLs for the custom message |
| send_to_all_emails | boolean | no | Cc each lead's additional stored addresses (from the extra-emails custom field configured on the Email channel); requires an Email channel in outreach_channels — for invoices that must also reach accounting |
| omit_core_instructions | boolean | no | Omit the assistant's core instructions when generating the message |
| statuses | array of string | no | Up to 100 unique funnel statuses, each 1–200 characters |
| include_no_status | boolean | no | Include clients with no funnel status |
| labels | array of string | no | Up to 100 unique labels, each 1–200 characters |
| labels_operator | string | no | or (any label) or and (all labels, default) |
| include_no_labels | boolean | no | Include clients with no labels |
| manager_ids | array of string | no | Up to 100 unique full UUIDs of assigned managers |
| targeting_channels | array of string | no | Up to 100 unique existing-chat channel types; only valid for withChats |
| targeting_assistants | array of integer | no | Up to 100 unique positive existing-chat assistant IDs; only valid for withChats |
| pipeline_ids | array of integer | no | Up to 100 unique positive Sales pipeline IDs |
| client_types | array of string | no | Up to 100 unique client types, each 1–200 characters; a client matches when its type equals one of them exactly |
| has_phone_number | boolean | no | Only clients with a phone number |
| has_email | boolean | no | Only clients with an email |
| last_active_cutoff_days | integer | no | Activity lookback for withChats, 0–3,650 days (0 uses the default 7); ignored when cutoff_date is given |
| cutoff_date | string | no | Inclusive withChats activity-range start as RFC3339 or YYYY-MM-DD |
| cutoff_end_date | string | no | Inclusive withChats activity-range end as RFC3339 or YYYY-MM-DD; must not precede cutoff_date |
| created_date_from | string | no | Inclusive creation-range start for allClients/withoutChats, as RFC3339 or YYYY-MM-DD |
| created_date_to | string | no | Inclusive creation-range end for allClients/withoutChats; must not precede created_date_from |
| audience_limit | integer | no | Maximum recipients, 0–10,000; 0 means unlimited |
| metadata_filters | array of object | no | Up to 20 ANDed custom-field filters: {field, operator, values} where operator is eq, in, or contains; eq requires one value |
| interval_seconds | integer | no | Pacing window, 0–86,400 seconds. Must be positive together with chats_per_interval, or both zero |
| chats_per_interval | integer | no | Recipients per pacing window, 0–1,000 |
| send_delay_seconds | integer | no | Delay between individual sends, 0–86,400 seconds; default 7 |
| number_of_lines | integer | no | Concurrent Calls lines, 0–100; requires a Calls channel; 0 uses the channel default |
Metadata filter object
Every object in metadata_filters must contain all three fields. Filters are ANDed and field/value comparisons are case-insensitive. Duplicate field names are rejected case-insensitively.
| Field | Type | Required | Description |
|---|---|---|---|
| field | string | yes | Custom-field name, 1–200 characters |
| operator | string | yes | eq, in, or contains |
| values | array of string | yes | 1–100 unique non-empty values; eq requires exactly one value; each value is at most 20,000 characters |
Email attachment object
Each email_attachments entry may be a public HTTP(S) URL string or this object:
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | yes | Public HTTP(S) URL, maximum 4,096 characters; embedded credentials and local/private hosts are rejected |
| name | string | no | Display filename, maximum 200 characters; defaults from the URL path |
Notes: when the message is AI-composed, the assistant sees the full lead context — profile fields, both custom-data buckets, notes, tasks, and attachment names — so instructions like "mention their open invoice" work.
For a naive schedule_run_at paired with timezone_field, a static campaign validates the resulting UTC instant for every resolved recipient before it is stored. A dynamic campaign cannot know its future recipients yet, so its wall time must still be in the future at the easternmost supported offset (UTC+14). This prevents a past local time from turning into an immediate send when the scheduler wakes.
Returns {follow_up_id, status, stats: {matched}}. A successful call has already stored the campaign; it is not a simulation.
immediate_outreach_update_status
Pause, resume, or reset an immediate outreach campaign. The tool rejects unsafe lifecycle jumps and uses optimistic status matching. A running campaign must first move to user_paused; only a user-paused campaign can resume as scheduled. draft clears all progress and statistics and is not available directly from a running state.
| Param | Type | Required | Description |
|---|---|---|---|
| follow_up_id | string | yes | Positive decimal immediate-outreach follow-up ID |
| status | string | yes | user_paused to pause, scheduled to resume, draft to reset (clears all progress and stats) |
Returns {success, follow_up_id, status}.
immediate_outreach_preview_audience
Preview the matched audience without creating or scheduling a campaign. Returns matched_count, missing_count, a five-lead sample, and the resolved cutoff range. It accepts the same audience fields as immediate_outreach_create (but no channel, message, or scheduling fields). Preview is read-only; creation is the separate external effect.
| Param | Type | Required | Description |
|---|---|---|---|
| client_source | string | yes | withChats, allClients, or withoutChats |
| statuses | array of string | no | Up to 100 unique funnel statuses, each 1–200 characters |
| include_no_status | boolean | no | Include clients with no funnel status |
| labels | array of string | no | Up to 100 unique labels, each 1–200 characters |
| labels_operator | string | no | or or and; default and |
| include_no_labels | boolean | no | Include clients with no labels |
| manager_ids | array of string | no | Up to 100 unique manager UUIDs |
| targeting_channels | array of string | no | Up to 100 unique existing-chat channel types; withChats only |
| targeting_assistants | array of integer | no | Up to 100 unique positive existing-chat assistant IDs; withChats only |
| pipeline_ids | array of integer | no | Up to 100 unique positive pipeline IDs |
| client_types | array of string | no | Up to 100 unique client types, each 1–200 characters; a client matches when its type equals one of them exactly |
| has_phone_number | boolean | no | Only clients with a phone number |
| has_email | boolean | no | Only clients with an email |
| last_active_cutoff_days | integer | no | Activity window for withChats, 0–3,650 days (default 7); ignored when cutoff_date is given |
| cutoff_date | string | no | Inclusive existing-chat activity-range start as RFC3339 or YYYY-MM-DD |
| cutoff_end_date | string | no | Inclusive existing-chat activity-range end; must not precede cutoff_date |
| created_date_from | string | no | Inclusive client creation-range start for allClients/withoutChats |
| created_date_to | string | no | Inclusive client creation-range end; must not precede created_date_from |
| audience_limit | integer | no | Max recipients, 0–10,000; 0 means unlimited |
| metadata_filters | array of object | no | Up to 20 custom-field filters; same object shape and limits as create |
Returns {matched_count, missing_count, sample_clients, cutoff_date, cutoff_end_date}. sample_clients contains at most five {client_id, client_name} rows.
outbound_call
Initiate a single outbound voice call to a specific lead via a Twilio- or SIP-trunk-backed Calls channel. For multi-recipient call campaigns use immediate_outreach_create. WebSDK Calls channels (the kind create_calls_channel_sdk makes) can't be used — they need a browser to start the call. Calls are real and immediate: confirm the recipient and opening line with the user first. Returns the conversation id; the transcript lands on the chat record after the call.
| Param | Type | Required | Description |
|---|---|---|---|
| client_id | string | yes | The lead's UUID, copied verbatim from a list_clients/get_client result. The lead must have a phone number on file |
| first_message | string | yes | The literal opening line the voice assistant says when the call connects — the lead will hear it |
| assistant_id | string | no | Positive numeric assistant ID as a string; disambiguates when more than one assistant has a Calls channel and is auto-picked when only one outbound-capable channel exists |
| page_id | string | no | Specific Calls channel page_id, when an assistant has several |
| manager | string | no | Manager attribution string |
Notes: the voice assistant receives the lead's full context on the call — profile, both custom-data buckets, notes, and tasks.
Returns status, client and channel identifiers, the dialed number, and the provider conversation_id. A successful call means dialing was initiated; it is not a preview.
send_one_off_message
Perform a single ad-hoc outbound action for a specific lead through a non-Calls channel. Messaging channels send message; Ringostat, UniTalk, and Binotel instead initiate telephony calls and do not deliver that field as customer-visible text. For an AI voice call with a literal spoken opening line, use outbound_call. For campaigns, use immediate_outreach_create. The action is real and immediate: confirm the recipient, channel, and exact effect with the user first. If unsure which channel to use, call list_channels.
| Param | Type | Required | Description |
|---|---|---|---|
| client_id | string | yes | The lead's UUID, copied verbatim from a list_clients/get_client result |
| assistant_id | string | no | Positive numeric assistant ID as a string; the assistant whose identity owns this message. Leave it out when the lead card has a default assistant (set with client_update); that one is used |
| communication_channel | string | no | WhatsApp, WhatsAppBusiness, Telegram, TelegramAccount, PersonalWhatsApp, Viber, EChatViber, ViberPhone, EChatTelegram, Instagram, Messenger, Email, SwWidget, OcWidget, MCBWebhooks, Chat2Desk, TikTok, OLXAccount, SMS, Ringostat, UniTalk, or Binotel. Calls is rejected—use outbound_call. Leave it out when the lead card has a default channel for the assistant; that one is used |
| message | string | yes | Plain text for messaging channels. Required but not delivered or spoken by the Ringostat, UniTalk, and Binotel call providers |
| subject | string | no | Email subject line (Email only) |
| to | array of string | no | Email only: primary recipients (max 20). Replaces the lead's own address — for reaching e.g. a billing address. Defaults to the lead's email. Outlook accepts only one To; use cc for the rest |
| include_client | boolean | no | Email only: with to, also add the lead's own address as the first To recipient |
| cc | array of string | no | Email only: Cc addresses (max 20) |
| include_all_emails | boolean | no | Email only: Cc every address stored on the lead's extra-emails field (configured on the Email channel) without having to know them |
| bcc | array of string | no | Email only: blind copies (max 20) |
| attachments | array of string | no | Public HTTP(S) URLs of files to send; multiple URLs supported. On Email each file becomes a real attachment, and all files normally arrive on one email (Gmail; Outlook and rare fallback cases deliver one email per file instead); on messaging channels files are delivered as media where supported |
| include_documents | boolean | no | Email only: staple the lead's card documents (up to 50) to the email. URLs are read server-side — the agent never sees them. Each stapled document's send history is updated |
| reply_to_message_id | string | no | ID of the message being replied to (threaded replies on supported channels) |
| manager | string | no | Manager attribution string |
| page_id | string | no | Specific channel page_id; auto-resolved when the assistant has exactly one channel of that type |
Notes: Ringostat, UniTalk, and Binotel are call providers despite this tool's legacy name; selecting one initiates a real operator callback/direct call. to/cc/bcc on any non-Email channel is a hard error, not a silent drop; invalid addresses are hard errors too. Recipient resolution: the lead's email is the default To; to replaces it; include_client re-adds the lead; duplicates resolve To > Cc > Bcc. A lead with no email can still be mailed by passing to. The response echoes the real recipients, and they are also recorded in the chat history.
Returns {status: "sent", client_id, channel, page_id} plus chat_id when a chat record is available and the resolved to/cc/bcc arrays for Email. Success means a real send occurred; there is currently no no-effect preview for this tool.
schedule_message
Schedule one message or voice call to one lead for a later time. At that time the follow-up engine composes it from task_description (or sends that text word for word with use_as_custom_message) and delivers it through the lead's chat channel, the channel's preferred follow-up channels, or the channel override. Use it to plan a later touch or to hand a lead to a voice assistant (channel: "Calls__<assistant_id>"); to send something now, use send_one_off_message or outbound_call. Confirm the recipient, sender, content, time zone, and time with the user first: the scheduled item is real and runs on its own.
| Param | Type | Required | Description |
|---|---|---|---|
| client_id | string | yes | The lead's UUID, copied from a list_clients or get_client result |
| task_description | string | yes | What to do at that time (for example "call the candidate and run the screening script"); the message or call is composed from it unless use_as_custom_message is true |
| delay_minutes | integer | conditional | Run this many minutes from now, 1–10,080 (7 days). The safe choice when you do not know the current time. Give this or run_at with timezone, not both |
| run_at | string | conditional | Local wall time in timezone, ISO 8601 without an offset (for example 2026-05-12T09:00:00); do not convert to UTC. Must be in the future |
| timezone | string | conditional | IANA zone for run_at, for example Europe/Kyiv; required with run_at |
| channel | string | no | Channel override as ChannelType__AssistantID (for example Calls__2359, Email__42); delivery falls back to the channel's preferred follow-up channels if it fails. Required when the lead has no chat yet. Only a channel that can message a lead first (immediate_outreach_capable in list_channels) or the channel of the lead's own chat can be used |
| use_as_custom_message | boolean | no | Send task_description word for word, without the AI |
| omit_core_instructions | boolean | no | Compose from task_description alone, without the assistant's core instructions |
| email_subject | string | no | Email only: the subject |
| generate_email_subject | boolean | no | Let the AI write the subject; defaults to true for a composed message without email_subject |
| trigger_on_delivery_failed | boolean | no | When every channel fails to deliver, run delivery_failed_prompt instead. Requires delivery_failed_prompt |
| delivery_failed_prompt | string | conditional | The alternative instruction. It has no tool that sends a message, so naming a channel here does not switch channels |
| delivery_failed_channel | string | no | ChannelType__AssistantID the delivery-failed retry sends on, for example a messenger after a call that could not connect. Without it the retry can only use the lead's own chat channel. Refused unless trigger_on_delivery_failed is true |
| spread_interval_seconds | integer | no | Seconds between staggered sends when several scheduled items of the account land at the same time; default 5, 0 turns staggering off |
The scheduled item belongs to the lead's chat. If the lead has no chat on the delivery channel, one is opened there: phone-based channels use the lead's phone number and Email their email, so the lead must have that field. A lead with no chat at all needs channel. An Instagram, Messenger, or Telegram bot channel is refused when the lead has no chat on it, and so is a channel the chat cannot deliver on; a refused call leaves nothing behind. Official WhatsApp (WhatsApp__<id>) is accepted for a lead with a phone number, and a chat is opened, but Meta refuses the message when it runs unless the lead has written to that number in the last 24 hours: prefer Personal WhatsApp, SMS, Calls, or Email to reach a lead first.
Returns {status: "scheduled", follow_up_id, client_id, client_name, chat_id, chat_created, run_at, channel}, with delivery_failed_warning when the delivery-failed retry has no channel it could reach. The chat's timeline shows the pending item until it runs. Inspect it with list_follow_ups.
Knowledge Base (sales_knowledge)
FAQ knowledge bases, product feeds, and manually managed product catalogs. The knowledge_base parameter is a group name: use an existing one to add an integration to that group, or a new unique name to start one. Caps: max 3 Product Feed and 3 Products integrations per knowledge base.
Documents from a link, each source's indexing status, a re-index, and a search test have their own tools at the end of this section.
learn_website
Crawl the useful pages of a business website and write stored FAQ/business facts plus starter questions for one assistant. Unlike live website knowledge, stored knowledge also reaches voice assistants. Re-running replaces the stored knowledge this operation wrote before and can take 15–30 seconds.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | Assistant to teach |
| url | string | yes | Business website |
| knowledge_base | string | yes | Knowledge-base group for the stored source |
add_website_knowledge
Add a website as a live search/read source. There is no indexing wait; the assistant reads current pages at answer time. Assistants without a knowledge base can be attached to the named group: see assistant_id.
| Param | Type | Required | Description |
|---|---|---|---|
| url | string | yes | Public website URL, including https:// |
| knowledge_base | string | yes | Existing or new knowledge-base group |
| name | string | no | Human-readable source name; defaults to the domain |
| assistant_id | integer | no | The assistant that should use this knowledge. It is attached to the group if it has no knowledge base yet; an assistant already using a different group is left as it is. Omitted: the group is attached only when exactly one assistant of the account has no knowledge base yet |
create_faq_knowledge_base
Create a new FAQ knowledge base integration, optionally seeded with entries.
| Param | Type | Required | Description |
|---|---|---|---|
| knowledge_base | string | yes | Knowledge base group name |
| name | string | yes | Human-readable name (e.g. Shipping FAQ) |
| language | string | no | Content language (english, russian, spanish, …); default english |
| entries | array | no | Initial entries; each is {id, question, answer} (id used for diffing on updates). Can be added later via update_faq_knowledge_base_entries |
| assistant_id | integer | no | The assistant that should use this knowledge. It is attached to the group if it has no knowledge base yet; an assistant already using a different group is left as it is. Omitted: the group is attached only when exactly one assistant of the account has no knowledge base yet |
Notes: seeded entries are indexed synchronously; on indexing failure the integration still exists and the response reports created_but_indexing_failed.
list_faq_knowledge_base_entries
List all entries in a FAQ knowledge base integration: IDs, questions, answers. It also reads Business Documents (connected Google Docs): their entries are the documents' text chunks as the assistant retrieves them (ids chunk_001…), marked type: business_documents and read_only; edit the Google Doc, not these entries.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | The FAQ or Business Documents integration ID. Omitted: an account with one knowledge base gets its entries; an account with several gets the list of them with their ids |
update_faq_knowledge_base_entries
Modify entries in a FAQ knowledge base. Use any combination of the three operations; the backend reads current entries, applies changes (remove → update → add), re-uploads, and re-indexes.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The FAQ integration ID |
| add | array | no | New entries {id, question, answer}; an id that already exists is treated as an update |
| update | array | no | Existing entries matched by id; provide only the fields to change (question and/or answer) |
| remove | array of string | no | IDs of entries to delete |
Notes: updates are serialized per integration — a concurrent update errors with "another update to this knowledge base is already in progress — retry in a moment". At least one effective change is required.
delete_faq_knowledge_base
Delete a FAQ knowledge base integration and all its indexed data. Irreversible.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The FAQ integration ID |
create_product_feed_integration
Create a Product Feed integration that indexes products from a feed URL for semantic search (JSON, XML, Google Shopping, YML, …). Asynchronous — returns processing immediately; check status with get_integration.
| Param | Type | Required | Description |
|---|---|---|---|
| knowledge_base | string | yes | Knowledge base group name |
| feed_url | string | yes | Publicly accessible feed URL (http/https), or a Shopify store address — see Shopify store seller |
| language | string | yes | Content language |
| name | string | no | Unique name within the knowledge base; lowercase a–z and underscores only, max 20 chars |
| index_images | boolean | no | Index product images for visual search (slower); default false |
| autoupdate_interval_hours | integer | no | Re-read the feed every 1, 3, 6, 12 or 24 hours; omit to leave auto-update off |
| fast_start | boolean | no | Shopify stores: index the first 250 products first so the assistant can answer within a couple of minutes, then sync the rest right after; default false |
| assistant_id | integer | no | The assistant that should use this knowledge. It is attached to the group if it has no knowledge base yet; an assistant already using a different group is left as it is. Omitted: the group is attached only when exactly one assistant of the account has no knowledge base yet |
create_products_integration
Create a Products integration — a manually managed product catalog, optionally seeded. Products can carry arbitrary attributes.
| Param | Type | Required | Description |
|---|---|---|---|
| knowledge_base | string | yes | Knowledge base group name |
| name | string | yes | Catalog name (e.g. Summer Collection) |
| language | string | no | Content language; default english |
| products | array | no | Initial products; each object needs name, all other keys are arbitrary attributes. For image indexing use image_link, image_url, additional_images, or any key containing image/photo/picture/thumbnail/gallery/media |
| index_images | boolean | no | Index product images for visual search; default false |
| assistant_id | integer | no | The assistant that should use this knowledge. It is attached to the group if it has no knowledge base yet; an assistant already using a different group is left as it is. Omitted: the group is attached only when exactly one assistant of the account has no knowledge base yet |
Notes: product ids are auto-generated when missing.
list_products
List the products in a catalog. A manually managed Products catalog and a Product Spreadsheet (a connected Google Sheet) return a page of products with their attributes: products_count is the whole catalog, returned is how many came back, and next_offset pages onward (absent on the last page). A Product Feed is indexed for search rather than stored as a list, so it returns live counts and the source feed URL; search it through the sales assistant to see actual products. Every answer names its kind in type and says in products_listable whether it can be paged.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | A Products, Product Spreadsheet, or Product Feed integration ID. Omitted: an account with one catalog gets it, several come back as a catalogs list with each one's live counts, and none says so |
| limit | number | no | Products per page, default 50, at most 200. Ignored when products_listable is false |
| offset | number | no | Products to skip: pass next_offset from the previous answer; omit on the first call |
update_products
Modify products in a Products integration: any combination of add / update / remove, applied in the order remove → update → add, then re-indexed.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The Products catalog integration ID |
| add | array of object | no | New products; each must have name, other keys arbitrary |
| update | array of object | no | Existing products matched by id; only provided fields are merged |
| remove | array of string | no | Product IDs to delete |
Notes: same per-integration serialization as FAQ updates; at least one effective change required.
delete_products_integration
Delete a Products integration and all its indexed data. Irreversible.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The Products catalog integration ID |
delete_product_feed_integration
Delete a Product Feed integration and all indexed data. Irreversible. This tool refuses manually managed Products integrations; use the matching delete tool for that catalog type.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The Product Feed integration ID |
add_document_knowledge
Add a document from an https link to a knowledge-base group, as the dashboard's Business Files upload does.
| Param | Type | Required | Description |
|---|---|---|---|
| url | string | yes | https link to the file itself |
| knowledge_base | string | yes | Existing or new knowledge-base group |
| name | string | no | File name to show; defaults to the link's |
| assistant_id | integer | no | The assistant that should use this knowledge (below) |
| dry_run | boolean | no | Download and check the file without saving it |
MyChatBot downloads the file from the public internet only: https on the standard port, no user name or password in the link, and a public host — not localhost, a .local or .internal name, or a private address, and every address the host resolves to must be public. At most five redirects are followed, each held to the same rules. The server must answer HTTP 200 with at most 25 MB within 60 seconds. The type is read from the content, not the name: PDF, Word .docx, or UTF-8 text — TXT, or CSV when the link ends in .csv or the server says text/csv; a CSV is stored and indexed as text. A web page, an empty file, and other archives or office files are refused; add a website with add_website_knowledge.
The document joins the group's Business Files source, or a new one when the group has none. This tool adds to a source that holds fewer than 30 documents, and refuses while that source is being indexed or a refresh of it is running. Text and CSV must be UTF-8. The stored file takes the detected type's extension (a CSV is stored as name.csv.txt); a dry run for a group with no Business Files source answers creates_source: true instead of an integration_id. Indexing runs in the background, re-reads all of the source's documents, and takes a few minutes; list_knowledge_sources shows when it is done. With assistant_id, that assistant is attached to the group if it has no knowledge base yet; one that uses another group is left as it is. Without it, the group is attached only when exactly one assistant has no knowledge base.
Returns integration_id, document (file_name, type, bytes), documents_after, status: "in_progress", and attached_assistants. A dry_run makes every check, the download included, and saves nothing.
list_knowledge_sources
List the account's knowledge sources with their indexing status.
| Param | Type | Required | Description |
|---|---|---|---|
| knowledge_base | string | no | Only this group |
| assistant_id | string | no | Only the group this assistant uses |
Each source has integration_id, kind, name, knowledge_base, and status: done or error once indexing has finished, in_progress while it runs (one that has shown no progress for 15 minutes says so; it may be retried), unknown when no indexing has been recorded, and live for a website, which is read when the assistant answers (site is its host). A product feed shows its current stage instead, such as parsing or indexing; get_integration reads a feed's live progress. A Google source that must be signed in again shows auth_error. Where set, it also has error, warning, documents (file names, the first 30), total_documents, indexing_started_at, last_successful_fetch_at, source_stale, and used_by (the assistants that answer from the group).
refresh_knowledge_source
Re-index one knowledge source: its files, FAQ, feed, or Google document are read again and its index is rebuilt in the background.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | From list_knowledge_sources |
| dry_run | boolean | no | Show what would run without starting it |
A website source is read live and needs none: the answer says so and nothing runs. A source being indexed right now (in_progress with progress in the last 15 minutes, or a product feed at a stage such as parsing) is refused; check list_knowledge_sources in a few minutes. Of two refreshes of one source at once, one starts and the other is told a refresh is already running; the dashboard's refresh skips a source in that state too. An integration that is not a knowledge source is refused. Returns refreshed and status: "in_progress". An account re-indexes at most 300 times a day, scheduled feed updates included; past that, a refresh does not run.
test_knowledge_search
Ask an assistant's knowledge a customer's question and see what it retrieves.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant ID |
| query | string | yes | A customer's question, at most 500 characters |
It runs knowledge searches only: the question context the assistant's knowledge sources add to a reply (knowledge: per source, up to 8 snippets and their total), and the assistant's own knowledge-base search_* tools, such as catalog and website search (searches: each tool's result or error). No other tool is called. At most 6 search tools run, 30 seconds each; a search tool that takes no plain question is listed as skipped, and search_truncated says more were left out. Snippets are cut to 1,200 characters and tool results to 4,000. A website is searched live. Nothing is saved. A deleted assistant is refused.
Integrations (sales_integrations)
The account's integrations, and API Actions: an assistant tool that calls your own HTTPS endpoint with the fields it collects. Credentials never pass through these tools: API keys, header values, passwords, and sign-ins are entered in the dashboard. The tools give the link to the dashboard card, check what is stored, and attach or detach.
list_integrations
List all integrations for this account: CRMs, knowledge bases, MCPs, automations. Returns a projection per row (id, integration, type, knowledge-base fields).
| Param | Type | Required | Description |
|---|---|---|---|
| type | string | no | Filter by integration type; knowledge_base filters on the knowledge-base flag |
get_integration
Get detailed info about a specific integration including metadata and configuration. Sensitive fields are stripped: secrets, passwords, refresh tokens, and headers. access_token is kept only where it is not a credential: an automation's token or placeholder, and the Calendar's native and the platform's other markers (never on a CRM, booking, Google, Instagram, or Task Manager row). A URL loses its login, query, and fragment. A Custom MCP server's URL and an API Action's endpoint are cut to their scheme and host. Any other value (a CRM's or booking system's key, a Google or Instagram token, a Task Manager's secret) is left out, with its expiry. When something was left out, credentials_note says it is kept in the dashboard; integration_verify checks that it works. integration_id must be a positive id.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The integration ID |
get_integration_config_link
Generate a link to the web UI integration configuration page.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The integration ID |
update_integration_trigger
Change one trigger of an automation, such as a Trigger Outreach campaign: its instruction, name, custom message, channels, or funnel status. Only the fields you pass change; everything else on the trigger stays as it was. Read the automation with get_integration and take the trigger's id from metadata.triggers[] first. To add or delete a trigger, use create_integration_trigger or delete_integration_trigger (sales_automations).
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The automation's integration ID (from list_integrations) |
| trigger_id | string | yes | The trigger's id in metadata.triggers[] |
| agent_instruction | string | no | Full replacement of what the assistant should do when it reaches out; used unless use_custom_message is on |
| name | string | no | The trigger's display name |
| use_custom_message | boolean | no | true sends custom_message word for word; false has the assistant compose from agent_instruction |
| custom_message | string | no | The exact first message; sent only while use_custom_message is on (set both in one call if it is off) |
| channels | array of string | no | Outreach channels, for example ["Email", "TelegramAccount"]; replaces the current list |
| funnel_status | string | no | Capture-only triggers: the funnel status captured leads get |
| confirm | boolean | no | Absent or false: preview only, nothing is written. true: apply |
Without confirm: true the answer is a before/after preview and nothing changes: show it to the user, then call again with confirm: true. This edits a live campaign, and there is no version history to roll back to.
Refused, with nothing written: an integration that is not an automation, a trigger id that is not in it (the message lists the ids it has), a call with no field to change, an unknown field or channel, an empty channels list on a trigger that sends outreach, a custom_message on a trigger that composes with the assistant unless use_custom_message: true comes in the same call, a change that would leave the trigger with nothing to compose from, a funnel_status on a trigger that is not capture-only, and a trigger that another Trigger Outreach row with the same webhook token answers for (the message names the integration to edit instead).
api_action_create
Create an API Action on one of the account's assistants and attach it. Pro plan or higher. It is live at once on an active assistant. The endpoint must be public HTTPS, as in the dashboard (see API Action limits). A name that another action of the account has, a built-in tool's name, or a tool name the assistant already has is refused. A {field} in the url that is not a declared field, is not required text or number, or sits in the scheme, host, port, or after # is refused with a message that names the problem, for example "the URL uses {uuid}, which is not a field"; api_action_update checks it the same way. (The dashboard form catches the path and query cases before saving, with its own message.)
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant that gets the action |
| name | string | yes | The action's name, at most 80 bytes (fewer characters in a non-Latin script); the assistant sees it as a tool named after it |
| url | string | yes | Your https:// endpoint. Write {field} in the path or query to insert a required text or number field's value; that field is then not sent in the body (see Use a field in the URL) |
| method | string | no | POST (default), GET, PUT, or PATCH |
| description | string | no | When the assistant should call it and what it does, at most 1000 bytes |
| parameters | array | no | The fields the assistant collects: {name, type, required, description}; type is string, number, boolean, string_list, or object_list |
| response_note | string | no | How the assistant should use the answer, at most 1000 bytes |
| send_client_context | boolean | no | Also send the customer's name, phone, and chat to the endpoint (default false) |
Header values, such as an API key or a bearer token, are never set or read over MCP: add them in the dashboard (Integrations → the assistant → MCP Providers → API Action). A headers argument is refused. The answer has the new action's integration_id; list_integrations lists them later.
api_action_update
Change an API Action. Only the fields given change; the rest, and the stored header values, stay as they are.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The API Action's integration id |
| name, url, method, description, parameters, response_note, send_client_context | no | As in api_action_create; parameters is the full list |
An action that has header values keeps its server — scheme, host, and port: moving it to another one would send those values there, so that change is made in the dashboard. A new path on the same server is fine. There is no dry run; read the action first with get_integration. If the action was saved in the dashboard between the read and the write, the named fields are applied again to the fresher copy (its header values included); after three tries it answers version_conflict and changes nothing. The answer shows the configuration, the endpoint's host, and the header names, never a value.
api_action_attach
Attach an existing API Action to another assistant of the account. Pro plan or higher.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The API Action's integration id |
| assistant_id | string | yes | The assistant that gets it |
An assistant that already has the action is left as it is. A tool of the same name on that assistant is refused.
integration_setup_link
The dashboard link that opens one integration card on one assistant, where the owner connects it: a CRM by its API key or sign-in, Spreadsheet Orders or Google Calendar by signing in with Google, a Custom MCP server with its headers or sign-in, or the built-in Calendar. Nothing is created; keys and sign-ins are entered on the card only.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant to connect it to |
| kind | string | yes | The card's name: KeyCRM, Kommo, Bitrix, Hubspot, SalesDrive, Sitniks, NetHunt, Monday, Zoho, Salesforce, GoHighLevel, Odoo, OneBox, Altegio, Wlaunch, Servio HMS, EasyMS, Spreadsheet Orders, Google Calendar, Calendar, or Custom MCP |
The answer has the link, the owner_steps on the card, and can_connect. It is false when the account's plan does not include the card (blocked_by_plan, the same gate as the dashboard), or when another integration on the assistant blocks it (blocked_by). An assistant has one CRM at a time, and Google Calendar does not go next to the built-in Calendar, Altegio, or Wlaunch. When the card is already connected to the assistant, the answer has already_connected and its integration_id instead (Custom MCP excepted: an assistant can have several servers). For an API Action use api_action_create; for order taking, enable_order_taking.
integration_verify
One read with an integration's stored credentials, changing nothing about the integration (a Custom MCP server that signs in may have its token renewed and stored, as a chat turn does). It never returns a key or a token.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The integration (from list_integrations) |
| Integration | What it checks |
|---|---|
| Custom MCP server | The server lists its tools with its stored headers or sign-in; the answer lists them, marks those that are on, and names any that are on but no longer listed |
| KeyCRM, Hubspot, Sitniks, NetHunt, Monday | The CRM answers a read with the stored key (pipelines, deal pipelines, order statuses, folders, or boards; up to 20 names) |
| Spreadsheet Orders | The Google sign-in opens the spreadsheet; its first row has column names; platform columns still to be added on the next sync |
| Product Spreadsheet, Business Documents | The Google sign-in opens every file; a product sheet's first row has id and product |
| Google Calendar | The Google connection is active |
status is ok; needs_owner (the fix says what to change on the card, with its link); unreachable (the service did not answer; try again later); failed; or not_supported (other kinds, which are not checked here). Each integration can be checked once every 20 seconds; sooner answers rate_limited. A server's refusal is shown without its URL's path or query and without any value the request carried; when a header value shorter than 6 characters was sent, the server's words are not shown at all. A Custom MCP server saved at http:// with headers or a sign-in is not called with them: the answer is needs_owner, and the fix is to change its URL to https in the dashboard (custom_mcp_set_tools refuses it the same way).
integration_attach
Attach an integration that is already connected to another assistant of the account: a CRM, a Custom MCP server, Spreadsheet Orders, or Google Calendar. Its tools go live for that assistant's customers at once.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The integration |
| assistant_id | string | yes | The assistant that gets it |
| confirm | boolean | no | Absent or false: preview only. true: apply |
The account's plan must include it, as for a connect in the dashboard. Refused: a CRM with no key stored, a second CRM on the assistant, Spreadsheet Orders with no spreadsheet picked or no sign-in, a second Spreadsheet Orders, Google Calendar with no finished sign-in, a second Google Calendar, Google Calendar next to the built-in Calendar, Altegio or Wlaunch, and a Custom MCP server whose tool names the assistant already has from another integration. API Actions use api_action_attach; the built-in Calendar is switched on in the dashboard.
integration_detach
Detach an integration from one assistant and keep it: the assistant loses its tools, while other assistants that use it keep it.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The integration |
| assistant_id | string | yes | The assistant to take it from |
| confirm | boolean | no | Absent or false: preview only. true: apply |
It takes a CRM, a booking system, a Custom MCP server, an API Action, Spreadsheet Orders, or Google Calendar. The preview says what the assistant loses. It warns when the assistant's session export or hand-off reply notifications call a tool of the server being detached.
custom_mcp_create
Connect a Custom MCP server to an assistant by its URL. Pro plan or higher.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant that gets the server's tools |
| name | string | yes | A name for the server, 1–60 characters, unique on the account |
| url | string | yes | The server's https:// URL, on port 443 or 8443, with a public host |
| tools | array of string | no | Only these of the server's tools go on (default: every tool it lists) |
| confirm | boolean | no | Absent or false: preview only. true: apply |
MyChatBot asks the server for its tools first, from an address that reaches public hosts only, and stays on the server's own scheme, host, and port. The answer's mode says what happens:
ready: the server answers, and it is saved and attached with its tools on. They go live for the assistant's customers at once.needs_headers: the server refuses without credentials. It is saved and attached with no tools on. The owner adds the headers on the server's tab in the dashboard (thelink). Thenintegration_verifylists its tools andcustom_mcp_set_toolsswitches them on.sign_in: the server asks for an OAuth sign-in. Nothing is saved; the owner adds the server in the dashboard and chooses Sign in.
A headers argument is refused. So is a URL that carries a credential: a login (user:password@), a query, or a path segment of 20 characters or more that mixes letters and digits, as hosted servers that take no headers put their key. Add such a server in the dashboard, where its URL stays out of the conversation. A name or a URL the account already uses is refused; attach that server with integration_attach. Results show the server's scheme and host only.
custom_mcp_set_tools
Choose which of a Custom MCP server's tools its assistants use, as the dashboard's tool picker does.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | yes | The server's integration |
| tools | array of string | yes | Every tool to keep on, by the server's name for it; the rest go off |
| confirm | boolean | no | Absent or false: preview only. true: apply |
The server is asked for its current tools first, with its stored headers or sign-in. The preview shows the tools before and after, the assistants that get the change, and the new tools the server does not mark read-only. A name the server does not list is refused, as is a tool that another integration of one of those assistants already offers. A change saved in the dashboard meanwhile answers version_conflict. Only the picks change; the headers stay.
Account & Usage (sales_account)
get_account_summary
High-level account summary: totals for assistants, clients, chats, integrations, follow-ups, pipelines, and orders, plus a business_profile (company name + website). No parameters.
get_subscription_info
Current subscription plan: tokens left, plan name, trial flag, expiry dates. Returns {"message": "no subscription found"} when there is no subscription. No parameters.
get_token_usage
Token usage statistics: prompt and completion tokens with per-model and per-assistant breakdowns.
| Param | Type | Required | Description |
|---|---|---|---|
| since_date | string | no | Start date (RFC3339 or YYYY-MM-DD) |
| to_date | string | no | End date (RFC3339 or YYYY-MM-DD) |
| assistant_id | string | no | Filter by assistant ID |
get_usage_summary
Combined usage summary for a date range: token counts, order count, chat count, and client count. The response may include an errors map keyed by source when a sub-query fails — that counter is unreliable; re-fetch, often by narrowing the date range.
| Param | Type | Required | Description |
|---|---|---|---|
| since_date | string | no | Start date — prefer RFC3339; plain YYYY-MM-DD is silently ignored by the client-count filter |
| to_date | string | no | End date — prefer RFC3339; plain YYYY-MM-DD is silently ignored by the chat- and client-count filters |
Orders (sales_orders)
list_orders
List orders with optional filtering.
| Param | Type | Required | Description |
|---|---|---|---|
| since_date | string | no | Start date (RFC3339 or YYYY-MM-DD) |
| to_date | string | no | End date (RFC3339 or YYYY-MM-DD) |
| client_id | string | no | Filter by client ID (channel-specific) |
| status | string | no | pending, awaiting_payment, paid, in_progress, completed, cancelled |
| source | string | no | native, spreadsheet, etc. |
| order_ref | string | no | Exact order reference (e.g. MCB-A7F3C2) |
| assistant_id | number | no | Filter by assistant ID |
| limit | number | no | Max results (default 50, max 200) |
| offset | number | no | Offset for pagination |
get_order
Full details of a single order.
| Param | Type | Required | Description |
|---|---|---|---|
| order_ref | string | no* | Order reference (e.g. MCB-A7F3C2); takes precedence when both are given |
| order_id | number | no* | Numeric order ID |
*One of the two is required. A missing order returns {"error": "Order not found"} as JSON.
get_order_stats
Order statistics: total count, total revenue, breakdown by channel and status, and the number of won deals still missing an amount. Revenue per order is total_amount, falling back to product_price when total_amount is 0 (product_amount is the item count and is not multiplied in).
| Param | Type | Required | Description |
|---|---|---|---|
| since_date | string | no | Start date (RFC3339 or YYYY-MM-DD) |
| to_date | string | no | End date (RFC3339 or YYYY-MM-DD) |
| Response field | Description |
|---|---|
| total_orders | Number of orders in the period |
| total_revenue | Sum of the orders' revenue, in whole currency units (3096.00 means 3096 in the order currency) |
| by_channel | Order count per communication channel |
| by_status | Order count per order status |
| won_without_amount | Won deals whose amount is still missing: orders marked as a lead's won deal (won_at set) that have neither a total_amount nor a product_price above 0, and a status other than cancelled. It stays 0 until a pipeline has a status marked as its successful deal. |
delete_test_orders
Delete test orders that have no communication channel (created during testing, not from real conversations). Returns the deleted count. No parameters.
enable_order_taking
Enable or reconfigure conversational order taking for one assistant. Existing settings are updated rather than duplicated.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | Assistant that should take orders |
| currency | string | no | Three-letter currency code; default USD |
| requires_payment | boolean | no | Whether payment is required before confirmation; default false |
| required_fields | array | no | Customer fields {name, type, required, options?}; type is text, number, or select |
| item_fields | array | no | Per-product fields with the same shape |
disable_order_taking
Remove the assistant's ability to record new orders while preserving existing orders and its normal answering/recommendation behavior.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | Assistant that should stop taking orders |
Calendar (sales_calendar)
The five reads require a Calendar integration on the account; without one they return {"error": "Calendar integration not found"} as JSON. The setup tools below answer a not_found error instead.
An account can hold several built-in calendars, one per assistant. The setup tools take an optional integration_id (from calendar_get_settings). It may be left out when the account has one calendar; with several, a call without it is refused and the message lists them. Staff, services, and bookings are looked up on the calendar named. Every write previews before confirm: true. Nothing here notifies a customer, as in the dashboard.
list_events
List calendar events.
| Param | Type | Required | Description |
|---|---|---|---|
| since_date | string | no | Start date (RFC3339 or YYYY-MM-DD), matched against event start time |
| to_date | string | no | End date (RFC3339 or YYYY-MM-DD) |
| status | string | no | confirmed, completed, cancelled, no_show |
| staff_id | number | no | Filter by staff member ID |
| service_id | number | no | Filter by service ID |
| limit | number | no | Max results (default 50, max 200) |
| offset | number | no | Offset for pagination |
get_event
Full details of a single event.
| Param | Type | Required | Description |
|---|---|---|---|
| event_ref | string | yes | Event reference (e.g. EVT-B3F1D8). A missing event returns {"error": "Event not found"} as JSON |
get_event_stats
Event statistics: total count, breakdown by status and staff.
| Param | Type | Required | Description |
|---|---|---|---|
| since_date | string | no | Start date (RFC3339 or YYYY-MM-DD) |
| to_date | string | no | End date (RFC3339 or YYYY-MM-DD) |
list_calendar_staff
List staff members configured for booking (id, the calendar's integration_id, name, role, color). No parameters.
list_calendar_services
List services configured for booking (id, the calendar's integration_id, name, duration, price, currency, and a group class's is_group and max_capacity). No parameters.
calendar_get_settings
Read each calendar's settings: the assistants that use it, its timezone (UTC when unset, as bookings read it), default duration, business hours, currency, booking instructions, and how many staff and services it has.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | One calendar; without it, every calendar of the account |
Staff with no hours of their own work the business hours, Monday to Friday. The assistant offers times inside each member's hours (check_availability); a booking itself is checked only against other bookings.
calendar_get_staff_hours
One staff member's weekly hours in the calendar's timezone (own_hours: true), or the business hours they fall back to.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | The calendar |
| staff_id | integer | yes | The staff member (from list_calendar_staff) |
calendar_update_settings
Change the calendar's settings. Only the fields given change.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | The calendar |
| timezone | string | no | An IANA timezone, for example Europe/Vienna |
| default_duration_minutes | integer | no | The length of a booking without a service, 5–480 |
| business_start, business_end | string | no | HH:MM; the end comes after the start |
| currency | string | no | A three-letter code, for example EUR |
| booking_instructions | string | no | What the assistant is told about booking, up to 4000 characters; "" clears them |
| confirm | boolean | no | Absent or false: preview only. true: apply |
A new timezone keeps every booking at its moment and reads all working hours on the new zone's clock; the preview says so. A zone outside the dashboard's list works, but the dashboard's picker does not show it. If the settings were saved in the dashboard meanwhile, the call answers version_conflict and changes nothing.
calendar_set_staff_hours
Set one staff member's working hours for the whole week.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | The calendar |
| staff_id | integer | yes | The staff member |
| days | object | yes | Each working day (monday … sunday) with its windows, for example {"monday": [{"start": "09:00", "end": "13:00"}, {"start": "14:00", "end": "18:00"}]}. A day left out is a day off; {} returns the member to the business hours |
| confirm | boolean | no | Absent or false: preview only. true: apply |
Windows are HH:MM (an end may be 24:00), at most six a day, and may not overlap. The preview shows the hours before and after and lists the bookings to come (180 days) that fall outside the new hours. Those bookings are kept; move or cancel them with calendar_booking_update. The dashboard's hours editor shows one window per day: saving the hours there keeps only the first. If a write fails, the old hours are put back.
calendar_staff_create
Add a staff member. The assistant can book them at once, within the hours given, or the business hours (Monday to Friday) until they have their own.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | The calendar |
| name | string | yes | Their name, unique on the calendar, up to 80 characters |
| role | string | no | Their role |
| color | string | no | #RRGGBB (default #3B82F6) |
| days | object | no | Their hours, as in calendar_set_staff_hours |
| confirm | boolean | no | Absent or false: preview only. true: apply |
calendar_staff_update
Rename a staff member, or change their role or colour.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | The calendar |
| staff_id | integer | yes | The staff member |
| name | string | no | A new name, unique on the calendar |
| role | string | no | A new role; "" clears it |
| color | string | no | #RRGGBB |
| confirm | boolean | no | Absent or false: preview only. true: apply |
calendar_staff_delete
Remove a staff member and their working hours.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | The calendar |
| staff_id | integer | yes | The staff member |
| confirm | boolean | no | Absent or false: preview only. true: apply |
Refused while they have bookings to come that are not cancelled (the message names up to five); move or cancel those first. Past bookings keep their record. The member is removed first, then their hours.
calendar_service_create
Add a service customers can book.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | The calendar |
| name | string | yes | The service, unique on the calendar |
| duration_minutes | integer | yes | Its length, at least 5 minutes; 1440 or more books by the day |
| price | integer | no | Its price in whole currency units |
| currency | string | no | A three-letter code |
| is_group | boolean | no | A group class: several customers per session |
| max_capacity | integer | no | A group class's seats per session (1–1000); required with is_group, refused without it |
| confirm | boolean | no | Absent or false: preview only. true: apply |
calendar_service_update
Change a service's name, duration, price, currency, or a group class's seats.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | The calendar |
| service_id | integer | yes | The service (from list_calendar_services) |
| name, duration_minutes, price, currency | no | As in calendar_service_create | |
| max_capacity | integer | no | A group class's seats; refused for a service that is not one |
| confirm | boolean | no | Absent or false: preview only. true: apply |
Bookings already made keep their times; new ones take a new duration.
calendar_service_delete
Remove a service. Refused while it has bookings to come; move or cancel those first.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | The calendar |
| service_id | integer | yes | The service |
| confirm | boolean | no | Absent or false: preview only. true: apply |
calendar_booking_create
Book a customer, as the dashboard's new appointment does. The slot is taken at once and the assistant stops offering it. Nobody is notified: tell the customer yourself.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | The calendar |
| staff_id | integer | yes | The staff member |
| service_id | integer | yes | The service; not a group class (book its seats through the assistant or the dashboard) |
| start | string | yes | YYYY-MM-DD HH:MM in the calendar's timezone, or RFC 3339 with an offset; not in the past |
| client_name | string | yes | The customer's name |
| client_phone | string | no | Their phone (7–15 digits); a phone or an email is required |
| client_email | string | no | Their email |
| notes | string | no | Notes, up to 2000 characters |
| confirm | boolean | no | Absent or false: preview only. true: apply |
The end follows from the service's duration; a service of whole days ends after that many calendar days at the same time of day, so a night across a daylight-saving change lasts 23 or 25 hours. A slot that overlaps another booking of the same staff member is refused with the booking it conflicts with. A time outside the member's hours is booked with a warning (a booking of a day or more never is). If another booking takes the slot at the same moment, this one is not kept and the answer is the conflict. The answer has the new event_ref, unused by the account's other bookings when it is saved.
calendar_booking_update
Change one booking by its reference: move it, set its status, or its notes. Nobody is notified.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | string | no | The calendar |
| event_ref | string | yes | The booking's reference, for example EVT-B3F1D8 (from list_events) |
| start | string | no | A new start, as in calendar_booking_create. The booking keeps its length (a stay keeps its number of nights) |
| staff_id | integer | no | A new staff member |
| service_id | integer | no | A new service (not a group class); the booking then takes the service's length |
| status | string | no | confirmed, completed, no_show, or cancelled |
| notes | string | no | New notes; "" clears them |
| confirm | boolean | no | Absent or false: preview only. true: apply |
A cancelled booking frees its slot; cancelling a seat in a group class frees the seat in its session. A move, and bringing back a cancelled booking, is checked for overlap. A seat in a group class is not moved here, and a cancelled one is not brought back (book it again). A reference that two bookings share is refused, since a change by reference would change both; change it in the dashboard.
Automations (sales_automations)
Read, diagnose, and configure the Lead Forms automation — the per-form rules that route Meta (Facebook/Instagram) Lead Gen submissions to an assistant, as set up on Automations → Lead Forms. Connecting the Facebook page itself is OAuth and stays in the app; these tools manage what happens after leads arrive. automation_set_paused pauses or resumes the automations listed in its table. The area also sets up Trigger Outreach triggers, an assistant's Keyword Responder, and Lead Form Instant Outreach, writing the same settings as their pages in the app.
Two conventions specific to this area:
- Writes are a two-step confirm. Calling
set_lead_form_mapping,remove_lead_form_mapping,automation_set_paused,create_integration_trigger,delete_integration_trigger,set_keyword_responder, orset_lead_form_instant_outreachwithoutconfirm: trueapplies nothing — it returns a before/after preview to review (or show the user). Call again withconfirm: trueto apply. - Writes fail closed.
set_lead_form_mappingverifies the form exists on one of the account's own connected pages against the live list, andset_keyword_responderchecks each post it adds with Meta; if the service is unreachable, the call errors and nothing changes. - Someone else's save wins. An edit re-reads what it changes just before writing; if the automation was saved in between (in the app or by another call), the write is refused with
version_conflict— read it again and repeat.
set_lead_form_mapping also requires a plan that includes social account connections — the same entitlement as connecting the page. Removing a mapping works on any plan.
list_lead_forms
List the account's connected Facebook pages and each page's Meta Lead Gen forms, with whether each form already has a mapping. Use it to discover form ids before configuring.
| Param | Type | Required | Description |
|---|---|---|---|
| social_account_id | string | no | One connected social account id (from a previous call). Absent = all connected Facebook accounts |
Each form comes back with id, name, status, leads_count, and mapped (plus assistant_id and outreach_mode when mapped). A page whose Ads add-on is missing returns an empty form list with ads_addon_missing: true.
get_lead_forms_automation
The full automation config — every per-form mapping — plus a per-form diagnosis answering "why aren't this form's leads reaching anyone": form missing from every connected page, assistant deleted, no active channel of the chosen type for that assistant. No parameters.
Returns configured: false with guidance when the automation was never set up. When the social service is unreachable the stored config still comes back, with forms_verified: false instead of form-existence issues.
set_lead_form_mapping
Create or update the mapping for one form. Updating an existing mapping changes only the parameters you pass — everything else survives.
| Param | Type | Required | Description |
|---|---|---|---|
| form_id | string | yes | Meta Lead Gen form id (from list_lead_forms) |
| assistant_id | string | on create | Assistant that handles this form's leads |
| outreach_mode | string | no | capture-and-outreach (default on create) — capture the lead AND message them. capture-only — capture into Contacts, send the person nothing |
| channels | string[] | in outreach mode | Outreach channels, e.g. ["Email", "TelegramAccount"]. Each must be an active connected channel of the assistant |
| agent_instruction | string | in outreach mode | What the assistant should do when it messages the lead (unless use_custom_message) |
| use_custom_message | boolean | no | Send custom_message verbatim instead of having the assistant compose the opener |
| custom_message | string | with use_custom_message | The verbatim first message |
| custom_email_subject | string | no | Email channel only: subject line |
| funnel_status | string | no | capture-only mode: funnel status (pipeline stage name) to drop captured leads into. Must not contain double quotes |
| notify_telegram_on_capture | boolean | no | capture-only mode: notify the assistant's Flight Control Telegram group on every captured lead |
| send_delay | string | no | Delay before sending, as a Go duration: "5m", "1h30m" |
| send_window_start / send_window_end | string | no | Daily send window, HH:MM (set together) |
| send_timezone | string | no | IANA timezone for the window, e.g. Europe/Kyiv |
| interval_seconds | number | no | Minimum seconds between sends to consecutive leads of this form |
| pipeline_id | number | no | Pipeline to drop this form's leads into |
| skip_chat_history | boolean | no | Don't write the form submission into the lead's chat history |
| form_name | string | no | Display-name override; defaults to the live form name |
| confirm | boolean | no | Absent/false = preview only. true = apply |
Switching a mapping to outreach mode clears funnel_status and notify_telegram_on_capture (they only apply to capture-only routing) — same rule as the app editor.
remove_lead_form_mapping
Remove (disable) one form's mapping. Its leads are still captured into Contacts — capture is always-on — but no automation fires for them.
| Param | Type | Required | Description |
|---|---|---|---|
| form_id | string | yes | Form id of the mapping to remove |
| confirm | boolean | no | Absent/false = preview only. true = apply |
automation_set_paused
Pause or resume one automation without deleting it — the Connect agent switch on its page in the app. Its settings, webhook URLs, and assistant links stay.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | integer | yes | The automation's ID (list_integrations with type: "automation") |
| paused | boolean | yes | true pauses, false resumes |
| confirm | boolean | no | Absent/false = preview only. true = apply |
What a pause does depends on the automation. For every kind below, sends that were already queued when it was paused are dropped, not held, even if it is resumed before they were due.
| Automation | While paused |
|---|---|
| Trigger Outreach | Its webhook URLs answer skipped: a lead that arrives is not captured, and nobody is messaged |
| Keyword Responder (Instagram and Facebook) | Comments on its monitored posts get no public reply and no DM; each is logged as skipped ("automation paused") |
| Lead Forms | Leads are still captured into Contacts, but nobody is messaged or notified |
| Lead Form Instant Outreach | Lead-form emails are ignored (they stay in the mailbox): no lead is created, and nobody is messaged |
Other automation kinds (Lead Hydration, Telegram Funnel, Client Base Outreach, Lead Supervisor) don't read the switch yet, so pausing them is refused — stop them from the app instead. On resume the automation handles new events again; nothing that arrived while it was paused is replayed, and dropped sends are not resent. The preview lists the assistants the automation acts for and returns paused_before, paused_after, and the effect; a call that changes nothing returns changed: false.
Older accounts can have two Trigger Outreach automations that share one webhook URL, and the older one is the one that answers it. Pausing or resuming the newer one is then refused with the older one's ID: change that one first, then the newer one.
create_integration_trigger
Add a webhook trigger to an assistant's Trigger Outreach automation — Add trigger on its page in the app. When the assistant has no Trigger Outreach automation yet, it is created and attached to the assistant. Each lead another system POSTs to the trigger's webhook URL is captured into Contacts and, in capture-and-outreach mode, messaged by the assistant.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant whose leads these are |
| name | string | yes | The trigger's name; it also names the lead source in Contacts. Unique within the automation |
| outreach_mode | string | no | capture-and-outreach (default) or capture-only |
| channels | string[] | in outreach mode | Channels the assistant may message the lead on, each connected and on for the assistant. Ringostat and UniTalk are refused: Trigger Outreach doesn't send on them |
| agent_instruction | string | in outreach mode | What the assistant should do when it messages the lead (unless use_custom_message) |
| use_custom_message | boolean | no | Send custom_message word for word |
| custom_message | string | with use_custom_message | The exact first message; {name}, {context}, {phone_number} and {email} are filled from the payload |
| custom_email_subject | string | with a custom message on Email | The email subject |
| funnel_status | string | no | capture-only: the funnel status captured leads get |
| notify_telegram_on_capture | boolean | no | capture-only: post each captured lead to the assistant's Flight Control group |
| send_delay | string | no | Wait before messaging: "5m", "1h30m", "2d", or minutes |
| send_window_start / send_window_end | string | no | Daily send window, HH:MM (set together, with send_timezone) |
| send_timezone | string | with a window | IANA zone of the window, e.g. Europe/Kyiv |
| interval_seconds | integer | no | At least this many seconds between two sends of this trigger |
| skip_chat_history | boolean | no | Don't load the lead's earlier conversation |
| skip_if_contacted_within_hours | integer | no | Skip a lead already messaged within this many hours (1–8760; 0 = never) |
| confirm | boolean | no | Absent/false = preview only. true = create |
The confirmed call returns integration_id, trigger_id, and webhook_url (https://api.mychatbot.app/webhook/triggers/{token}/{trigger_id}). The URL is a credential — whoever holds it can send leads in — so give it only to the system that will call it. Send a POST with JSON phone_number and/or email, plus optional name, context, company_name, and country_code. An automation holds at most 20 triggers. Status callbacks, call result callbacks, recipients and attachments stay in the app; edit a trigger's instruction, message, channels or name with update_integration_trigger (sales_integrations).
delete_integration_trigger
Delete one trigger — the trash button on the Trigger Outreach page.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | integer | yes | The Trigger Outreach automation |
| trigger_id | string | yes | The trigger's ID: the last part of its webhook URL, or metadata.triggers[] of get_integration |
| confirm | boolean | no | Absent/false = preview only. true = delete |
Its webhook URL stops working (calls get "not found"), its sends still queued are dropped, and leads it already captured stay in Contacts with their source. A new trigger gets a new URL, so this can't be undone. The preview shows how many leads came through it. On older accounts where two automations share one webhook URL, the trigger is deleted from both.
list_social_posts
The newest posts of the Instagram account or Facebook Page connected to an assistant — the post picker of the Keyword Responder page — with watched: true on posts its Keyword Responder already monitors.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant |
| platform | string | yes | instagram, or facebook (the Page connected as its Messenger channel) |
| page_id | string | no | Which page, when the assistant has several on that platform (the result lists them as pages) |
| limit | integer | no | 1–100 posts (default 25) |
| after | string | no | next from a previous call, for older posts |
set_keyword_responder
Create or change an assistant's Keyword Responder (Instagram and Facebook comments). When the assistant has none, it is created and attached; otherwise only what you pass changes.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant whose Instagram or Facebook channel receives the comments |
| keywords | string[] | on create | Replaces the keyword list; a comment matches when it contains one (any case). Repeats are dropped |
| add_post_ids | string[] | on create | Posts to start monitoring (list_social_posts), checked with Meta among the page's newest 500 posts |
| remove_post_ids | string[] | no | Posts to stop monitoring; their per-post settings go with them |
| public_reply | string | no | The reply under a matching comment, at most 200 characters; empty posts none |
| dm_agent_instruction | string | on create, unless a custom DM | What the assistant should do in the DM |
| dm_use_custom_message | boolean | no | Send dm_custom_message word for word instead |
| dm_custom_message | string | with dm_use_custom_message | The exact DM, at most 1,000 characters |
| confirm | boolean | no | Absent/false = preview only. true = apply |
It answers comments as soon as it is saved; pause it with automation_set_paused. Per-post overrides stay in the app. Removing every post is refused — pause it instead.
set_lead_form_instant_outreach
Create or change a Lead Form Instant Outreach automation: lead-form notification emails from one sender, arriving in the assistant's connected mailbox, become leads; in collect-and-outreach mode the assistant also messages each lead.
| Param | Type | Required | Description |
|---|---|---|---|
| integration_id | integer | to change one | The automation to change; absent = create one |
| assistant_id | string | on create | The assistant whose mailbox receives the emails |
| lead_source_email | string | on create | The sender address of the form notifications, e.g. forms@example.com |
| mode | string | on create | collect-only or collect-and-outreach |
| channels | string[] | in outreach mode | Channels to message the lead on, in order of preference, each connected and on for the assistant |
| agent_instruction | string | in outreach mode | What the assistant should do when it messages the lead |
| funnel_status | string | no | collect-only: the funnel status captured leads get (empty clears it) |
| notify_telegram_on_capture | boolean | no | collect-only: post each captured lead to the assistant's Flight Control group |
| send_delay / send_window_start / send_window_end / send_timezone / interval_seconds | no | Outreach schedule, as for triggers; an empty string clears one | |
| confirm | boolean | no | Absent/false = preview only. true = apply |
The assistant needs its Email channel connected — the mailbox the form emails arrive in. An assistant holds up to 5, one per sender. Switching to collect-only clears the channels and the schedule; switching to collect-and-outreach clears the funnel status. A custom message sent word for word is set in the app.
Testing & evals (sales_evals)
Saved eval scenarios (fixed lists of user turns, captured from real chats or written inline) plus eval runs that replay them against an assistant's current or candidate instructions and score answer stability. The workflow — capture, replay, A/B a prompt, measure consistency — is walked through in Testing & evals.
Conventions specific to this area:
- Ids are numeric.
scenario_idandrun_idare the numeric ids returned by the matching save/list/start tool; a JSON number or a numeric string both work. - Eval traffic is free. Every turn a run executes is recorded as test-chat usage, never billed as customer messages.
- Runs are asynchronous.
eval_run_startreturns immediately; polleval_run_getfor progress and results. One queued-or-running run per account — a second start is rejected (not queued) with the active run's id. - 30-turn cap. Scenarios and runs replay at most 30 user turns. Chat and test-chat sources with more turns are cut to the first 30 and flagged; longer inline
messageslists are rejected.
eval_scenario_save
Save (or update, by name) an eval scenario: a fixed list of user messages to test an assistant against, replayable any number of times. Provide the user turns exactly one of three ways: messages (inline), from_chat_id (copy the user turns of a real chat), or from_test_chat_assistant_id (copy the user turns of that assistant's current test-chat session).
| Param | Type | Required | Description |
|---|---|---|---|
| name | string | yes | Scenario name — the upsert key: saving again with the same name updates the scenario in place |
| description | string | no | What this scenario tests |
| assistant_id | string | no | Associate the scenario with an assistant (bookkeeping only — eval_run_start always names its assistant explicitly) |
| messages | array of (string or object) | one-of | The user turns, in order (max 30). Each item is the turn's text, or {content, images} for a turn where the client sends photos (images: public http(s) URLs, max 4; content optional when images is given). Provide exactly one of messages, from_chat_id, from_test_chat_assistant_id |
| from_chat_id | string | one-of | Build the scenario from a real chat: its user turns become the messages, in order. Photos the client sent stay with their turn and are replayed as pictures. Use the chat id from list_chats |
| from_test_chat_assistant_id | string | one-of | Build the scenario from the assistant's current test-chat session history (the one test_chat_send writes), photo turns included |
| reference | string[] | no | Expected assistant answers, aligned per turn (item i = expected answer to turn i; use "" for turns without a reference). Enables the vs_reference similarity score in runs |
json
// eval_scenario_save — inline turns, the first one a photo with no text
{
"name": "photo-lookup",
"messages": [
{ "images": ["https://static.mychatbot.app/chat-images/….jpg"] },
"Is it available in size M?"
]
}Notes: only user turns are captured from a chat — activity events and platform notes are skipped, and empty turns dropped. A captured photo turn keeps its images and loses the copy of the URL the live channel adds to the text, so a replay sends the client's original turn rather than a bare link. A chat or test-chat source with more than 30 user turns is cut to the first 30, and the response carries turns_truncated: true plus a note; an inline messages list over 30 is rejected instead. reference may be shorter than the turn list but not longer. Returns {scenario_id, name, turns, updated} — updated: true means an existing scenario of that name was overwritten.
eval_scenario_list
List saved eval scenarios: scenario_id, name, description, turns (count), has_reference, timestamps, and assistant_id when one was associated. No parameters.
eval_scenario_get
Get one saved scenario in full: the user turns (messages, each {content}, plus images on a photo turn) and the reference answers when set.
| Param | Type | Required | Description |
|---|---|---|---|
| scenario_id | number | yes | The scenario id from eval_scenario_list or eval_scenario_save |
eval_scenario_delete
Delete a saved eval scenario. Past runs that used it keep their transcripts (the run's scenario_id link is cleared, results stay readable).
| Param | Type | Required | Description |
|---|---|---|---|
| scenario_id | number | yes | The scenario id from eval_scenario_list |
eval_run_start
Start an eval run: replay a conversation's user turns against an assistant and record every response and tool call. With the defaults (runs: 1, judge: "none") this is a plain replay — one pass, transcript only. The run executes in the background — poll eval_run_get.
| Param | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | yes | The assistant to evaluate |
| scenario_id | number | one-of | Run a saved scenario. Provide exactly one of scenario_id, source_chat_id, messages |
| source_chat_id | string | one-of | Replay a real chat's user turns directly (activity events and platform notes skipped; photos replayed as pictures). Use the chat id from list_chats |
| messages | array of (string or object) | one-of | Inline user turns, in order (max 30). Each item is the turn's text, or {content, images} for a photo turn — same shape as eval_scenario_save |
| runs | number | no | Iterations over the turns (default 1, max 5). Each iteration is an independent conversation from a clean history |
| judge | string | no | none (default): transcripts only. similarity: deterministic token-overlap scoring — per-turn consistency across iterations (needs runs ≥ 2) and per-turn score vs the scenario's reference answers (when set). No extra LLM cost |
| instruction_override | string | no | Candidate instruction text to test instead of the assistant's saved instructions. The live assistant is not modified. Omit to run the assistant as currently configured |
| skills_override | string[] | no | Explicit skill selection for the run: skill slugs from assistant_list_skills. An empty array runs with no skills. Omit to use the assistant's configured skills |
Notes: exactly one source is required. A slug in skills_override the assistant doesn't have is an error listing the available slugs. If a run is already queued or running for the account the call fails with that run's id — poll or eval_run_cancel it first. Returns {run_id, status: "queued", turns, runs, judge}. Runs have a 15-minute wall-clock deadline; a run that exceeds it flips to error with completed turns retained.
eval_run_get
Get an eval run's status and results. While the run is executing (status running) it returns the turns completed so far — poll it to watch progress.
| Param | Type | Required | Description |
|---|---|---|---|
| run_id | number | yes | The run id from eval_run_start |
| include_tool_details | boolean | no | false (default): tool calls carry name + result preview only. true: include each call's arguments too |
Returns JSON (abbreviated):
json
{
"run_id": 41,
"assistant_id": 42,
"status": "done",
"runs": 3,
"judge": "similarity",
"source": { "type": "scenario", "scenario_id": 17, "turns": 3 },
"instruction_override_used": true,
"iterations": [
{
"iteration": 1,
"turns": [
{
"turn": 1,
"user_message": "how much is delivery?",
"response": "Delivery is 80 UAH…",
"tool_calls": [
{ "name": "get_faq", "result_preview": "…", "result_bytes": 412 }
]
}
]
}
],
"metrics": {
"judge": "similarity",
"per_turn": [ { "turn": 1, "consistency": 0.88, "vs_reference": 0.83 } ],
"overall_consistency": 0.86,
"overall_vs_reference": 0.83
},
"created_at": "2026-08-07T09:14:02Z",
"finished_at": "2026-08-07T09:16:47Z"
}Notes: status is one of queued, running, done, error, cancelled; while running the response carries a note that results are partial. A turn replayed with photos also carries user_images (their URLs); a photo-only turn has an empty user_message. instruction_override_used: true appears when the run tested candidate text (the text itself is not echoed); skills_override echoes the pinned slug list when one was given. Metrics appear only when judge: "similarity" was requested: scores are 0–1 (higher = more similar), consistency needs ≥ 2 iterations, vs_reference needs a non-empty reference for the turn, and the overall_* values average the defined per-turn scores. Failed turns and iterations carry an error field. Oversized results (~2 MB cap) are trimmed with results_truncated: true and a truncation_note — never silently. A failed run has status error and a top-level error message.
eval_run_cancel
Cancel a queued or running eval run (best effort — the current turn finishes first). Completed turns stay available via eval_run_get. Cancelling an already-finished run is a no-op that reports the final status.
| Param | Type | Required | Description |
|---|---|---|---|
| run_id | number | yes | The run id from eval_run_start |
See also
- Build and operate outreach with Claude or Codex — end-to-end use of the
sales_outreachtools - Testing & evals — the capture → replay → A/B → stability workflow behind the
sales_evalstools, with a worked example - Sales Platform MCP — connecting Claude Code, Codex, or Cursor to these tools
- Connectors cheat sheet — the app walkthrough and per-agent toggles
- Working with the Sales Platform — the operator/runtime split and outreach hand-off
- Tools & toggles — the single Sales Platform tools toggle
