Collega MCP server
Collega runs a store's WhatsApp customer service. Its bot answers customers and hands a conversation to a person when it cannot. The MCP server lets the store's own agent be that person: read the queue, answer the customer, and teach the bot so it can answer next time.
In short
The Collega MCP server lets a store's agent (Claude, for example) connect to one store with defined permissions, read conversations waiting for a person, answer customers and update the bot's knowledge base. It is a paid feature: to request it for your store, email bar@collega.co.il. The Collega team opens the connection, and the store sees it in Settings › Connections.
Connect
The server is https://my.collega.co.il/mcp (Streamable HTTP, stateless, JSON responses). Access is per store and is turned on by the Collega team: we register your OAuth client and bind it to exactly one store. There is no dynamic client registration and no public client.
- Request a connection: MCP access is a paid feature, enabled per store. Email bar@collega.co.ilwith the store's name and the agent you will connect (and say if you also want the escalation webhook). You receive a client id and secret, and the store sees the connection in Settings › Connections.
- In Claude: Settings › Connectors › Add custom connector. Enter the server URL, open Advanced settings and enter the client id and the client secret. In ChatGPT, enter the server URL and choose your own OAuth client (not automatic registration). On Connect you are sent to
https://my.collega.co.il: sign in with your store account and approve the permissions the connection asks for. - A backend without a person uses the client-credentials grant at the
token_endpointthe authorization server publishes, and sends the token as a bearer header:
curl -s -X POST https://my.collega.co.il/mcp \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Auth
OAuth 2.1 with confidential clients only. Discovery starts at https://my.collega.co.il/.well-known/oauth-protected-resource/mcp, which names the authorization server, https://login.collega.co.il (the tokens' issuer; its metadata is at https://login.collega.co.il/.well-known/oauth-authorization-server). Sign-in and consent happen on https://my.collega.co.il. A person's token is accepted only if it was issued for this server (its audience is https://my.collega.co.il/mcp), and only while that person is still a member of the store. Revoking the client or removing the person cuts access on the next call. Calls are limited to 60 per minute per client.
Scopes (a client never gets more than the store granted it):
| conversations:read | Read customer conversations and their state. |
| conversations:write | Switch a conversation between the bot and a human representative. |
| messages:send | Send messages and templates to customers. |
| knowledge:read | Search the store's knowledge base. |
| knowledge:write | Create, update and delete knowledge entries. |
| orders:read | Read a conversation's customer orders. |
| insights:read | Read the store's service metrics. |
| resolutions:read | Read the resolution reports this store's agents filed. |
| pii:read | See customer phone numbers and full names. Without it they are masked. |
Tools
Every tool acts on the one store your client is bound to; no tool takes a store argument. Conversations are named by opaque ids that never contain a phone number.
list_waiting_conversations
Scope: conversations:read · read-only
Conversations a person is handling right now (the bot escalated them, or the store took them over), longest-waiting first. This is the queue to work from. Each row carries `handoff.started_at_ms`: pass it back as `expected_handoff_started_at` when you reply, so two agents never answer the same escalation.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"limit": {
"default": 50,
"type": "integer",
"minimum": 1,
"maximum": 100
}
}
}Example arguments
{
"limit": 20
}list_conversations
Scope: conversations:read · read-only
The store's conversations by most recent activity, whoever is handling them. Use list_waiting_conversations for the queue a person must answer.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"limit": {
"default": 25,
"type": "integer",
"minimum": 1,
"maximum": 100
}
}
}Example arguments
{
"limit": 25
}search_conversations
Scope: conversations:read · read-only
Find conversations by customer phone (any part of the number), customer name, or words in the messages, across the store's recent conversations. Returns matching conversations with the message that matched.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"query": {
"type": "string",
"minLength": 2,
"description": "Phone digits, a name, or text."
},
"limit": {
"default": 10,
"type": "integer",
"minimum": 1,
"maximum": 50
}
},
"required": [
"query"
]
}Example arguments
{
"query": "אוהל 4 אנשים",
"limit": 10
}get_conversation
Scope: conversations:read · read-only
A conversation's customer-visible messages (newest last), who is handling it, and the WhatsApp 24-hour window. Voice notes arrive already transcribed (`transcribed_from_audio`); images and files come as short-lived links. Read this right before replying: pass `last_message_id` and `handoff.started_at_ms` to reply_to_customer.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"minLength": 1,
"description": "The conversation's id as Collega returned it (list_* / search_conversations)."
},
"limit": {
"default": 50,
"description": "How many of the most recent messages to return.",
"type": "integer",
"minimum": 1,
"maximum": 200
}
},
"required": [
"conversation_id"
]
}Example arguments
{
"conversation_id": "Qm9vdHMxMjM0",
"limit": 50
}get_conversation_state
Scope: conversations:read · read-only
Cheap check of a conversation's state without its messages: who is handling it, the 24-hour window, the latest message id, and whether the store paused sending. Use it to confirm nothing changed before acting.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"minLength": 1,
"description": "The conversation's id as Collega returned it (list_* / search_conversations)."
}
},
"required": [
"conversation_id"
]
}Example arguments
{
"conversation_id": "Qm9vdHMxMjM0"
}reply_to_customer
Scope: messages:send · reaches the customer
Send your reply to the customer on WhatsApp, exactly as written, the way a person at the store replies from the admin. Only while a person is handling the conversation (handled_by 'human'); a conversation the bot handles is refused. If the bot escalated it, the bot takes it back after your reply; if the store took it over, it stays with the store. Pass what you read: expected_last_message_id and expected_handoff_started_at from get_conversation. If anything changed, nothing is sent and you get the new messages; reconsider, then retry. `resolution` is required and is checked before sending: it records why the bot could not answer and what you did. If your answer is reusable for future customers, first save it with create_knowledge or update_knowledge (search_knowledge before creating) and list the ids in resolution.knowledge_ids, so the bot can answer it next time.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"minLength": 1,
"description": "The conversation's id as Collega returned it (list_* / search_conversations)."
},
"message": {
"type": "string",
"minLength": 1,
"maxLength": 4096,
"description": "The exact text the customer will receive."
},
"expected_last_message_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "`last_message_id` from your latest get_conversation."
},
"expected_handoff_started_at": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "`handoff.started_at_ms` from your latest read."
},
"acknowledge_changes": {
"default": false,
"description": "Send even though the conversation changed. Only after reading the change and deciding the reply still fits.",
"type": "boolean"
},
"idempotency_key": {
"description": "Optional but recommended. The same key with the same arguments returns the first result instead of sending twice; the same key with different arguments is refused.",
"type": "string",
"minLength": 8,
"maxLength": 100,
"pattern": "^[A-Za-z0-9_.:-]+$"
},
"resolution": {
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": [
"store_data_unreached",
"store_data_conflict",
"knowledge_missing",
"external_system",
"owner_decision",
"out_of_scope"
],
"description": "Why the bot could not answer this itself: store_data_unreached (the data exists in a store system the bot cannot read), store_data_conflict (two sources disagree), knowledge_missing (nobody taught it), external_system (needs a system outside the store, e.g. a courier), owner_decision (a person had to decide), out_of_scope (not something the bot should handle)."
},
"outcome": {
"type": "string",
"enum": [
"answered",
"action_taken",
"handed_to_owner"
],
"description": "answered: you told the customer something. action_taken: you also did something in a system. handed_to_owner: you told the customer a person will follow up."
},
"facts": {
"maxItems": 20,
"type": "array",
"items": {
"type": "object",
"properties": {
"claim": {
"type": "string",
"minLength": 1,
"maxLength": 500,
"description": "One fact your reply relies on."
},
"source": {
"type": "string",
"enum": [
"product",
"order",
"coupon",
"knowledge",
"external_system",
"web",
"person"
]
},
"system": {
"description": "Which system, e.g. 'woocommerce', 'cargo-courier'.",
"type": "string",
"maxLength": 100
},
"reference": {
"description": "Order number, SKU, URL or record id.",
"type": "string",
"maxLength": 200
},
"as_of": {
"description": "When the fact was read, ISO 8601.",
"type": "string",
"maxLength": 40
}
},
"required": [
"claim",
"source"
]
},
"description": "The facts the reply states, each with where it came from."
},
"actions": {
"maxItems": 20,
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"minLength": 1,
"maxLength": 80,
"description": "e.g. 'created_coupon', 'opened_return'."
},
"system": {
"type": "string",
"maxLength": 100
},
"reference": {
"type": "string",
"maxLength": 200
},
"params": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
}
},
"required": [
"type"
]
},
"description": "What you did in other systems before replying. Empty if nothing."
},
"tools_used": {
"maxItems": 30,
"type": "array",
"items": {
"type": "string",
"maxLength": 100
},
"description": "Your own tools outside Collega that you used."
},
"policy_rule": {
"description": "The store rule you applied, if any, in a sentence.",
"type": "string",
"maxLength": 500
},
"identity": {
"type": "object",
"properties": {
"matched_by": {
"type": "string",
"enum": [
"phone",
"order_id",
"none"
],
"description": "How you tied the customer to their records."
},
"order_ids": {
"maxItems": 20,
"type": "array",
"items": {
"type": "string",
"maxLength": 60
}
}
},
"required": [
"matched_by"
],
"description": "How you identified the customer."
},
"bot_issue": {
"description": "Set when the bot told the customer something wrong.",
"type": "object",
"properties": {
"bot_said": {
"type": "string",
"minLength": 1,
"maxLength": 500
},
"correct": {
"type": "string",
"minLength": 1,
"maxLength": 500
}
},
"required": [
"bot_said",
"correct"
]
},
"knowledge_ids": {
"maxItems": 20,
"type": "array",
"items": {
"type": "string",
"maxLength": 80
},
"description": "Knowledge entries you created or updated for this case (create_knowledge / update_knowledge). Empty when nothing reusable was learned."
},
"summary": {
"type": "string",
"minLength": 1,
"maxLength": 1000,
"description": "Two or three sentences: what the customer needed and what you did."
}
},
"required": [
"category",
"outcome",
"facts",
"actions",
"tools_used",
"identity",
"knowledge_ids",
"summary"
],
"description": "Required. How this reply came about, for the platform team. Never shown to the customer."
}
},
"required": [
"conversation_id",
"message",
"expected_last_message_id",
"expected_handoff_started_at",
"resolution"
]
}Example arguments
{
"conversation_id": "Qm9vdHMxMjM0",
"message": "היי! ההזמנה שלך יצאה היום עם שליח, מספר מעקב 123456.",
"expected_last_message_id": "msg-1760000000000-ab12cd",
"expected_handoff_started_at": 1760000000000,
"idempotency_key": "reply-Qm9vdHMxMjM0-1",
"resolution": {
"category": "store_data_unreached",
"outcome": "answered",
"facts": [
{
"claim": "Order 4521 shipped today",
"source": "external_system",
"system": "courier",
"reference": "123456"
}
],
"actions": [],
"tools_used": [
"courier_lookup"
],
"identity": {
"matched_by": "phone",
"order_ids": [
"4521"
]
},
"knowledge_ids": [],
"summary": "Customer asked where their order is. The courier system shows it left today; sent the tracking number."
}
}set_handoff
Scope: conversations:write
Switch who handles a conversation: 'handoff' to a person (the bot stops answering), 'ai' back to the bot. Taking over a conversation the bot escalated makes it the store's until someone returns it. Returning it to the bot ends the handoff. A reason is required. To 'handoff': needs_owner_decision, needs_external_system, customer_asked_for_human, complaint_or_sensitive, bot_answer_wrong, other. To 'ai': resolved, customer_resolved_it, no_reply_needed, unnecessary_escalation, other. With 'other', `details` is required. Pass expected_handoff_started_at (handoff.started_at_ms, or null when there was none) so you never undo a change someone else just made.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"minLength": 1,
"description": "The conversation's id as Collega returned it (list_* / search_conversations)."
},
"mode": {
"type": "string",
"enum": [
"handoff",
"ai"
]
},
"reason": {
"type": "string",
"enum": [
"needs_owner_decision",
"needs_external_system",
"customer_asked_for_human",
"complaint_or_sensitive",
"bot_answer_wrong",
"other",
"resolved",
"customer_resolved_it",
"no_reply_needed",
"unnecessary_escalation"
]
},
"details": {
"description": "Required when reason is 'other'; welcome otherwise.",
"type": "string",
"maxLength": 500
},
"expected_handoff_started_at": {
"description": "handoff.started_at_ms as you last read it; null if no handoff was active.",
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
}
},
"required": [
"conversation_id",
"mode",
"reason"
]
}Example arguments
{
"conversation_id": "Qm9vdHMxMjM0",
"mode": "ai",
"reason": "resolved",
"expected_handoff_started_at": 1760000000000
}send_template
Scope: messages:send · reaches the customer
Send one of the store's approved templates (from list_templates) to the customer. Use it when the 24-hour window is closed. Refused on a conversation the bot handles unless the store enabled AI composing.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"minLength": 1,
"description": "The conversation's id as Collega returned it (list_* / search_conversations)."
},
"template_name": {
"type": "string",
"minLength": 1
},
"language": {
"type": "string",
"minLength": 1
},
"idempotency_key": {
"description": "Optional but recommended. The same key with the same arguments returns the first result instead of sending twice; the same key with different arguments is refused.",
"type": "string",
"minLength": 8,
"maxLength": 100,
"pattern": "^[A-Za-z0-9_.:-]+$"
}
},
"required": [
"conversation_id",
"template_name",
"language"
]
}Example arguments
{
"conversation_id": "Qm9vdHMxMjM0",
"template_name": "order_followup",
"language": "he",
"idempotency_key": "tmpl-Qm9vdHMxMjM0-1"
}list_templates
Scope: messages:send · read-only
The store's approved WhatsApp templates that can be sent as-is (no variables). Templates are the only way to reach a customer more than 24 hours after they last wrote.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {}
}Example arguments
{}get_customer_orders
Scope: orders:read · read-only
The orders placed with the phone number of a conversation's customer, read live from the store's shop, newest first, with spend totals. Matching is by the WhatsApp number only; a customer who ordered with another number will not show up. Without pii:read the billing name is reduced to initials and personal links are omitted.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"minLength": 1,
"description": "The conversation's id as Collega returned it (list_* / search_conversations)."
}
},
"required": [
"conversation_id"
]
}Example arguments
{
"conversation_id": "Qm9vdHMxMjM0"
}get_customer_memory
Scope: conversations:read · read-only
What the store knows about a conversation's customer from earlier conversations and their orders: facts and preferences, products and purchases (written from the shop's own orders only), and a line per past conversation. Written by Collega a day after a conversation goes quiet, so the newest messages are not in it — read get_conversation for those. Each line is dated and may be out of date; check order status, stock and prices with the other tools. `memory` is null when nothing has been written yet. Read-only: the memory is never edited by hand.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"minLength": 1,
"description": "The conversation's id as Collega returned it (list_* / search_conversations)."
}
},
"required": [
"conversation_id"
]
}Example arguments
{
"conversation_id": "Qm9vdHMxMjM0"
}search_knowledge
Scope: knowledge:read · read-only
Search the store's knowledge base with the same retrieval the customer bot uses: facts it answers with and procedures it follows. Use it before answering a customer (to match what the store already says) and before creating knowledge (to update instead of duplicate). Results carry the id update_knowledge and delete_knowledge need. Results with source 'store_policies' are the store's own policies text, which the bot also answers from: read-only here (no id), the store edits it in its settings.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"query": {
"type": "string",
"minLength": 1,
"description": "What to look up, any language."
},
"product_id": {
"description": "Prefer knowledge scoped to this catalog product.",
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
},
"required": [
"query"
]
}Example arguments
{
"query": "מדיניות החזרות"
}create_knowledge
Scope: knowledge:write
Teach the store's bot something reusable, live immediately: a 'fact' it answers with, or a 'procedure' it follows. Returns the new id and any near-duplicates it found — if one is the same thing, update that one and delete this. Rules: save only facts you verified, phrased as general store knowledge. Never include a specific customer's details (names, phones, order numbers) or a one-off promise. Never store a temporary price or stock level — the bot reads those live. search_knowledge first and update the matching entry instead of creating a near-duplicate. After saving, list the id in reply_to_customer's resolution.knowledge_ids.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"shape": {
"type": "string",
"enum": [
"fact",
"procedure"
]
},
"title": {
"description": "A procedure's title; a fact's headline question.",
"type": "string",
"maxLength": 200
},
"body": {
"type": "string",
"minLength": 1,
"maxLength": 4000,
"description": "A fact: the answer, in the store's language. A procedure: the runbook the bot follows (what to ask, what to do)."
},
"topic": {
"type": "string",
"enum": [
"warranty",
"shipping",
"returns",
"compatibility",
"sizes",
"colors",
"promotions",
"payment",
"usage",
"specs",
"pricing"
],
"description": "One of: warranty, shipping, returns, compatibility, sizes, colors, promotions, payment, usage, specs, pricing."
},
"product_id": {
"description": "Scope to one catalog product; omit for store-wide.",
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"phrases": {
"description": "Ways a customer would ask this. Collega writes paraphrases when omitted.",
"maxItems": 10,
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 200
}
}
},
"required": [
"shape",
"body",
"topic"
]
}Example arguments
{
"shape": "fact",
"title": "כמה זמן לוקח משלוח לאילת?",
"body": "משלוח לאילת מגיע תוך 3–5 ימי עסקים.",
"topic": "shipping"
}update_knowledge
Scope: knowledge:write
Replace an existing entry's content (id from search_knowledge), live immediately. Send the whole new body, not a diff. Rules: save only facts you verified, phrased as general store knowledge. Never include a specific customer's details (names, phones, order numbers) or a one-off promise. Never store a temporary price or stock level — the bot reads those live. search_knowledge first and update the matching entry instead of creating a near-duplicate. After saving, list the id in reply_to_customer's resolution.knowledge_ids.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"minLength": 1,
"description": "kbf-… (fact) or kbpr-… (procedure)."
},
"shape": {
"type": "string",
"enum": [
"fact",
"procedure"
]
},
"title": {
"type": "string",
"maxLength": 200
},
"body": {
"type": "string",
"minLength": 1,
"maxLength": 4000,
"description": "A fact: the answer, in the store's language. A procedure: the runbook the bot follows (what to ask, what to do)."
},
"topic": {
"type": "string",
"enum": [
"warranty",
"shipping",
"returns",
"compatibility",
"sizes",
"colors",
"promotions",
"payment",
"usage",
"specs",
"pricing"
],
"description": "One of: warranty, shipping, returns, compatibility, sizes, colors, promotions, payment, usage, specs, pricing."
},
"product_id": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"phrases": {
"description": "Ways a customer would ask this. Collega writes paraphrases when omitted.",
"maxItems": 10,
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 200
}
}
},
"required": [
"id",
"shape",
"body",
"topic"
]
}Example arguments
{
"id": "kbf-1a2b3c",
"shape": "fact",
"body": "משלוח לאילת מגיע תוך 2–4 ימי עסקים.",
"topic": "shipping"
}delete_knowledge
Scope: knowledge:write · destructive
Delete a knowledge entry that is wrong or duplicated (id from search_knowledge). Live immediately: the bot stops using it on its next answer.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^kb(f|pr)-",
"description": "kbf-… (fact) or kbpr-… (procedure)."
}
},
"required": [
"id"
]
}Example arguments
{
"id": "kbf-1a2b3c"
}list_resolutions
Scope: resolutions:read · read-only
Resolution reports filed with this store's MCP replies, newest first, optionally filtered by category. Use it to find what the bot keeps missing — the categories and facts show which data it cannot reach and which knowledge to add.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"since": {
"description": "Only reports after this time (ISO 8601).",
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"category": {
"type": "string",
"enum": [
"store_data_unreached",
"store_data_conflict",
"knowledge_missing",
"external_system",
"owner_decision",
"out_of_scope"
]
},
"limit": {
"default": 25,
"type": "integer",
"minimum": 1,
"maximum": 100
}
}
}Example arguments
{
"category": "knowledge_missing",
"limit": 25
}report_capability_gap
Scope: any connection
Tell the Collega team something you needed and could not do through these tools — a missing tool, a missing field, an action you were not allowed. Goes to the platform backlog, not to the store. Report once per kind of gap, not per conversation.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"ask": {
"type": "string",
"minLength": 5,
"maxLength": 1000,
"description": "What you needed, as a request: 'read a product's stock per warehouse'."
},
"kind": {
"type": "string",
"enum": [
"missing",
"forbidden"
],
"description": "missing: no tool does it. forbidden: a tool exists but refused."
},
"workaround": {
"description": "What you did instead, if anything.",
"type": "string",
"maxLength": 1000
}
},
"required": [
"ask",
"kind"
]
}Example arguments
{
"ask": "Read a product's stock per warehouse",
"kind": "missing",
"workaround": "Told the customer a person will check the branch."
}get_insights
Scope: insights:read · read-only
The store's customer-service metrics as shown on its insights dashboard: conversations, how many the bot handled alone, response times and escalations, for the last 7 days, 30 days, or all time. Returns the totals; pass daily: true for the day-by-day series behind the charts.
Input schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"range": {
"default": "7d",
"type": "string",
"enum": [
"7d",
"30d",
"all"
]
},
"daily": {
"default": false,
"description": "Also return the per-day series (large: one row per day).",
"type": "boolean"
}
}
}Example arguments
{
"range": "30d"
}Errors
A refused call returns an MCP tool error whose text is JSON: {"error": "<code>", "message": "...", "details": {...}}. Branch on error:
| not_found | The conversation, entry or order does not exist in this store. Re-list instead of guessing ids. |
| invalid_input | Fix the arguments named in `details` and call again. |
| insufficient_scope | The connection was not granted the scope this tool needs. Ask the store to grant it; do not retry. |
| rate_limited | Wait `details.retry_after_seconds` and call again. |
| conversation_changed | New messages arrived or the conversation changed since you read it. Read `details`, reconsider your reply, then call again with the new expected values (or `acknowledge_changes: true` if your reply still fits). |
| window_closed | WhatsApp's 24-hour window is closed. Only an approved template can reach the customer now (send_template). |
| sending_paused | The store paused the bot for this conversation or the whole store. Do not send; tell the store owner. |
| raw_requires_handoff | The bot is handling this conversation. Call set_handoff with mode 'handoff' first if a person really needs to answer. |
| idempotency_conflict | This idempotency key was already used with different arguments. Use a new key for a new message. |
| in_progress | A call with this idempotency key is still running. Wait a few seconds and call again with the same key. |
| send_failed | WhatsApp rejected the send. Nothing reached the customer; the store sees the failure in its timeline. |
| not_configured | The store has not set up what this tool needs (for example a shop connection). |
| knowledge_rejected | The knowledge change was refused; `details` says which item and why. |
| internal_error | Collega failed while running the tool. Nothing is assumed to have happened unless the tool's own read says so; retry later. |
Escalation webhook
A store can have Collega POST to your endpoint when a conversation is handed to a person, so your agent starts at once instead of polling. Configure it in Settings › Connections: the URL, your bearer token, which handoffs to send (the bot's escalations, the store's takeovers, or both), whether to also send handoff.ended and handoff.customer_message, and an hourly cap (default 30). handoff.customer_message fires when the customer writes again in a conversation that is still with a person (the bot does not answer there), once per burst of messages, and counts toward the same cap. Deliveries are not retried; each carries a unique event id. Changes your own agent makes through set_handoff are not sent back to you.
Every delivery is signed per Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature: v1, and a base64 HMAC-SHA256 of {id}.{timestamp}.{body}, keyed with the signing secret shown in Settings › Connections), so any Standard Webhooks library verifies it. Your bearer token, if you set one, arrives as Authorization: Bearer …. Each delivery is attempted once with a 10-second timeout; “Send test” delivers a test event. The claude_routine format wraps the same JSON as a string in {"text": ...} with an anthropic-versionheader, so the URL can be a Claude routine's fire endpoint directly.
{
"id": "evt_…",
"type": "handoff.started",
"occurred_at": "2026-10-11T10:00:00.000Z",
"conversation_id": "Qm9vdHMxMjM0",
"handoff_started_at": "2026-10-11T10:00:00.000Z",
"initiated_by": "bot",
"customer": { "name": "ד. כ.", "phone": "…4567" },
"last_messages": [{ "from": "customer", "text": "…", "at": "…" }],
"admin_url": "https://my.collega.co.il/admin/conversations/Qm9vdHMxMjM0"
}Changes and versioning
Additive changes (a new tool, a new optional argument, a new field in a result) ship without notice. Removing or renaming a tool, argument or error code is announced here at least 30 days ahead.