Integrasi Inference hooks adalah server keamanan AI: sebuah layanan HTTPS yang dipanggil oleh Anthropic. Untuk setiap permintaan yang diatur, server Anda menerima POST yang ditandatangani yang membawa transkrip percakapan dan merespons dengan keputusan izinkan atau tolak. Halaman ini mendokumentasikan protokol untuk membangun server tersebut: skema permintaan dan keputusan, verifikasi tanda tangan, dan kontrak operasional.
Untuk mengaktifkan Inference hooks dan mengarahkannya ke endpoint Anda, lihat Mengonfigurasi Inference hooks. Untuk memahami apa itu Inference hooks dan kapan menggunakannya, lihat ikhtisar Inference hooks.
Integrasi terkecil yang berfungsi adalah server yang membaca setiap permintaan dan mengizinkannya. Jalankan salah satu server berikut, ekspos di URL https:// publik (misalnya, di belakang reverse proxy yang menangani terminasi TLS atau sebuah tunnel), lalu minta administrator Anda untuk mengaturnya sebagai endpoint dan menguji koneksi: hasil Test connection melaporkan keputusan izinkan yang dikembalikan server Anda.
# Jalankan dengan: 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):
# Kuras body; transkrip bisa berukuran 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 mengirim POST HTTPS ke URL yang dikonfigurasi administrator Anda. Seluruh URL yang dikonfigurasi adalah endpoint-nya: tidak ada sufiks path tetap, jadi pilih path apa pun yang sesuai dengan server Anda.
Host server keamanan AI Anda di tempat yang dapat dijangkau Anthropic: URL https:// pada port 443, pada host yang dapat dirutekan secara publik (rentang privat, loopback, dan carrier-grade NAT ditolak pada saat koneksi), dengan sertifikat yang tervalidasi terhadap trust store CA publik, merespons tanpa redirect. URL yang dikonfigurasi harus menjadi tujuan akhir. Mengonfigurasi Inference hooks membahas cara administrator Anda mengatur dan menguji URL tersebut.
Setiap permintaan membawa header tetap berikut, bersama dengan header permintaan kustom apa pun yang dikonfigurasi administrator Anda dan, setelah organisasi Anda memiliki signing secret, header tanda tangan webhook-* yang dijelaskan di Memverifikasi tanda tangan:
| Header | Nilai |
|---|---|
Content-Type | application/json |
User-Agent | anthropic-dlp/1 |
Accept-Encoding | identity |
Saat ini hanya ada satu event hook: prompt frame, dikirim sekali per permintaan inferensi yang diatur, sebelum inferensi dimulai. Anthropic menahan permintaan tersebut sampai server keamanan AI Anda merespons atau batas waktu keputusan habis.
Body permintaan adalah objek JSON dengan field-field berikut:
| Field | Tipe | Deskripsi |
|---|---|---|
type | string | Event hook. Selalu "prompt" saat ini; tipe event lain akan diperkenalkan di masa mendatang, jadi tangani nilai yang tidak dikenali dengan baik (lihat Kompatibilitas ke depan). |
request_id | string | Pengidentifikasi opaque per panggilan inferensi untuk korelasi. Sama dengan header webhook-id. |
tenant_id | string atau null | Pengidentifikasi opaque untuk organisasi tempat permintaan tersebut berasal. |
actor | object | Principal yang diatribusikan pada permintaan, dibedakan berdasarkan type ("user" adalah satu-satunya nilai yang dikirim saat ini): id (pengidentifikasi bertag, stabil di seluruh permintaan untuk akun yang sama) dan email_address (jika tersedia). Baik id maupun email_address dapat bernilai null. |
source | object | Aplikasi asal: application (lihat Nilai source). |
messages | array | Transkrip percakapan hingga titik inferensi. Lihat Blok konten. |
session_id | string atau null | Pengidentifikasi percakapan opaque, jika ada. Jangan mem-parse-nya. Untuk Claude Code, ini adalah pengidentifikasi sesi best-effort yang dinyatakan oleh klien. |
model | string atau null | Pengidentifikasi model publik untuk permintaan ini, jika tersedia. |
metadata | object | Map ekstensi yang dicadangkan berisi kunci string ke nilai string, dikirim kosong saat ini. Jangan mengharuskan apa pun darinya, dan toleransi ketidakhadirannya, kehadirannya, dan kunci apa pun yang muncul. |
Contoh body permintaan:
{
"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": {}
}Setiap entri dalam messages memiliki role berupa user atau assistant (hasil alat muncul di bawah role user, sesuai dengan model konten Messages API publik) dan array content berisi blok yang dibedakan berdasarkan type:
type blok | Field |
|---|---|
text | text: konten teks. |
tool_use | id: pengidentifikasi yang dirujuk oleh hasil alat yang cocok. tool_name: nama alat. input: argumen yang diteruskan model ke alat. |
tool_result | content: output alat sebagai teks, dengan bagian-bagian digabungkan oleh baris baru; bagian biner seperti gambar digantikan oleh penanda placeholder, dan byte mentah tidak pernah dikirim. is_error: apakah panggilan alat gagal. tool_name: nama alat, sehingga kebijakan dapat mengondisikan pada identitas alat tanpa merujuk silang ke blok sebelumnya. tool_use_id: id dari blok tool_use yang cocok. |
attachment | file_name: nama file atau path asli. media_type: tipe media lampiran. size_bytes: ukuran file asli. text: konten teks lampiran jika tersedia, seperti teks dokumen yang diekstrak, transkrip audio, atau metadata tautan. Byte lampiran mentah tidak pernah dikirim. |
Blok yang type-nya tidak Anda kenali adalah penambahan yang kompatibel ke depan. Satu-satunya field yang dijamin adalah type; kebijakan Anda boleh memeriksa field lain apa pun yang ada, tetapi tidak boleh menolak permintaan karena tipe yang tidak dikenali.
Transkrip adalah percakapan sebagaimana yang dilihat pengguna akhir, hingga titik inferensi: teks transkrip, panggilan alat dan hasilnya, teks lampiran yang diekstrak, dan giliran sebelumnya. Transkrip tidak pernah menyertakan prompt sistem, definisi alat, konteks internal Anthropic, penalaran tersembunyi Claude, atau byte file mentah.
Giliran yang setiap bloknya dikecualikan akan dihilangkan seluruhnya, jadi jangan berasumsi ada pergantian ketat antara user dan assistant.
Transkrip dikirim tanpa pemotongan, sehingga percakapan panjang dengan lampiran besar menghasilkan body permintaan yang besar, hingga batas atas 10 MB. Naikkan batas body server Anda untuk menerima batas tersebut. Beberapa default umum jauh lebih kecil, termasuk client_max_body_size nginx sebesar 1 MB dan express.json() Express sebesar 100 kB, dan body yang ditolak dihitung sebagai kegagalan webhook, sehingga di bawah penanganan kegagalan Allow the request, prompt yang terlalu besar akan mencapai model tanpa diperiksa.
source.application adalah string terbuka, bukan enum tertutup. Nilai yang diketahui adalah claude-ai dan claude-code; pengujian koneksi menggunakan config-test. Nilai baru dapat muncul, dan server Anda tidak boleh menolak permintaan karena nilai yang tidak dikenalinya.
Perlakukan source.application sebagai metadata routing yang bersifat informatif, bukan batas kepercayaan: jangan mendasarkan keputusan kebijakan yang kritis terhadap keamanan hanya pada nilai ini.
Respons dengan HTTP 200 dan body keputusan JSON untuk kedua hasil; field action yang membedakannya. Untuk mengizinkan permintaan:
{
"action": "allow"
}Untuk menolaknya:
{
"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"
}| Field | Batasan | Semantik |
|---|---|---|
action | "allow" atau "deny"; wajib | allow membiarkan inferensi berlanjut; deny menolaknya. |
deny_reason | string atau null; maksimal 500 karakter, nilai yang lebih panjang dipotong | Ditampilkan kepada pengguna akhir ketika action adalah deny; diabaikan pada allow. |
reference_id | string atau null; maksimal 50 karakter dari [A-Za-z0-9._:/-] | Pengidentifikasi Anda sendiri untuk evaluasi ini. Dicatat pada aktivitas kepatuhan inference_hooks_request_denied dari penolakan tersebut dan tidak pernah ditampilkan kepada pengguna akhir. Jaga agar tetap opaque: tanpa konten permintaan dan tanpa data pribadi. |
Penolakan tidak pernah dibuang karena masalah format: deny_reason yang terlalu besar dipotong, reference_id yang salah format dibuang secara diam-diam, dan action tetap dihormati.
Sebaliknya tidak berlaku. Apa pun selain HTTP 200 dengan keputusan yang dapat di-parse adalah kegagalan webhook, dan penanganan kegagalan organisasi Anda yang berlaku, bukan keputusan. Secara khusus:
action apa pun selain allow atau deny diperlakukan sebagai kegagalan webhook.Anthropic membaca paling banyak 64 KiB dari body respons, dan body harus tidak terkompresi. Redirect tidak diikuti, dan cookie diabaikan. Field yang tidak dikenal dalam body keputusan diabaikan, sehingga Anda dapat mengembalikan objek yang lebih kaya bersama dengan field yang didokumentasikan di sini.
Permintaan ditandatangani sesuai spesifikasi Standard Webhooks, menggunakan tiga header. Anthropic mengirim nama header dalam huruf kecil, dan proxy bebas mengubah kapitalisasinya, jadi cari header tersebut secara case-insensitive.
| Header | Isi |
|---|---|
webhook-id | Pengidentifikasi unik untuk pengiriman ini. Sama dengan request_id pada body. Gunakan sebagai kunci idempotensi dan sebagai komponen pertama dari payload yang ditandatangani. |
webhook-timestamp | Waktu Unix dalam detik, sebagai string desimal, saat permintaan ditandatangani. Tolak timestamp yang berjarak lebih dari lima menit dari jam server Anda, ke arah mana pun. |
webhook-signature | Satu atau lebih nilai v1,<base64> yang dipisahkan spasi, masing-masing adalah HMAC-SHA256 atas {webhook-id}.{webhook-timestamp}.{raw body bytes}. Terima permintaan jika ada nilai yang cocok dengan milik Anda, menggunakan perbandingan constant-time. |
Dua detail yang menyebabkan sebagian besar bug verifikasi:
whsec_, di-encode dengan alfabet base64 standar (+ dan /), begitu juga tanda tangan di header. Decoder URL-safe menghasilkan byte kunci yang salah setiap kali secret mengandung + atau /, yang terjadi pada sebagian besar kasus.Setelah organisasi Anda memiliki signing secret, setiap permintaan yang dikirim Anthropic ditandatangani, dan mengaktifkan Inference hooks memerlukannya, jadi tolak permintaan apa pun yang tiba tanpa tanda tangan. Satu pengecualian: pengujian koneksi yang dikirim sebelum penyimpanan pertama organisasi Anda tiba tanpa tanda tangan, karena signing secret belum ada. Terima permintaan tanpa tanda tangan sampai administrator Anda mengonfirmasi bahwa secret sudah ada, lalu tolak permintaan tersebut.
Merotasi secret adalah peralihan langsung, tetapi permintaan yang ditandatangani dengan secret sebelumnya masih dapat tiba selama sekitar satu menit setelahnya, ditambah apa pun yang sudah dalam perjalanan. Buat server keamanan AI Anda menerima tanda tangan dari kedua secret selama peralihan sehingga permintaan yang tertinggal tersebut tidak ditolak.
Sampel berikut adalah implementasi server, jadi tidak ada tab shell: server keamanan AI adalah layanan HTTPS yang berjalan lama, bukan permintaan sekali jalan. Setiap sampel hanya menggunakan pustaka standar bahasa tersebut; proyek Standard Webhooks juga menerbitkan pustaka verifikasi untuk sebagian besar bahasa.
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()
)
# Bandingkan byte: compare_digest pada str gagal untuk input non-ASCII.
return any(
hmac.compare_digest(expected, candidate.encode())
for candidate in signatures.split()
)Administrator Anda mengatur batas waktu keputusan antara 1 dan 10.000 ms (default 5.000 ms). Anggaran tersebut mencakup seluruh pertukaran: koneksi, TLS handshake, permintaan, dan respons.
Anthropic melakukan retry tepat satu kali, setelah jeda 100 ms, dan hanya ketika upaya koneksi gagal. Retry berbagi anggaran timeout yang sama dan membawa webhook-id yang sama serta tanda tangan yang sama. Setelah server keamanan AI Anda merespons, pertukaran tidak pernah di-retry.
Timeout, status non-200 (termasuk redirect), body respons yang tidak dapat di-parse atau terlalu besar, dan endpoint yang tidak dapat dijangkau semuanya adalah kegagalan webhook. Kegagalan webhook tidak pernah menjadi penolakan; sebaliknya, pengaturan penanganan kegagalan organisasi Anda yang menentukan apakah permintaan yang terpengaruh diblokir atau dilanjutkan tanpa pemeriksaan.
Kegagalan webhook berkelanjutan yang dapat diatribusikan ke server keamanan AI Anda memicu "circuit breaker" (pemutus sirkuit) yang menghentikan penegakan: Anthropic berhenti menghubungi server Anda, dan penanganan kegagalan berlaku untuk setiap permintaan. Pemulihan terjadi di sisi admin: perbaiki server, lalu minta administrator Anda mengaktifkan kembali Enforce verdicts. Lihat Circuit breaker.
Penegakan menambahkan waktu bolak-balik server keamanan AI Anda ke "latency" (latensi) setiap permintaan yang diatur di organisasi Anda. Jaga agar keputusan tetap cepat, dan lakukan load-test pada server Anda sebelum meluncurkannya ke organisasi besar.
Permintaan ke server keamanan AI Anda berasal dari 160.79.106.0/24, bagian dari rentang IP keluar yang dipublikasikan Anthropic. Masukkan blok tersebut ke allowlist, bukan rentang masuk di halaman yang sama, yang tidak mencakupnya. Allowlisting mempersempit eksposur server Anda, tetapi bukan pengganti verifikasi tanda tangan: blok tersebut membawa lalu lintas egress Anthropic di luar Inference hooks.
Protokol berkembang tanpa merusak server yang ditulis dengan benar. Server Anda harus mengabaikan:
metadata.source.application baru.actor.type baru. actor adalah union yang dibedakan berdasarkan type, dan "user" adalah satu-satunya jenis yang dikirim saat ini; jenis di masa mendatang hanya menjamin bahwa type ada.type yang tidak dikenali.Jangan pernah menolak permintaan karena tipe blok atau field yang tidak dikenali; baca field yang Anda ketahui dan lewati sisanya.
Tipe event hook lain akan diperkenalkan di masa mendatang. Tipe event baru adalah penambahan yang tidak dapat ditangani server Anda dengan melewati sebuah field: permintaan tetap membutuhkan keputusan. Ketika type tingkat atas adalah nilai yang tidak Anda kenali, kembalikan keputusan izinkan alih-alih status error; respons error adalah kegagalan webhook, dan kegagalan berkelanjutan memicu circuit breaker.
Server keamanan AI produksi membuat beberapa pilihan desain di luar protokol wire.
Deduplikasi berdasarkan webhook-id. Header webhook-id unik per pengiriman dan sama dengan request_id pada body, dan retry kegagalan koneksi menggunakannya kembali, sehingga berfungsi sebagai kunci idempotensi. Jika Anda mencatat keputusan, gunakan ini sebagai kunci catatan.
Catat keputusan dan gabungkan penolakan. Simpan setiap keputusan yang Anda kembalikan bersama dengan reference_id-nya. Setiap penolakan dicatat sebagai aktivitas kepatuhan inference_hooks_request_denied yang membawa reference_id yang dikembalikan server Anda, sehingga Anda dapat menggabungkan penolakan di Activity Feed dengan catatan yang cocok di sistem Anda sendiri.
Arsipkan dengan server yang selalu mengizinkan. Untuk menangkap transkrip secara real-time tanpa mengawasinya, kembalikan {"action": "allow"} tanpa syarat dan simpan frame setelah merespons. Ini adalah alternatif berbasis push untuk polling Compliance API, dan menjawab sebelum Anda menyimpan menjaga waktu bolak-balik Anda keluar dari jalur kritis pengguna.
Tulis deny_reason untuk pengguna akhir. Teks yang Anda kembalikan adalah apa yang dilihat pengguna ketika permintaan mereka diblokir, dipotong pada 500 karakter. Beri tahu mereka apa yang harus diubah, seperti jenis konten apa yang harus dihapus, alih-alih mengeluarkan kode scanner yang hanya dapat diinterpretasikan oleh tim Anda.
Aktifkan Inference hooks, hubungkan dan uji endpoint Anda, serta kontrol penegakan, penanganan kegagalan, dan peluncuran.
Apa itu Inference hooks, bagaimana perjalanan bolak-balik keputusan bekerja, dan kapan menggunakannya.
Was this page helpful?