Claude Platform Docs
AdministraçãoHooks de inferência

Desenvolva uma integração de Inference hooks

Construa o servidor de segurança de IA que recebe requisições assinadas de Inference hooks, as verifica e retorna vereditos de permitir ou negar.

Uma integração de Inference hooks é um "AI security server" (servidor de segurança de IA): um serviço HTTPS que a Anthropic chama. Para cada requisição governada, seu servidor recebe um POST assinado contendo a transcrição da conversa e responde com um veredito de permitir ou negar. Esta página documenta o protocolo para construir esse servidor: os esquemas de requisição e de veredito, a verificação de assinatura e o contrato operacional.

Para ativar os Inference hooks e apontá-los para o seu endpoint, consulte Configurar Inference hooks. Para saber o que são os Inference hooks e quando usá-los, consulte a visão geral dos Inference hooks.

Obtenha uma primeira ida e volta de veredito

A menor integração funcional é um servidor que lê cada requisição e a permite. Execute um dos servidores a seguir, exponha-o em uma URL https:// pública (por exemplo, atrás de um proxy reverso com terminação TLS em um host que você controla, não um serviço de túnel reverso; consulte Receber uma requisição) e, em seguida, peça ao seu administrador para defini-lo como o endpoint e testar a conexão: o resultado de Test connection (Testar conexão) informa o veredito de permissão que seu servidor retornou.

# Execute com: 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):
        # Esvazie o corpo; transcrições podem ter 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()

Receber uma requisição

A Anthropic envia um POST HTTPS para a URL que seu administrador configura. A URL configurada inteira é o endpoint: não há sufixo de caminho fixo, então escolha qualquer caminho que seja adequado ao seu servidor.

Hospede seu servidor de segurança de IA onde a Anthropic possa alcançá-lo: uma URL https:// na porta 443, em um host publicamente roteável (faixas privadas, de loopback e de NAT de nível de operadora são recusadas no momento da conexão), com um certificado que seja validado pelo repositório público de confiança de CAs, respondendo sem redirecionamentos. A URL configurada deve ser o destino final. Hosts de túnel reverso (ngrok e serviços de túnel semelhantes) não são suportados: a política de rede da Anthropic os bloqueia. Hospede seu servidor em um domínio que você controla. Configurar Inference hooks explica como seu administrador define e testa a URL.

Toda requisição carrega estes cabeçalhos fixos, junto com quaisquer cabeçalhos de requisição personalizados que seu administrador tenha configurado e, uma vez que sua organização tenha um segredo de assinatura, os cabeçalhos de assinatura webhook-* descritos em Verificar a assinatura:

CabeçalhoValor
Content-Typeapplication/json
User-Agentanthropic-dlp/1
Accept-Encodingidentity

Existe hoje um único evento de hook: o "prompt frame" (quadro de prompt), enviado uma vez por requisição de inferência governada, antes de a inferência começar. A Anthropic retém a requisição até que seu servidor de segurança de IA responda ou até que o tempo limite do veredito expire.

O prompt frame

O corpo da requisição é um objeto JSON com estes campos:

CampoTipoDescrição
typestringO evento de hook. Sempre "prompt" hoje; outros tipos de evento serão introduzidos no futuro, então trate um valor não reconhecido de forma adequada (consulte Compatibilidade futura).
request_idstringIdentificador opaco por chamada de inferência, para correlação. É igual ao cabeçalho webhook-id.
tenant_idstring ou nullIdentificador opaco da organização à qual a requisição pertence.
actorobjectO principal ao qual a requisição é atribuída, discriminado por type ("user" é o único valor enviado hoje): id (um identificador com tag, estável entre requisições da mesma conta) e email_address (quando disponível). Tanto id quanto email_address podem ser null.
sourceobjectA aplicação de origem: application (consulte Valores de source).
messagesarrayA transcrição da conversa até o ponto da inferência. Consulte Blocos de conteúdo.
session_idstring ou nullIdentificador opaco da conversa, quando existir. Não o analise. Para o Claude Code, é um identificador de sessão declarado pelo cliente, em regime de melhor esforço.
modelstring ou nullIdentificador público do modelo para esta requisição, quando disponível.
metadataobjectMapa de extensão reservado de chaves string para valores string, enviado vazio hoje. Não exija nada dele e tolere sua ausência, sua presença e quaisquer chaves que apareçam.

Um exemplo de corpo de requisição:

{
  "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": {}
}

Blocos de conteúdo

