Claude Platform Docs
AdminInference Hooks

Eine Inference-hooks-Integration entwickeln

Erstelle den KI-Sicherheitsserver, der signierte Inference-hooks-Anfragen empfängt, sie verifiziert und Allow- oder Deny-Verdicts zurückgibt.

Eine Inference-hooks-Integration ist ein „AI security server“ (KI-Sicherheitsserver): ein HTTPS-Dienst, den Anthropic aufruft. Für jede kontrollierte Anfrage erhält dein Server einen signierten POST, der das Gesprächstranskript enthält, und antwortet mit einem „verdict“ (Urteil) – allow oder deny. Diese Seite dokumentiert das Protokoll zum Erstellen dieses Servers: die Anfrage- und Verdict-Schemas, die Signaturprüfung und den operativen Vertrag.

Um Inference hooks einzuschalten und auf deinen Endpunkt zu richten, siehe Inference hooks konfigurieren. Um zu erfahren, was Inference hooks sind und wann du sie verwenden solltest, siehe die Übersicht zu Inference hooks.

Einen ersten Verdict-Roundtrip erreichen

Die kleinste funktionierende Integration ist ein Server, der jede Anfrage liest und sie erlaubt. Führe einen der folgenden Server aus, stelle ihn unter einer öffentlichen https://-URL bereit (zum Beispiel hinter einem TLS-terminierenden Reverse-Proxy auf einem Host, den du kontrollierst, nicht über einen Reverse-Tunnel-Dienst; siehe Eine Anfrage empfangen) und lass dann deinen Administrator ihn als Endpunkt festlegen und die Verbindung testen: Das Ergebnis von Test connection (Verbindung testen) meldet das Allow-Verdict, das dein Server zurückgegeben hat.

# Ausführen mit: 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):
        # Body vollständig einlesen; Transkripte können mehrere Megabyte groß sein.
        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()

Eine Anfrage empfangen

Anthropic sendet einen HTTPS-POST an die URL, die dein Administrator konfiguriert. Die gesamte konfigurierte URL ist der Endpunkt: Es gibt kein festes Pfadsuffix, wähle also einen beliebigen Pfad, der zu deinem Server passt.

Hoste deinen KI-Sicherheitsserver dort, wo Anthropic ihn erreichen kann: eine https://-URL auf Port 443, auf einem öffentlich routbaren Host (private, Loopback- und Carrier-Grade-NAT-Bereiche werden beim Verbindungsaufbau abgelehnt), mit einem Zertifikat, das gegen den öffentlichen CA-Trust-Store validiert, und mit Antworten ohne Weiterleitungen. Die konfigurierte URL muss das endgültige Ziel sein. Reverse-Tunnel-Hosts (ngrok und ähnliche Tunnel-Dienste) werden nicht unterstützt: Anthropics Netzwerkrichtlinie blockiert sie. Hoste deinen Server auf einer Domain, die du kontrollierst. Inference hooks konfigurieren beschreibt, wie dein Administrator die URL festlegt und testet.

Jede Anfrage trägt diese festen Header, zusammen mit allen benutzerdefinierten Anfrage-Headern, die dein Administrator konfiguriert hat, und – sobald deine Organisation ein Signing-Secret hat – den webhook-*-Signatur-Headern, die in Die Signatur verifizieren beschrieben sind:

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

Es gibt heute ein einziges Hook-Ereignis: den „prompt frame“ (Prompt-Frame), der einmal pro kontrollierter Inferenzanfrage gesendet wird, bevor die Inferenz beginnt. Anthropic hält die Anfrage zurück, bis dein KI-Sicherheitsserver antwortet oder das Verdict-Timeout abläuft.

Der Prompt-Frame

Der Anfrage-Body ist ein JSON-Objekt mit diesen Feldern:

