Vai al contenuto principale
Quando una regola del Firewall restituisce il verdetto pending_approval, il gateway mette in attesa la chiamata a tool e notifica il tuo sistema di approvazione fuori banda. Quella notifica è un POST HTTP firmato — il payload di webhook del firewall — e questa pagina documenta la sua forma esatta così puoi verificare la firma, instradare l’evento e inviare la tua decisione. Questo è il fratello asincrono del flusso di approvazione in-console descritto nella pagina Firewall. Il percorso di console (un revisore clicca approva/rifiuta) non richiede alcun webhook. Il webhook serve a quando vuoi che una macchina — il tuo bot di ticketing, un’azione Slack o l’agent runtime — risolva l’hold. Entrambi i percorsi sono first-writer-wins, quindi puoi eseguirli fianco a fianco.
Il webhook è un segnale di routing best-effort, non il canale HITL autoritativo. Il long-poll dell’agent stesso su GET /api/v1/firewall/approvals/:id è la rete di sicurezza — se una notifica viene persa o il tuo endpoint è brevemente down, la chiamata held emerge comunque nella console e si risolve normalmente. Il webhook permette solo a una macchina di reagire più velocemente di quanto farebbe un umano.

1. Il payload di webhook del firewall in sintesi

OrcaRouter invia in POST un envelope JSON all’URL che configuri, firmato con un segreto condiviso. Ecco una consegna completa — header 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
  }
}
L’envelope è la stessa forma che OrcaRouter usa per ogni evento firmato, quindi un unico ricevitore può instradare molti tipi di evento basandosi su X-Orca-Event senza fare parsing del corpo.

2. Campi dell’envelope

Sempre firewall.approval.pending per un hold di approvazione. Replicato nell’header X-Orca-Event così puoi instradare prima di fare parsing del corpo.
L’id intero del workspace la cui policy ha messo la chiamata in attesa. Utile quando un endpoint riceve webhook da più workspace.
Timestamp RFC 3339 / UTC (precisione al nanosecondo) di quando il gateway ha accodato l’approvazione. Parsabile da qualsiasi tooling di eventi standard.
Il blocco di cui il tuo callback ha bisogno per risolvere il gate. Campi in §3.

3. Il payload data

Il blocco data porta tutto ciò che serve per instradare e risolvere l’hold — deliberatamente nessun argomento del tool. Il webhook è un segnale di routing; il contesto completo della chiamata (tool, args, la regola che è scattata) vive nella tab Approvals della console e nell’audit log, dove è soggetto a controllo d’accesso.
CampoTipoSignificato
approval_idstringL’id contro cui invii la tua decisione.
tool_namestringIl tool in attesa, es. db.export.
request_idstringLa richiesta di relay che ha innescato l’hold.
conversation_idstringL’id di conversazione / sessione dell’agent.
policy_idintLa policy del firewall che ha corrisposto.
rule_idintLa regola che ha restituito pending_approval.
Ti servono gli argomenti o la clausola corrispondente per prendere la decisione? Leggili dalla tab Approvals della console (Developer+), o fai fare al tuo agent il polling su GET /api/v1/firewall/approvals/:id con il suo token del gateway. Il webhook intenzionalmente non porta mai gli args sul filo.

4. Verificare la firma

Ogni consegna è firmata così puoi rifiutare le contraffazioni. L’header della firma è:
X-Orca-Signature: sha256=<hex HMAC-SHA256(secret, raw_body)>
dove secret è il segreto del webhook di approvazione che imposti sul workspace e raw_body sono i byte esatti del corpo della richiesta. Calcola l’HMAC sui byte grezzi — ri-serializzare il JSON parsato cambierà gli spazi bianchi e romperà il confronto. Verifica in tempo costante:
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)
La firma del webhook in uscita copre solo il corpo. Il callback in entrata che invii indietro (§6) firma approval_id + un newline + corpo — una costruzione diversa, di proposito, così una firma catturata non può essere riprodotta tra approvazioni diverse. Non riutilizzare un’unica routine di firma per entrambe le direzioni.

5. Configurare il webhook

L’URL di destinazione e il segreto condiviso sono impostazioni del workspace — impostali una volta nella console (o tramite l’API delle impostazioni; Developer+). Non c’è coinvolgimento di un operatore e nulla da fare deploy.
1

Imposta l'URL e il segreto

Nelle impostazioni del Firewall, imposta il tuo endpoint HTTPS come URL del webhook di approvazione e un segreto condiviso robusto. L’URL deve essere https:// — le destinazioni in plaintext sono rifiutate — e il segreto è write-only (non viene mai restituito in lettura; la risposta delle impostazioni riporta solo se uno è impostato).
2

Crea una regola pending_approval

Aggiungi una regola del Firewall il cui verdetto è pending_approval (o usa il preset HITL). Vedi Regole del firewall.
3

Ricevi e verifica

Il tuo endpoint riceve il POST firmato alla successiva chiamata held. Verifica la firma (§4), poi o risolvi tramite il callback o falla emergere per un umano.
Una chiamata held funziona comunque con nessun webhook configurato — si limita a comparire nella console perché un umano la risolva. E se imposti un URL ma nessun segreto, il gateway salta del tutto il dispatch, perché l’endpoint di callback accetta solo richieste firmate: una notifica che non potresti autenticare al ritorno sarebbe inutile.

6. Il callback: risolvere l’hold

Per approvare o rifiutare via macchina, invia in POST a:
POST /api/v1/firewall/approvals/:id/callback
con lo stesso :id che hai ricevuto come approval_id, firmato con lo stesso segreto condiviso. Il corpo è una decisione:
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 del corpoObbligatorioValori
decisionapproved o rejected
reasonnoNota in testo libero, registrata nell’audit log.
Una decisione approved lascia passare il tentativo successivo dell’agent una volta — l’agent ri-invia la chiamata originale con un header monouso X-OrcaRouter-Firewall-Approval. Una decisione rejected mantiene la chiamata bloccata.
La risoluzione è idempotente e first-writer-wins. Se un umano ha già risolto l’hold dalla console — o arriva un callback duplicato — l’endpoint restituisce 200 con already_resolved: true e la decisione originale resta valida. Sicuro da ritentare.

7. Stati di approvazione

Una chiamata held attraversa questi stati; il tuo callback guida la transizione fuori da pending:
StatoSignificato
pendingIn attesa di una decisione (lo stato al momento del webhook).
approvedRisolto — la chiamata gated può procedere una volta.
rejectedRisolto — la chiamata gated resta bloccata.
expiredL’hold è scaduto senza una decisione.

8. Riferimenti correlati

Firewall — flusso HITL

Come pending_approval mette in attesa una chiamata e l’agent ri-invia con l’header di approvazione monouso.

Codici di errore

firewall_approval_pending e le altre risposte HTTP del firewall.

Glossario dei verdetti

Ogni verdetto del firewall, incluso pending_approval.

API Firewall

Il riferimento completo delle rotte di console + gateway.
Per come gli hold si inseriscono nel modello di controllo più ampio, vedi Modalità di applicazione e Chiamate a tool pericolose.