Cada entrada em messages tem um role de user ou assistant (resultados de ferramentas aparecem sob o papel user, correspondendo ao modelo de conteúdo público da Messages API) e um array content de blocos discriminados por type:

type do blocoCampos
texttext: o conteúdo de texto.
tool_useid: o identificador que o resultado de ferramenta correspondente referencia. tool_name: o nome da ferramenta. input: os argumentos que o modelo passou para a ferramenta.
tool_resultcontent: a saída da ferramenta como texto, com as partes unidas por quebras de linha; partes binárias, como imagens, são substituídas por marcadores de placeholder, e bytes brutos nunca são enviados. is_error: se a chamada da ferramenta falhou. tool_name: o nome da ferramenta, para que uma política possa condicionar pela identidade da ferramenta sem cruzar referências com um bloco anterior. tool_use_id: o id do bloco tool_use correspondente.
attachmentfile_name: o nome ou caminho original do arquivo. media_type: o tipo de mídia do anexo. size_bytes: o tamanho do arquivo original. text: o conteúdo de texto do anexo quando disponível, como texto extraído de documento, uma transcrição de áudio ou metadados de link. Bytes brutos de anexos nunca são enviados.

Com exceção de type, do text de um bloco text e do content e is_error de um bloco tool_result, qualquer um desses campos pode ser null quando o valor não é conhecido; por exemplo, uma imagem chega como um bloco attachment com file_name e text definidos como null.

Um bloco cujo type você não reconhece é uma adição compatível com versões futuras. O único campo que ele garante é type; sua política pode inspecionar quaisquer outros campos presentes, mas não deve rejeitar a requisição por causa de um tipo não reconhecido.

O que a transcrição contém

A transcrição é a conversa como o usuário final a vê, até o ponto da inferência: texto da transcrição, chamadas de ferramentas e seus resultados, texto extraído de anexos e turnos anteriores. Ela nunca inclui prompts do sistema, definições de ferramentas, contexto interno da Anthropic, o raciocínio oculto do Claude ou bytes brutos de arquivos.

Um turno em que todos os blocos são excluídos é omitido por completo, então não presuma alternância estrita entre usuário e assistente.

As transcrições são enviadas sem truncamento, então uma conversa longa com anexos grandes produz um corpo de solicitação grande. Na prática, a "context window" (janela de contexto) do modelo mantém os corpos abaixo de cerca de 10 MB, mas o protocolo permite até 64 MiB. Vários padrões comuns são muito menores, incluindo o client_max_body_size do nginx, de 1 MB, e o express.json() do Express, de 100 kB, e um corpo rejeitado conta como uma falha de webhook; portanto, com o tratamento de falhas Allow the request, um prompt grande demais chegaria ao modelo sem inspeção.

Valores de source

source.application é uma string aberta, não um enum fechado. Valores comuns são claude-ai, claude-code e cowork; testes de conexão e verificações de recuperação automáticas do circuit breaker usam config-test. Novos valores podem aparecer, e seu servidor não deve rejeitar uma solicitação por causa de um valor que não reconhece.

Trate source.application como metadado de roteamento consultivo, não como uma fronteira de confiança: não baseie uma decisão de política crítica para a segurança apenas nele.

Retornar um veredito

Responda com HTTP 200 e um corpo JSON de veredito para ambos os resultados; o campo action discrimina. Para permitir a requisição:

{
  "action": "allow"
}

Para negá-la:

{
  "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"
}
CampoRestriçõesSemântica
action"allow" ou "deny"; obrigatórioallow deixa a inferência prosseguir; deny a rejeita.
deny_reasonstring ou null; no máximo 500 caracteres, valores mais longos são truncadosExibido ao usuário final quando action é deny; ignorado em allow.
reference_idstring ou null; no máximo 50 caracteres de [A-Za-z0-9._:/-]Seu próprio identificador para esta avaliação. Ele é registrado na atividade de conformidade inference_hooks_request_denied da negação e nunca é exibido ao usuário final. Mantenha-o opaco: sem conteúdo da requisição e sem dados pessoais.

Uma negação nunca é descartada por um problema de formatação: um deny_reason grande demais é truncado, um reference_id malformado é descartado silenciosamente, e a action ainda é respeitada.

O inverso não vale. Qualquer coisa diferente de HTTP 200 com um veredito analisável é uma falha de webhook, e o tratamento de falhas da sua organização se aplica em vez de um veredito. Em particular:

  • Não sinalize uma negação com um status de erro. Uma resposta diferente de 200 é uma falha, não uma negação.
  • Qualquer valor de action diferente de allow ou deny é tratado como uma falha de webhook.

