Este guia detalha o que o Codewo envia ao seu sistema em cada evento, como validar a assinatura, o que acontece quando o seu servidor falha e como acompanhar tudo pela tela. Para a primeira configuração, comece por Começando com Webhooks.
O que é
Um webhook de saída é um POST em JSON que o Codewo faz para uma URL https:// sua quando um evento acontece. Cada endpoint escolhe os eventos que quer receber e tem o próprio segredo de assinatura (começa com whsec_).
A tela fica em Configurações → Integrações → Webhooks e depende do plano da empresa. Ver pede a permissão Ver integrações; criar, editar, testar, reenviar, pausar e excluir pedem Gerenciar integrações.
Os 11 eventos
| Evento | Nome na tela | Quando dispara |
|---|---|---|
conversation.created |
Conversa criada | Conversa nova, em qualquer canal |
conversation.assigned |
Conversa atribuída | Muda o responsável (pessoa, equipe ou atendente virtual), inclusive quando fica sem ninguém |
conversation.status_changed |
Status da conversa alterado | Muda o status sem ser resolução: aberta, pendente, em andamento, adiada ou spam, inclusive ao reabrir |
conversation.closed |
Conversa resolvida | A conversa é resolvida |
contact.created |
Contato criado | Contato novo, inclusive o que nasce da primeira mensagem num canal |
contact.updated |
Contato atualizado | Muda nome, e-mail, telefone, empresa, status, origem do cadastro, foto ou dono da carteira |
contact.tag_added |
Etiqueta adicionada ao contato | Uma etiqueta entra no contato (um evento por etiqueta) |
order.created |
Venda registrada | Todo pedido registrado: negócio ganho no funil, proposta aceita, venda pela conversa, pedido manual, API, integração ou recorrência |
order.status_changed |
Status do pedido alterado | O pedido passa a contar como venda (rascunho para confirmado) ou deixa de contar (confirmado para cancelado ou de volta a rascunho). Rascunho cancelado não dispara |
order.delivered |
Pedido entregue | A entrega passa a Entregue, pela tela, por etapa do pedido, por automação ou por integração, inclusive na retirada pelo cliente. Se a entrega sair de Entregue e voltar, dispara de novo |
quote.accepted |
Orçamento aceito | A proposta é aceita pelo cliente no link ou registrada pela equipe como aceita. Aceitar de novo a mesma proposta não repete o evento |
Não disparam: carga de histórico de uma integração (a primeira importação de pedidos de um ERP, por exemplo), etiquetas que um contato herda numa mesclagem de duplicados e, nos eventos de Vendas, empresa que não usa orçamentos e pedidos.
Como funciona
Criar o endpoint
- Clique em Novo endpoint.
- Preencha Nome, Descrição (opcional) e URL de destino (só
https://). - Marque os eventos na grade, separada em Conversas, Contatos e Vendas. Cada evento mostra o nome, o código e quando dispara.
- Clique em Criar webhook e copie o segredo na janela Copie o signing secret agora. Ele não aparece de novo.
- Clique em Testar no cartão do endpoint para receber um evento de teste.
Editar muda nome, descrição, URL e eventos, sem trocar o segredo.
Envelope e cabeçalhos
Todo aviso tem o mesmo envelope:
{
"id": "01K5TXQ3F8Q7ZC1V3W9J2B6M4N",
"type": "conversation.assigned",
"created_at": "2026-09-22T16:41:58-03:00",
"data": { }
}
id: identificador da entrega, igual em todas as tentativas automáticas dela. É a chave para não processar duas vezes.type: o evento.created_at: o horário do envio desta tentativa, e não o do fato. Numa nova tentativa, ele muda.
Cabeçalhos: Content-Type: application/json e três próprios, cujos nomes terminam em -Event (o type), -Delivery (o id) e -Signature (a assinatura). Os três nomes completos aparecem em qualquer requisição de teste que o seu servidor receber. Os de assinatura e de entrega também estão no quadro Como funciona, no fim da tela de Webhooks.
O que vem em data
Conversas trazem data.conversation:
{
"conversation": {
"id": 4821,
"status": "open",
"priority": "medium",
"channel_type": "whatsapp",
"contact": { "id": 912, "name": "Ana Souza", "email": "ana.souza@exemplo.com.br", "phone": "5519999999999" },
"assigned_to": { "id": 7, "name": "Carlos Lima", "type": "user" },
"created_at": "2026-09-22T16:30:10-03:00",
"last_message_at": "2026-09-22T16:41:55-03:00",
"resolved_at": null
}
}
Contatos trazem data.contact com id, name, email, phone, company_name, status, source, tags (lista de {id, name, color}), created_at e updated_at. O contact.updated acrescenta changed_fields, a lista dos campos que mudaram. O contact.tag_added acrescenta tag, com a etiqueta que acabou de entrar.
Pedidos (order.created, order.status_changed, order.delivered) trazem os campos direto em data:
| Campo | Conteúdo |
|---|---|
id |
Id do pedido |
number |
Número exibido, como PED-2026-0001 |
status |
draft, confirmed ou cancelled |
source |
Origem: manual, conversation, crm, quote, integration ou recurrence |
sold_at |
Data da venda |
total_cents |
Total em centavos |
currency |
Moeda |
items_count |
Quantidade de linhas |
contact_id |
Contato, ou null |
delivery_status |
pending, preparing, ready, shipped, in_transit, delivered ou cancelled |
Proposta aceita (quote.accepted) traz em data: quote_id, number (como ORC-00001), version, order_id (o pedido gerado pelo aceite), total_cents (com as escolhas do cliente), contact_id e accepted_by (o nome informado no aceite, que pode vir null).
O que os payloads ainda não trazem
order.createdchega sem os totais.total_cents,items_countedelivery_statusvêmnull, ecurrencyquase sempre também. Ao receber o evento, busque o pedido pela API emGET /api/v1/orders/{id}(permissão Ler pedidos e vendas). Os outros dois eventos de pedido chegam com os valores preenchidos.- Pedido sem linhas nem pagamento. Nenhum evento de pedido traz os itens ou as parcelas. Também não há evento de pagamento recebido: esse fato existe só como gatilho de Automações.
- Dono da carteira. Quando a carteira muda, o
contact.updatedtrazassigned_toemchanged_fields, mas o payload não diz quem é o novo dono. Busque emGET /api/v1/contacts/{id}. - Via do aceite. O
quote.acceptednão diz se foi o cliente no link ou a equipe que registrou. O mesmo aceite gera também umorder.createddo pedido novo. - Atribuição e status juntos. Se a atribuição e o status (sem ser resolução) mudam no mesmo momento, sai só o
conversation.assigned, que já traz o status novo.
Validar a assinatura
O cabeçalho terminado em -Signature vem assim: t=1727034118,v1=5f2c.... Nas 24 horas depois de uma rotação do segredo, vem com duas assinaturas: t=...,v1=<com o segredo novo>,v1=<com o antigo>.
1. Leia o corpo cru da requisição, byte a byte como chegou.
2. Separe o cabeçalho pelas vírgulas. Guarde t e TODOS os v1.
3. Calcule HMAC-SHA256 de t + "." + corpo_cru , com o segredo inteiro (incluindo whsec_) como chave.
4. Compare, em tempo constante, com cada v1. Aceite se qualquer um bater.
5. Recuse se t estiver a mais de 5 minutos do seu relógio.
Use a comparação em tempo constante da sua linguagem (hash_equals no PHP, crypto.timingSafeEqual no Node.js, hmac.compare_digest no Python).
Resposta esperada
- Responda com qualquer código 2xx em até 10 segundos. O Codewo marca a entrega como feita.
- Qualquer outro código, erro de conexão ou demora é falha.
- O log guarda os primeiros 2 KB da sua resposta, então uma mensagem de erro curta no corpo ajuda a diagnosticar pela tela.
Novas tentativas
Cada entrega tem até 5 tentativas, sempre com o mesmo id:
| Tentativa | Quando |
|---|---|
| 1ª | Na hora do evento |
| 2ª | 30 segundos depois da 1ª falha |
| 3ª | 5 minutos depois da 2ª falha |
| 4ª | 30 minutos depois da 3ª falha |
| 5ª | 2 horas depois da 4ª falha |
Falhou na 5ª, a entrega é abandonada. No log, a tentativa com nova tentativa agendada aparece como Retentando.
Pausa automática
O endpoint conta as falhas seguidas, de todos os eventos juntos, e zera o contador a cada sucesso. Na 5ª falha seguida, ele é pausado. Com um único evento falhando, isso coincide com a última tentativa dele. Com vários eventos falhando ao mesmo tempo, a pausa pode vir em segundos, antes das novas tentativas.
- O cartão mostra Pausado (auto) e o aviso "Pausado automaticamente após 5 falhas consecutivas".
- Quem criou o endpoint recebe o aviso Webhook desativado automaticamente, no app e, conforme as preferências de aviso da pessoa, por e-mail e push.
- As novas tentativas agendadas que vencerem durante a pausa são descartadas, e os eventos seguintes não são enviados nem guardados.
- Corrija o servidor e clique em Reativar. O contador volta a zero. Depois de reativar, o que falhou antes da pausa pode ser reenviado uma entrega por vez, em Entregas → Reenviar. O que aconteceu durante a pausa não aparece no log: recupere pela API.
Para suspender de propósito (uma manutenção no seu servidor, por exemplo), use Pausar no menu do endpoint e depois Ativar. Vale a mesma regra: enquanto pausado, nada é enviado.
Rotação do segredo
Use quando o segredo pode ter vazado ou quando alguém que o conhecia deixou a equipe.
- No menu do endpoint, clique em Rotacionar secret e confirme.
- O segredo novo aparece uma vez, na mesma janela da criação. Copie.
- Por 24 horas, todo aviso sai assinado com os dois segredos. Atualize o seu servidor nesse prazo.
- Passadas as 24 horas, só o segredo novo assina.
Log de entregas e saúde
- Números do topo: endpoints ativos, eventos nas últimas 24 horas, taxa de sucesso e falhas nas últimas 24 horas.
- Barra de saúde no cartão de cada endpoint: as últimas 50 entregas, verde para sucesso e vermelho para falha.
- Entregas: abre as últimas 100, com filtros Todas, Sucesso, Falhas e Retentando. Cada linha mostra o código de resposta (ou Retentando, quando há nova tentativa agendada), o evento, o número da tentativa a partir da 2ª, o tempo de resposta e o erro, quando houve. Clicando, aparecem o Payload enviado, a Resposta do servidor e o botão Reenviar.
- Registros com mais de 30 dias são apagados automaticamente.
Reenviar manda o mesmo data como uma entrega nova: id novo e ciclo de tentativas recomeçando do zero.
Excluir (no menu) para o endpoint na hora e tira o histórico de entregas da tela, sem volta.
Pegadinhas comuns
- Os trechos de exemplo da janela do segredo são só um ponto de partida. Os de PHP e Node.js conferem só a primeira assinatura, que é a do segredo novo, e o de Python dá erro quando chegam duas. Antes de usá-los, ajuste para aceitar qualquer
v1, como no roteiro acima. Do contrário, nas 24 horas depois de uma rotação, o servidor que ainda usa o segredo antigo (e, com o trecho em Python, qualquer servidor) recusa as entregas até o endpoint ser pausado. - Reenviar com o endpoint pausado não envia nada, mesmo com o aviso de que o reenvio foi enfileirado. Reative primeiro.
- O código na lista de Entregas não é o
idque você recebe. Cada linha da lista identifica uma tentativa. Para cruzar com o log do seu sistema, abra a entrega e procure oidno Payload enviado. - O evento de teste usa o primeiro evento marcado, com dados fictícios, ids
0e"_test": true. Nos eventos de Vendas, o teste chega no formato de contato. Filtre_testno seu sistema para não gravar o teste como dado real. - A grade mostra o grupo Vendas mesmo para quem não usa orçamentos e pedidos. Nesse caso, esses eventos nunca disparam.
- A ordem de chegada não é garantida. Uma nova tentativa pode chegar depois de um aviso mais recente. Quando a ordem importa, busque o estado atual pela API.
- Só quem criou o endpoint é avisado da pausa. Se essa pessoa sai de férias ou deixa a empresa, a integração pode ficar parada sem ninguém perceber.
- Corpo convertido quebra a assinatura. Frameworks que transformam o JSON em objeto antes de você ler mudam espaços e a ordem dos campos. Calcule o HMAC sobre o corpo cru.
- Endereço interno não recebe. O Codewo não alcança
localhostnem a rede interna da empresa. Para desenvolver, use um túnel que publique o seu computador num endereçohttps://.
Boas práticas
- Endpoint dedicado. Uma URL só para receber os avisos do Codewo, separada das rotas comuns do seu sistema.
- Receba, valide, enfileire e responda 200. O processamento de verdade fica numa fila do seu lado.
- Valide a assinatura sempre. Sem ela, qualquer um pode mandar um aviso falso para a sua URL.
- Deduplique pelo
id. Receber o mesmo aviso duas vezes não pode criar dois registros no seu sistema. - Trate o aviso como sinal e busque o detalhe pela API quando precisar de dados completos ou do estado mais recente.
- Monitore do seu lado. Um alerta quando a taxa de erro sobe pega o problema antes da pausa automática.