Para Builders
Webhooks
Um webhook e a porta pela qual seu codigo entra no Daguito. Qualquer sistema externo —seu backend, um CRM, um cron— pode disparar um fluxo com uma unica chamada. Ha dois transportes: HTTP para chamadas pontuais e WebSocket para fluxos longos em streaming. Cada token e guardado em hash, cada entrega de saida vai assinada e tudo fica registrado.
O que e um webhook
Um webhook e um ponto de entrada com nome que aponta para um fluxo. Voce entrega um token a um sistema externo e esse sistema dispara o fluxo quando precisa. Voce nao expoe sua API: expoe um endpoint estreito e assinado que so sabe executar aquele fluxo.
Voce escolhe o transporte ao criar o webhook, conforme for consumi-lo:
- HTTP — para integracoes request/response. Voce chama, espera, recebe a saida.
- Streaming — um canal WebSocket bidirecional para fluxos longos e eventos ao vivo.
Os tokens nunca sao guardados em texto plano. Um hash SHA-256 e armazenado para resolve-los em O(1), e o texto plano so e devolvido uma vez, ao criar o webhook.
Tipos e transporte
O campo type do webhook decide qual endpoint o token ativa. Ha dois valores: http e streaming.
Webhooks HTTP
Um webhook HTTP expoe dois modos, conforme quanto quem chama pode esperar:
- Sincrono —
POST /h/:token. O token e a unica autenticacao. Bloqueia ate o fluxo terminar e devolve a saida na resposta. - Assincrono —
POST /v1/webhooks/:id/messagescom auth Bearer. Responde 202 na hora e o fluxo roda em segundo plano; os resultados chegam por callbacks.
Webhooks de streaming
Um webhook de streaming abre um WebSocket bidirecional em /v1/webhooks/:id/stream. O token viaja como parametro ?token= na conexao. O cliente conduz uma sessao com frames e recebe os eventos do fluxo ao vivo.
Estas sao as tres formas de invocar um webhook, conforme o tipo:
# HTTP — sincronico (one-shot, el token es la unica auth)
POST https://api.daguito.com/h/dgsk_wh_...
# HTTP — asincronico (202 inmediato, auth Bearer)
POST https://api.daguito.com/v1/webhooks/:id/messages
Authorization: Bearer dgsk_wh_...
# Streaming — WebSocket bidireccional
wss://api.daguito.com/v1/webhooks/:id/stream?token=dgsk_wh_... O tipo e imutavel: e fixado na criacao e nao muda depois. Para passar de HTTP a streaming e preciso criar um webhook novo.
Tokens e seguranca
Cada webhook tem um token com o qual se autentica. O modelo e simples e rigoroso:
- Formato — prefixo
dgsk_wh_seguido de 24 bytes aleatorios codificados em base64url. O prefixo antigosk_wh_também é aceito. - Armazenamento — guarda-se um hash SHA-256, nunca o token em texto plano. O texto plano não persiste após a criação.
- Previa — no painel voce ve
dgsk_wh_...LAST4, com os ultimos 4 caracteres, o bastante para reconhece-lo sem expo-lo. - Uma so vez — o texto plano e devolvido na resposta de criacao e nao pode ser recuperado depois. Guarde-o no seu gerenciador de segredos.
// Formato y hashing del token
const plaintext = 'dgsk_wh_' + crypto.randomBytes(24).toString('base64url')
const tokenHash = sha256(plaintext) // solo se guarda el hash
const tokenPreview = 'dgsk_wh_...' + plaintext.slice(-4) // y una vista previa Nao ha rotacao automatica de tokens nem endpoint de exclusao. Para invalidar um token, mude o status do webhook para revoked; para um novo, crie outro webhook.
HTTP sincrono: POST /h/:token
E a forma mais direta: uma unica chamada que bloqueia ate o fluxo terminar. Ideal para integracoes que precisam da resposta de imediato.
- Requisicao — o corpo e JSON livre e e entregue ao fluxo como
base_input. - Resposta —
{ ok, execution_id, status, output }. - Timeout — o proxy do edge corta aos 100 segundos. Para fluxos mais longos, use o transporte de streaming.
- Faturamento — a rota valida a situacao de faturamento da organizacao e responde 402 se nao estiver em dia.
No SDK, use runWebhook para esta chamada sincrona:
import { runWebhook } from '@daguito/sdk'
const result = await runWebhook({
apiUrl: 'https://api.daguito.com',
token: 'dgsk_wh_...', // token en texto plano
input: { user_id: '123', action: 'process' },
timeoutMs: 30000,
})
console.log(result)
// { ok: boolean, executionId: string, status: string, output: unknown } HTTP assincrono: POST /v1/webhooks/:id/messages
Quando voce nao quer bloquear, este modo enfileira a mensagem e responde na hora. O fluxo roda a parte e os resultados chegam por callbacks.
- Requisição —
{ session_key, message, base_input? }, onde message é o objeto de mensagem normalizado. - Resposta — 202 imediato com
{ ok, status, message_id, session_key }. - Auth — token Bearer lido do header
Authorization. - Quando usar — integracoes de alto volume que precisam de baixa latencia e nao podem esperar o resultado.
Esta rota não foi pensada para fluxos muito longos: processa a entrega imediatamente. Para processos de longa duração, é melhor dividi-los em passos mais curtos.
Callbacks de saida
Um callback e um POST de servidor para servidor que o Daguito envia a uma URL sua quando ocorre um evento do fluxo. Assim voce recebe resultados sem precisar fazer polling.
- Filtro — cada callback declara um array
eventscom padroes (por exemploflow.completedounode.*). So esses eventos disparam o POST. - Assinatura — o corpo vai assinado com HMAC-SHA256 usando
secret_hash, no headerx-daguito-signature: sha256=<hex>. - Headers — alem disso viajam
x-daguito-event(tipo) ex-daguito-webhook-id(id do webhook). - Payload —
{ event, webhook_id, session_key, flow_id, at, data }, onde data depende do evento.
Forma do payload
// Headers del POST saliente
x-daguito-signature: sha256=<hex> // HMAC-SHA256 del cuerpo, con secret_hash
x-daguito-event: flow.completed // el tipo de evento
x-daguito-webhook-id: wh_abc123 // id del webhook
Content-Type: application/json
// Cuerpo
{
"event": "flow.completed",
"webhook_id": "wh_abc123",
"session_key": "webhook:...",
"flow_id": "...",
"at": "2026-01-01T12:00:00Z",
"data": { /* especifico del evento */ }
} Verificar a assinatura
Recalcule o HMAC sobre o corpo cru (sem reserializar) com o seu segredo e compare com o header. Se nao bater, rejeite a requisicao.
import crypto from 'node:crypto'
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(rawBody) // el cuerpo en bytes, sin re-serializar
.digest('hex')
const received = req.headers['x-daguito-signature'] // sha256=<hex>
if (received !== 'sha256=' + expected) {
throw new Error('signature mismatch')
} Cada tentativa de callback, com sucesso ou falha, fica registrada para que você possa depurar suas entregas.
Streaming por WebSocket
Para fluxos longos —geracoes de LLM, pipelines de visao, varias ferramentas encadeadas— o WebSocket evita o limite do proxy e entrega os eventos a medida que ocorrem.
Ciclo de vida da sessao
O cliente conduz uma sessao com frames: session.start cria o session_key, session.open dispara o fluxo com o base_input, message envia texto ou audio e session.end a encerra. O servidor confirma cada passo e empurra os FlowEvent.
// El cliente envia:
{ "type": "session.start", "session_key": "..." }
{ "type": "session.open", "base_input": { /* ... */ } }
{ "type": "message", "message": { /* normalized message */ } }
{ "type": "tool_result", "call_id": "...", "ok": true, "result": {} }
{ "type": "session.end" }
{ "type": "ping" }
// El servidor responde:
{ "type": "ready", "webhook_id": "wh_abc123" }
{ "type": "session.started", "session_key": "..." }
{ "type": "session.opened", "session_key": "...", "execution_id": "..." }
{ "type": "session.ended" }
{ "type": "pong", "at": "..." }
{ "type": "error", "message": "..." }
// ...y FlowEvent por cada evento que pase el filtro events_downstream Os eventos que chegam ao cliente sao filtrados pelo array events_downstream do webhook: padroes com curinga (por exemplo flow.* ou node.token) compilados uma vez por sessao.
O SDK envolve toda a sessao em runWebhookStream, que nao tem o limite de 100 s do proxy:
import { runWebhookStream } from '@daguito/sdk'
const result = await runWebhookStream({
apiUrl: 'https://api.daguito.com',
webhookId: 'wh_abc123',
token: 'dgsk_wh_...', // token de streaming
input: { vision_image: '...' },
timeoutMs: 300000, // 5 minutos — sin limite del proxy
})
console.log(result)
// { ok, executionId, status: 'completed'|'failed'|'unknown', output, elapsedMs } A reconexao nao e automatica. Cada socket se autentica separadamente com o token, entao seu cliente deve reconectar e reabrir a sessao se a conexao cair.
Eventos de fluxo
Tanto os callbacks quanto o WebSocket entregam FlowEvent: o mesmo modelo de eventos que percorre a execucao de um fluxo do inicio ao fim.
Tipos de evento
- Ciclo do fluxo —
flow.started,flow.resumed,flow.completed,flow.failed,flow.paused,flow.branch. - Nos —
node.started,node.completed,node.progress,node.emit,node.token. - Sincronizacao e controle —
merge.progressehandoff.requested.
O envelope do evento
export type EventType =
| 'flow.started' | 'flow.resumed' | 'flow.completed'
| 'flow.failed' | 'flow.paused' | 'flow.branch'
| 'node.started' | 'node.completed' | 'node.progress'
| 'node.emit' | 'node.token'
| 'merge.progress' | 'handoff.requested'
export interface FlowEvent {
type: EventType
session_key: string
flow_id?: string // uuid
node_id?: string
branch_id?: string
at: string // timestamp ISO
data: Record<string, unknown> // la forma depende del tipo
} Filtros com curinga
Tanto events (callbacks) quanto events_downstream (streaming) aceitam padroes com curinga. O ponto separa segmentos e o asterisco atua como curinga:
'flow.completed' // coincidencia exacta
'flow.*' // todos los eventos de flow
'node.*' // todos los eventos de node
'*' // todos los eventos Configuracao e limites
A configuracao do webhook vive em config e depende do tipo. Estes sao os principais limites:
- http.timeout_ms — entre 1 e 60000 ms, por padrao 30000.
- streaming.events_downstream — obrigatorio em webhooks de streaming; define quais eventos chegam ao cliente.
- audio —
max_session_seconds,max_bytes_per_sessionemax_concurrent_per_webhook(0 = sem teto). - name — entre 1 e 200 caracteres.
- URL de callback — URL valida, no maximo 2048 caracteres; o array events precisa de ao menos uma entrada.
export type WebhookType = 'http' | 'streaming'
export type WebhookStatus = 'active' | 'paused' | 'revoked'
export interface Webhook {
id: string // wh_*
org_id: string
flow_id: string // uuid
name: string // 1-200 chars
type: WebhookType // fija el transporte
token_preview: string // dgsk_wh_...LAST4
status: WebhookStatus
config: {
http?: {
timeout_ms: number // 1-60000, default 30000
outbound_callbacks: OutboundCallback[]
}
streaming?: {
audio_format?: string
sample_rate?: number
events_downstream: string[] // requerido, patrones con comodin
}
audio?: {
max_session_seconds?: number
max_bytes_per_session?: number
max_concurrent_per_webhook?: number // 0 = sin tope
}
}
last_used_at: string | null
} Em webhooks de streaming, events_downstream e obrigatorio e nao tem valor padrao. Se voce o omitir, a lista de permitidos fica vazia e o cliente nao recebera nenhum evento.
Auditoria e observabilidade
Cada entrega de saída é registrada para que você possa depurar e tentar novamente. O Daguito mantém um histórico de entregas que você pode inspecionar.
- Somente acrescimo — a tabela nunca e atualizada; cada nova tentativa insere uma linha nova.
- O que é registrado — a URL de callback, o tipo de evento, o número da tentativa, o status, o código HTTP de resposta, uma prévia da resposta, o erro (se houver) e os horários da próxima tentativa e da entrega.
- Retenção — os registros de entrega são mantidos por 30 dias.
- last_used_at — atualizado ao resolver o token com sucesso; se a atualizacao falhar, nao e fatal.
Cada entrega com falha fica registrada com seu status e o horário da próxima tentativa. A nova tentativa automática está no nosso roadmap.