Webhooks
O webhook_url do endpoint de Chat recebe a resposta do agente depois que a requisição original já terminou. Ele é um callback por chamada, indicado quando a sua aplicação não deve manter a conexão aberta durante a execução do agente.
Fluxo assíncrono
Section titled “Fluxo assíncrono”- Envie
webhook_urle não usesync: trueemPOST /chat/{id}. - A API registra a entrada e responde
202 Acceptedcomstatus: "processing"oustatus: "debounced". - O agente é executado em background.
- Quando existe uma resposta externa não vazia, o SquadOS faz um
POSTpara a URL informada.
curl -X POST https://api.squados.io/v1/chat/API_INBOX_ID \ -H "Authorization: Bearer pk_sua_chave_aqui" \ -H "Content-Type: application/json" \ -d '{ "message": "Qual é o status do pedido 1234?", "sync": false, "webhook_url": "https://integracao.exemplo.com/squados/callback", "metadata": { "correlation_id": "evt_01J7Y8Q3", "ticket_id": "TKT-9981" } }'Uma entrada que passa pela fila de agrupamento retorna:
{ "status": "debounced", "group_id": "d4e5f6a7-b8c9-0123-def0-234567890123", "conversation_id": "c3d4e5f6-a7b8-9012-cdef-123456789012"}Sem agrupamento, a confirmação pode ser somente:
{ "status": "processing", "conversation_id": "c3d4e5f6-a7b8-9012-cdef-123456789012"}O 202 não contém a resposta nem o message_id do assistente. Ele confirma que a entrada foi aceita, não que o callback será entregue.
Callback de resposta
Section titled “Callback de resposta”O callback usa Content-Type: application/json. Uma resposta comum tem este formato:
{ "event": "message.completed", "agent_id": "550e8400-e29b-41d4-a716-446655440000", "success": true, "conversation_id": "c3d4e5f6-a7b8-9012-cdef-123456789012", "message_id": "e5f6a7b8-c9d0-1234-ef01-345678901234", "response": "O pedido 1234 está em separação e será enviado amanhã.", "model": "provider/model", "credits_used": 3, "attachments_processed": 0, "responding_agent_name": "Atendimento", "metadata": { "correlation_id": "evt_01J7Y8Q3", "ticket_id": "TKT-9981" }, "timestamp": "2026-09-01T14:32:07.000Z"}| Campo | Tipo | Regra |
|---|---|---|
event | string | Na resposta normal, message.completed. |
agent_id | UUID | Agente associado à execução. |
success | boolean | true em message.completed. |
conversation_id | UUID | Use para consultar a conversa e reconciliar o resultado. |
message_id | UUID | Identifica a mensagem do agente. Pode faltar em um caminho que não conseguiu persistir uma mensagem externa. |
response | string | Texto final entregue ao canal externo. |
model | string | Modelo efetivamente usado; pode faltar em callbacks de follow-up. |
credits_used | number | Créditos da execução; pode faltar em callbacks de follow-up. |
attachments_processed | integer | Quantidade de anexos da entrada; pode faltar em callbacks de follow-up. |
responding_agent_name | string | Nome do agente que produziu esta resposta. |
metadata | object | Dados de correlação preservados da entrada; pode faltar quando nada foi enviado. |
timestamp | date-time | Momento em que o payload foi montado. |
O runtime contém uma variante message.error para uma falha interna durante a etapa de entrega, com agent_id, conversation_id, error, metadata e timestamp. Ela não é uma confirmação garantida de toda falha: se o mesmo destino estiver inacessível, o payload de erro também não conseguirá chegar. Reconcilie ausências pelo histórico da conversa.
Agrupamento e metadata
Section titled “Agrupamento e metadata”Com sync: false, mensagens próximas na mesma conversa podem ser agrupadas em uma única execução. O texto e os anexos são combinados em ordem cronológica, mas o callback preserva apenas o primeiro objeto metadata disponível no grupo (first wins).
Use um identificador de correlação estável entre as entradas que podem cair no mesmo grupo. Não dependa de valores diferentes no metadata de cada mensagem agrupada.
Transferências e follow-ups
Section titled “Transferências e follow-ups”Uma transferência entre agentes pode produzir dois callbacks message.completed, nesta ordem:
- a mensagem do agente de origem, com
pre_transfer: true; - a resposta do agente de destino, sem esse marcador.
Use message_id como chave de idempotência e não trate conversation_id como identificador único de callback.
Um follow-up automático da mesma conversa também pode chegar à URL registrada. Ele usa message.completed e acrescenta:
| Campo | Tipo | Significado |
|---|---|---|
followup | boolean | Sempre true nessa variante. |
followup_attempt | integer | Número da tentativa lógica do follow-up. Não é tentativa de entrega HTTP. |
Destino permitido e autenticação
Section titled “Destino permitido e autenticação”O destino precisa ser uma URL HTTP ou HTTPS pública com DNS resolvível. O SquadOS bloqueia loopback, redes privadas e reservadas, endpoints de metadata e hostnames locais ou internos. Essa validação acontece durante o processamento assíncrono; por isso, uma URL bloqueada ainda pode receber um 202 inicial e nunca receber o callback.
Use HTTPS. O callback envia somente o header Content-Type: application/json: ele não possui assinatura, Authorization nem cabeçalhos personalizados. Se o receptor exigir autenticação, use um caminho opaco e de alta entropia e valide também um valor de correlação imprevisível no metadata. Não coloque credenciais reutilizadas de outros sistemas nessa URL.
Entrega e recuperação de falhas
Section titled “Entrega e recuperação de falhas”Responda com qualquer status 2xx. Respostas não-2xx, falhas de rede e URLs bloqueadas são registradas como falha; o corpo devolvido pelo receptor é registrado com limite de 5.000 caracteres.
O callback atual faz no máximo uma tentativa de entrega e não possui reenvio automático. Projete o fluxo como at-most-once:
- persista rapidamente o payload e responda
2xx; - deduplique por
message_id; - não use a chegada do callback como única fonte de verdade;
- se ele não chegar, consulte
GET /conversations/{conversationId}/messagespara reconciliar o histórico.
Consulte Chat para o contrato completo da requisição e Conversas para leitura do histórico.