Un'integrazione Inference hooks è un server di sicurezza IA: un servizio HTTPS che Anthropic chiama. Per ogni richiesta governata, il tuo server riceve una POST firmata contenente la trascrizione della conversazione e risponde con un verdetto di autorizzazione o rifiuto. Questa pagina documenta il protocollo per costruire quel server: gli schemi di richiesta e verdetto, la verifica della firma e il contratto operativo.
Per attivare gli Inference hooks e puntarli al tuo endpoint, consulta Configurare gli Inference hooks. Per sapere cosa sono gli Inference hooks e quando usarli, consulta la panoramica degli Inference hooks.
L'integrazione funzionante più piccola è un server che legge ogni richiesta e la autorizza. Esegui uno dei seguenti server, esponilo a un URL https:// pubblico (ad esempio, dietro un reverse proxy con terminazione TLS o un tunnel), quindi chiedi al tuo amministratore di impostarlo come endpoint e testare la connessione: il risultato di Test connection riporta il verdetto di autorizzazione restituito dal tuo server.
# Esegui 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):
# Svuota il body; le trascrizioni possono essere di megabyte.
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 invia una POST HTTPS all'URL configurato dal tuo amministratore. L'intero URL configurato è l'endpoint: non esiste un suffisso di percorso fisso, quindi scegli qualsiasi percorso adatto al tuo server.
Ospita il tuo server di sicurezza IA dove Anthropic può raggiungerlo: un URL https:// sulla porta 443, su un host instradabile pubblicamente (gli intervalli privati, di loopback e carrier-grade NAT vengono rifiutati al momento della connessione), con un certificato che si convalida rispetto al trust store delle CA pubbliche, che risponde senza redirect. L'URL configurato deve essere la destinazione finale. Configurare gli Inference hooks spiega come il tuo amministratore imposta e testa l'URL.
Ogni richiesta contiene questi header fissi, insieme a eventuali header di richiesta personalizzati configurati dal tuo amministratore e, una volta che la tua organizzazione dispone di un segreto di firma, gli header di firma webhook-* descritti in Verificare la firma:
| Header | Valore |
|---|---|
Content-Type | application/json |
User-Agent | anthropic-dlp/1 |
Accept-Encoding | identity |
Oggi esiste un solo evento hook: il prompt frame, inviato una volta per ogni richiesta di inferenza governata, prima che l'inferenza inizi. Anthropic trattiene la richiesta finché il tuo server di sicurezza IA non risponde o scade il timeout del verdetto.
Il corpo della richiesta è un oggetto JSON con questi campi:
| Campo | Tipo | Descrizione |
|---|---|---|
type | string | L'evento hook. Sempre "prompt" oggi; altri tipi di evento saranno introdotti in futuro, quindi gestisci con tolleranza un valore non riconosciuto (vedi Compatibilità futura). |
request_id | string | Identificatore opaco per chiamata di inferenza, utile per la correlazione. Uguale all'header webhook-id. |
tenant_id | string o null | Identificatore opaco dell'organizzazione a cui appartiene la richiesta. |
actor | object | Il principal a cui è attribuita la richiesta, discriminato su type ("user" è l'unico valore inviato oggi): id (un identificatore con tag, stabile tra le richieste per lo stesso account) ed email_address (quando disponibile). Sia id che email_address possono essere null. |
source | object | L'applicazione di origine: application (vedi Valori di source). |
messages | array | La trascrizione della conversazione fino al punto dell'inferenza. Vedi Blocchi di contenuto. |
session_id | string o null | Identificatore opaco della conversazione, quando ne esiste uno. Non analizzarlo. Per Claude Code è un identificatore di sessione best-effort, dichiarato dal client. |
model | string o null | Identificatore pubblico del modello per questa richiesta, quando disponibile. |
metadata | object | Mappa di estensione riservata di chiavi stringa a valori stringa, inviata vuota oggi. Non richiedere nulla da essa e tollera la sua assenza, la sua presenza e qualsiasi chiave che appaia. |
Un esempio di corpo della richiesta:
{
"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": {}
}Ogni voce in messages ha un role di user o assistant (i risultati degli strumenti appaiono sotto il ruolo user, in linea con il modello di contenuto della Messages API pubblica) e un array content di blocchi discriminati da type:
type del blocco | Campi |
|---|---|
text | text: il contenuto testuale. |
tool_use | id: l'identificatore a cui fa riferimento il risultato dello strumento corrispondente. tool_name: il nome dello strumento. input: gli argomenti che il modello ha passato allo strumento. |
tool_result | content: l'output dello strumento come testo, con le parti unite da caratteri di nuova riga; le parti binarie come le immagini sono sostituite da marcatori segnaposto e i byte grezzi non vengono mai inviati. is_error: indica se la chiamata allo strumento è fallita. tool_name: il nome dello strumento, così che una policy possa condizionarsi sull'identità dello strumento senza fare riferimento incrociato a un blocco precedente. tool_use_id: l'id del blocco tool_use corrispondente. |
attachment | file_name: il nome o percorso del file originale. media_type: il media type dell'allegato. size_bytes: la dimensione del file originale. text: il contenuto testuale dell'allegato quando disponibile, come testo estratto da un documento, una trascrizione audio o metadati di un link. I byte grezzi dell'allegato non vengono mai inviati. |
Un blocco il cui type non riconosci è un'aggiunta compatibile con le versioni future. L'unico campo che garantisce è type; la tua policy può ispezionare qualsiasi altro campo presente, ma non deve rifiutare la richiesta a causa di un tipo non riconosciuto.
La trascrizione è la conversazione così come la vede l'utente finale, fino al punto dell'inferenza: testo della trascrizione, chiamate agli strumenti e relativi risultati, testo estratto dagli allegati e turni precedenti. Non include mai prompt di sistema, definizioni degli strumenti, contesto interno di Anthropic, il ragionamento nascosto di Claude o byte grezzi dei file.
Un turno in cui ogni blocco è escluso viene omesso interamente, quindi non presumere una rigida alternanza tra user e assistant.
Le trascrizioni vengono inviate senza troncamento, quindi una conversazione lunga con allegati di grandi dimensioni produce un corpo di richiesta grande, fino a un limite superiore di 10 MB. Aumenta il limite del corpo del tuo server per accettare quel tetto. Diversi valori predefiniti comuni sono molto più piccoli, tra cui client_max_body_size di nginx a 1 MB ed express.json() di Express a 100 kB, e un corpo rifiutato conta come un errore del webhook, quindi con la gestione degli errori Allow the request un prompt sovradimensionato raggiungerebbe il modello senza essere ispezionato.
source.application è una stringa aperta, non un enum chiuso. I valori noti sono claude-ai e claude-code; i test di connessione usano config-test. Nuovi valori potrebbero apparire e il tuo server non deve rifiutare una richiesta a causa di un valore che non riconosce.
Tratta source.application come metadati di routing informativi, non come un confine di fiducia: non basare una decisione di policy critica per la sicurezza solo su di esso.
Rispondi con HTTP 200 e un corpo JSON di verdetto per entrambi gli esiti; il campo action discrimina. Per autorizzare la richiesta:
{
"action": "allow"
}Per rifiutarla:
{
"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 | Vincoli | Semantica |
|---|---|---|
action | "allow" o "deny"; obbligatorio | allow consente all'inferenza di procedere; deny la rifiuta. |
deny_reason | string o null; al massimo 500 caratteri, valori più lunghi troncati | Mostrato all'utente finale quando action è deny; ignorato su allow. |
reference_id | string o null; al massimo 50 caratteri da [A-Za-z0-9._:/-] | Il tuo identificatore per questa valutazione. Viene registrato nell'attività di conformità inference_hooks_request_denied del rifiuto e non viene mai mostrato all'utente finale. Mantienilo opaco: nessun contenuto della richiesta e nessun dato personale. |
Un rifiuto non viene mai scartato per un problema di formattazione: un deny_reason sovradimensionato viene troncato, un reference_id malformato viene silenziosamente eliminato e l'action viene comunque rispettata.
Il contrario non vale. Qualsiasi cosa diversa da HTTP 200 con un verdetto analizzabile è un errore del webhook, e si applica la gestione degli errori della tua organizzazione invece di un verdetto. In particolare:
action diverso da allow o deny viene trattato come un errore del webhook.Anthropic legge al massimo 64 KiB del corpo della risposta, e il corpo deve essere non compresso. I redirect non vengono seguiti e i cookie vengono ignorati. I campi sconosciuti nel corpo del verdetto vengono ignorati, quindi puoi restituire un oggetto più ricco insieme ai campi documentati qui.
Le richieste sono firmate secondo la specifica Standard Webhooks, utilizzando tre header. Anthropic invia i nomi degli header in minuscolo, e i proxy sono liberi di modificarne la capitalizzazione, quindi cercali in modo case-insensitive.
| Header | Contenuto |
|---|---|
webhook-id | Identificatore univoco per questa consegna. Uguale al request_id del corpo. Usalo come chiave di idempotenza e come primo componente del payload firmato. |
webhook-timestamp | Tempo Unix in secondi, come stringa decimale, di quando la richiesta è stata firmata. Rifiuta un timestamp che si discosta di più di cinque minuti dall'orologio del tuo server, in entrambe le direzioni. |
webhook-signature | Uno o più valori v1,<base64> separati da spazi, ciascuno un HMAC-SHA256 su {webhook-id}.{webhook-timestamp}.{raw body bytes}. Accetta la richiesta se un qualsiasi valore corrisponde al tuo, usando un confronto a tempo costante. |
Due dettagli causano la maggior parte dei bug di verifica:
whsec_, codificato con l'alfabeto base64 standard (+ e /), così come la firma nell'header. Un decoder URL-safe deriva i byte della chiave errati ogni volta che il segreto contiene + o /, il che accade nella maggior parte dei casi.Una volta che la tua organizzazione dispone di un segreto di firma, ogni richiesta inviata da Anthropic è firmata, e abilitare gli Inference hooks ne richiede uno, quindi rifiuta qualsiasi richiesta che arrivi non firmata. Un'eccezione: un test di connessione inviato prima del primo salvataggio della tua organizzazione arriva non firmato, perché il segreto di firma non esiste ancora. Accetta richieste non firmate finché il tuo amministratore non conferma che il segreto esiste, poi rifiutale.
La rotazione del segreto è un passaggio immediato, ma le richieste firmate con il segreto precedente possono ancora arrivare per circa un minuto dopo, più qualsiasi cosa già in transito. Fai in modo che il tuo server di sicurezza IA accetti firme da entrambi i segreti durante il passaggio, così che quelle richieste ritardatarie non vengano rifiutate.
I seguenti esempi sono implementazioni server, quindi non c'è una scheda shell: un server di sicurezza IA è un servizio HTTPS a lunga esecuzione piuttosto che una richiesta singola. Ogni esempio usa solo la libreria standard del linguaggio; il progetto Standard Webhooks pubblica anche librerie di verifica per la maggior parte dei linguaggi.
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()
)
# Confronta i byte: compare_digest su str genera un errore con input non-ASCII.
return any(
hmac.compare_digest(expected, candidate.encode())
for candidate in signatures.split()
)Il tuo amministratore imposta un timeout del verdetto tra 1 e 10.000 ms (5.000 ms per impostazione predefinita). Il budget copre l'intero scambio: connessione, handshake TLS, richiesta e risposta.
Anthropic riprova esattamente una volta, dopo un ritardo di 100 ms, e solo quando il tentativo di connessione fallisce. Il retry condivide lo stesso budget di timeout e porta lo stesso webhook-id e la stessa firma. Una volta che il tuo server di sicurezza IA ha risposto, lo scambio non viene mai ritentato.
Timeout, stati non-200 (redirect inclusi), corpi di risposta non analizzabili o sovradimensionati ed endpoint irraggiungibili sono tutti errori del webhook. Un errore del webhook non diventa mai un rifiuto; invece, l'impostazione di gestione degli errori della tua organizzazione decide se la richiesta interessata viene bloccata o procede senza ispezione.
Errori del webhook prolungati attribuibili al tuo server di sicurezza IA fanno scattare un "circuit breaker" (interruttore automatico) che interrompe l'enforcement: Anthropic smette di contattare il tuo server e la gestione degli errori si applica a ogni richiesta. Il ripristino avviene lato amministratore: correggi il server, quindi chiedi al tuo amministratore di riattivare Enforce verdicts. Vedi Circuit breaker.
L'enforcement aggiunge il round trip del tuo server di sicurezza IA alla "latency" (latenza) di ogni richiesta governata nella tua organizzazione. Mantieni il verdetto veloce ed esegui test di carico sul tuo server prima di distribuirlo a una grande organizzazione.
Le richieste al tuo server di sicurezza IA provengono da 160.79.106.0/24, parte degli intervalli IP in uscita pubblicati da Anthropic. Inserisci in allowlist quel blocco, non gli intervalli in entrata sulla stessa pagina, che non lo coprono. L'allowlisting restringe l'esposizione del tuo server, ma non sostituisce la verifica della firma: il blocco trasporta traffico in uscita di Anthropic oltre agli Inference hooks.
Il protocollo cresce senza rompere i server scritti correttamente. Il tuo server deve ignorare:
metadata.source.application.actor.type. actor è un'unione discriminata su type, e "user" è l'unico tipo inviato oggi; un tipo futuro garantisce solo che type sia presente.type non riconosciuto.Non rifiutare mai una richiesta a causa di un tipo di blocco o campo non riconosciuto; leggi i campi che conosci e salta il resto.
Altri tipi di evento hook saranno introdotti in futuro. Un nuovo tipo di evento è un'aggiunta che il tuo server non può gestire saltando un campo: la richiesta necessita comunque di un verdetto. Quando il type di primo livello è un valore che non riconosci, restituisci un verdetto di autorizzazione piuttosto che uno stato di errore; una risposta di errore è un errore del webhook, ed errori prolungati fanno scattare il circuit breaker.
Un server di sicurezza IA di produzione fa alcune scelte di progettazione oltre al protocollo di rete.
Deduplica su webhook-id. L'header webhook-id è univoco per consegna ed è uguale al request_id del corpo, e un retry per errore di connessione lo riutilizza, quindi funziona come chiave di idempotenza. Se registri i verdetti, usa questo come chiave dei record.
Registra i verdetti e collega i rifiuti. Memorizza ogni verdetto che restituisci insieme al suo reference_id. Ogni rifiuto viene registrato come attività di conformità inference_hooks_request_denied che contiene il reference_id restituito dal tuo server, così puoi collegare i rifiuti nell'Activity Feed ai record corrispondenti nel tuo sistema.
Archivia con un server always-allow. Per catturare le trascrizioni in tempo reale senza controllarle, restituisci {"action": "allow"} incondizionatamente e persisti il frame dopo aver risposto. Questa è un'alternativa push-based al polling della Compliance API, e rispondere prima di persistere mantiene il tuo round trip fuori dal percorso critico dell'utente.
Scrivi deny_reason per l'utente finale. Il testo che restituisci è ciò che l'utente vede quando la sua richiesta viene bloccata, troncato a 500 caratteri. Digli cosa cambiare, ad esempio quale tipo di contenuto rimuovere, piuttosto che emettere un codice di scanner che solo il tuo team può interpretare.
Abilita gli Inference hooks, connetti e testa il tuo endpoint e controlla enforcement, gestione degli errori e rollout.
Cosa sono gli Inference hooks, come funziona il round trip del verdetto e quando usarli.
Was this page helpful?