Saltar al contenido principal
Cuando una regla del Firewall devuelve el veredicto pending_approval, el gateway retiene la llamada a herramienta y notifica a tu propio sistema de aprobación fuera de banda. Esa notificación es un POST HTTP firmado — el payload de webhook del firewall — y esta página documenta su forma exacta para que puedas verificar la firma, enrutar el evento y publicar tu decisión de vuelta. Este es el hermano asíncrono del flujo de aprobación dentro de la consola descrito en la página de Firewall. La ruta de consola (un revisor hace clic en aprobar/rechazar) no necesita ningún webhook. El webhook es para cuando quieres que una máquina — tu propio bot de tickets, acción de Slack o agent-runtime — resuelva la retención. Ambas rutas son first-writer-wins, así que puedes ejecutarlas en paralelo.
El webhook es una señal de enrutamiento de mejor esfuerzo, no el canal HITL autoritativo. El propio long-poll del agente sobre GET /api/v1/firewall/approvals/:id es el respaldo — si una notificación se pierde o tu endpoint está brevemente caído, la llamada retenida sigue apareciendo en la consola y se resuelve con normalidad. El webhook solo deja que una máquina reaccione más rápido de lo que lo haría un humano.

1. El payload de webhook del firewall de un vistazo

OrcaRouter hace POST de un sobre JSON a la URL que configures, firmado con un secreto compartido. Aquí hay una entrega completa — cabeceras y cuerpo:
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
  }
}
El sobre tiene la misma forma que OrcaRouter usa para cada evento firmado, así que un receptor puede enrutar muchos tipos de evento a partir de X-Orca-Event sin parsear el cuerpo.

2. Campos del sobre

Siempre firewall.approval.pending para una retención de aprobación. Reflejado en la cabecera X-Orca-Event para que puedas enrutar antes de parsear el cuerpo.
El id entero del espacio de trabajo cuya política retuvo la llamada. Útil cuando un endpoint recibe webhooks de varios espacios de trabajo.
Timestamp RFC 3339 / UTC (precisión de nanosegundos) de cuándo el gateway encoló la aprobación. Parseable por cualquier herramienta estándar de eventos.
El bloque que tu callback necesita para resolver la compuerta. Campos en §3.

3. El payload data

El bloque data lleva todo lo necesario para enrutar y resolver la retención — deliberadamente sin argumentos de herramienta. El webhook es una señal de enrutamiento; el contexto completo de la llamada (herramienta, args, la regla que se disparó) vive en la pestaña Approvals de la consola y en el log de auditoría, donde está controlado por acceso.
CampoTipoSignificado
approval_idstringEl id contra el que publicas tu decisión.
tool_namestringLa herramienta retenida, p. ej. db.export.
request_idstringLa solicitud de relay que disparó la retención.
conversation_idstringEl id de conversación / sesión del agente.
policy_idintLa política de firewall que coincidió.
rule_idintLa regla que devolvió pending_approval.
¿Necesitas los argumentos o la cláusula coincidente para tomar la decisión? Léelos desde la pestaña Approvals de la consola (Developer+), o haz que tu agente haga polling de GET /api/v1/firewall/approvals/:id con su token de gateway. El webhook intencionadamente nunca lleva args por el cable.

4. Verificar la firma

Cada entrega está firmada para que puedas rechazar falsificaciones. La cabecera de firma es:
X-Orca-Signature: sha256=<hex HMAC-SHA256(secret, raw_body)>
donde secret es el secreto de webhook de aprobación que estableces en el espacio de trabajo y raw_body son los bytes exactos del cuerpo de la solicitud. Calcula el HMAC sobre los bytes en bruto — reserializar el JSON parseado cambiará el espacio en blanco y romperá la comparación. Verifica en tiempo 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)
La firma del webhook saliente cubre solo el cuerpo. El callback entrante que publicas de vuelta (§6) firma approval_id + un salto de línea + cuerpo — una construcción diferente, a propósito, para que una firma capturada no pueda reproducirse entre aprobaciones. No reutilices una rutina de firma para ambas direcciones.

5. Configurar el webhook

La URL de destino y el secreto compartido son ajustes del espacio de trabajo — establécelos una vez en la consola (o vía la API de settings; Developer+). No hay implicación de operador ni nada que desplegar.
1

Establece la URL y el secreto

En los ajustes del Firewall, establece tu endpoint HTTPS como la URL de webhook de aprobación y un secreto compartido fuerte. La URL debe ser https:// — los destinos en texto plano se rechazan — y el secreto es de solo escritura (nunca se devuelve al leer; la respuesta de settings solo reporta si hay uno establecido).
2

Autora una regla pending_approval

Añade una regla del Firewall cuyo veredicto sea pending_approval (o usa el preset HITL). Ve Reglas del firewall.
3

Recibe y verifica

Tu endpoint recibe el POST firmado en la siguiente llamada retenida. Verifica la firma (§4), luego o bien resuelve vía el callback o exponla para un humano.
Una llamada retenida sigue funcionando sin webhook configurado — simplemente aparece en la consola para que un humano la resuelva. Y si estableces una URL pero ningún secreto, el gateway omite el despacho por completo, porque el endpoint de callback solo acepta solicitudes firmadas: una notificación que no pudieras autenticar de vuelta sería inútil.

6. El callback: resolver la retención

Para aprobar o rechazar por máquina, haz POST de vuelta a:
POST /api/v1/firewall/approvals/:id/callback
con el mismo :id que recibiste como approval_id, firmado con el mismo secreto compartido. El cuerpo es una decisión:
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 cuerpoRequeridoValores
decisionapproved o rejected
reasonnoNota de texto libre, registrada en el log de auditoría.
Una decisión approved deja pasar el siguiente intento del agente una vez — el agente reenvía la llamada original con una cabecera de un solo uso X-OrcaRouter-Firewall-Approval. Una decisión rejected mantiene la llamada bloqueada.
La resolución es idempotente y first-writer-wins. Si un humano ya resolvió la retención desde la consola — o llega un callback duplicado — el endpoint devuelve 200 con already_resolved: true y la decisión original prevalece. Seguro de reintentar.

7. Estados de aprobación

Una llamada retenida se mueve a través de estos estados; tu callback impulsa la transición fuera de pending:
EstadoSignificado
pendingA la espera de una decisión (el estado en el momento del webhook).
approvedResuelto — la llamada gobernada puede proceder una vez.
rejectedResuelto — la llamada gobernada permanece bloqueada.
expiredLa retención caducó sin una decisión.

8. Referencias relacionadas

Firewall — flujo HITL

Cómo pending_approval retiene una llamada y el agente reenvía con la cabecera de aprobación de un solo uso.

Códigos de error

firewall_approval_pending y las otras respuestas HTTP del firewall.

Glosario de veredictos

Cada veredicto del firewall, incluyendo pending_approval.

API de Firewall

La referencia completa de rutas de consola + gateway.
Para saber cómo encajan las retenciones en el modelo de control más amplio, ve Modos de aplicación y Llamadas a herramienta peligrosas.