Skip to main content
Em vez de consultar o histórico em Consultar logs para saber o que aconteceu com cada mensagem, cadastre uma URL e receba um 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 um external_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):
Se uma falha ainda não foi classificada, 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 delivered pode chegar antes do sent. Use data.occurred_at para 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.