🔔Webhook de status
O Webhook de status entrega, na sua URL, o que aconteceu com cada mensagem — em vez de você consultar. Vale para todo disparo da conta: broadcast, automação, pela API ou pelo painel, envio real ou de teste.
São até dois eventos por mensagem
O primeiro evento sai no despacho e tem dois desfechos possíveis: ou a mensagem saiu, ou não saiu. O segundo sai quando a confirmação de entrega chega até nós — quando e se chegar.
sent_to_carrier
No instante em que a mensagem é despachada da nossa fila para envio
Sim, se a mensagem saiu
cancelled ou blocked_blacklist
Quando a mensagem não sai: telefone na sua blacklist, telefone inválido ou crédito insuficiente. O crédito é estornado
Sim, no lugar do primeiro
delivered
Quando a confirmação de entrega no aparelho chega até nós
Não
Quando a mensagem não sai, não haverá segundo evento — não há confirmação de entrega a receber.
Não há prazo e não há desfecho garantido. Nem todo envio produz confirmação de entrega; quando não produz, a mensagem fica em sent_to_carrier e não muda mais. Não trate a ausência do delivered como falha: sent_to_carrier é um estado possivelmente final. Use o delivered como confirmação positiva. Em geral, retornos de delivered chegam em até 2 horas após o envio. Em alguns casos de telefone desligado ou retentativas o retorno pode demorar mais.
Isso é a mesma regra da consulta por API, e é deliberado: não inventamos desfecho. Um delivered que você recebe é uma confirmação real que chegou até nós.
O cancelamento que parte de você não gera evento. PUT /sms/broadcasts/{id}/cancel é um pedido seu, e devolver por POST o que você acabou de pedir não acrescenta informação. Os eventos de cancelamento existem para o que você não pediu.
Quando você não recebe nada
O webhook começa a valer no momento do envio, não no da mudança de status. Uma mensagem só entra na fila de eventos se, quando ela saiu, a sua conta já tinha URL cadastrada.
Cadastrou a URL depois de disparar
Nada daquela mensagem — nem o sent_to_carrier, nem o delivered que chegar depois
Removeu a URL e cadastrou de novo
Nada do que foi disparado no intervalo
Mensagem anterior à ativação do recurso na conta
Nada
Mensagem anterior a 02/09/2026 criada fora da API
Nada — antes dessa data só o envio criado pela API recebia o id público que identifica a mensagem. Não houve backfill
Em todos os casos o status continua correto em GET /sms/messages/{id} — só o POST não sai.
Endpoints
PUT /me/webhook
Função: cadastra ou substitui a URL que recebe os eventos e o segredo usado para assinar.
Corpo de envio:
Corpo de resposta — 200 OK:
Guarde o secret nesta resposta. Ela é a única em que ele aparece — o GET devolve apenas has_secret. Se perder, faça um novo PUT: o segredo anterior é substituído.
Com o header X-On-Behalf-Of, a configuração é gravada na conta filha, que é de quem são as mensagens.
GET /me/webhook
Função: mostra a configuração atual da conta. O segredo nunca é devolvido.
Corpo de resposta — 200 OK:
DELETE /me/webhook
Função: desliga o envio de eventos e apaga URL e segredo.
Responde 200 OK. A partir daí nenhuma mensagem nova entra na fila de eventos — inclusive as disparadas antes de você cadastrar de novo.
O evento
Um único tipo de evento (message.status) para os dois POSTs. O formato não muda; o que muda é o status. Você implementa um handler só.
Os campos de mensagem são exatamente os de GET /sms/messages/{id}. Não há campo exclusivo do webhook além de origin.
O bloco origin
É o que distingue um disparo em massa de uma régua de automação sem você cruzar o id contra as suas tabelas. Em automação ele ganha um campo a mais:
Em automação o nome vem da campanha porque a sequência não tem nome próprio — ela é uma posição na régua. O campaign_id acompanha para você conseguir agrupar.
Verificando a assinatura
Todo POST leva dois headers:
X-SmsFunnel-Signature
sha256= seguido do HMAC-SHA256 do corpo bruto, com o seu segredo
X-SmsFunnel-Event-Id
Identificador do evento, para idempotência
Assine o corpo exatamente como recebido, antes de qualquer parse — reserializar o JSON muda os bytes e quebra a comparação.
Use o X-SmsFunnel-Event-Id para idempotência. As retentativas de um mesmo evento repetem o id: se você já processou aquele id, descarte.
Entrega e retentativas
Timeout
5 segundos por tentativa
Sucesso
Qualquer 2xx
Retentativa
5xx, 408, 429 e falha de rede — até 5 tentativas, com espera de 10s, 30s, 1min, 5min e 15min
Desistência
Demais 4xx — tratamos como endereço ou contrato errado, e não insistimos
Responda rápido: processe de forma assíncrona e devolva 200 imediatamente. Um endpoint lento gera retentativa mesmo tendo recebido o evento.
Last updated