POST a cada mudança de status das mensagens que você enviou pela API (Enviar mensagens).
Cadastrar
No painel, em Canais → Configuração geral, informe a URL do seu endpoint e ative o webhook. É preciso ter a permissão de configurar o webhook de status no seu perfil de acesso.- A URL precisa usar HTTPS, sem usuário ou senha.
- Endereços internos são recusados:
localhost, IPs de rede privada ou reservada e nomes sem domínio público. A checagem acontece ao salvar e de novo a cada envio. - Na primeira vez que você salva, a tela mostra o segredo (
whsec_...) uma única vez. Guarde-o: depois de fechar o aviso, ele não aparece de novo. Ao regenerar o segredo, o antigo deixa de valer na hora e o novo também aparece uma única vez. - O botão Enviar teste envia um evento
webhook.testà URL já salva (sem repetir em caso de falha) e mostra o status HTTP e o tempo de resposta. Com uma edição de URL ainda não salva, o botão fica desabilitado: salve primeiro.
Ligar o callback à sua mensagem
Mande umexternal_id (texto, até 255 caracteres) no send-message — por exemplo, o id do pedido no seu sistema. Ele volta em todo callback daquela mensagem. No lote, cada item de messages aceita o seu. Cada item de results do envio também devolve o message_id, o mesmo data.message_id do callback.
Quando o envio é adiado pelo limite por minuto, a resposta vem com deferred: true e status: "queued", sem message_id (o registro da mensagem só nasce quando ela é reprocessada), e devolve o external_id que você mandou. Os callbacks começam quando a mensagem é de fato processada: case-os pelo external_id.
O callback
POST na sua URL, com Content-Type: application/json, User-Agent: OctaSend-Webhook/1.0 e o cabeçalho X-OctaSend-Signature:
Cada mudança de status gera um evento.
delivered e read só chegam quando o provedor confirma (o WhatsApp, por exemplo). O evento webhook.test tem o mesmo formato, com external_id: "pedido-exemplo-123" e status: "delivered"; ignore-o no processamento de mensagens.
Exemplo de callback de uma mensagem que falhou (WhatsApp para um número sem conta):
failure_code vem como unknown.
Códigos de falha (failure_code)
Toda mensagem que falha (failed) ou é pulada (skipped) leva um failure_code: um código estável, em inglês e em snake_case, que diz por que ela não saiu. Use o código, e não o texto de error_message, para decidir o que fazer: o texto é para exibir ou registrar e pode mudar.
Falhas registradas antes de este campo existir podem vir com failure_code nulo na leitura pela API; os webhooks e as falhas novas sempre trazem o código.
Onde ele aparece: no callback do webhook de status (data.failure_code, preenchido só quando status é failed; null nos demais), no resultado de cada canal do send-message e na leitura do histórico (query-message-logs, get_message e list_messages).
Reenvio: é seguro automatizar o reenvio de
rate_limited, provider_unavailable e channel_disconnected (este só depois de reconectar o canal). Nunca reenvie recipient_opted_out, recipient_not_on_channel e invalid_recipient: o resultado será o mesmo.
Códigos novos: o conjunto pode crescer com o tempo. Trate qualquer valor que você não conheça como unknown, sem quebrar o seu processamento.
Não confunda com error_code: o error_code é um campo mais antigo, que aparece só em algumas respostas do send-message (por exemplo daily_limit_reached, plan_quota_reached, opt_out_check_failed e channel_check_failed) e, na leitura do histórico, pode trazer o código bruto do provedor. Ele não mudou. O failure_code é o campo estável, com o catálogo acima.
Verificar a assinatura
t é o timestamp Unix (segundos) do envio e v1 é o HMAC-SHA256, em hexadecimal, da string "<t>.<corpo>", usando o seu segredo como chave. O corpo é o JSON exatamente como chegou, em bytes: calcule sobre o corpo bruto, antes de qualquer JSON.parse. Compare em tempo constante e rejeite timestamps fora de uma tolerância (5 minutos nos exemplos) para barrar replay.
Resposta esperada
Responda com qualquer 2xx em até 10 segundos. Qualquer outra coisa conta como falha: status fora de 2xx, erro de conexão, tempo esgotado ou redirecionamento (3xx). A OctaSend não segue redirects, então cadastre a URL final.Tentativas e desativação
- Cada evento tem até 5 tentativas. Depois de uma falha, a próxima vem após 1, 5, 15 e 30 minutos (a verificação roda a cada minuto, então os intervalos podem variar em cerca de 1 minuto).
- Se as 5 tentativas falharem, o evento fica como esgotado. Ele pode ser reenviado manualmente na tela de Configuração geral, com o mesmo
event_id. - Depois de 20 eventos esgotados seguidos (qualquer entrega com sucesso zera a contagem), o webhook é desativado automaticamente e o dono e os administradores ativos da conta (inclusive o perfil Master) recebem um aviso no sino do painel. Depois de corrigir o endpoint, reative-o na tela; a contagem recomeça do zero.
- Enquanto o webhook estiver desativado (por você ou automaticamente), as mudanças de status novas não geram evento. Os eventos que já estavam na fila esperam e são enviados quando você reativar; um evento com mais de 30 dias é encerrado sem envio.
- O histórico de envios e tentativas da tela é guardado por 30 dias.
Boas práticas ao receber
- Entrega pelo menos uma vez: o mesmo evento pode chegar mais de uma vez (resposta perdida, reenvio manual). O
event_idé o mesmo em todas as tentativas: guarde os já processados e ignore repetidos. - A ordem não é garantida: um
deliveredpode chegar antes dosent. Usedata.occurred_atpara decidir o estado mais recente e não sobrescreva um estado novo por um antigo. - Responda rápido: devolva 2xx assim que validar e guardar o evento, e processe o resto depois.
- Só mensagens enviadas pela API são notificadas. Envios do painel, campanhas e automações não geram callback.