Saltar para o conteúdo principal
Quando uma regra de Firewall retorna o veredito pending_approval, o gateway retém a chamada de ferramenta e notifica seu próprio sistema de aprovação fora de banda. Essa notificação é um HTTP POST assinado — o payload de webhook de firewall — e esta página documenta seu formato exato para que você possa verificar a assinatura, rotear o evento e postar sua decisão de volta. Este é o irmão assíncrono do fluxo de aprovação no console descrito na página de Firewall. O caminho de console (um revisor clica aprovar/rejeitar) não precisa de webhook algum. O webhook é para quando você quer que uma máquina — seu próprio bot de ticketing, ação de Slack ou runtime de agente — resolva o hold. Ambos os caminhos são first-writer-wins, então você pode rodá-los lado a lado.
O webhook é um sinal de roteamento de melhor esforço, não o canal HITL autoritativo. O próprio long-poll do agente em GET /api/v1/firewall/approvals/:id é o backstop — se uma notificação for perdida ou seu endpoint estiver brevemente fora do ar, a chamada retida ainda aparece no console e resolve normalmente. O webhook apenas deixa uma máquina reagir mais rápido do que um humano reagiria.

1. O payload de webhook de firewall em resumo

O OrcaRouter faz POST de um envelope JSON para a URL que você configura, assinado com um segredo compartilhado. Aqui está uma entrega completa — headers e corpo:
POST /your-approval-endpoint HTTP/1.1
Content-Type: application/json
X-Orca-Event: firewall.approval.pending
X-Orca-Signature: sha256=9f86d0818988...c2c9a3e1b4d7

{
  "event": "firewall.approval.pending",
  "workspace_id": 42,
  "occurred_at": "2026-06-09T12:00:00.123456789Z",
  "data": {
    "approval_id": "665f1a2b3c4d5e6f7a8b9c0d",
    "tool_name": "db.export",
    "request_id": "req_01J9X...",
    "conversation_id": "conv_8f2a...",
    "policy_id": 7,
    "rule_id": 31
  }
}
O envelope tem o mesmo formato que o OrcaRouter usa para todo evento assinado, de modo que um único receptor pode rotear muitos tipos de evento a partir de X-Orca-Event sem fazer parse do corpo.

2. Campos do envelope

Sempre firewall.approval.pending para um hold de aprovação. Espelhado no header X-Orca-Event para que você possa rotear antes de fazer parse do corpo.
O id inteiro do workspace cuja política reteve a chamada. Útil quando um endpoint recebe webhooks de vários workspaces.
Timestamp RFC 3339 / UTC (precisão de nanossegundos) de quando o gateway enfileirou a aprovação. Parseável por qualquer ferramenta de evento padrão.
O bloco que seu callback precisa para resolver o portão. Campos em §3.

3. O payload data

O bloco data carrega tudo que é necessário para rotear e resolver o hold — deliberadamente sem argumentos de ferramenta. O webhook é um sinal de roteamento; o contexto completo da chamada (ferramenta, args, a regra que disparou) vive na aba Approvals do console e no log de auditoria, onde tem controle de acesso.
CampoTipoSignificado
approval_idstringO id contra o qual você posta sua decisão.
tool_namestringA ferramenta retida, ex.: db.export.
request_idstringA requisição de relay que disparou o hold.
conversation_idstringO id da conversa / sessão do agente.
policy_idintA política de firewall que correspondeu.
rule_idintA regra que retornou pending_approval.
Precisa dos argumentos ou da cláusula correspondente para tomar a decisão? Leia-os a partir da aba Approvals do console (Developer+), ou faça seu agente consultar GET /api/v1/firewall/approvals/:id com seu token de gateway. O webhook intencionalmente nunca carrega args pela rede.

4. Verificando a assinatura

