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

PeristiwaDipicu saat
job.succeededTugas latar belakang selesai: pembuatan satu buku penuh (kind: "generateBook"), tugas narasi dan ekspor, serta render PDF (kind: "productionRender").
job.failedTugas yang sama gagal, dibatalkan, atau dihentikan oleh pengawas tugas macet.
book.publishedBuku diterbitkan, dari permukaan mana pun (studio, Kopilot, REST, MCP). Tepat satu peristiwa per penerbitan.
book.unpublishedBuku 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

HeaderNilai
anthra-signaturet=<unix seconds>,v1=<hex HMAC-SHA256>
anthra-webhook-idId endpoint tujuan pengiriman.
user-agentAnthra-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:

PercobaanDikirim
1Segera
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

GET/v1/webhookscakupan: books:read

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

POST/v1/webhookscakupan: books:write

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

GET/v1/webhooks/{webhookId}cakupan: books:read

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

PATCH/v1/webhooks/{webhookId}cakupan: books:write

Kirim sebagian dari:

FieldTipeEfek
urlstringURL pengiriman baru (pemeriksaan alamat publik yang sama seperti saat pendaftaran).
eventsstring[]Ganti daftar langganan.
descriptionstring | nullLabel teks bebas.
activebooleantrue mengaktifkan kembali endpoint yang nonaktif dan mengatur ulang hitungan kegagalannya; false menjeda pengiriman.
rotateSecretbooleanBuat 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

DELETE/v1/webhooks/{webhookId}cakupan: books:write

Mencabut pendaftaran endpoint. Pengiriman dan percobaan ulang yang tertunda langsung berhenti.