FeldTypBeschreibung
typestringDas Hook-Ereignis. Heute immer "prompt"; weitere Ereignistypen werden in Zukunft eingeführt, behandle einen unbekannten Wert also tolerant (siehe Vorwärtskompatibilität).
request_idstringOpaker Bezeichner pro Inferenzaufruf zur Korrelation. Entspricht dem webhook-id-Header.
tenant_idstring oder nullOpaker Bezeichner für die Organisation, zu der die Anfrage gehört.
actorobjectDer Principal, dem die Anfrage zugeordnet wird, diskriminiert über type ("user" ist der einzige heute gesendete Wert): id (ein getaggter Bezeichner, stabil über Anfragen desselben Kontos hinweg) und email_address (sofern verfügbar). Sowohl id als auch email_address können null sein.
sourceobjectDie Ursprungsanwendung: application (siehe Source-Werte).
messagesarrayDas Gesprächstranskript bis zum Zeitpunkt der Inferenz. Siehe Inhaltsblöcke.
session_idstring oder nullOpaker Gesprächsbezeichner, sofern einer existiert. Parse ihn nicht. Für Claude Code ist es ein vom Client behaupteter Sitzungsbezeichner nach bestem Bemühen.
modelstring oder nullÖffentlicher Modellbezeichner für diese Anfrage, sofern verfügbar.
metadataobjectReservierte Erweiterungs-Map von String-Schlüsseln auf String-Werte, heute leer gesendet. Verlange nichts von ihr und toleriere ihr Fehlen, ihr Vorhandensein und alle Schlüssel, die erscheinen.

Ein Beispiel für einen Anfrage-Body:

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

Inhaltsblöcke

Jeder Eintrag in messages hat eine role von user oder assistant (Tool-Ergebnisse erscheinen unter der Rolle user, entsprechend dem Inhaltsmodell der öffentlichen Messages API) und ein content-Array von Blöcken, die über type diskriminiert werden:

Block-typeFelder
texttext: der Textinhalt.
tool_useid: der Bezeichner, auf den das zugehörige Tool-Ergebnis verweist. tool_name: der Name des Tools. input: die Argumente, die das Modell an das Tool übergeben hat.
tool_resultcontent: die Ausgabe des Tools als Text, wobei Teile durch Zeilenumbrüche verbunden sind; binäre Teile wie Bilder werden durch Platzhaltermarkierungen ersetzt, und rohe Bytes werden nie gesendet. is_error: ob der Tool-Aufruf fehlgeschlagen ist. tool_name: der Name des Tools, damit eine Richtlinie auf die Tool-Identität abstellen kann, ohne einen früheren Block heranzuziehen. tool_use_id: die id des zugehörigen tool_use-Blocks.
attachmentfile_name: der ursprüngliche Dateiname oder Pfad. media_type: der Medientyp des Anhangs. size_bytes: die Größe der Originaldatei. text: der Textinhalt des Anhangs, sofern verfügbar, etwa extrahierter Dokumenttext, ein Audiotranskript oder Link-Metadaten. Rohe Anhang-Bytes werden nie gesendet.

Ein Block, dessen type du nicht erkennst, ist eine vorwärtskompatible Ergänzung. Das einzige Feld, das er garantiert, ist type; deine Richtlinie darf alle anderen vorhandenen Felder inspizieren, darf die Anfrage aber nicht wegen eines unbekannten Typs ablehnen.

Was das Transkript enthält

Das Transkript ist das Gespräch, wie der Endnutzer es sieht, bis zum Zeitpunkt der Inferenz: Transkripttext, Tool-Aufrufe und ihre Ergebnisse, extrahierter Anhangstext und vorherige Turns. Es enthält niemals System-Prompts, Tool-Definitionen, Anthropic-internen Kontext, Claudes verborgenes Reasoning oder rohe Datei-Bytes.

Ein Turn, dessen sämtliche Blöcke ausgeschlossen sind, wird vollständig weggelassen, gehe also nicht von einem strikten Wechsel zwischen user und assistant aus.

Transkripte werden ungekürzt gesendet, sodass ein langes Gespräch mit großen Anhängen einen großen Anfrage-Body erzeugt, bis zu einer Obergrenze von 10 MB. Erhöhe das Body-Limit deines Servers, um diese Obergrenze zu akzeptieren. Mehrere gängige Standardwerte sind deutlich kleiner, darunter nginx client_max_body_size mit 1 MB und Express express.json() mit 100 kB, und ein abgelehnter Body zählt als Webhook-Fehler, sodass unter der Fehlerbehandlung Allow the request (Anfrage erlauben) ein übergroßer Prompt das Modell ungeprüft erreichen würde.

Source-Werte

source.application ist ein offener String, kein geschlossenes Enum. Bekannte Werte sind claude-ai und claude-code; Verbindungstests verwenden config-test. Neue Werte können erscheinen, und dein Server darf eine Anfrage nicht wegen eines Werts ablehnen, den er nicht erkennt.

