Claude Platform Docs
AdministraciónHooks de inferencia

Desarrolla una integración de Inference hooks

Construye el servidor de seguridad de IA que recibe solicitudes firmadas de Inference hooks, las verifica y devuelve veredictos de permitir o denegar.

Una integración de Inference hooks es un "AI security server" (servidor de seguridad de IA): un servicio HTTPS al que Anthropic llama. Para cada solicitud gobernada, tu servidor recibe un POST firmado que lleva 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 aprender qué son los Inference hooks y cuándo usarlos, consulta la descripción general de Inference hooks.

Obtén un primer viaje de ida y vuelta de veredicto

La integración funcional más pequeña es un servidor que lee cada solicitud y la permite. Ejecuta uno de los siguientes servidores, expónlo en una URL pública https:// (por ejemplo, detrás de un proxy inverso con terminación TLS en un host que controles, no un servicio de túnel inverso; consulta Recibir una solicitud), y luego pide a tu administrador que lo establezca como el endpoint y pruebe la conexión: el resultado de Test connection informa el veredicto de permitir que devolvió tu servidor.

# 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()

Recibir una solicitud

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 a nivel de operador se rechazan al 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. Los hosts de túnel inverso (ngrok y servicios de túnel similares) no son compatibles: la política de red de Anthropic los bloquea. Aloja tu servidor en un dominio que controles. Configurar Inference hooks cubre 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:

EncabezadoValor
Content-Typeapplication/json
User-Agentanthropic-dlp/1
Accept-Encodingidentity

Hoy existe 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 marco de prompt

El cuerpo de la solicitud es un objeto JSON con estos campos:

CampoTipoDescripción
typestringEl evento de hook. Siempre "prompt" hoy; en el futuro se introducirán otros tipos de eventos, así que maneja un valor no reconocido de forma elegante (consulta Compatibilidad hacia adelante).
request_idstringIdentificador opaco por llamada de inferencia para correlación. Es igual al encabezado webhook-id.
tenant_idstring o nullIdentificador opaco de la organización a la que pertenece la solicitud.
actorobjectEl principal al que se atribuye la solicitud, discriminado por type ("user" es el único valor enviado hoy): 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.
sourceobjectLa aplicación de origen: application (consulta Valores de source).
messagesarrayLa transcripción de la conversación hasta el punto de inferencia. Consulta Bloques de contenido.
session_idstring o nullIdentificador opaco de conversación, cuando existe uno. No lo analices. Para Claude Code es un identificador de sesión de mejor esfuerzo, afirmado por el cliente.
modelstring o nullIdentificador público del modelo para esta solicitud, cuando está disponible.
metadataobjectMapa de extensión reservado de claves string a valores string, enviado vacío hoy. No exijas 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": "alice@example.com"
  },
  "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": {}
}

Bloques de contenido

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 público de la Messages API) y un array content de bloques discriminados por type:

type del bloqueCampos
texttext: el contenido de texto.
tool_useid: 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_resultcontent: 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 nunca se envían bytes sin procesar. 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.
attachmentfile_name: el nombre o ruta original del archivo. 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. Nunca se envían los bytes sin procesar del adjunto.

Aparte de type, el text de un bloque text, y el content y el is_error de un bloque tool_result, cualquiera de estos campos puede ser null cuando el valor no se conoce; por ejemplo, una imagen llega como un bloque attachment con file_name y text establecidos en null.

Un bloque cuyo type no reconozcas es una adición compatible hacia adelante. El único campo que garantiza es type; tu política puede inspeccionar cualquier otro campo presente, pero no debe rechazar la solicitud debido a un tipo no reconocido.

Qué contiene la transcripción

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 sin procesar de archivos.

Un turno cuyos bloques están todos 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. En la práctica, la "context window" (ventana de contexto) del modelo mantiene los cuerpos por debajo de unos 10 MB, pero el protocolo permite hasta 64 MiB. Varios valores predeterminados comunes son mucho más pequeños, incluidos client_max_body_size de nginx con 1 MB y express.json() de Express con 100 kB, y un cuerpo rechazado cuenta como un fallo del webhook, por lo que con el manejo de fallos Allow the request, un prompt demasiado grande llegaría al modelo sin inspeccionar.

Valores de source

source.application es un string abierto, no un enum cerrado. Los valores comunes son claude-ai, claude-code y cowork; las pruebas de conexión y las comprobaciones de recuperación automáticas del circuit breaker usan config-test. Pueden aparecer valores nuevos, y tu servidor no debe rechazar una solicitud por uno que no reconozca.

Trata source.application como metadatos de enrutamiento orientativos, no como un límite de confianza: no bases una decisión de política crítica para la seguridad únicamente en él.

Devolver un veredicto

Responde con HTTP 200 y un cuerpo JSON de veredicto 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"
}
CampoRestriccionesSemántica
action"allow" o "deny"; obligatorioallow permite que la inferencia continúe; deny la rechaza.
deny_reasonstring o null; como máximo 500 caracteres, los valores más largos se truncanSe muestra al usuario final cuando action es deny; se ignora en allow.
reference_idstring o null; como 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 ni 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 sigue respetando.

