Combot is your company's WhatsApp and AI Phone. The API only needs the API key: with it, your system sends messages (text, PDF, copy PIX and boleto buttons) and, if you want, receives replies and handles the phone.
Available on the Bot with AI plan. Generate your key in Integrations.
Overview
The API is REST over HTTPS and uses JSON. The base URL is:
https://combot.com.br/api/v1api.combot.com.br and app.combot.com.br do not exist — use exactly the address above.
First: what will your system do?
Only send messages (access code, notice, charge, reminder) — the most common case.Requires: only the API key and POST /api/v1/messages. Do not configure a webhook.
Receive what the customer writes and reply through your system (ERP, CRM, provider).Requires: the API key and an https address in your system. When the key is saved, your system calls PUT /api/v1/integration once — nobody copies a URL or checks anything in the Combot panel.
Handle the AI Phone with your system's data (internet provider, cable TV, bank, any ERP) — like the ready-made SGP integration.Requires: case 2 with phoneAi: true. See Phone via your system.
No system: use Combot's trainable AI in the panel, or request a reply from it at POST /api/v1/ai/reply.
For AI agents
The API is built for integrations — including by AI agents. There is a machine-readable description ready for automatic consumption:
/llms.txt — service summary and endpoints in text, in the llms.txt format.
/openapi.json — OpenAPI 3.1 specification (import into AI tools and SDKs).
Just point your agent to the OpenAPI and use the company's API key as the Bearer token. On the screen of the system it integrates, ask only for the API key — no “webhook secret” field.
Authentication
All calls use your API key in the Authorization header. Generate and rotate the key in Integrations.
Authorization: Bearer cb_live_sua_chave_aquiKeep the key on the backend. Never expose it in the browser or in public apps.
Rules that don't change
They apply to any system, ERP or AI:
The only credential is the API key. There is no webhook secret, whsec_, app token or password.
The webhook is not mandatory: it only exists for those who receive (cases 2 and 3), and your system configures it on its own via the API.
To reply to a message that arrived via the webhook, use the conversationId that came in it — not the phone number.
To start a conversation (charge, notice), use to with the phone number in any format: with or without 55, with or without the 9th digit, with punctuation. Combot finds the real WhatsApp account and uses the existing conversation with the customer.
Always check ok in the response. ok: false means the message did not arrive, and error tells you why (e.g.: numero_sem_whatsapp).
In the webhook, respond 200 within 8 seconds and process afterwards; use message.id to avoid replying twice.
Send WhatsApp message
Sends a text message through your company's connected WhatsApp number.
to accepts the phone number in any format — (38) 99999-0000, 38999990000, +55 38 9999-0000. WhatsApp stores some mobile numbers without the 9th digit; Combot figures out the right form on its own.
Body — by phone:
{
"to": "5511999998888",
"text": "Olá! Recebemos seu pedido e já estamos preparando."
}Or replying to an existing conversation:
{
"conversationId": "clx123...",
"text": "Seu protocolo é #4821."
}Copy buttons (PIX, boleto) — optional:
Send up to 3 buttons in buttons. Each one copies a code with a single tap. Combot also sends the code in its own message, inside a code block — this matters because a bare code (PIX contains br.gov.bcb.pix) becomes a link in WhatsApp and the customer ends up copying only a piece of it.
{
"to": "5511999998888",
"text": "*PIX copia-e-cola* — R$ 119,90",
"footer": "Fast Telekom",
"buttons": [
{ "displayText": "📋 Copiar código Pix", "copyCode": "00020126..." },
{ "displayText": "📋 Copiar código do boleto", "copyCode": "34191.79001..." }
]
}displayText = button text (up to 25 characters). copyCode = what goes to the clipboard. footer is optional. If the customer's device doesn't display the button, the code still arrives in the following message.
File (boleto PDF) and image (PIX QR Code):
Provide type + url (public https URL — Combot downloads the file to send it; internal or private network addresses are refused). With media, the text field is no longer mandatory: the caption goes in caption. The document arrives as a card with the file name and a download button.
{
"to": "5511999998888",
"type": "document",
"url": "https://exemplo.com/boleto.pdf",
"filename": "Fatura.pdf",
"caption": "Total: R$ 119,90"
}{
"to": "5511999998888",
"type": "image",
"url": "https://exemplo.com/qrcode-pix.png",
"caption": "Aponte a câmera do banco e pague"
}Example (cURL):
curl -X POST https://combot.com.br/api/v1/messages \
-H "Authorization: Bearer cb_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{"to":"5511999998888","text":"Olá!"}'Response:
{
"ok": true,
"simulated": false,
"messageId": "clx456...",
"conversationId": "clx123..."
}When it doesn't reach the customer (HTTP 200, but ok: false):
{
"ok": false,
"error": "numero_sem_whatsapp",
"messageId": "clx456...",
"conversationId": "clx123..."
}Generate AI reply
Generates a reply using the AI agent and your company's trained knowledge base. It sends nothing to the customer — it only returns the text.
Body:
{ "message": "vocês entregam em Curitiba?" }Response:
{
"reply": "Sim! Entregamos em Curitiba em até 3 dias úteis.",
"handoff": false,
"tokensIn": 812,
"tokensOut": 34
}handoff: true indicates that the AI suggests transferring to a human agent.
Configure receiving (your system calls it on its own)
Tells Combot where to deliver what your system receives. Call it when the user saves the key in your system. Send only what you want to change. GET /api/v1/integration shows the current state.
{
"webhookUrl": "https://seu-sistema.com/webhooks/combot",
"forwardInbound": true,
"phoneAi": false
}webhookUrl: https on a public domain, without a port. null turns off receiving.
forwardInbound: true = messages go to your system and Combot's AI does not reply.
phoneAi: true = the AI Phone uses your system's training and tools.
Response:
{ "ok": true, "webhookUrl": "https://seu-sistema.com/webhooks/combot", "forwardInbound": true, "phoneAi": false }Receive messages (case 2)
Only for case 2. After the PUT /api/v1/integration with forwardInbound: true, every message received is sent via POST to your address, and Combot's AI stays silent (your system is the one that replies):
Body sent to your system:
{
"event": "message.received",
"conversationId": "clx123...",
"contact": { "id": "clx...", "name": "Maria", "phone": "5511999998888" },
"message": {
"id": "clm789...",
"externalId": "wamid.HBg...",
"text": "quero trocar meu produto",
"direction": "inbound"
},
"isNewContact": false
}Reply to the customer by calling POST /api/v1/messages with the conversationId received. Return 200 quickly — heavy processing should be asynchronous on your side.
Deduplication: use message.id (Combot's stable id) or message.externalId (the WhatsApp wamid) as an idempotent key — Meta may redeliver the same event.
Phone with AI through your system (case 3)
The call is answered by Combot's voice AI on the company's number — language, voice and minutes remain Combot's —, but what it knows and what it does comes from your system. Example: the person calls, says their CPF, the AI queries your system (“your plan is Fibra 300, up to date”) and sends the 2nd copy via WhatsApp. Call with phoneAi: true in PUT /api/v1/integration (or the owner chooses in Phone with AI → Who answers → My system).
During the call, Combot makes a POST to your address (the same one as in case 2), separating by the event field:
The language and voice are chosen on each number's page, including when the ERP answers. The ERP receives language and voice and must respond with compatible information. Combot's voice follows the selected language; it does not depend on the language used in the panel.
To validate the connection without making a call, implement the integration.test event: verify the signature as usual and respond {"ok":true}, without creating orders, tickets or messages. The Test connection button on the Integrations screen sends this signed event and waits up to 6 seconds.
1. call.started — respond within 6 seconds
// recebe
{ "event": "call.started", "evento": "ligacao.inicio", "ligacaoId": "clx...", "de": "31999990000", "para": "553130309999", "idioma": "en-US", "voz": "ana" }
// devolve
{
"ok": true,
"saudacao": "Empresa Exemplo, olá! Aqui é a Ana. Em que posso ajudar?",
"instrucoes": "Você é a Ana, da Empresa Exemplo... (o treinamento e, se 'de' for cliente, os dados dele)",
"ferramentas": [
{
"nome": "consultar_assinatura",
"descricao": "Acha o cliente pelo CPF e diz o plano e se está em dia.",
"parametros": [{ "nome": "cpf_cnpj", "tipo": "string", "descricao": "Só números", "obrigatorio": true }]
},
{ "nome": "enviar_segunda_via", "descricao": "Manda a fatura com PIX no WhatsApp de quem liga.", "parametros": [] }
]
}No response in time, ok: false or HTTP 409: the call states that the ERP is unavailable and ends politely. It does not use any other Combot training, schedule, product or service.
2. call.tool — the AI requested a tool; respond within 20 seconds
// recebe
{ "event": "call.tool", "ligacaoId": "clx...", "de": "31999990000",
"ferramenta": "consultar_assinatura", "argumentos": { "cpf_cnpj": "12345678909" } }
// devolve — "texto" é o que a IA vai FALAR
{ "ok": true, "texto": "Cliente Maria. Plano Fibra 300, em dia." }Never return a PIX or barcode code for the AI to dictate: send it via WhatsApp with POST /api/v1/messages (to = the number of) and return “sent via WhatsApp”. Something slow? Respond first and do it afterwards.
3. call.ended — for your history
{ "event": "call.ended", "ligacaoId": "clx...", "de": "31999990000",
"duracaoSeg": 140, "resumo": "...", "transcricao": "..." }Signature (optional)
Every call from Combot to your system (messages and phone) carries the header X-Combot-Signature: HMAC-SHA256 of the raw body, made with your API key — the same one as in Authorization. There is no other secret. Checking is optional, but recommended:
import crypto from "crypto";
function isValid(rawBody, signature, apiKey) {
const expected =
"sha256=" + crypto.createHmac("sha256", apiKey).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}Errors
The API uses conventional HTTP codes and a body { "error": "..." }:
401 missing_token / invalid_token — key missing or invalid.
402 billing_blocked — the account is blocked due to an overdue monthly fee. Pay in Financeiro and the API comes back.
403 plan_without_api_access — the company's plan does not enable the API.
403 account_inactive — the account has been closed or blocked.
400 missing_text / missing_recipient / missing_message — incomplete body.
400 invalid_webhook_url / webhook_url_required — invalid or missing address in the PUT /api/v1/integration.
429 ai_monthly_limit_reached — the month's AI has reached the plan limit (POST /api/v1/ai/reply); it comes back the following month.
403 plan_without_ai — the plan does not include AI.
429 rate_limit_exceeded — wait for the time in the Retry-After header and try again.
404 conversation_not_found — conversation does not exist for the company.
409 no_whatsapp_channel — no WhatsApp connected in the company.
200 with ok: false — the message was recorded, but did not reach the customer; error says why.
When something does not work
Find where the message stopped, in this order:
The customer's message does not reach your system. Call GET /api/v1/integration: the webhookUrl must be your address, over https, and forwardInbound must be true. Also check whether the company's WhatsApp is Connected in Canais.
It arrives, but your system refuses it. If you check the signature, use the raw body (before converting the JSON) and the API key; respond 200 within 8 seconds — process afterwards.
Your system replies, but the customer does not receive it. Read the ok and the error of the POST /api/v1/messages response: numero_sem_whatsapp means that phone number does not have WhatsApp; errors that start with evolution_ are almost always the company's phone disconnected — read the QR Code again.
It replies twice. Use message.id as the key: the same event may arrive more than once.
Ready to integrate?
Generate your API key in Integrações — it is the only thing your system needs.