Behandle source.application als beratende Routing-Metadaten, nicht als Vertrauensgrenze: Stütze eine sicherheitskritische Richtlinienentscheidung nicht allein darauf.

Ein Verdict zurückgeben

Antworte für beide Ergebnisse mit HTTP 200 und einem JSON-Verdict-Body; das Feld action diskriminiert. Um die Anfrage zu erlauben:

{
  "action": "allow"
}

Um sie abzulehnen:

{
  "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"
}
FeldEinschränkungenSemantik
action"allow" oder "deny"; erforderlichallow lässt die Inferenz fortfahren; deny lehnt sie ab.
deny_reasonstring oder null; höchstens 500 Zeichen, längere Werte werden gekürztWird dem Endnutzer angezeigt, wenn action deny ist; bei allow ignoriert.
reference_idstring oder null; höchstens 50 Zeichen aus [A-Za-z0-9._:/-]Dein eigener Bezeichner für diese Auswertung. Er wird in der Compliance-Aktivität inference_hooks_request_denied der Ablehnung aufgezeichnet und dem Endnutzer nie angezeigt. Halte ihn opak: kein Anfrageinhalt und keine personenbezogenen Daten.

Ein Deny wird niemals wegen eines Formatierungsproblems verworfen: Ein übergroßer deny_reason wird gekürzt, eine fehlerhaft formatierte reference_id wird stillschweigend verworfen, und die action wird trotzdem berücksichtigt.

Das Umgekehrte gilt nicht. Alles andere als HTTP 200 mit einem parsebaren Verdict ist ein Webhook-Fehler, und anstelle eines Verdicts greift die Fehlerbehandlung deiner Organisation. Insbesondere:

  • Signalisiere ein Deny nicht mit einem Fehlerstatus. Eine Nicht-200-Antwort ist ein Fehler, kein Deny.
  • Jeder action-Wert außer allow oder deny wird als Webhook-Fehler behandelt.

Anthropic liest höchstens 64 KiB des Antwort-Bodys, und der Body muss unkomprimiert sein. Weiterleitungen werden nicht verfolgt, und Cookies werden ignoriert. Unbekannte Felder im Verdict-Body werden ignoriert, sodass du neben den hier dokumentierten Feldern ein reichhaltigeres Objekt zurückgeben kannst.

Die Signatur verifizieren

Anfragen werden gemäß der Standard Webhooks-Spezifikation mit drei Headern signiert. Anthropic sendet die Header-Namen in Kleinbuchstaben, und Proxys dürfen ihre Schreibweise ändern, schlage sie also ohne Berücksichtigung der Groß-/Kleinschreibung nach.

HeaderInhalt
webhook-idEindeutiger Bezeichner für diese Zustellung. Entspricht der request_id des Bodys. Verwende ihn als Idempotenzschlüssel und als erste Komponente der signierten Payload.
webhook-timestampUnix-Zeit in Sekunden als Dezimalstring, zu der die Anfrage signiert wurde. Lehne einen Zeitstempel ab, der mehr als fünf Minuten von der Uhr deines Servers abweicht, in beide Richtungen.
webhook-signatureEin oder mehrere durch Leerzeichen getrennte v1,<base64>-Werte, jeweils ein HMAC-SHA256 über {webhook-id}.{webhook-timestamp}.{raw body bytes}. Akzeptiere die Anfrage, wenn irgendein Wert mit deinem übereinstimmt, unter Verwendung eines Vergleichs in konstanter Zeit.

Zwei Details verursachen die meisten Verifizierungsfehler:

  • Verifiziere rohe Bytes. Berechne den HMAC über den Body genau so, wie er empfangen wurde, vor jeglichem JSON-Parsing oder erneuter Kodierung.
  • Dekodiere das Secret mit einem Standard-Base64-Decoder. Das Signing-Secret ist der Wert nach dem Präfix whsec_, kodiert mit dem Standard-Base64-Alphabet (+ und /), ebenso wie die Signatur im Header. Ein URL-sicherer Decoder leitet die falschen Schlüssel-Bytes ab, sobald das Secret + oder / enthält, was meistens der Fall ist.