Lo contrario no se cumple. 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:

  • No señales una denegación con un estado de error. Una respuesta distinta de 200 es un fallo, no una denegación.
  • Cualquier valor de 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í.

Verificar la firma

Las solicitudes se firman según la especificación Standard Webhooks, usando tres encabezados. Anthropic envía los nombres de los encabezados en minúsculas, y los proxies son libres de cambiar su capitalización, así que búscalos sin distinguir mayúsculas de minúsculas.

EncabezadoContenido
webhook-idIdentificador único para esta entrega. Es igual al request_id del cuerpo. Úsalo como clave de idempotencia y como el primer componente de la carga firmada.
webhook-timestampTiempo Unix en segundos, como string decimal, del momento en que 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-signatureUno o más valores v1,<base64> separados por espacios, cada uno un HMAC-SHA256 sobre {webhook-id}.{webhook-timestamp}.{raw body bytes}. 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:

  • Verifica los bytes sin procesar. Calcula el HMAC sobre el cuerpo exactamente como se recibió, antes de cualquier análisis JSON o recodificación.
  • Decodifica el secreto con un decodificador base64 estándar. El secreto de firma es el valor después del prefijo whsec_, codificado con el alfabeto base64 estándar (+ y /), al igual que la firma en el encabezado. Un decodificador seguro para URL 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, todas las solicitudes que envía Anthropic están firmadas, incluida la prueba de conexión, porque el flujo de configuración genera el secreto antes de la primera prueba. Habilitar Inference hooks requiere un secreto, así que rechaza cualquier solicitud que llegue sin firmar. Una excepción: una organización que habilitó Inference hooks antes de que se exigiera el secreto sigue enviando solicitudes sin firmar hasta que su administrador genere uno. Acepta solicitudes sin firmar solo hasta que tu administrador confirme que el secreto existe y, después, 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, más 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 lanza error ante entrada no ASCII.
    return any(
        hmac.compare_digest(expected, candidate.encode())
        for candidate in signatures.split()
    )

Semántica operativa

Tiempo de espera y reintento

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, handshake 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.

Fallos de webhook

Los tiempos de espera agotados, los estados distintos de 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.

Circuit breaker

Los fallos de webhook sostenidos atribuibles a tu servidor de seguridad de IA activan un "circuit breaker" (disyuntor) 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.

A partir de 10 minutos después de la activación, Anthropic comprueba si tu servidor se ha recuperado: como máximo aproximadamente una vez por minuto, envía a tu servidor la misma solicitud de prueba sintética que envía Test connection (source.application es config-test), firmada como cualquier otra solicitud y sin contenido de usuario. Respóndela normalmente. Un veredicto válido, de permitir o denegar, restablece el circuit breaker y se reanuda la aplicación de políticas; un fallo del webhook deja el circuit breaker activado y las comprobaciones continúan. Un administrador también puede restablecer el circuit breaker en cualquier momento, y los cambios de configuración del administrador detienen las comprobaciones automáticas; consulta Circuit breaker.

Cada activación se registra como una actividad inference_hooks_circuit_breaker_tripped en el Activity Feed, una actividad por activación. Mientras el disyuntor está activado, no se registran actividades de Inference hooks por solicitud, por lo que la actividad de activación es el único registro en el feed de la ventana de activación.

Latencia

La aplicación de políticas agrega el viaje de ida y vuelta de tu servidor de seguridad de IA a la "latency" (latencia) de cada solicitud gobernada en tu organización. Mantén el veredicto rápido y realiza pruebas de carga de tu servidor antes de desplegarlo en una organización grande.

Direcciones IP de origen

Las solicitudes a tu servidor de seguridad de IA se originan desde 160.79.106.0/24, parte de los rangos de IP de salida publicados por Anthropic. Agrega ese bloque a tu lista de permitidos, no los rangos de entrada de la misma página, que no lo cubren. La lista de permitidos reduce la exposición de tu servidor, pero no sustituye la verificación de firmas: el bloque transporta tráfico de salida de Anthropic más allá de los Inference hooks.

Compatibilidad hacia adelante

El protocolo crece sin romper los servidores escritos correctamente. Tu servidor debe ignorar:

  • Campos de nivel superior desconocidos en el marco de prompt.
  • Claves desconocidas en metadata.
  • Nuevos valores de source.application.
  • Nuevos valores de actor.type. actor es una unión discriminada por type, y "user" es el único tipo enviado hoy; un tipo futuro garantiza solo que type esté presente.
  • Bloques de contenido con un 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.

En el futuro se introducirán otros tipos de eventos de hook. 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.

Diseña tu integración

Un servidor de seguridad de IA de producción toma algunas decisiones de diseño más allá del protocolo de comunicación.

Deduplica por 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 ese valor como clave de los registros.

Registra veredictos y une 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 devolvió tu servidor, por lo que puedes unir 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 marco después de responder. Esta es una alternativa basada en push al sondeo de la Compliance API, y responder antes de persistir mantiene tu viaje 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 pueda interpretar.

Próximos pasos

Habilita Inference hooks, conecta y prueba tu endpoint, y controla la aplicación de políticas, el manejo de fallos y el despliegue.

Qué son los Inference hooks, cómo funciona el viaje de ida y vuelta del veredicto y cuándo usarlos.

Was this page helpful?