Criar conta grátis
NovoLimite de requisições com Retry-AfterVer

Do primeiro envio ao webhook assinado.

Uma API REST para WhatsApp, SMS, RCS, e-mail e push, com chave de teste e de produção, repetição segura e eventos assinados. O contrato público inteiro está no openapi.json.

A chave de teste começa com ccx_test_ e usa uma carteira de teste. Nada chega a cliente nenhum.

Base da APIhttps://api.messages.ccxcompany.co
Começo rápidoccx_test_
Pronto. Nada chegou a cliente nenhum e nada foi debitado.
  1. Crie uma chave de APIccx_test_••••••••Aparece uma vez
  2. Envie a mensagemPOST /messages201 na fila
  3. Receba o status no seu webhookmessage.deliveredAssinatura válida
Exemplo com uma chave de teste. Valores com … são exemplos.

Seu primeiro envio, em três passos.

Comece com a chave de teste: ela usa a carteira de teste e um envio simulado, então nada chega a cliente nenhum e nada é debitado de verdade.

POST /messages
curl -X POST https://api.messages.ccxcompany.co/messages \  -H "Authorization: Bearer $CCX_API_KEY" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: pedido-4821-saiu" \  -d '{    "numberId": "num_…",    "to": "+5511…",    "type": "text",    "payload": { "text": { "body": "Seu pedido saiu para entrega" } }  }'

Corpo resumido; o objeto completo da mensagem está no openapi.json. Valores com … são exemplos.

Chaves, escopos e repetição segura.

Toda chamada leva Authorization: Bearer com a sua chave. Cada rota pede um escopo, e a chave só faz o que os escopos dela permitem.

ccx_test_Chave de testeUsa a carteira de teste e o transporte simulado. Serve para homologar a integração sem gastar.
ccx_live_Chave de produçãoEnvia de verdade e debita a carteira de produção no momento do envio. É a chave guardada do nosso lado que decide o ambiente, não o corpo do pedido.
Escoposmessages:sendmessages:readsms:sendemail:sendAlguns exemplos. Chave sem o escopo da rota recebe 403. Crie chaves em Configurações, API Keys; o segredo aparece uma vez só.
metadataUm objeto seu que viaja com a mensagem e volta intacto em todo webhook dela. Bom para guardar o id do pedido no seu sistema.

Repita sem enviar duas vezes.

Nos envios de WhatsApp, SMS e push, o cabeçalho Idempotency-Key deixa você repetir a mesma chamada por 24 horas. Mesma chave e mesmo corpo devolvem a resposta original. Mesma chave com outro corpo dá 409. Experimente.

Use um valor seu, como o número do pedido, para que uma repetição depois de um timeout não envie duas vezes.
A chave vale por 24 horas. Depois disso, o mesmo valor conta como um envio novo.
O 409 vem com meta.code IDEMPOTENCY_CONFLICT. Use uma chave nova para um envio novo. No RCS, qualquer chave repetida dá 409.
  1. Idempotency-Key: pedido-4821 · corpo A201msg_01… criada e na fila
  2. Idempotency-Key: pedido-4821 · corpo A201Resposta original de msg_01…, nada foi enviado de novo.
  3. Idempotency-Key: pedido-4821 · corpo B409CONFLICT · IDEMPOTENCY_CONFLICT
1mensagem criada

Respostas simuladas e resumidas. Valores com … são exemplos.

Webhooks assinados, com reentrega.

Cadastre o endereço, escolha os eventos e guarde o segredo. Cada entrega sai com X-CCX-Signature, X-CCX-Event, X-CCX-Delivery-Id e X-CCX-Idempotency-Key.

Responda 2xx em até 10 segundosEnfileire o processamento e devolva 200 na hora. Passou de 10 segundos, conta como falha.
Cinco novas tentativasCom 5xx, 408, 429 ou sem resposta: depois de 5 s, 30 s, 2 min, 10 min e 1 h. Outro 4xx é recusa definitiva, e 410 desativa o webhook.
Trate reentregaReentrega é esperada: use X-CCX-Idempotency-Key para registrar cada evento uma vez só.
Valide antes de salvarPOST /webhooks/validate manda um ping assinado para a URL antes do cadastro. O segredo pode ser trocado em POST /webhooks/{id}/rotate-secret.
Novas tentativas quando o seu endereço responde 5xx, 408 ou 429, ou não responde
  1. 5 s
  2. 30 s
  3. 2 min
  4. 10 min
  5. 1 h
import { createHmac, timingSafeEqual } from "node:crypto"; // X-CCX-Signature: t=<unix>,v1=<hex>export function assinaturaValida(segredo, corpo, cab) {  const p = Object.fromEntries(    cab.split(",").map((kv) => kv.split("=")));  if (Math.abs(Date.now() / 1000 - p.t) > 300) return false;  const esperado = createHmac("sha256", segredo)    .update(p.t + "." + corpo).digest("hex");  return esperado.length === p.v1.length &&    timingSafeEqual(Buffer.from(esperado, "hex"),                    Buffer.from(p.v1, "hex"));}

