Chamadas e webhooks no mesmo formato da WhatsApp Cloud API da Meta. Referência da Meta
A API da dispara.dev segue a WhatsApp Cloud API da Meta. O caminho, o corpo da requisição, a resposta e os webhooks têm o mesmo formato da documentação oficial da Meta. Se você já tem código escrito para a Cloud API, troque a URL base e o token.
URL base: https://api.dispara.dev/v1. No lugar de {PHONE_NUMBER_ID} e {WABA_ID}, use os IDs que aparecem na visão geral de cada número no painel.
Os endpoints disponíveis hoje são envio de mensagens e gerenciamento de templates. Para o restante da Cloud API, a documentação da Meta continua sendo a referência dos campos.
Toda requisição leva a sua chave de API no header Authorization, no lugar do token de acesso da Meta. Cada chave pertence a um número: ela só envia mensagens por ele. Gere e revogue chaves em Chaves de API, dentro do número, no painel.
Sem chave válida a resposta é 401. Com a assinatura inativa a resposta é 402. Os erros usam o mesmo formato da Graph API: error.message, error.type e error.code.
Authorization: Bearer dsp_live_8f2a...
# 401 Unauthorized
{
"error": {
"message": "Chave de API inválida",
"type": "OAuthException",
"code": 190
}
}POST /v1/{PHONE_NUMBER_ID}/messages. O texto livre só pode ser enviado dentro da janela de 24 horas depois da última mensagem do cliente. Fora dela, use um template.
curl -X POST https://api.dispara.dev/v1/106540352242922/messages \
-H "Authorization: Bearer dsp_live_8f2a..." \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "5511988887777",
"type": "text",
"text": {
"preview_url": false,
"body": "Seu pedido #4821 saiu para entrega."
}
}'
# 200 OK
{
"messaging_product": "whatsapp",
"contacts": [
{ "input": "5511988887777", "wa_id": "5511988887777" }
],
"messages": [
{ "id": "wamid.HBgNNTUxMTk4ODg4Nzc3FQIAERgSQTBF..." }
]
}Mesmo endpoint, com type template. Os parâmetros em components preenchem as variáveis {{1}}, {{2}} e seguintes do corpo do template aprovado.
curl -X POST https://api.dispara.dev/v1/106540352242922/messages \
-H "Authorization: Bearer dsp_live_8f2a..." \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"to": "5511988887777",
"type": "template",
"template": {
"name": "pedido_enviado",
"language": { "code": "pt_BR" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Maria" },
{ "type": "text", "text": "#4821" },
{ "type": "text", "text": "terça-feira" }
]
}
]
}
}'
# 200 OK
{
"messaging_product": "whatsapp",
"contacts": [
{ "input": "5511988887777", "wa_id": "5511988887777" }
],
"messages": [
{ "id": "wamid.HBgNNTUxMTk4ODg4Nzc3FQIAERgSQTBF..." }
]
}Envie o link público do arquivo em link, ou o id de uma mídia já enviada à Meta. Os tipos image, document, audio e video seguem o mesmo formato.
curl -X POST https://api.dispara.dev/v1/106540352242922/messages \
-H "Authorization: Bearer dsp_live_8f2a..." \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"to": "5511988887777",
"type": "image",
"image": {
"link": "https://seu-sistema.com/pedidos/4821.jpg",
"caption": "Comprovante do pedido #4821"
}
}'Mensagens interativas com até três botões. Quando o cliente toca em um, você recebe um webhook com type interactive e o id do botão.
curl -X POST https://api.dispara.dev/v1/106540352242922/messages \
-H "Authorization: Bearer dsp_live_8f2a..." \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"to": "5511988887777",
"type": "interactive",
"interactive": {
"type": "button",
"body": { "text": "Seu pedido saiu para entrega. Quer rastrear?" },
"action": {
"buttons": [
{ "type": "reply", "reply": { "id": "rastrear", "title": "Rastrear" } },
{ "type": "reply", "reply": { "id": "suporte", "title": "Falar com suporte" } }
]
}
}
}'Marca uma mensagem recebida como lida. A resposta é { "success": true }.
curl -X POST https://api.dispara.dev/v1/106540352242922/messages \
-H "Authorization: Bearer dsp_live_8f2a..." \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"status": "read",
"message_id": "wamid.HBgNNTUxMTk4ODg4Nzc3FQIAERgSQTBF..."
}'GET /v1/{WABA_ID}/message_templates. Aceita os mesmos parâmetros da Meta, como limit, name e status, e devolve a mesma resposta, com paging.
curl "https://api.dispara.dev/v1/102290129340398/message_templates?limit=10&status=APPROVED" \
-H "Authorization: Bearer dsp_live_8f2a..."
# 200 OK
{
"data": [
{
"id": "1234567890123456",
"name": "pedido_enviado",
"language": "pt_BR",
"status": "APPROVED",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Olá, {{1}}! Seu pedido {{2}} chega na {{3}}."
}
]
}
],
"paging": { "cursors": { "before": "MAZDZD", "after": "MjQZD" } }
}POST /v1/{WABA_ID}/message_templates. O template passa pela aprovação da Meta e o status muda de PENDING para APPROVED ou REJECTED. Para apagar, use DELETE no mesmo caminho com ?name=.
curl -X POST https://api.dispara.dev/v1/102290129340398/message_templates \
-H "Authorization: Bearer dsp_live_8f2a..." \
-H "Content-Type: application/json" \
-d '{
"name": "pedido_enviado",
"language": "pt_BR",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Olá, {{1}}! Seu pedido {{2}} chega na {{3}}.",
"example": { "body_text": [["Maria", "#4821", "terça-feira"]] }
}
]
}'
# 200 OK
{ "id": "1234567890123456", "status": "PENDING", "category": "UTILITY" }Quando a Meta recusa uma requisição, devolvemos o status e o corpo de erro dela sem alterações. Os erros da própria dispara.dev usam o mesmo formato.
{
"error": {
"message": "(#131030) Recipient phone number not in allowed list",
"type": "OAuthException",
"code": 131030,
"fbtrace_id": "AhXmFzM4H36Arbx3WDsC6RJ"
}
}Cadastre a URL em Webhooks, dentro do número, no painel. Cada número tem a própria URL. A cada mensagem recebida ou mudança de status, enviamos um POST com o mesmo JSON que a Meta enviaria (object, entry, changes, value), contendo apenas os eventos daquele número.
Responda com qualquer 2xx para confirmar o recebimento.
Se o seu servidor responder com erro ou não responder em 10 segundos, tentamos de novo com intervalos crescentes, até 8 vezes. Cada tentativa aparece no painel. Não fazemos o desafio hub.challenge no seu endpoint.
Quando um cliente envia uma mensagem de texto para o seu número.
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "102290129340398",
"changes": [
{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "5511999990000",
"phone_number_id": "106540352242922"
},
"contacts": [
{
"profile": { "name": "Maria Souza" },
"wa_id": "5511988887777"
}
],
"messages": [
{
"from": "5511988887777",
"id": "wamid.HBgNNTUxMTk4ODg4Nzc3FQIAERgSQTBF...",
"timestamp": "1761488000",
"type": "text",
"text": { "body": "Oi, queria saber do meu pedido #4821" }
}
]
}
}
]
}
]
}A mensagem traz o id da mídia. Baixe o arquivo pela Graph API da Meta, em GET /{MEDIA_ID}, e depois na URL devolvida.
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "102290129340398",
"changes": [
{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "5511999990000",
"phone_number_id": "106540352242922"
},
"contacts": [
{ "profile": { "name": "Maria Souza" }, "wa_id": "5511988887777" }
],
"messages": [
{
"from": "5511988887777",
"id": "wamid.HBgNNTUxMTk4ODg4Nzc3FQIAERgSQTBF...",
"timestamp": "1761488100",
"type": "image",
"image": {
"caption": "Essa é a caixa que chegou",
"mime_type": "image/jpeg",
"sha256": "Ki8bZ3JvYmF...",
"id": "1138264548317301"
}
}
]
}
}
]
}
]
}Quando o cliente toca em um botão de mensagem interativa, o evento vem com type interactive. Botões de template chegam com type button.
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "102290129340398",
"changes": [
{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "5511999990000",
"phone_number_id": "106540352242922"
},
"contacts": [
{ "profile": { "name": "Maria Souza" }, "wa_id": "5511988887777" }
],
"messages": [
{
"from": "5511988887777",
"id": "wamid.HBgNNTUxMTk4ODg4Nzc3FQIAERgSQTBF...",
"timestamp": "1761488200",
"type": "interactive",
"interactive": {
"type": "button_reply",
"button_reply": { "id": "rastrear", "title": "Rastrear" }
}
}
]
}
}
]
}
]
}Cada mensagem enviada passa por sent, delivered e read. Se falhar, o status é failed e vem com errors.
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "102290129340398",
"changes": [
{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "5511999990000",
"phone_number_id": "106540352242922"
},
"statuses": [
{
"id": "wamid.HBgNNTUxMTk4ODg4Nzc3FQIAERgSQTBF...",
"status": "delivered",
"timestamp": "1761488004",
"recipient_id": "5511988887777"
}
]
}
}
]
}
]
}Ainda não tem uma chave? Crie sua conta.