Webhook
Alih-alih terus memantau /v1/jobs, daftarkan endpoint HTTPS dan Anthra akan mengirim POST berisi peristiwa JSON bertanda tangan ke sana saat tugas latar belakang selesai atau buku diterbitkan. Endpoint juga bisa dikelola dari Akun → Webhook, atau lewat MCP (create_webhook, list_webhooks, delete_webhook, ping_webhook).
Webhook mencakup setiap buku di akun, jadi endpoint di bawah menolak kunci API yang dibatasi ke buku tertentu dengan 403 forbidden.
Peristiwa
| Peristiwa | Dipicu saat |
|---|---|
job.succeeded | Tugas latar belakang selesai: pembuatan satu buku penuh (kind: "generateBook"), tugas narasi dan ekspor, serta render PDF (kind: "productionRender"). |
job.failed | Tugas yang sama gagal, dibatalkan, atau dihentikan oleh pengawas tugas macet. |
book.published | Buku diterbitkan, dari permukaan mana pun (studio, Kopilot, REST, MCP). Tepat satu peristiwa per penerbitan. |
book.unpublished | Buku yang sudah terbit ditarik dari publik. |
* | Berlangganan semua peristiwa, termasuk yang ditambahkan nanti. |
Payload
Setiap pengiriman adalah POST dengan content-type: application/json dan amplop ini. id tetap sama di setiap percobaan ulang peristiwa yang sama — gunakan untuk menghapus duplikat.
{
"id": "evt_Qm9yZ2V0LW1lLW5vdA",
"event": "job.succeeded",
"createdAt": "2026-11-02T09:14:03.000Z",
"data": {
"kind": "productionRender",
"jobId": "6c1e…",
"bookId": "8f1c…",
"format": "pdf",
"status": "succeeded",
"resultUrl": "https://…/book.pdf",
"error": null
}
}book.published membawa { bookId, title, slug, visibility, readerUrl }; book.unpublished membawa { bookId, title }. Peristiwa tugas membawa kind, bookId, status, dan error, ditambah field khusus sesuai jenisnya.
Header
| Header | Nilai |
|---|---|
anthra-signature | t=<unix seconds>,v1=<hex HMAC-SHA256> |
anthra-webhook-id | Id endpoint tujuan pengiriman. |
user-agent | Anthra-Webhooks/1.0 |
Verifikasi tanda tangan
Tanda tangannya adalah HMAC-SHA256(secret, "<t>.<raw body>") dalam hex, dengan secret berupa nilai whsec_… yang dikembalikan saat Anda membuat endpoint (atau terakhir merotasi secret-nya). Stempel waktu termasuk dalam materi yang ditandatangani, jadi tolak pengiriman yang t-nya berselisih lebih dari lima menit dari jam Anda — itu mencegah pengiriman yang tersadap diputar ulang. Selalu verifikasi terhadap byte mentah permintaan, sebelum parsing JSON apa pun, dan bandingkan dalam waktu konstan.
import { createHmac, timingSafeEqual } from 'node:crypto'
export function verifyAnthra(rawBody, header, secret, toleranceSecs = 300) {
const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=')))
const t = Number(parts.t)
if (!Number.isFinite(t) || !parts.v1) return false
if (Math.abs(Date.now() / 1000 - t) > toleranceSecs) return false
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest()
const given = Buffer.from(parts.v1, 'hex')
return given.length === expected.length && timingSafeEqual(given, expected)
}import hashlib, hmac, time
def verify_anthra(raw_body: bytes, header: str, secret: str, tolerance=300) -> bool:
parts = dict(kv.split("=", 1) for kv in header.split(","))
t, v1 = int(parts.get("t", 0)), parts.get("v1", "")
if abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, v1)Percobaan ulang dan penonaktifan otomatis
Balas dengan 2xx apa pun dalam 8 detik untuk mengonfirmasi pengiriman. Selain itu — status non-2xx, waktu habis, error koneksi — dihitung sebagai kegagalan, dan body yang sama dikirim ulang (dengan t dan tanda tangan baru) dengan jadwal berikut:
| Percobaan | Dikirim |
|---|---|
| 1 | Segera |
| 2 | ~1 menit setelah percobaan 1 gagal |
| 3 | ~5 menit kemudian |
| 4 | ~30 menit kemudian |
| 5 | ~2 jam kemudian |
| 6 | ~8 jam kemudian (percobaan terakhir) |
Percobaan ulang diproses setiap lima menit, jadi perkirakan selisih beberapa menit. Setelah 10 percobaan gagal berturut-turut (di semua peristiwa), endpoint dinonaktifkan: active menjadi false, disabledAt diisi, dan percobaan ulang yang tertunda dibuang. Perbaiki penerimanya, lalu aktifkan kembali dengan PATCH /v1/webhooks/{webhookId} dan { "active": true }, yang juga mengatur ulang hitungan kegagalan. Pengiriman tidak pernah dikirim ke alamat privat, loopback, atau link-local, dan pengalihan tidak diikuti.
Tampilkan daftar endpoint
Mengembalikan { webhooks, availableEvents }. Setiap webhook membawa id, url, events, description, active, lastDeliveryAt, lastStatus, lastError, failureCount, dan disabledAt. Secret penandatanganan tidak pernah dikembalikan di sini.
Daftarkan endpoint
Body: url (URL http(s) yang bisa dijangkau publik), events (nama dari tabel di atas, atau ["*"]), dan description opsional. Respons berisi secret penandatanganan — simpan sekarang, karena tidak akan dikembalikan lagi.
curl -X POST "$ANTHRA_BASE/webhooks" \
-H "authorization: Bearer $ANTHRA_TOKEN" \
-H "content-type: application/json" \
-d '{ "url": "https://example.com/anthra", "events": ["job.succeeded", "job.failed", "book.published"] }'
{
"data": {
"webhook": { "id": "3d0f…", "url": "https://example.com/anthra", "active": true, … },
"secret": "whsec_…"
},
"meta": { "warning": "Store this secret now — it is not returned again." }
}Baca endpoint dan log pengirimannya
Mengembalikan endpoint beserta percobaan pengiriman terbarunya, terbaru lebih dulu (?limit= 1–100, bawaan 20). Setiap percobaan memiliki eventId, event, attempt, status (pending | succeeded | failed), statusCode, latencyMs, error, nextRetryAt (saat percobaan berikutnya dijadwalkan), dan createdAt. Log disimpan selama 30 hari.
Perbarui, aktifkan kembali, atau rotasi secret
Kirim sebagian dari:
| Field | Tipe | Efek |
|---|---|---|
url | string | URL pengiriman baru (pemeriksaan alamat publik yang sama seperti saat pendaftaran). |
events | string[] | Ganti daftar langganan. |
description | string | null | Label teks bebas. |
active | boolean | true mengaktifkan kembali endpoint yang nonaktif dan mengatur ulang hitungan kegagalannya; false menjeda pengiriman. |
rotateSecret | boolean | Buat secret penandatanganan baru, dikembalikan sekali sebagai secret. Secret lama langsung berhenti berlaku, termasuk untuk percobaan ulang yang sudah dijadwalkan. |
curl -X PATCH "$ANTHRA_BASE/webhooks/$WEBHOOK" \
-H "authorization: Bearer $ANTHRA_TOKEN" \
-H "content-type: application/json" \
-d '{ "active": true, "rotateSecret": true }'Hapus endpoint
Mencabut pendaftaran endpoint. Pengiriman dan percobaan ulang yang tertunda langsung berhenti.