Toda entrega é assinada para que você possa rejeitar falsificações. O header de assinatura é:
X-Orca-Signature: sha256=<hex HMAC-SHA256(secret, raw_body)>
onde secret é o segredo de webhook de aprovação que você define no workspace e raw_body são os bytes exatos do corpo da requisição. Compute o HMAC sobre os bytes brutos — re-serializar o JSON parseado mudará o whitespace e quebrará a comparação. Verifique em tempo constante:
import hmac, hashlib

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header)
A assinatura do webhook outbound cobre apenas o corpo. O callback inbound que você posta de volta (§6) assina approval_id + uma quebra de linha + corpo — uma construção diferente, de propósito, para que uma assinatura capturada não possa ser reusada entre aprovações. Não reuse uma única rotina de assinatura para ambas as direções.

5. Configurando o webhook

A URL de destino e o segredo compartilhado são configurações de workspace — defina-os uma vez no console (ou via a API de settings; Developer+). Não há envolvimento de operador e nada a fazer deploy.
1

Defina a URL e o segredo

Nas configurações de Firewall, defina seu endpoint HTTPS como a URL de webhook de aprovação e um segredo compartilhado forte. A URL deve ser https:// — destinos em texto puro são rejeitados — e o segredo é somente-escrita (ele nunca é retornado na leitura; a resposta de settings só reporta se um está definido).
2

Crie uma regra pending_approval

Adicione uma regra de Firewall cujo veredito é pending_approval (ou use o preset HITL). Veja Regras de firewall.
3

Receba e verifique

Seu endpoint recebe o POST assinado na próxima chamada retida. Verifique a assinatura (§4), depois resolva via o callback ou exponha-a para um humano.
Uma chamada retida ainda funciona com nenhum webhook configurado — ela apenas aparece no console para um humano resolver. E se você definir uma URL mas nenhum segredo, o gateway pula o dispatch inteiramente, porque o endpoint de callback só aceita requisições assinadas: uma notificação que você não conseguisse autenticar de volta seria inútil.

6. O callback: resolvendo o hold

Para aprovar ou rejeitar por máquina, faça POST de volta para:
POST /api/v1/firewall/approvals/:id/callback
com o mesmo :id que você recebeu como approval_id, assinado com o mesmo segredo compartilhado. O corpo é uma decisão:
BODY='{"decision":"approved","reason":"ticket OPS-4821 approved by on-call"}'
SIG="sha256=$(printf '%s\n%s' "$APPROVAL_ID" "$BODY" \
  | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')"

curl https://api.orcarouter.ai/api/v1/firewall/approvals/$APPROVAL_ID/callback \
  -H "Content-Type: application/json" \
  -H "X-Orca-Signature: $SIG" \
  -d "$BODY"
Campo do corpoObrigatórioValores
decisionsimapproved ou rejected
reasonnãoNota em texto livre, registrada no log de auditoria.
Uma decisão approved deixa a próxima tentativa do agente passar uma vez — o agente reenvia a chamada original com um header de uso único X-OrcaRouter-Firewall-Approval. Uma decisão rejected mantém a chamada bloqueada.
A resolução é idempotente e first-writer-wins. Se um humano já resolveu o hold a partir do console — ou um callback duplicado chega — o endpoint retorna 200 com already_resolved: true e a decisão original prevalece. Seguro para retry.

7. Estados de aprovação

Uma chamada retida passa por estes estados; seu callback aciona a transição para fora de pending:
EstadoSignificado
pendingAguardando uma decisão (o estado no momento do webhook).
approvedResolvida — a chamada bloqueada pode prosseguir uma vez.
rejectedResolvida — a chamada bloqueada permanece bloqueada.
expiredO hold expirou sem uma decisão.

8. Referências relacionadas

Firewall — fluxo HITL

Como pending_approval retém uma chamada e o agente reenvia com o header de aprovação de uso único.

Códigos de erro

firewall_approval_pending e as outras respostas HTTP do firewall.

Glossário de vereditos

Cada veredito de firewall, incluindo pending_approval.

API de Firewall

A referência completa de rotas de console + gateway.
Para como os holds se encaixam no modelo de controle mais amplo, veja Modos de enforcement e Chamadas de ferramenta perigosas.