A Anthropic lê no máximo 64 KiB do corpo da resposta, e o corpo deve estar sem compressão. Redirecionamentos não são seguidos, e cookies são ignorados. Campos desconhecidos no corpo do veredito são ignorados, então você pode retornar um objeto mais rico junto com os campos documentados aqui.

Verificar a assinatura

As requisições são assinadas conforme a especificação Standard Webhooks, usando três cabeçalhos. A Anthropic envia os nomes dos cabeçalhos em minúsculas, e proxies podem alterar a capitalização, então procure-os sem diferenciar maiúsculas de minúsculas.

CabeçalhoConteúdo
webhook-idIdentificador único desta entrega. É igual ao request_id do corpo. Use-o como chave de idempotência e como o primeiro componente do payload assinado.
webhook-timestampHorário Unix em segundos, como string decimal, de quando a requisição foi assinada. Rejeite um timestamp com mais de cinco minutos de diferença do relógio do seu servidor, em qualquer direção.
webhook-signatureUm ou mais valores v1,<base64> separados por espaço, cada um sendo um HMAC-SHA256 sobre {webhook-id}.{webhook-timestamp}.{raw body bytes}. Aceite a requisição se qualquer valor corresponder ao seu, usando uma comparação de tempo constante.

Dois detalhes causam a maioria dos bugs de verificação:

  • Verifique os bytes brutos. Calcule o HMAC sobre o corpo exatamente como recebido, antes de qualquer análise JSON ou recodificação.
  • Decodifique o segredo com um decodificador base64 padrão. O segredo de assinatura é o valor após o prefixo whsec_, codificado com o alfabeto base64 padrão (+ e /), assim como a assinatura no cabeçalho. Um decodificador URL-safe deriva os bytes de chave errados sempre que o segredo contém + ou /, o que acontece na maioria das vezes.

Assim que sua organização tiver um segredo de assinatura, toda solicitação que a Anthropic envia é assinada, incluindo o teste de conexão, porque o fluxo de configuração gera o segredo antes do primeiro teste. Ativar os Inference hooks exige um segredo, então rejeite qualquer solicitação que chegue sem assinatura. Uma exceção: uma organização que ativou os Inference hooks antes de o segredo ser obrigatório continua enviando solicitações não assinadas até que seu administrador gere um. Aceite solicitações não assinadas apenas até que seu administrador confirme que o segredo existe e, depois disso, rejeite-as.

Rotacionar o segredo é uma troca imediata, mas requisições assinadas com o segredo anterior ainda podem chegar por cerca de um minuto depois, além de qualquer coisa já em trânsito. Faça seu servidor de segurança de IA aceitar assinaturas de ambos os segredos durante a transição, para que essas requisições atrasadas não sejam rejeitadas.

Os exemplos a seguir são implementações de servidor, então não há aba de shell: um servidor de segurança de IA é um serviço HTTPS de longa duração, e não uma requisição única. Cada exemplo usa apenas a biblioteca padrão da linguagem; o projeto Standard Webhooks também publica bibliotecas de verificação para a maioria das linguagens.

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

    # Compare bytes: compare_digest em str lança exceção com entrada não ASCII.
    return any(
        hmac.compare_digest(expected, candidate.encode())
        for candidate in signatures.split()
    )

Semântica operacional

Tempo limite e nova tentativa

Seu administrador define um tempo limite de veredito entre 1 e 10.000 ms (5.000 ms por padrão). O orçamento cobre toda a troca: conexão, handshake TLS, requisição e resposta.

A Anthropic tenta novamente exatamente uma vez, após um atraso de 100 ms, e somente quando a tentativa de conexão falha. A nova tentativa compartilha o mesmo orçamento de tempo limite e carrega o mesmo webhook-id e a mesma assinatura. Uma vez que seu servidor de segurança de IA tenha respondido, a troca nunca é repetida.

Falhas de webhook

Tempos limite esgotados, status diferentes de 200 (incluindo redirecionamentos), corpos de resposta não analisáveis ou grandes demais e endpoints inalcançáveis são todos falhas de webhook. Uma falha de webhook nunca se torna uma negação; em vez disso, a configuração de tratamento de falhas da sua organização decide se a requisição afetada é bloqueada ou prossegue sem inspeção.

Circuit breaker