Calcule sobre o corpo bruto, exatamente como chegou, sem reserializar o JSON. A janela de 5 minutos no carimbo t barra reenvio de evento antigo.

  • message.sent
  • message.delivered
  • message.read
  • message.failed
  • message.received
  • template.approved
  • template.rejected
  • sms.delivered
  • sms.received
  • email.delivered
  • email.bounced
  • email.opened
  • wallet.low_balance

Quando a resposta não é 2xx.

Todo erro vem no formato problem details (RFC 7807), com um code estável que o seu código pode comparar. Escolha um para ver a resposta.

Resposta · 409 CONFLICT
HTTP/1.1 409Content-Type: application/problem+json {  "type": "about:blank",  "title": "conflict",  "code": "CONFLICT",  "status": 409,  "detail": "idempotency key reused with a different payload",  "meta": { "code": "IDEMPOTENCY_CONFLICT" }}

Exemplo de corpo de erro, como a API devolve.

O que dá para fazer pela API.

As rotas abaixo estão no contrato público. O que não está no openapi.json não é contrato e pode mudar.

WhatsAppPOST /messagesGET /messagesGET /messages/{id}
SMSPOST /v2/sms/messagesPOST /v2/sms/previewGET /v2/sms/replies
RCSPOST /v2/rcs/messagesGET /v2/rcs/messages/{id}
E-mailPOST /v2/email/messagesGET /v2/email/messages/{id}
PushPOST /v2/push/messagesPOST /v2/push/tokensPOST /v2/push/preview
Contatos e listas/contacts/contacts/import/v2/lists/v2/segments
Templates e flowsGET /v2/templatesPOST /v2/templatesGET /v2/flows
Webhooks/webhooksPOST /webhooks/validatePOST /webhooks/{id}/rotate-secret
Jornadas/v2/journeysPOST /v2/journeys/{id}/activatePOST /v2/journeys/{id}/entries
Exportações e carteiraGET /v2/exports/{whatsapp|sms|rcs|email}GET /wallet

Guias para o que mais trava.

Respostas curtas para as dúvidas que aparecem quando a integração sai do papel.

Quantos segmentos meu SMS vai cobrar?
71CaracteresGSM-7Codificação1Segmento

Acentos trocados antes do envio: á vira a. A mensagem inteira cabe no GSM-7, então continua valendo 160 caracteres por um segmento.

Contado do jeito que a fatura conta. POST /v2/sms/preview devolve segmentos e preço antes de enviar. O SMS vai para números do Brasil; rotas internacionais são sob consulta.

Meu template foi rejeitado pela Meta. E agora?

Quase sempre é formato, não conteúdo: promoção declarada como utilidade, variável no começo ou no fim do corpo, ou botão de URL sem um prefixo fixo. Antes de enviar para a Meta, o editor do painel avisa quando o texto tem linguagem promocional agressiva, quando um template de marketing não tem botão de opt-out e quando o corpo passa de 1.024 caracteres.

Quanto tempo leva para aprovar meu número?

São duas etapas. A verificação do número, com o código por SMS ou ligação, é rápida. A verificação do negócio, que libera limites maiores de envio, é feita pela própria Meta, e o prazo é dela.

Quantos segmentos meu SMS vai cobrar?

Até 160 caracteres é um segmento; acima disso, um a cada 152. Com emoji, a mensagem vai em Unicode: até 70 é um segmento, acima disso um a cada 67. Acento que não existe no GSM-7 é trocado pelo equivalente quando a mensagem inteira cabe no GSM-7, então acento não encarece. POST /v2/sms/preview devolve segmentos e preço antes de enviar. O SMS vai para números do Brasil; rotas internacionais são sob consulta.

O que acontece se o saldo acabar?

A carteira é pré-paga e o débito acontece no envio. Sem saldo, SMS, RCS e e-mail respondem 402, e os envios de WhatsApp e push falham com o status de saldo insuficiente. Campanha de SMS que fica sem saldo no meio pausa com o motivo na tela, e o Retomar envia as mensagens recusadas por falta de saldo junto com o resto. A carteira de teste é separada e nunca debita valor real.

Meus e-mails estão caindo em spam

Publique SPF, DKIM e DMARC do domínio, suba o volume aos poucos em domínio novo, tire da lista os endereços inválidos e separe transacional de marketing em subdomínios. Endereço que volta com bounce entra na lista de supressão.

Meu webhook não recebe os eventos

Confira se o endereço responde 2xx em até 10 segundos, se a assinatura é calculada sobre o corpo bruto e se o webhook está ativo, não pausado. Um 4xx do seu lado (fora 408 e 429) não é repetido; 5xx e falta de resposta são, depois de 5 s, 30 s, 2 min, 10 min e 1 h.

Como convido o time e defino permissões?

Convide por e-mail e escolha um dos nove papéis, de proprietário a somente leitura. A organização pode exigir verificação em duas etapas de todos os membros, com 30 dias de carência. Não há cobrança por usuário, então ninguém precisa dividir login.

Travou em alguma rota? Fale com quem mantém a API.

Criando a conta pelo site, você ganha R$ 10 de crédito de teste.