Una integración de Inference hooks es un servidor de seguridad de IA: un servicio HTTPS al que Anthropic llama. Para cada solicitud gobernada, tu servidor recibe un POST firmado que contiene la transcripción de la conversación y responde con un veredicto de permitir o denegar. Esta página documenta el protocolo para construir ese servidor: los esquemas de solicitud y veredicto, la verificación de firmas y el contrato operativo.
Para activar los Inference hooks y apuntarlos a tu endpoint, consulta Configurar Inference hooks. Para saber qué son los Inference hooks y cuándo usarlos, consulta la descripción general de Inference hooks.
La integración funcional más pequeña es un servidor que lee cada solicitud y la permite. Ejecuta uno de los siguientes servidores, exponlo en una URL pública https:// (por ejemplo, detrás de un proxy inverso que termine TLS o un túnel), y luego pide a tu administrador que lo configure como endpoint y pruebe la conexión: el resultado de Test connection (Probar conexión) informa el veredicto de permitir que tu servidor devolvió.
# Ejecuta con: python server.py
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class VerdictHandler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1" # keep the connection open between verdicts
def do_POST(self):
# Drena el cuerpo; las transcripciones pueden ocupar megabytes.
self.rfile.read(int(self.headers.get("Content-Length", 0)))
verdict = b'{"action": "allow"}'
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(verdict)))
self.end_headers()
self.wfile.write(verdict)
ThreadingHTTPServer(("", 8000), VerdictHandler).serve_forever()Anthropic envía un POST HTTPS a la URL que configura tu administrador. La URL configurada completa es el endpoint: no hay un sufijo de ruta fijo, así que elige cualquier ruta que se adapte a tu servidor.
Aloja tu servidor de seguridad de IA donde Anthropic pueda alcanzarlo: una URL https:// en el puerto 443, en un host enrutable públicamente (los rangos privados, de loopback y de NAT de grado operador se rechazan en el momento de la conexión), con un certificado que se valide contra el almacén de confianza de CA públicas, respondiendo sin redirecciones. La URL configurada debe ser el destino final. Configurar Inference hooks explica cómo tu administrador establece y prueba la URL.
Cada solicitud lleva estos encabezados fijos, junto con cualquier encabezado de solicitud personalizado que tu administrador haya configurado y, una vez que tu organización tenga un secreto de firma, los encabezados de firma webhook-* descritos en Verificar la firma:
| Encabezado | Valor |
|---|---|
Content-Type | application/json |
User-Agent | anthropic-dlp/1 |
Accept-Encoding | identity |
Actualmente hay un solo evento de hook: el "prompt frame" (marco de prompt), enviado una vez por cada solicitud de inferencia gobernada, antes de que comience la inferencia. Anthropic retiene la solicitud hasta que tu servidor de seguridad de IA responda o transcurra el tiempo de espera del veredicto.
El cuerpo de la solicitud es un objeto JSON con estos campos:
| Campo | Tipo | Descripción |
|---|---|---|
type | string | El evento de hook. Siempre es "prompt" actualmente; otros tipos de eventos se introducirán en el futuro, así que maneja un valor no reconocido de forma adecuada (consulta Compatibilidad futura). |
request_id | string | Identificador opaco por llamada de inferencia para correlación. Es igual al encabezado webhook-id. |
tenant_id | string o null | Identificador opaco de la organización a la que pertenece la solicitud. |
actor | object | El principal al que se atribuye la solicitud, discriminado por type ("user" es el único valor enviado actualmente): id (un identificador etiquetado, estable entre solicitudes para la misma cuenta) y email_address (cuando está disponible). Tanto id como email_address pueden ser null. |
source | object | La aplicación de origen: application (consulta Valores de source). |
messages | array | La transcripción de la conversación hasta el punto de inferencia. Consulta Bloques de contenido. |
session_id | string o null | Identificador opaco de conversación, cuando existe. No lo analices. Para Claude Code es un identificador de sesión de mejor esfuerzo, afirmado por el cliente. |
model | string o null | Identificador público del modelo para esta solicitud, cuando está disponible. |
metadata | object | Mapa de extensión reservado de claves string a valores string, enviado vacío actualmente. No requieras nada de él, y tolera su ausencia, su presencia y cualquier clave que aparezca. |
Un ejemplo de cuerpo de solicitud:
{
"type": "prompt",
"request_id": "req_abc123",
"tenant_id": "11111111-1111-1111-1111-111111111111",
"actor": {
"type": "user",
"id": "user_01AbCdEfGhIjKlMnOpQrStUv",
"email_address": "[email protected]"
},
"source": {
"application": "claude-ai"
},
"session_id": "22222222-2222-2222-2222-222222222222",
"model": "claude-sonnet-4-5",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Summarize the attached report."
},
{
"type": "attachment",
"file_name": "q2-report.pdf",
"media_type": "application/pdf",
"size_bytes": 48213,
"text": "Q2 revenue grew 14% quarter over quarter..."
}
]
}
],
"metadata": {}
}Cada entrada en messages tiene un role de user o assistant (los resultados de herramientas aparecen bajo el rol user, coincidiendo con el modelo de contenido de la API pública de Messages) y un array content de bloques discriminados por type:
type de bloque | Campos |
|---|---|
text | text: el contenido de texto. |
tool_use | id: el identificador al que hace referencia el resultado de herramienta correspondiente. tool_name: el nombre de la herramienta. input: los argumentos que el modelo pasó a la herramienta. |
tool_result | content: la salida de la herramienta como texto, con las partes unidas por saltos de línea; las partes binarias como imágenes se reemplazan por marcadores de posición, y los bytes sin procesar nunca se envían. is_error: si la llamada a la herramienta falló. tool_name: el nombre de la herramienta, para que una política pueda condicionarse a la identidad de la herramienta sin hacer referencia cruzada a un bloque anterior. tool_use_id: el id del bloque tool_use correspondiente. |
attachment | file_name: el nombre o ruta del archivo original. media_type: el tipo de medio del adjunto. size_bytes: el tamaño del archivo original. text: el contenido de texto del adjunto cuando está disponible, como texto extraído de un documento, una transcripción de audio o metadatos de un enlace. Los bytes sin procesar del adjunto nunca se envían. |
Un bloque cuyo type no reconozcas es una adición compatible con versiones futuras. El único campo que garantiza es type; tu política puede inspeccionar cualquier otro campo que esté presente, pero no debe rechazar la solicitud debido a un tipo no reconocido.
La transcripción es la conversación tal como la ve el usuario final, hasta el punto de inferencia: texto de la transcripción, llamadas a herramientas y sus resultados, texto extraído de adjuntos y turnos anteriores. Nunca incluye indicaciones del sistema, definiciones de herramientas, contexto interno de Anthropic, el razonamiento oculto de Claude ni bytes de archivo sin procesar.
Un turno en el que todos los bloques están excluidos se omite por completo, así que no asumas una alternancia estricta entre usuario y asistente.
Las transcripciones se envían sin truncar, por lo que una conversación larga con adjuntos grandes produce un cuerpo de solicitud grande, hasta un límite superior de 10 MB. Aumenta el límite de cuerpo de tu servidor para aceptar ese máximo. Varios valores predeterminados comunes son mucho más pequeños, incluido client_max_body_size de nginx en 1 MB y express.json() de Express en 100 kB, y un cuerpo rechazado cuenta como un fallo de webhook, por lo que bajo el manejo de fallos Allow the request (Permitir la solicitud), un prompt de gran tamaño llegaría al modelo sin ser inspeccionado.
source.application es una cadena abierta, no una enumeración cerrada. Los valores conocidos son claude-ai y claude-code; las pruebas de conexión usan config-test. Pueden aparecer nuevos valores, y tu servidor no debe rechazar una solicitud por uno que no reconozca.
Trata source.application como metadatos de enrutamiento informativos, no como un límite de confianza: no bases una decisión de política crítica para la seguridad únicamente en él.
Responde con HTTP 200 y un cuerpo de veredicto JSON para ambos resultados; el campo action discrimina. Para permitir la solicitud:
{
"action": "allow"
}Para denegarla:
{
"action": "deny",
"deny_reason": "This prompt appears to contain customer payment card data, which your organization's policy does not allow.",
"reference_id": "scan_01HXPT4R9V"
}| Campo | Restricciones | Semántica |
|---|---|---|
action | "allow" o "deny"; obligatorio | allow permite que la inferencia continúe; deny la rechaza. |
deny_reason | string o null; máximo 500 caracteres, los valores más largos se truncan | Se muestra al usuario final cuando action es deny; se ignora en allow. |
reference_id | string o null; máximo 50 caracteres de [A-Za-z0-9._:/-] | Tu propio identificador para esta evaluación. Se registra en la actividad de cumplimiento inference_hooks_request_denied de la denegación y nunca se muestra al usuario final. Mantenlo opaco: sin contenido de la solicitud y sin datos personales. |
Una denegación nunca se descarta por un problema de formato: un deny_reason demasiado grande se trunca, un reference_id mal formado se descarta silenciosamente, y el action se respeta de todos modos.
Lo contrario no aplica. Cualquier cosa que no sea HTTP 200 con un veredicto analizable es un fallo de webhook, y se aplica el manejo de fallos de tu organización en lugar de un veredicto. En particular:
action distinto de allow o deny se trata como un fallo de webhook.Anthropic lee como máximo 64 KiB del cuerpo de la respuesta, y el cuerpo debe estar sin comprimir. No se siguen las redirecciones y se ignoran las cookies. Los campos desconocidos en el cuerpo del veredicto se ignoran, por lo que puedes devolver un objeto más rico junto con los campos documentados aquí.
Las solicitudes se firman según la especificación Standard Webhooks, usando tres encabezados. Anthropic envía los nombres de encabezado en minúsculas, y los proxies pueden cambiar las mayúsculas y minúsculas, así que búscalos sin distinguir entre mayúsculas y minúsculas.
| Encabezado | Contenido |
|---|---|
webhook-id | Identificador único para esta entrega. Es igual al request_id del cuerpo. Úsalo como clave de idempotencia y como el primer componente de la carga útil firmada. |
webhook-timestamp | Tiempo Unix en segundos, como cadena decimal, de cuando se firmó la solicitud. Rechaza una marca de tiempo que difiera más de cinco minutos del reloj de tu servidor, en cualquier dirección. |
webhook-signature | Uno o más valores v1,<base64> separados por espacios, cada uno un HMAC-SHA256 sobre {webhook-id}.{webhook-timestamp}.{bytes del cuerpo sin procesar}. Acepta la solicitud si algún valor coincide con el tuyo, usando una comparación de tiempo constante. |
Dos detalles causan la mayoría de los errores de verificación:
whsec_, codificado con el alfabeto base64 estándar (+ y /), al igual que la firma en el encabezado. Un decodificador URL-safe deriva los bytes de clave incorrectos siempre que el secreto contenga + o /, lo cual ocurre la mayoría de las veces.Una vez que tu organización tiene un secreto de firma, cada solicitud que Anthropic envía está firmada, y habilitar Inference hooks requiere uno, así que rechaza cualquier solicitud que llegue sin firmar. Una excepción: una prueba de conexión enviada antes del primer guardado de tu organización llega sin firmar, porque el secreto de firma aún no existe. Acepta solicitudes sin firmar hasta que tu administrador confirme que el secreto existe, luego recházalas.
Rotar el secreto es un cambio inmediato, pero las solicitudes firmadas con el secreto anterior aún pueden llegar durante aproximadamente un minuto después, además de cualquier cosa que ya esté en tránsito. Haz que tu servidor de seguridad de IA acepte firmas de ambos secretos durante el cambio para que esas solicitudes rezagadas no sean rechazadas.
Los siguientes ejemplos son implementaciones de servidor, por lo que no hay pestaña de shell: un servidor de seguridad de IA es un servicio HTTPS de larga duración en lugar de una solicitud única. Cada ejemplo usa solo la biblioteca estándar del lenguaje; el proyecto Standard Webhooks también publica bibliotecas de verificación para la mayoría de los lenguajes.
import base64
import hashlib
import hmac
import time
TOLERANCE_SECONDS = 300
def verify(secret: str, headers: dict[str, str], body: bytes) -> bool:
"""Return True if the body was signed by Anthropic for this organization.
Anthropic sends header names in lowercase, but proxies are free to
re-case them, so normalize the lookup to lowercase.
"""
lowercased = {name.lower(): value for name, value in headers.items()}
try:
message_id = lowercased["webhook-id"]
timestamp = lowercased["webhook-timestamp"]
signatures = lowercased["webhook-signature"]
except KeyError:
return False # unsigned request: not from Anthropic
try:
signed_at = int(timestamp)
except ValueError:
return False
if abs(time.time() - signed_at) > TOLERANCE_SECONDS:
return False # replayed, or the clocks disagree
try:
key = base64.b64decode(secret.removeprefix("whsec_"), validate=True)
except ValueError:
return False # misconfigured secret: reject rather than crash
payload = f"{message_id}.{timestamp}.".encode() + body
expected = b"v1," + base64.b64encode(
hmac.new(key, payload, hashlib.sha256).digest()
)
# Compara bytes: compare_digest con str falla con entrada no ASCII.
return any(
hmac.compare_digest(expected, candidate.encode())
for candidate in signatures.split()
)Tu administrador establece un tiempo de espera de veredicto entre 1 y 10,000 ms (5,000 ms por defecto). El presupuesto cubre todo el intercambio: conexión, negociación TLS, solicitud y respuesta.
Anthropic reintenta exactamente una vez, después de un retraso de 100 ms, y solo cuando el intento de conexión falla. El reintento comparte el mismo presupuesto de tiempo de espera y lleva el mismo webhook-id y la misma firma. Una vez que tu servidor de seguridad de IA ha respondido, el intercambio nunca se reintenta.
Los tiempos de espera agotados, los estados que no son 200 (incluidas las redirecciones), los cuerpos de respuesta no analizables o demasiado grandes, y los endpoints inalcanzables son todos fallos de webhook. Un fallo de webhook nunca se convierte en una denegación; en su lugar, la configuración de manejo de fallos de tu organización decide si la solicitud afectada se bloquea o continúa sin inspección.
Los fallos de webhook sostenidos atribuibles a tu servidor de seguridad de IA activan un "circuit breaker" (interruptor de circuito) que detiene la aplicación de políticas: Anthropic deja de contactar a tu servidor, y el manejo de fallos se aplica a cada solicitud. La recuperación ocurre del lado del administrador: arregla el servidor, luego pide a tu administrador que vuelva a activar Enforce verdicts (Aplicar veredictos). Consulta Circuit breaker.
La aplicación de políticas agrega el tiempo de ida y vuelta de tu servidor de seguridad de IA a la latencia de cada solicitud gobernada en tu organización. Mantén el veredicto rápido y realiza pruebas de carga en tu servidor antes de implementarlo en una organización grande.
Las solicitudes a tu servidor de seguridad de IA se originan desde 160.79.106.0/24, parte de los rangos de IP salientes publicados de Anthropic. Agrega ese bloque a tu lista de permitidos, no los rangos entrantes de la misma página, que no lo cubren. Agregar a la lista de permitidos reduce la exposición de tu servidor, pero no es un sustituto de la verificación de firmas: el bloque transporta tráfico saliente de Anthropic más allá de los Inference hooks.
El protocolo crece sin romper servidores escritos correctamente. Tu servidor debe ignorar:
metadata.source.application.actor.type. actor es una unión discriminada por type, y "user" es el único tipo enviado actualmente; un tipo futuro solo garantiza que type esté presente.type no reconocido.Nunca rechaces una solicitud debido a un tipo de bloque o campo no reconocido; lee los campos que conoces y omite el resto.
Otros tipos de eventos de hook se introducirán en el futuro. Un nuevo tipo de evento es una adición que tu servidor no puede manejar omitiendo un campo: la solicitud aún necesita un veredicto. Cuando el type de nivel superior sea un valor que no reconozcas, devuelve un veredicto de permitir en lugar de un estado de error; una respuesta de error es un fallo de webhook, y los fallos sostenidos activan el circuit breaker.
Un servidor de seguridad de IA de producción toma algunas decisiones de diseño más allá del protocolo de comunicación.
Deduplica con webhook-id. El encabezado webhook-id es único por entrega y es igual al request_id del cuerpo, y un reintento por fallo de conexión lo reutiliza, por lo que funciona como clave de idempotencia. Si registras veredictos, usa este valor como clave de los registros.
Registra veredictos y correlaciona denegaciones. Almacena cada veredicto que devuelvas junto con su reference_id. Cada denegación se registra como una actividad de cumplimiento inference_hooks_request_denied que lleva el reference_id que tu servidor devolvió, por lo que puedes correlacionar las denegaciones en el Activity Feed con los registros correspondientes en tu propio sistema.
Archiva con un servidor que siempre permite. Para capturar transcripciones en tiempo real sin vigilarlas, devuelve {"action": "allow"} incondicionalmente y persiste el frame después de responder. Esta es una alternativa basada en push a consultar la Compliance API, y responder antes de persistir mantiene tu tiempo de ida y vuelta fuera de la ruta crítica del usuario.
Escribe deny_reason para el usuario final. El texto que devuelves es lo que el usuario ve cuando su solicitud es bloqueada, truncado a 500 caracteres. Dile qué cambiar, como qué tipo de contenido eliminar, en lugar de emitir un código de escáner que solo tu equipo puede interpretar.
Habilita los Inference hooks, conecta y prueba tu endpoint, y controla la aplicación de políticas, el manejo de fallos y la implementación.
Qué son los Inference hooks, cómo funciona el ciclo completo de veredicto y cuándo usarlos.
Was this page helpful?