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.

GET/api/v1/accounts/{account_id}/webhooks

Lista todos os webhooks configurados na conta.

bash
curl -s "https://chat.seudominio.com/api/v1/accounts/1/webhooks" \
  -H "api_access_token: YOUR_TOKEN" | jq .
200Lista de webhooks (aninhada em payload.webhooks)
json
{
  "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>"
      }
    ]
  }
}
POST/api/v1/accounts/{account_id}/webhooks

Registra um novo webhook para receber eventos.

Body (envolto em objeto webhook)

NomeTipoObrigatorioDescricao
urlstringSimURL HTTP ou HTTPS que receberá os eventos via POST. Em produção, prefira HTTPS.
subscriptionsarray[string] | nullNaoLista 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.
namestringNaoNome descritivo do webhook
inbox_idintegerNaoAssocia 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"
      ]
    }
  }'
200Webhook criado (aninhado em payload.webhook; inclui name, account_id e secret)
json
{
  "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>"
    }
  }
}
404inbox_id não existe na conta autenticada ou pertence a outra conta.
422Objeto webhook malformado, URL inválida, subscriptions vazia, desconhecida ou fora de array[string], ou inbox_id malformado.
PATCH/api/v1/accounts/{account_id}/webhooks/{webhook_id}

Atualiza os campos permitidos de um webhook.

Body (webhook)

NomeTipoObrigatorioDescricao
urlstringNaoNova URL do webhook
subscriptionsarray[string] | nullNaoNova 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.
namestringNaoNovo nome descritivo
inbox_idintegerNaoInbox associada como metadado; não torna a entrega inbox-scoped
bash
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"
      ]
    }
  }'
200Webhook atualizado, envolto em { payload: { webhook: { ... } } }.
404webhook_id não existe na conta, ou inbox_id não existe na conta autenticada ou pertence a outra conta.
422Atualização vazia, campo malformado, URL inválida ou subscriptions vazia, desconhecida ou fora de array[string].
DELETE/api/v1/accounts/{account_id}/webhooks/{webhook_id}

Remove um webhook. Eventos não serão mais enviados para a URL.

bash
curl -X DELETE "https://chat.seudominio.com/api/v1/accounts/1/webhooks/2" \
  -H "api_access_token: YOUR_TOKEN"
200Webhook removido; resposta sem corpo.

Eventos Disponíveis

O catálogo abaixo contém os 32 valores aceitos em subscriptions.

EventoDescrição
conversation_createdNova conversa criada
conversation_updatedConversa atualizada
conversation_status_changedStatus da conversa alterado
conversation_typing_onIndicador de digitação iniciado
conversation_typing_offIndicador de digitação encerrado
message_createdNova mensagem recebida ou enviada
message_updatedMensagem atualizada
contact_createdNovo contato criado
contact_updatedDados do contato atualizados
webwidget_triggeredWidget de chat acionado no site
inbox_createdNova inbox criada
inbox_updatedInbox atualizada
broadcast_startedDisparo em massa iniciado
broadcast_completedDisparo em massa concluído
broadcast_auto_pausedDisparo 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.

CampoTipoDescrição
content_attributes.external_errorstringMotivo 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_codeintegerO 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

json
{
  "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 falhaexternal_error_codeFormato de external_error
WhatsApp Cloud API recusa no envioPresenteSó o texto do erro. Ex.: "Recipient phone number not in allowed list"
WhatsApp — falha de entrega reportada pela MetaPresenteCó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 envioAusenteSó o texto do erro
WAHA, UAZAPI e NooviConnectAusenteTexto do conector; quando a resposta não traz motivo legível, um texto genérico com o nome do conector e o status HTTP
TelegramPresenteCódigo também prefixado no texto, separado por vírgula. Ex.: "403, Forbidden: bot was blocked by the user"
SMS, Facebook, Instagram e LineAusenteTexto 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.createdNovo atendimento agendado
appointment.updatedDados do atendimento atualizados
appointment.confirmedAtendimento confirmado
appointment.completedAtendimento marcado como realizado
appointment.cancelledAtendimento cancelado
appointment.no_showCliente não compareceu
appointment.rescheduledReagendamento realizado
reminder.sentLembrete enviado com sucesso
reminder.failedFalha no envio do lembrete
professional.createdProfissional criado
professional.updatedProfissional atualizado
service.createdServiço criado
service.updatedServiço atualizado
follow_up_scheduledFollow-up agendado
follow_up_sentMensagem de follow-up enviada
follow_up_failedFalha no envio do follow-up
follow_up_cancelledFollow-up cancelado (inclui excluir um follow-up pendente)
broadcast_follow_up_sentFollow-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

json
{
  "event": "reminder.sent",
  "account_id": 1,
  "reminder_id": 88,
  "appointment_id": 42,
  "sent_at": "2026-06-14T10:00:00.000Z"
}

Payload — reminder.failed

json
{
  "event": "reminder.failed",
  "account_id": 1,
  "reminder_id": 88,
  "appointment_id": 42,
  "last_error": "Numero invalido ou fora de cobertura"
}