Sobald deine Organisation ein Signing-Secret hat, ist jede Anfrage, die Anthropic sendet, signiert, und das Aktivieren von Inference hooks erfordert eines, lehne also jede Anfrage ab, die unsigniert eintrifft. Eine Ausnahme: Ein Verbindungstest, der vor dem ersten Speichern deiner Organisation gesendet wird, trifft unsigniert ein, weil das Signing-Secret noch nicht existiert. Akzeptiere unsignierte Anfragen, bis dein Administrator bestätigt, dass das Secret existiert, und lehne sie danach ab.

Das Rotieren des Secrets ist eine sofortige Umstellung, aber Anfragen, die mit dem vorherigen Secret signiert wurden, können noch etwa eine Minute danach eintreffen, zuzüglich allem, was bereits unterwegs ist. Lass deinen KI-Sicherheitsserver während der Umstellung Signaturen beider Secrets akzeptieren, damit diese Nachzügler nicht abgelehnt werden.

Die folgenden Beispiele sind Server-Implementierungen, daher gibt es keinen Shell-Tab: Ein KI-Sicherheitsserver ist ein lang laufender HTTPS-Dienst und keine einmalige Anfrage. Jedes Beispiel verwendet nur die Standardbibliothek der Sprache; das Standard Webhooks-Projekt veröffentlicht außerdem Verifizierungsbibliotheken für die meisten Sprachen.

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

    # Bytes vergleichen: compare_digest auf str löst bei Nicht-ASCII-Eingabe einen Fehler aus.
    return any(
        hmac.compare_digest(expected, candidate.encode())
        for candidate in signatures.split()
    )

Operative Semantik

Timeout und Wiederholung

Dein Administrator legt ein Verdict-Timeout zwischen 1 und 10.000 ms fest (standardmäßig 5.000 ms). Das Budget deckt den gesamten Austausch ab: Verbindung, TLS-Handshake, Anfrage und Antwort.

Anthropic wiederholt genau einmal, nach einer Verzögerung von 100 ms, und nur, wenn der Verbindungsversuch fehlschlägt. Die Wiederholung teilt dasselbe Timeout-Budget und trägt dieselbe webhook-id und dieselbe Signatur. Sobald dein KI-Sicherheitsserver geantwortet hat, wird der Austausch nie wiederholt.

Webhook-Fehler

Timeouts, Nicht-200-Statuscodes (einschließlich Weiterleitungen), nicht parsebare oder übergroße Antwort-Bodys und nicht erreichbare Endpunkte sind alle Webhook-Fehler. Ein Webhook-Fehler wird niemals zu einem Deny; stattdessen entscheidet die Einstellung zur Fehlerbehandlung deiner Organisation, ob die betroffene Anfrage blockiert wird oder ohne Prüfung fortfährt.

Circuit Breaker

Anhaltende Webhook-Fehler, die deinem KI-Sicherheitsserver zuzuschreiben sind, lösen einen „circuit breaker“ (Schutzschalter) aus, der die Durchsetzung stoppt: Anthropic kontaktiert deinen Server nicht mehr, und die Fehlerbehandlung gilt für jede Anfrage.

Ab 10 Minuten nach dem Auslösen testet Anthropic, ob sich dein Server erholt hat: Höchstens etwa einmal pro Minute wird eine Anfrage, getragen vom eigenen Traffic deiner Organisation, zur Prüfung an deinen Server zugestellt, signiert und geformt wie jede andere. Antworte normal darauf. Ein gültiges Verdict, allow oder deny, setzt den Circuit Breaker zurück, und die Durchsetzung wird fortgesetzt. Ein Webhook-Fehler lässt den Circuit Breaker ausgelöst, und das Testen geht weiter. In beiden Fällen fährt die Testanfrage selbst für ihren Nutzer fort: Ihr Verdict wird nicht durchgesetzt, und ein fehlgeschlagener Test blockiert sie nicht, selbst unter Block the request (Anfrage blockieren). Ein Administrator kann den Circuit Breaker außerdem jederzeit zurücksetzen, und Konfigurationsänderungen durch Administratoren stoppen das automatische Testen; siehe Circuit Breaker.

Jedes Auslösen wird als inference_hooks_circuit_breaker_tripped-Aktivität im Activity Feed aufgezeichnet, eine Aktivität pro Auslösen. Während der Circuit Breaker ausgelöst ist, werden keine Inference-hooks-Aktivitäten pro Anfrage aufgezeichnet, sodass die Auslöse-Aktivität der einzige Eintrag des Feeds über das ausgelöste Zeitfenster ist.