Falhas de webhook sustentadas atribuíveis ao seu servidor de segurança de IA acionam um "circuit breaker" (disjuntor) que interrompe a aplicação da política: a Anthropic para de contatar seu servidor, e o tratamento de falhas se aplica a todas as requisições.

A partir de 10 minutos após o acionamento, a Anthropic verifica se seu servidor se recuperou: no máximo cerca de uma vez por minuto, ela envia ao seu servidor a mesma solicitação de teste sintética que Test connection envia (source.application é config-test), assinada como qualquer outra solicitação e sem conteúdo de usuário. Responda a ela normalmente. Um veredicto válido, de permissão ou negação, redefine o circuit breaker e a fiscalização é retomada; uma falha de webhook mantém o circuit breaker acionado, e as verificações continuam. Um administrador também pode redefinir o circuit breaker a qualquer momento, e alterações de configuração feitas pelo administrador interrompem as verificações automáticas; consulte Circuit breaker.

Cada acionamento é registrado como uma atividade inference_hooks_circuit_breaker_tripped no Feed de atividades, uma atividade por acionamento. Enquanto o circuit breaker está acionado, nenhuma atividade de Inference hooks por requisição é registrada, então a atividade de acionamento é o único registro do feed sobre a janela em que ele esteve acionado.

Latência

A aplicação da política adiciona a ida e volta do seu servidor de segurança de IA à "latency" (latência) de toda requisição governada na sua organização. Mantenha o veredito rápido e faça testes de carga no seu servidor antes de implantá-lo em uma organização grande.

Endereços IP de origem

As requisições ao seu servidor de segurança de IA se originam de 160.79.106.0/24, parte das faixas de IP de saída publicadas pela Anthropic. Coloque esse bloco na lista de permissões, não as faixas de entrada da mesma página, que não o cobrem. A lista de permissões reduz a exposição do seu servidor, mas não substitui a verificação de assinatura: o bloco transporta tráfego de saída da Anthropic além dos Inference hooks.

Compatibilidade futura

O protocolo cresce sem quebrar servidores escritos corretamente. Seu servidor deve ignorar:

  • Campos de nível superior desconhecidos no prompt frame.
  • Chaves desconhecidas em metadata.
  • Novos valores de source.application.
  • Novos valores de actor.type. actor é uma união discriminada por type, e "user" é o único tipo enviado hoje; um tipo futuro garante apenas que type esteja presente.
  • Blocos de conteúdo com um type não reconhecido.

Nunca rejeite uma requisição por causa de um tipo de bloco ou campo não reconhecido; leia os campos que você conhece e ignore o restante.

Outros tipos de evento de hook serão introduzidos no futuro. Um novo tipo de evento é uma adição que seu servidor não consegue tratar ignorando um campo: a requisição ainda precisa de um veredito. Quando o type de nível superior for um valor que você não reconhece, retorne um veredito de permissão em vez de um status de erro; uma resposta de erro é uma falha de webhook, e falhas sustentadas acionam o circuit breaker.

Projete sua integração

Um servidor de segurança de IA de produção faz algumas escolhas de design além do protocolo de comunicação.

Deduplique por webhook-id. O cabeçalho webhook-id é único por entrega e igual ao request_id do corpo, e uma nova tentativa por falha de conexão o reutiliza, então ele funciona como chave de idempotência. Se você registra vereditos, use-o como chave dos registros.

Registre vereditos e cruze negações. Armazene cada veredito que você retorna junto com seu reference_id. Toda negação é registrada como uma atividade de conformidade inference_hooks_request_denied contendo o reference_id que seu servidor retornou, então você pode cruzar as negações no Feed de atividades com os registros correspondentes no seu próprio sistema.

Arquive com um servidor que sempre permite. Para capturar transcrições em tempo real sem policiá-las, retorne {"action": "allow"} incondicionalmente e persista o frame após responder. Esta é uma alternativa baseada em push à consulta periódica da Compliance API, e responder antes de persistir mantém sua ida e volta fora do caminho crítico do usuário.

Escreva o deny_reason para o usuário final. O texto que você retorna é o que o usuário vê quando sua requisição é bloqueada, truncado em 500 caracteres. Diga a ele o que mudar, como qual tipo de conteúdo remover, em vez de emitir um código de scanner que só sua equipe consegue interpretar.

Próximos passos

Habilite os Inference hooks, conecte e teste seu endpoint e controle a aplicação da política, o tratamento de falhas e a implantação.

O que são os Inference hooks, como funciona a ida e volta do veredito e quando usá-los.

Was this page helpful?