Uma integração de Inference hooks é um 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 permissão ou negação. Esta página documenta o protocolo para construir esse servidor: os esquemas de requisição e veredito, a verificação de assinatura e o contrato operacional.
Para ativar os Inference hooks e apontá-los para seu endpoint, consulte Configurar Inference hooks. Para saber o que são Inference hooks e quando usá-los, consulte a visão geral de Inference hooks.
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 pública https:// (por exemplo, atrás de um proxy reverso com terminação TLS ou de um túnel) e, em seguida, peça ao seu administrador para defini-lo como o endpoint e testar a conexão: o resultado de Test connection 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):
# Drene 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()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 (intervalos privados, de loopback e de NAT de nível de operadora são recusados no momento da conexão), com um certificado que valide contra o repositório público de confiança de CA, respondendo sem redirecionamentos. A URL configurada deve ser o destino final. Configurar Inference hooks aborda 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 configurou e, uma vez que sua organização tenha um segredo de assinatura, os cabeçalhos de assinatura webhook-* descritos em Verificar a assinatura:
| Cabeçalho | Valor |
|---|---|
Content-Type | application/json |
User-Agent | anthropic-dlp/1 |
Accept-Encoding | identity |
Existe um evento de hook atualmente: o prompt frame, enviado uma vez por requisição de inferência governada, antes do início da inferência. A Anthropic retém a requisição até que seu servidor de segurança de IA responda ou o tempo limite do veredito expire.
O corpo da requisição é um objeto JSON com estes campos:
| Campo | Tipo | Descrição |
|---|---|---|
type | string | O evento de hook. Sempre "prompt" atualmente; outros tipos de evento serão introduzidos no futuro, então trate um valor não reconhecido de forma adequada (consulte Compatibilidade futura). |
request_id | string | Identificador opaco por chamada de inferência para correlação. Igual ao cabeçalho webhook-id. |
tenant_id | string ou null | Identificador opaco da organização à qual a requisição pertence. |
actor | object | O principal ao qual a requisição é atribuída, discriminado por type ("user" é o único valor enviado atualmente): id (um identificador com tag, estável entre requisições para a mesma conta) e email_address (quando disponível). Tanto id quanto email_address podem ser null. |
source | object | A aplicação de origem: application (consulte Valores de source). |
messages | array | A transcrição da conversa até o ponto da inferência. Consulte Blocos de conteúdo. |
session_id | string ou null | Identificador opaco da conversa, quando existe. Não o analise. Para o Claude Code, é um identificador de sessão de melhor esforço, declarado pelo cliente. |
model | string ou null | Identificador público do modelo para esta requisição, quando disponível. |
metadata | object | Mapa de extensão reservado de chaves string para valores string, enviado vazio atualmente. 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": "[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 em messages tem um role de user ou assistant (resultados de ferramentas aparecem sob o role user, correspondendo ao modelo de conteúdo da Messages API pública) e um array content de blocos discriminados por type:
type do bloco | Campos |
|---|---|
text | text: o conteúdo de texto. |
tool_use | id: 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_result | content: a saída da ferramenta como texto, com 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 na identidade da ferramenta sem fazer referência cruzada a um bloco anterior. tool_use_id: o id do bloco tool_use correspondente. |
attachment | file_name: o nome ou caminho do arquivo original. 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 anexo nunca são enviados. |
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.
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 cujos blocos são todos excluídos é omitido inteiramente, então não presuma alternância estrita entre user e assistant.
As transcrições são enviadas sem truncamento, então uma conversa longa com anexos grandes produz um corpo de requisição grande, até um limite superior de 10 MB. Aumente o limite de corpo do seu servidor para aceitar esse teto. Vários padrões comuns são muito menores, incluindo client_max_body_size do nginx em 1 MB e express.json() do Express em 100 kB, e um corpo rejeitado conta como uma falha de webhook, então sob o tratamento de falha Allow the request, um prompt de tamanho excessivo chegaria ao modelo sem inspeção.
source.application é uma string aberta, não um enum fechado. Os valores conhecidos são claude-ai e claude-code; testes de conexão usam config-test. Novos valores podem aparecer, e seu servidor não deve rejeitar uma requisição por causa de um valor que não reconhece.
Trate source.application como metadados de roteamento informativos, não como um limite de confiança: não baseie uma decisão de política crítica de segurança apenas nele.
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"
}| Campo | Restrições | Semântica |
|---|---|---|
action | "allow" ou "deny"; obrigatório | allow permite que a inferência prossiga; deny a rejeita. |
deny_reason | string ou null; no máximo 500 caracteres, valores mais longos são truncados | Exibido ao usuário final quando action é deny; ignorado em allow. |
reference_id | string ou null; no máximo 50 caracteres de [A-Za-z0-9._:/-] | Seu próprio identificador para esta avaliação. É 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 de tamanho excessivo é truncado, um reference_id malformado é descartado silenciosamente, e o action ainda é honrado.
O inverso não se aplica. Qualquer coisa diferente de HTTP 200 com um veredito analisável é uma falha de webhook, e o tratamento de falha da sua organização se aplica em vez de um veredito. Em particular:
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 descompactado. 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.
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 são livres para alterar a capitalização, então consulte-os sem diferenciar maiúsculas de minúsculas.
| Cabeçalho | Conteúdo |
|---|---|
webhook-id | Identificador único para esta entrega. Igual ao request_id do corpo. Use-o como chave de idempotência e como o primeiro componente do payload assinado. |
webhook-timestamp | Tempo Unix em segundos, como uma 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-signature | Um 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:
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.Uma vez que sua organização tenha um segredo de assinatura, toda requisição que a Anthropic envia é assinada, e habilitar Inference hooks requer um, então rejeite qualquer requisição que chegue não assinada. Uma exceção: um teste de conexão enviado antes do primeiro salvamento da sua organização chega não assinado, porque o segredo de assinatura ainda não existe. Aceite requisições não assinadas até que seu administrador confirme que o segredo existe, depois 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 em vez de 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()
)
# Compara bytes: compare_digest em str gera erro com entrada não ASCII.
return any(
hmac.compare_digest(expected, candidate.encode())
for candidate in signatures.split()
)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 apenas 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.
Tempos limite, status não-200 (redirecionamentos incluídos), corpos de resposta não analisáveis ou de tamanho excessivo e endpoints inacessí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 falha da sua organização decide se a requisição afetada é bloqueada ou prossegue sem inspeção.
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 falha se aplica a todas as requisições. A recuperação acontece do lado do administrador: corrija o servidor e, em seguida, peça ao seu administrador para reativar Enforce verdicts. Consulte Circuit breaker.
A aplicação da política adiciona o tempo de ida e volta do seu servidor de segurança de IA à "latency" (latência) de cada 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.
As requisições para seu servidor de segurança de IA originam-se de 160.79.106.0/24, parte dos intervalos de IP de saída publicados pela Anthropic. Coloque esse bloco na lista de permissões, não os intervalos de entrada na mesma página, que não o cobrem. Colocar na lista de permissões reduz a exposição do seu servidor, mas não substitui a verificação de assinatura: o bloco carrega tráfego de saída da Anthropic além dos Inference hooks.
O protocolo cresce sem quebrar servidores escritos corretamente. Seu servidor deve ignorar:
metadata.source.application.actor.type. actor é uma união discriminada por type, e "user" é o único tipo enviado atualmente; um tipo futuro garante apenas que type está presente.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 resto.
Outros tipos de evento de hook serão introduzidos no futuro. Um novo tipo de evento é uma adição que seu servidor não pode 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.
Um servidor de segurança de IA de produção faz algumas escolhas de design além do protocolo de comunicação.
Deduplique com base em 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 uma chave de idempotência. Se você registrar vereditos, use-o como chave dos registros.
Registre vereditos e correlacione 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 carregando o reference_id que seu servidor retornou, então você pode correlacionar negações no Activity Feed 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 seu tempo de ida e volta fora do caminho crítico do usuário.
Escreva 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 apenas sua equipe pode interpretar.
Habilite 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 Inference hooks, como funciona o ciclo de veredito e quando usá-los.
Was this page helpful?