Webhooks
Configure webhooks para receber notificações HTTP em tempo real quando eventos ocorrem.
Para guia detalhado de implementação, veja o Guia de Webhooks.
Permissão de administrador
Listar, criar, atualizar e remover webhooks exige um token de usuário com perfil administrador na conta. Tokens de agentes não podem gerenciar estes endpoints.
/api/v1/accounts/{account_id}/webhooksLista todos os webhooks configurados na conta.
curl -s "https://chat.seudominio.com/api/v1/accounts/1/webhooks" \
-H "api_access_token: YOUR_TOKEN" | jq .{
"payload": {
"webhooks": [
{
"id": 1,
"name": "Integração principal",
"account_id": 1,
"url": "https://meuapp.com/webhooks/chat",
"subscriptions": [
"conversation_created",
"message_created",
"contact_created"
],
"secret": "<segredo HMAC>"
}
]
}
}/api/v1/accounts/{account_id}/webhooksRegistra um novo webhook para receber eventos.
Body (envolto em objeto webhook)
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
url | string | Sim | URL HTTP ou HTTPS que receberá os eventos via POST. Em produção, prefira HTTPS. |
subscriptions | array[string] | null | Nao | Lista não vazia de eventos. Na criação, omitir ou enviar null usa os oito eventos padrão; [] ou valores fora do catálogo retornam 422. |
name | string | Nao | Nome descritivo do webhook |
inbox_id | integer | Nao | Associa uma inbox ao registro. Neste endpoint, não altera webhook_type nem filtra a entrega por inbox. |
Wrapper do contrato
Envie os campos do webhook dentro de um objeto webhook: { "webhook": { "url": "...", "subscriptions": [...] } }.
Subscriptions padrão quando omitidas
Sem subscriptions, o registro nasce com conversation_status_changed, conversation_updated, conversation_created, contact_created, contact_updated, message_created, message_updated e webwidget_triggered.
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/webhooks" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"webhook": {
"url": "https://meuapp.com/webhooks/noovichat",
"subscriptions": [
"conversation_created",
"conversation_status_changed",
"message_created",
"contact_created"
]
}
}'{
"payload": {
"webhook": {
"id": 2,
"name": null,
"account_id": 1,
"url": "https://meuapp.com/webhooks/noovichat",
"subscriptions": ["conversation_created", "message_created"],
"secret": "<segredo usado para verificar a assinatura HMAC>"
}
}
}/api/v1/accounts/{account_id}/webhooks/{webhook_id}Atualiza os campos permitidos de um webhook.
Body (webhook)
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
url | string | Nao | Nova URL do webhook |
subscriptions | array[string] | null | Nao | Nova lista não vazia de eventos. null é ignorado e preserva a lista atual; sem outro campo permitido, a atualização fica vazia e retorna 422. |
name | string | Nao | Novo nome descritivo |
inbox_id | integer | Nao | Inbox associada como metadado; não torna a entrega inbox-scoped |
curl -X PATCH "https://chat.seudominio.com/api/v1/accounts/1/webhooks/2" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"webhook": {
"subscriptions": [
"conversation_created",
"message_created",
"contact_updated"
]
}
}'/api/v1/accounts/{account_id}/webhooks/{webhook_id}Remove um webhook. Eventos não serão mais enviados para a URL.
curl -X DELETE "https://chat.seudominio.com/api/v1/accounts/1/webhooks/2" \
-H "api_access_token: YOUR_TOKEN"Eventos Disponíveis
O catálogo abaixo contém os 32 valores aceitos em subscriptions.
| Evento | Descrição |
|---|---|
conversation_created | Nova conversa criada |
conversation_updated | Conversa atualizada |
conversation_status_changed | Status da conversa alterado |
conversation_typing_on | Indicador de digitação iniciado |
conversation_typing_off | Indicador de digitação encerrado |
message_created | Nova mensagem recebida ou enviada |
message_updated | Mensagem atualizada |
contact_created | Novo contato criado |
contact_updated | Dados do contato atualizados |
webwidget_triggered | Widget de chat acionado no site |
inbox_created | Nova inbox criada |
inbox_updated | Inbox atualizada |
broadcast_started | Disparo em massa iniciado |
broadcast_completed | Disparo em massa concluído |
broadcast_auto_paused | Disparo em massa pausado pelo sistema (erro da Meta que se repete para todo contato) |
Payload do Webhook
Cada evento envia um payload JSON com os dados do evento. O campo event identifica o tipo de evento. Veja exemplos no Guia de Webhooks.
Falha de Envio de Mensagem
Quando uma mensagem de saída falha, o NooviChat entrega um evento message_updated com o motivo dentro de content_attributes. Assine message_updated para receber esse evento.
| Campo | Tipo | Descrição |
|---|---|---|
content_attributes.external_error | string | Motivo da recusa, em texto legível, como o provedor devolveu. Presente apenas em mensagem que falhou; some quando a mensagem é reenviada com sucesso. |
content_attributes.external_error_code | integer | O código numérico do provedor, quando ele devolve um e o canal o expõe em campo próprio (veja a tabela por canal abaixo). Ausente quando não há código — o campo não é preenchido com palpite. |
O payload do webhook não traz o campo status
O evento message_updated carrega os mesmos campos de qualquer evento de mensagem (event, id, content, content_attributes, conversation, inbox, sender) e não inclui status. Detecte a falha pela presença de content_attributes.external_error — não por status === "failed", que nunca vai casar. O campo status existe na resposta REST da mensagem (API de Mensagens), não no payload do webhook.
Exemplo de payload
{
"event": "message_updated",
"id": 98765,
"message_type": "outgoing",
"content": "Ola! Seu pedido saiu para entrega.",
"content_type": "text",
"content_attributes": {
"external_error": "Recipient phone number not in allowed list: [REDACTED]",
"external_error_code": 131030
},
"created_at": "2026-08-25T14:30:00.000Z",
"private": false,
"source_id": "wamid.HBgNNTUxMTk5",
"conversation": { "id": 567 },
"inbox": { "id": 3, "name": "WhatsApp Vendas" },
"sender": { "id": 12, "name": "Ana", "type": "user" },
"account": { "id": 1, "name": "Minha Empresa" }
}Os objetos account, conversation, inbox e sender chegam completos; acima estão abreviados para destacar os campos da falha.
Onde cada canal põe o código
O comportamento não é uniforme entre canais, e a diferença importa para quem trata erro em automação. Confira o seu antes de escrever a regra.
| Origem da falha | external_error_code | Formato de external_error |
|---|---|---|
| WhatsApp Cloud API recusa no envio | Presente | Só o texto do erro. Ex.: "Recipient phone number not in allowed list" |
| WhatsApp — falha de entrega reportada pela Meta | Presente | Código prefixado no próprio texto. Quando a Meta manda o detalhe (error_data.details), ele vem depois de " — ". Ex.: "131026: Message undeliverable" ou "131053: Media upload error — <detalhe da Meta>" |
| WhatsApp 360dialog — recusa no envio | Ausente | Só o texto do erro |
| WAHA, UAZAPI e NooviConnect | Ausente | Texto do conector; quando a resposta não traz motivo legível, um texto genérico com o nome do conector e o status HTTP |
| Telegram | Presente | Código também prefixado no texto, separado por vírgula. Ex.: "403, Forbidden: bot was blocked by the user" |
| SMS, Facebook, Instagram e Line | Ausente | Texto do provedor, como ele devolveu |
Não decida fluxo comparando o texto
external_error é texto livre do provedor. Não existe catálogo estável de valores: a redação muda com o provedor, com o idioma dele e com quais campos de detalhe ele preencheu naquela resposta. O campo serve para exibir ao atendente e registrar em log.
Onde external_error_code existir, é ele a chave estável — numérico, não muda com idioma nem com redação. Onde não existir, e você precisar mesmo classificar, extraia o número do prefixo do texto nos dois casos da tabela acima e trate qualquer outro formato como "motivo não classificável", em vez de cair no else de uma comparação de string.
Nos canais WhatsApp o texto é higienizado e truncado
external_error atravessa o perímetro da sua instalação. Nos canais WhatsApp — Cloud API, falha de entrega da Meta, WAHA, UAZAPI e NooviConnect — o texto passa por uma limpeza antes de ser gravado: URLs, credenciais, tokens, e-mails, telefones e sequências longas de dígitos são substituídos por [REDACTED] (URLs por [REDACTED_URL]). Isso inclui o telefone do próprio contato: o corpo de resposta de um provedor não tem esquema garantido, e não dá para distinguir com segurança o número do destinatário de outro que o provedor tenha ecoado junto. O texto também é truncado em 500 bytes, sem marcador de corte.
Nos demais canais (SMS, Telegram, Facebook, Instagram, Line) o texto é gravado como o provedor devolveu. Em qualquer canal, trate o valor como conteúdo externo não confiável: escape antes de renderizar em HTML e filtre antes de gravar em log compartilhado.
Eventos de Atendimentos
Disponível em toda licença NooviChat válida quando Atendimentos está configurado na conta.
| Evento (campo event no payload) | Descrição |
|---|---|
appointment.created | Novo atendimento agendado |
appointment.updated | Dados do atendimento atualizados |
appointment.confirmed | Atendimento confirmado |
appointment.completed | Atendimento marcado como realizado |
appointment.cancelled | Atendimento cancelado |
appointment.no_show | Cliente não compareceu |
appointment.rescheduled | Reagendamento realizado |
reminder.sent | Lembrete enviado com sucesso |
reminder.failed | Falha no envio do lembrete |
professional.created | Profissional criado |
professional.updated | Profissional atualizado |
service.created | Serviço criado |
service.updated | Serviço atualizado |
follow_up_scheduled | Follow-up agendado |
follow_up_sent | Mensagem de follow-up enviada |
follow_up_failed | Falha no envio do follow-up |
follow_up_cancelled | Follow-up cancelado (inclui excluir um follow-up pendente) |
broadcast_follow_up_sent | Follow-up de disparo em massa enviado a quem não respondeu |
Nomes canônicos e compatibilidade
Use os nomes com ponto mostrados na tabela tanto em subscriptions quanto ao interpretar o campo evententregue. As grafias antigas com sublinhado ainda são aceitas em subscriptions apenas para retrocompatibilidade, mas a entrega é normalizada para a grafia canônica com ponto. Os eventos de follow-up e broadcast permanecem com sublinhado. Não existem eventos de exclusão de profissional ou serviço.
Payloads de Lembrete
Os eventos reminder.sent e reminder.failed são entregues com um payload simples (campos diretos, sem objetos aninhados).
Payload — reminder.sent
{
"event": "reminder.sent",
"account_id": 1,
"reminder_id": 88,
"appointment_id": 42,
"sent_at": "2026-06-14T10:00:00.000Z"
}Payload — reminder.failed
{
"event": "reminder.failed",
"account_id": 1,
"reminder_id": 88,
"appointment_id": 42,
"last_error": "Numero invalido ou fora de cobertura"
}