Pular para o conteúdo

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.

Três regras antes de começar.
  • 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 respondem 422 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

CampoObrigatórioO que é
phonesimNú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.
templatesimname (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.
namenãoNome do contato. Só vale quando o contato é novo ou ainda não tem nome.
channelIdnãoO 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.
assignToSelfnãotrue: 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-KeyrecomendadoUm valor único por pedido do seu sistema. Veja o passo 5.

curl

Terminal
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
// 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.

JSON
{
  "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

JSON
{
  "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"
  }
}
  • 202 quer dizer que a mensagem entrou na fila, não que chegou: o status começa em pending. O text já mostra o template com os valores preenchidos.
  • conversationCreated e contactCreated dizem 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: true ela 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 é failed e data.error.code diz o motivo (por exemplo recipient_invalid, número sem WhatsApp, ou template_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 por POST /conversations/{id}/messages com 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:

Terminal
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 o Retry-After) e 503. Exceção: 503 queue_unavailable, porque cada tentativa pode deixar uma mensagem com falha na conversa.
  • Corrija, não repita: 400, 404, 409 e 422 pedem 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 e GET /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.

StatuscodeQuando
400invalid_parameterphone que não é um telefone completo, ou outro campo do corpo inválido (veja param).
404resource_not_foundchannelId que não existe, é de outra empresa ou não está liberado para o dono do token.
409contact_in_other_walletO contato do número está na carteira de outro vendedor.
409conversation_with_other_userA conversa deste número, neste canal, está com outro vendedor.
409channel_disconnectedO canal está desligado ou precisa ser reconectado em Configurações.
422channel_not_officialO channelId é de um canal que não é WhatsApp oficial (QR code, Instagram, e-mail).
422no_official_channelSem channelId: a empresa não tem canal de WhatsApp oficial ativo liberado para o dono do token.
422template_not_foundNão existe template aprovado com esse name e language na conta do WhatsApp do canal.
422template_params_mismatchA quantidade de params é diferente da quantidade de variáveis do corpo do template.
422template_not_supportedTemplate com mídia ou variável no cabeçalho, ou botão de link com variável.
422recipient_opted_outO contato pediu para não receber mensagens (LGPD).
429rate_limitedLimite do token, da empresa ou de 30 envios por minuto do número.
503queue_unavailableA 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.
503template_check_unavailableNão deu para consultar os templates da conta agora. Nada foi criado nem enviado.
503idempotency_unavailablePedido 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 /conversations e POST /conversations/{id}/messages. O 31º no mesmo minuto responde 429 rate_limited com Retry-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 scheduledFor aqui).
  • 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