Webhooks são callbacks HTTP configurados para cada conta. Eles são acionados quando ações como a criação de uma mensagem ocorrem no Chatwoot. Vários webhooks podem ser criados para uma única conta.
Como adicionar um webhook?
Passo 1. Acesse Configurações → Integrações → Webhooks. Clique no botão "Configurar".

Passo 2. Clique no botão "Adicionar novo webhook". Um modal será aberto. Aqui, insira a URL para a qual a requisição POST deve ser enviada. Em seguida, você precisa selecionar os eventos aos quais deseja se inscrever. Esta opção permite que você escute apenas pelos eventos relevantes no Chatwoot.

O Chatwoot enviará uma requisição POST com o seguinte payload para as URLs configuradas para diversas atualizações em sua conta.
Exemplo de payload de webhook
{
"event": "message_created", // Nome do evento
"id": "1", // ID da mensagem
"content": "Hi", // Conteúdo da mensagem
"created_at": "2020-03-03 13:05:57 UTC", // Hora em que a mensagem foi enviada
"message_type": "incoming", // Terá o valor incoming, outgoing ou template. O usuário do widget envia mensagens incoming, o agente envia mensagens outgoing para o usuário.
"content_type": "enum", // Isto é um enum, pode ser input_select, cards, form ou text. O message_type será template se o content_type for um destes. O valor padrão é text
"content_attributes": {} // Será um objeto, valores diferentes são definidos abaixo
"source_id": "", // Será o id externo se a caixa de entrada for uma integração com Twitter ou Facebook.
"sender": { // Proverá detalhes do agente que enviou a mensagem
"id": "1",
"name": "Agent",
"email": "[email protected]"
},
"contact": { // Proverá detalhes do usuário que enviou a mensagem
"id": "1",
"name": "contact-name"
},
"conversation": { // Proverá detalhes da conversa
"display_id": "1", // ID da conversa visível no dashboard.
"additional_attributes": {
"browser": {
"device_name": "Macbook",
"browser_name": "Chrome",
"platform_name": "Macintosh",
"browser_version": "80.0.3987.122",
"platform_version": "10.15.2"
},
"referer": "<http://www.chatwoot.com>",
"initiated_at": "Tue Mar 03 2020 18:37:38 GMT-0700 (Mountain Standard Time)"
}
},
"account": { // Proverá detalhes da conta
"id": "1",
"name": "Chatwoot",
}
}
Eventos de webhook suportados no Chatwoot
O Chatwoot publica vários eventos para os endpoints de webhook configurados. Se você quiser configurar um webhook, consulte o guia aqui.
Cada evento possui sua estrutura de payload com base no tipo de modelo sobre o qual atuam. A seção a seguir descreve os principais objetos que usamos no Chatwoot e seus atributos.
Objetos
Um payload de evento pode incluir qualquer um dos seguintes objetos. Os vários tipos de objetos suportados pelo Chatwoot estão listados abaixo.
Conta
{
"id": "integer",
"name": "string"
}
Caixa de entrada
{
"id": "integer",
"name": "string"
}
Contato
{
"id": "integer",
"name": "string",
"avatar": "string",
"type": "contact",
"account": {
// <...Objeto Conta>
}
}
Usuário
{
"id": "integer",
"name": "string",
"email": "string",
"type": "user"
}
Conversa
{
"additional_attributes": {
"browser": {
"device_name": "string",
"browser_name": "string",
"platform_name": "string",
"browser_version": "string",
"platform_version": "string"
},
"referer": "string",
"initiated_at": {
"timestamp": "iso-datetime"
}
},
"can_reply": "boolean",
"channel": "string",
"id": "integer",
"inbox_id": "integer",
"contact_inbox": {
"id": "integer",
"contact_id": "integer",
"inbox_id": "integer",
"source_id": "string",
"created_at": "datetime",
"updated_at": "datetime",
"hmac_verified": "boolean"
},
"messages": ["Array de objetos de mensagem"],
"meta": {
"sender": {
// Objeto Contato
},
"assignee": {
// Objeto Usuário
}
},
"status": "string",
"unread_count": "integer",
"agent_last_seen_at": "unix-timestamp",
"contact_last_seen_at": "unix-timestamp",
"timestamp": "unix-timestamp",
"account_id": "integer"
}
Mensagem
{
"id": "integer",
"content": "string",
"message_type": "integer",
"created_at": "unix-timestamp",
"private": "boolean",
"source_id": "string / null",
"content_type": "string",
"content_attributes": "object",
"sender": {
"type": "string - contact/user"
// Objeto Usuário ou Contato
},
"account": {
// Objeto Conta
},
"conversation": {
// Objeto Conversa
},
"inbox": {
// Objeto Caixa de entrada
}
}
Exemplo de payload de webhook
{
"event": "event_name"
// Atributos relacionados ao evento
}
Eventos de Webhook
O Chatwoot suporta os seguintes eventos de webhook. Você pode se inscrever neles ao configurar um webhook no painel ou utilizando a API.
conversation_created
Este evento será acionado quando uma nova conversa for criada na conta. O payload do evento é o seguinte.
{
"event": "conversation_created"
// <...Atributos da Conversa>
}
conversation_updated
Este evento será acionado quando houver uma alteração em algum dos atributos da conversa.
{
"event": "conversation_updated",
"changed_attributes": [
{
"<nome_do_atributo>": {
"current_value": "",
"previous_value": ""
}
}
]
// <...Atributos da Conversa>
}
conversation_status_changed
Este evento será acionado quando o status da conversa for alterado.
Nota: Se você estiver utilizando APIs de agent bot em vez de webhooks, este evento ainda não é suportado.
{
"event": "conversation_status_changed"
// <...Atributos da Conversa>
}
message_created
Este evento será acionado quando uma mensagem for criada em uma conversa. O payload do evento é o seguinte.
{
"event": "message_created"
// <...Atributos da Mensagem>
}
message_updated
Este evento será acionado quando uma mensagem for atualizada em uma conversa. O payload do evento é o seguinte.
{
"event": "message_updated"
// <...Atributos da Mensagem>
}
webwidget_triggered
Este evento será acionado quando o usuário final abrir o widget de chat ao vivo.
{
"event": "webwidget_triggered",
"id": "",
"contact": {
// <...Objeto Contato>
},
"inbox": {
// <...Objeto Caixa de Entrada>
},
"account": {
// <...Objeto Conta>
},
"current_conversation": {
// <...Objeto Conversa>
},
"source_id": "string",
"event_info": {
"initiated_at": {
"timestamp": "date-string"
},
"referer": "string",
"widget_language": "string",
"browser_language": "string",
"browser": {
"browser_name": "string",
"browser_version": "string",
"device_name": "string",
"platform_name": "string",
"platform_version": "string"
}
}
}
conversation_typing_on
Este evento é acionado quando um agente começa a digitar em uma conversa. Pode ser uma nota privada ou uma mensagem para o cliente. Você pode usar o flag is_private para diferenciar entre os dois.
{
"event": "conversation_typing_on",
"conversation": { ...<Objeto Conversa> },
"user": { ... <Usuário / AgentBot / Objeto Capitão> },
"is_private": true
}
conversation_typing_off
Este evento é acionado quando o agente para de digitar ou sai da janela da conversa.
{
"event": "conversation_typing_off",
"conversation": { ...<Objeto Conversa> },
"user": { ... <Usuário / AgentBot / Objeto Capitão> },
"is_private": true
}
Verificando webhooks
O Chatwoot assina todas as requisições de webhook de saída para que seu servidor possa verificar se o payload foi enviado pelo Chatwoot e não foi adulterado. O segredo é exibido uma vez que o webhook é criado, e você pode visualizá-lo novamente no formulário de edição do webhook.
Toda requisição de webhook envia os seguintes cabeçalhos, que podem ser usados para calcular a assinatura HMAC do payload
-
X-Chatwoot-Signature: assinatura HMAC-SHA256 prefixada comsha256= -
X-Chatwoot-Timestamp: timestamp Unix (segundos) de quando a requisição foi assinada -
X-Chatwoot-Delivery: ID único de entrega para o evento de webhook (quando disponível)
A assinatura é calculada assim:
sha256=HMAC-SHA256(webhook_secret, "{timestamp}.{raw_body}")
Onde:
-
webhook_secreté o segredo associado ao webhook -
timestampé o valor do cabeçalhoX-Chatwoot-Timestamp -
raw_bodyé o corpo da requisição JSON bruta (não parseado/re-serializado)
Passos de Verificação
-
Extraia
X-Chatwoot-SignatureeX-Chatwoot-Timestampdos cabeçalhos da requisição -
Leia o corpo bruno da requisição como bytes (não parse e re-serialize)
-
Calcule a assinatura esperada:
sha256=HMAC-SHA256(secret, "{timestamp}.{raw_body}") -
Compare a assinatura calculada com a recebida usando uma comparação em tempo constante
-
Opcionalmente, rejeite requisições cujo timestamp seja muito antigo para prevenir ataques de repetição (replay)
Exemplos
Ruby
def verify_signature(raw_body, timestamp, received_signature, secret)
expected = "sha256=#{OpenSSL::HMAC.hexdigest('SHA256', secret, "#{timestamp}.#{raw_body}")}"
ActiveSupport::SecurityUtils.secure_compare(expected, received_signature)
end
Python
import hmac
import hashlib
def verify_signature(raw_body: bytes, timestamp: str, received_signature: str, secret: str) -> bool:
message = f"{timestamp}.".encode() + raw_body
expected = "sha256=" + hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_signature)
Node.js
const crypto = require("crypto");
function verifySignature(rawBody, timestamp, receivedSignature, secret) {
const message = `${timestamp}.${rawBody}`;
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(message).digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(receivedSignature)
);
}
Go
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
)
func verifySignature(rawBody []byte, timestamp, receivedSignature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(fmt.Sprintf("%s.%s", timestamp, rawBody)))
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(receivedSignature))
}
Notas Importantes
-
Sempre utilize o corpo bruto da requisição para verificação. Fazer o parse do JSON e depois re-serializar pode alterar a ordenação das chaves, espaços ou escape unicode, o que resultará em uma assinatura diferente.
-
Sempre utilize comparação em tempo constante (ex:
hmac.compare_digest,crypto.timingSafeEqual,ActiveSupport::SecurityUtils.secure_compare) para prevenir ataques de timing. -
Considere rejeitar requisições com timestamp mais antigos que 5 minutos para mitigar ataques de repetição.