Latenz

Die Durchsetzung addiert den Roundtrip deines KI-Sicherheitsservers zur „latency“ (Latenz) jeder kontrollierten Anfrage in deiner Organisation. Halte das Verdict schnell und führe Lasttests deines Servers durch, bevor du ihn für eine große Organisation ausrollst.

Quell-IP-Adressen

Anfragen an deinen KI-Sicherheitsserver stammen aus 160.79.106.0/24, einem Teil von Anthropics veröffentlichten ausgehenden IP-Bereichen. Setze diesen Block auf die Allowlist, nicht die eingehenden Bereiche auf derselben Seite, die ihn nicht abdecken. Allowlisting verringert die Angriffsfläche deines Servers, ist aber kein Ersatz für die Signaturprüfung: Der Block trägt ausgehenden Anthropic-Traffic über Inference hooks hinaus.

Vorwärtskompatibilität

Das Protokoll wächst, ohne korrekt geschriebene Server zu brechen. Dein Server muss Folgendes ignorieren:

  • Unbekannte Felder auf oberster Ebene des Prompt-Frames.
  • Unbekannte Schlüssel in metadata.
  • Neue source.application-Werte.
  • Neue actor.type-Werte. actor ist eine über type diskriminierte Union, und "user" ist die einzige heute gesendete Art; eine zukünftige Art garantiert nur, dass type vorhanden ist.
  • Inhaltsblöcke mit einem unbekannten type.

Lehne niemals eine Anfrage wegen eines unbekannten Blocktyps oder Felds ab; lies die Felder, die du kennst, und überspringe den Rest.

Weitere Hook-Ereignistypen werden in Zukunft eingeführt. Ein neuer Ereignistyp ist eine Ergänzung, die dein Server nicht durch Überspringen eines Felds behandeln kann: Die Anfrage benötigt trotzdem ein Verdict. Wenn der type auf oberster Ebene ein Wert ist, den du nicht erkennst, gib ein Allow-Verdict statt eines Fehlerstatus zurück; eine Fehlerantwort ist ein Webhook-Fehler, und anhaltende Fehler lösen den Circuit Breaker aus.

Deine Integration entwerfen

Ein produktiver KI-Sicherheitsserver trifft einige Designentscheidungen über das Wire-Protokoll hinaus.

Dedupliziere anhand von webhook-id. Der webhook-id-Header ist pro Zustellung eindeutig und entspricht der request_id des Bodys, und eine Wiederholung nach Verbindungsfehler verwendet ihn erneut, sodass er als Idempotenzschlüssel funktioniert. Wenn du Verdicts aufzeichnest, verwende ihn als Schlüssel für die Datensätze.

Zeichne Verdicts auf und verknüpfe Ablehnungen. Speichere jedes Verdict, das du zurückgibst, zusammen mit seiner reference_id. Jede Ablehnung wird als Compliance-Aktivität inference_hooks_request_denied aufgezeichnet, die die von deinem Server zurückgegebene reference_id trägt, sodass du Ablehnungen im Activity Feed mit den passenden Datensätzen in deinem eigenen System verknüpfen kannst.

Archiviere mit einem Always-Allow-Server. Um Transkripte in Echtzeit zu erfassen, ohne sie zu kontrollieren, gib bedingungslos {"action": "allow"} zurück und persistiere den Frame nach dem Antworten. Dies ist eine Push-basierte Alternative zum Polling der Compliance API, und das Antworten vor dem Persistieren hält deinen Roundtrip aus dem kritischen Pfad des Nutzers heraus.

Schreibe deny_reason für den Endnutzer. Der Text, den du zurückgibst, ist das, was der Nutzer sieht, wenn seine Anfrage blockiert wird, gekürzt auf 500 Zeichen. Sage ihm, was er ändern soll, etwa welche Art von Inhalt er entfernen soll, anstatt einen Scanner-Code auszugeben, den nur dein Team interpretieren kann.

Nächste Schritte

Aktiviere Inference hooks, verbinde und teste deinen Endpunkt und steuere Durchsetzung, Fehlerbehandlung und Rollout.

Was Inference hooks sind, wie der Verdict-Roundtrip funktioniert und wann du sie verwenden solltest.

Was this page helpful?