API v1 · Guia
Disparar um template quando algo acontece no seu sistema
Avise o cliente no WhatsApp oficial a partir do seu sistema (n8n, ERP, loja): uma chamada a POST /conversations, com exemplos em curl, Node.js e n8n.
O seu sistema (um n8n, um ERP, uma loja virtual) sabe quando algo acontece: o pedido foi enviado, o boleto vence amanhã, a consulta foi marcada. Com uma chamada a POST /conversations, o Rumo CRM manda um template do WhatsApp oficial para o cliente, mesmo que ele nunca tenha falado com a empresa. A API acha ou cria o contato e a conversa, coloca a mensagem na fila de envio e devolve os ids para você acompanhar o resultado.
- Só funciona no WhatsApp oficial. Número conectado por QR code responde
422 channel_not_official. - A primeira mensagem é sempre um template aprovado. Texto livre só depois que o cliente responde.
- A Meta cobra o template na conta do WhatsApp Business do número. O Rumo CRM não cobra por mensagem.
1. Conecte o número oficial e tenha o template aprovado
- Conecte o número da API oficial da Meta e ligue-o ao inbox: em Configurações, Integrações e Canais, ele fica no card WhatsApp Cloud API (Meta). Falar pela primeira vez com quem não conhece a empresa por um número de QR code é o que leva o WhatsApp a bloquear o número, e o Rumo CRM não consegue desfazer. Passo a passo: Como conectar a API oficial do WhatsApp.
- Crie o template em Configurações → Integrações → Canais, em Templates da Meta, e espere o status Aprovado (a Meta leva de 24 a 48 horas). Veja Como criar um template do WhatsApp. A API não cria nem edita template.
- Use um template com variáveis só no corpo (
{{1}},{{2}}...). Cabeçalho com imagem ou variável e botão de link com variável respondem422 template_not_supported. A lista de templates da conta é consultada com até 5 minutos de atraso: um template recém-aprovado pode levar esse tempo para ser aceito.
2. Crie o token com o escopo inbox:send
Em Configurações → Operação → API e integrações, crie um token e marque Inbox: enviar (inbox:send). Para consultar a mensagem depois, marque também Inbox: ler (inbox:read). O passo a passo do token está em Autenticação e escopos.
O token age em nome de quem o criou: a mensagem sai com essa pessoa como autora, o canal precisa estar liberado para ela e vale o papel dela. Para um robô, o token de um gerente é o mais simples. O vendedor que não vê todas as conversas só inicia conversa com contato sem responsável ou da carteira dele: contato da carteira de um colega responde 409 contact_in_other_wallet, e conversa que está com outro vendedor, 409 conversation_with_other_user. Nesses casos nada é criado.
3. Chame POST /conversations
| Campo | Obrigatório | O que é |
|---|---|---|
phone | sim | Número do cliente com DDD, com ou sem máscara ((15) 99807-3400 ou 5515998073400). Sem DDI, o Brasil (55) é assumido. Outro país: + e o DDI. |
template | sim | name (nome do template), language (como pt_BR) e params (os valores de {{1}}, {{2}}... na ordem). A quantidade de params tem de ser igual à de variáveis do corpo. |
name | não | Nome do contato. Só vale quando o contato é novo ou ainda não tem nome. |
channelId | não | O canal de WhatsApp oficial (id de GET /channels). Sem ele, a API usa o primeiro canal oficial ativo que o dono do token pode usar. Com mais de um número oficial, envie o channelId. |
assignToSelf | não | true: a conversa fica com o dono do token. Sem ele, a conversa nova nasce sem responsável, na fila do setor do canal. |
cabeçalho Idempotency-Key | recomendado | Um valor único por pedido do seu sistema. Veja o passo 5. |
curl
curl -X POST "https://rumocrm.com/api/v1/conversations" \
-H "Authorization: Bearer $RUMO_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-48213-enviado" \
-d '{
"phone": "(11) 90000-0000",
"name": "Ana Souza",
"template": {
"name": "pedido_enviado",
"language": "pt_BR",
"params": ["Ana", "48213"]
}
}'Node.js
Sem dependências (Node.js 20 ou mais novo). Repete só o que vale repetir, sempre com a mesma chave, como explica o passo 5.
// Node.js 20 ou mais novo, sem dependências. O token vem do ambiente (RUMO_TOKEN), nunca do código.
const esperar = (ms) => new Promise((pronto) => setTimeout(pronto, ms));
async function dispararTemplate({ telefone, nome, params, chave }) {
for (let tentativa = 1; ; tentativa++) {
let resposta;
try {
resposta = await fetch("https://rumocrm.com/api/v1/conversations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RUMO_TOKEN}`,
"Content-Type": "application/json",
// A mesma chave em toda tentativa: se a conexão cair depois de o pedido chegar, repetir não envia duas vezes.
"Idempotency-Key": chave,
},
body: JSON.stringify({
phone: telefone,
name: nome,
template: { name: "pedido_enviado", language: "pt_BR", params },
}),
});
} catch (erroDeRede) {
if (tentativa === 4) throw erroDeRede;
await esperar(2 ** tentativa * 500);
continue;
}
const corpo = await resposta.json();
if (resposta.status === 202) return corpo;
// 429 (limite de 30 por minuto do número) e 503 (nada foi feito): espere e repita com a mesma chave.
// queue_unavailable fica de fora: cada tentativa pode deixar uma mensagem com falha na conversa.
const repetivel = resposta.status === 429 || (resposta.status === 503 && corpo.error.code !== "queue_unavailable");
if (repetivel && tentativa < 4) {
await esperar((Number(resposta.headers.get("Retry-After")) || 2 ** tentativa) * 1000);
continue;
}
// Os outros erros (400, 404, 409, 422) pedem uma correção, não outra tentativa.
throw new Error(`${corpo.error.code}: ${corpo.error.message} (Request-Id ${corpo.error.requestId})`);
}
}
const resultado = await dispararTemplate({
telefone: "(11) 90000-0000",
nome: "Ana Souza",
params: ["Ana", "48213"],
chave: "pedido-48213-enviado",
});
console.log(resultado.conversationId, resultado.message.id, resultado.message.status); // ..., ..., "pending"n8n
Um nó HTTP Request pronto: cole o JSON abaixo no canvas do n8n. Ele lê o telefone, o nome e o id do pedido do item que chega ao nó ($json.cliente.telefone, $json.cliente.nome e $json.pedido.id: troque pelos campos do seu fluxo) e o token da variável de ambiente RUMO_TOKEN (ou use uma credencial Header Auth). A chave de idempotência usa o id do pedido: o n8n pode executar duas vezes o mesmo pedido e o cliente recebe uma mensagem só. Trocar pedido_enviado e os params pelos do seu template é tudo o que falta.
{
"nodes": [
{
"parameters": {
"method": "POST",
"url": "https://rumocrm.com/api/v1/conversations",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $env.RUMO_TOKEN }}"
},
{
"name": "Idempotency-Key",
"value": "=pedido-{{ $json.pedido.id }}-enviado"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ phone: $json.cliente.telefone, name: $json.cliente.nome, template: { name: 'pedido_enviado', language: 'pt_BR', params: [$json.cliente.nome, String($json.pedido.id)] } }) }}",
"options": {}
},
"name": "Avisar cliente no WhatsApp",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
0
],
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 2000
}
],
"connections": {}
}O que a API responde
{
"conversationId": "cmgx7k2a40001ab12cd34ef56",
"conversationCreated": true,
"contactId": "cmgx7k2a30000ab12cd34ef56",
"contactCreated": true,
"message": {
"id": "cmgx7k2a50002ab12cd34ef56",
"conversationId": "cmgx7k2a40001ab12cd34ef56",
"direction": "outbound",
"type": "template",
"text": "Oi Ana, seu pedido 48213 foi enviado.",
"media": null,
"status": "pending",
"error": null,
"internal": false,
"authorId": "cmgx7k29z0000ab12cd34ef56",
"scheduledFor": null,
"sentAt": "2026-10-12T14:03:21.000Z",
"createdAt": "2026-10-12T14:03:21.000Z",
"updatedAt": "2026-10-12T14:03:21.000Z"
}
}202quer dizer que a mensagem entrou na fila, não que chegou: ostatuscomeça empending. Otextjá mostra o template com os valores preenchidos.conversationCreatedecontactCreateddizem se a conversa e o contato foram criados agora ou já existiam. O número vale com ou sem máscara: o mesmo cliente nunca vira dois contatos.- A conversa nova nasce sem responsável, na fila do setor padrão do canal, como um lead que acabou de chegar. Com
assignToSelf: trueela já fica com o dono do token. Uma conversa que já existia (inclusive encerrada ou excluída) é reaproveitada e reaberta. - O contato novo dispara o webhook
contact.created. A chamada só envia a mensagem: não cria negócio no funil, não entra em cadência e não aciona regra de automação nem chatbot por conta própria.
4. Acompanhe o resultado
Cadastre um endereço em Configurações → Integrações → Técnico e marque os eventos abaixo (a verificação da assinatura está em Webhooks e a Rumo-Signature). Em todos, data é a mensagem, no formato de GET /conversations/{id}/messages/{messageId}: relacione com o message.id da resposta.
message.sent: o canal aceitou a mensagem. Ainda não é entrega: a Meta pode recusar depois.message.failed: a mensagem não saiu.data.statuséfailededata.error.codediz o motivo (por exemplorecipient_invalid, número sem WhatsApp, outemplate_rejected). Não há nova tentativa automática: reenviar cria outra mensagem.message.received: o cliente respondeu. A janela de 24 horas abre e, dentro dela, a conversa segue porPOST /conversations/{id}/messagescom texto livre.
Sem webhook, consulte a mensagem até o status virar sent, delivered, read ou failed. A consulta segue o acesso do dono do token: se ele é um vendedor que não atende o setor do canal, a conversa nova (sem responsável) não aparece para ele e o GET responde 404. Nesse caso use assignToSelf: true ou os webhooks:
curl "https://rumocrm.com/api/v1/conversations/$CONVERSATION_ID/messages/$MESSAGE_ID" \
-H "Authorization: Bearer $RUMO_TOKEN"5. Trate os erros e repita com a Idempotency-Key
Mande uma Idempotency-Key por pedido do seu sistema (o id do pedido serve). Se a conexão cair, o pedido der timeout ou o n8n executar duas vezes, repita com a mesma chave: a API devolve a resposta da primeira vez (com o cabeçalho Idempotent-Replayed: true) e a mensagem sai uma vez. Sem a chave, repetir envia outra mensagem (o contato e a conversa são reaproveitados). Veja também Idempotência.
- Repita com a mesma chave em queda de conexão,
429(espere oRetry-After) e503. Exceção:503 queue_unavailable, porque cada tentativa pode deixar uma mensagem com falha na conversa. - Corrija, não repita:
400,404,409e422pedem uma correção (número, canal, template, carteira). Essas recusas não ficam guardadas: corrigida a causa, a mesma chave funciona. - 500: a resposta fica guardada com a chave, e repetir devolve o mesmo erro. Antes de tentar com outra chave, confira se a mensagem saiu:
GET /contacts?search=<telefone>traz o contato eGET /conversations?contactId=<id>a conversa.
Erros desta chamada
Todo erro segue o formato de Erros da API. Na recusa, nada é criado: nem contato, nem conversa, nem mensagem.
| Status | code | Quando |
|---|---|---|
| 400 | invalid_parameter | phone que não é um telefone completo, ou outro campo do corpo inválido (veja param). |
| 404 | resource_not_found | channelId que não existe, é de outra empresa ou não está liberado para o dono do token. |
| 409 | contact_in_other_wallet | O contato do número está na carteira de outro vendedor. |
| 409 | conversation_with_other_user | A conversa deste número, neste canal, está com outro vendedor. |
| 409 | channel_disconnected | O canal está desligado ou precisa ser reconectado em Configurações. |
| 422 | channel_not_official | O channelId é de um canal que não é WhatsApp oficial (QR code, Instagram, e-mail). |
| 422 | no_official_channel | Sem channelId: a empresa não tem canal de WhatsApp oficial ativo liberado para o dono do token. |
| 422 | template_not_found | Não existe template aprovado com esse name e language na conta do WhatsApp do canal. |
| 422 | template_params_mismatch | A quantidade de params é diferente da quantidade de variáveis do corpo do template. |
| 422 | template_not_supported | Template com mídia ou variável no cabeçalho, ou botão de link com variável. |
| 422 | recipient_opted_out | O contato pediu para não receber mensagens (LGPD). |
| 429 | rate_limited | Limite do token, da empresa ou de 30 envios por minuto do número. |
| 503 | queue_unavailable | A fila de envio está fora do ar. Se a fila recusou depois de a mensagem ser registrada, ela fica na conversa com status: failed e error.code: queue_unavailable. |
| 503 | template_check_unavailable | Não deu para consultar os templates da conta agora. Nada foi criado nem enviado. |
| 503 | idempotency_unavailable | Pedido com Idempotency-Key e o controle de idempotência fora do ar. Nada foi feito. |
Custo
O Rumo CRM não cobra por mensagem. A Meta cobra cada template, por categoria, na conta do WhatsApp Business do número. Como o cliente ainda não escreveu, a janela de 24 horas está fechada e todas as categorias são cobradas. Valores por mensagem que o Rumo CRM usa na estimativa: marketing R$ 0,3217, utilidade R$ 0,0350 e autenticação R$ 0,0350. A fatura exata está no Gerenciador de Negócios da Meta, que pode mudar a tabela.
Limites
- 30 envios por minuto por número, somando
POST /conversationsePOST /conversations/{id}/messages. O 31º no mesmo minuto responde429 rate_limitedcomRetry-After, e nada é criado. Além disso valem os limites de 120 pedidos por minuto por token e 600 por empresa. - Janela de 24 horas. Ela abre quando o cliente responde. O template é a única mensagem que pode começar a conversa; depois da resposta, e dentro das 24 horas, o texto livre sai por
POST /conversations/{id}/messages. - Consentimento (opt-in). A Meta só permite mensagem iniciada pela empresa para quem aceitou receber no WhatsApp (formulário, compra, atendimento anterior). Disparar para quem não autorizou gera denúncia, derruba a qualidade do número e pode levar a restrição ou bloqueio dele. Guarde de onde veio cada autorização e só chame a rota para quem aceitou.
- Descadastro (LGPD). Quem pediu para não receber mensagens é recusado com
422 recipient_opted_out, e nada é criado. Inclua no template uma forma de sair, como “Para não receber mais, responda SAIR”.
O que a API não faz
- Não cria nem edita template: crie e espere a aprovação da Meta na tela, antes de integrar.
- Não manda texto livre nem arquivo na primeira mensagem, e não agenda (não existe
scheduledForaqui). - Não faz campanha em massa nem dispara para uma lista. Para um lote pequeno, chame a rota em sequência, respeitando os 30 envios por minuto; para uma lista de verdade, use a campanha na tela (Como disparar uma campanha pelo WhatsApp oficial).
- Não aciona automação interna: não cria negócio, não entra em cadência e não liga chatbot ou regra por conta própria.
- Não funciona em número de QR code, Instagram nem e-mail.
Para quem não é desenvolvedor, o mesmo caminho está explicado na Central de Ajuda: Disparar template pelo seu sistema (API).
PróximoSDK e exemplos