Webhooks
Ontvang voltooiingsmeldingen voor batchjobs
Voor batchjobs die langer dan een minuut duren zijn Webhooks goedkoper en sneller dan polling. Thunderbit POST't naar jouw URL wanneer een job een eindstatus bereikt.
Configureren bij het indienen
{
"urls": ["https://example.com/page1"],
"webhook": {
"url": "https://your-server.com/api/webhook/distill",
"secret": "whsec_your_secret_key",
"headers": { "X-Custom-Auth": "your-token" }
}
}Het veld webhook.headers accepteert een map met aangepaste headers, maar is gereserveerd voor toekomstig gebruik en wordt momenteel niet door de server verstuurd. Stem je verificatie uitsluitend af op de hieronder gedocumenteerde headers.
Request-headers
Elke aflevering bevat de volgende 4 headers:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Webhook-Event | batch.completed |
X-Webhook-Timestamp | Unix epoch in milliseconden |
X-Webhook-Signature | sha256=<base64-encoded HMAC-SHA256> |
Payload
{
"id": "batch_a1b2c3d4...",
"jobType": "batch_distill",
"status": "COMPLETED",
"total": 50,
"completed": 49,
"failed": 1,
"creditsUsed": 100,
"createdAt": "2026-04-26T10:00:00Z",
"completedAt": "2026-04-26T10:05:23Z"
}jobTypeisbatch_distillofbatch_extract.statusisCOMPLETED,FAILEDofCANCELLED.
De payload is bewust klein gehouden — haal de volledige resultaten op met GET /batch/distill/{id} (of /batch/extract/{id}) nadat je de callback hebt ontvangen.
Signatuurverificatie
Als secret is ingesteld, bevat elke aflevering een X-Webhook-Signature-header.
- Algoritme: HMAC-SHA256
- Te ondertekenen string:
<X-Webhook-Timestamp>.<raw-json-body>(een letterlijke.ertussen) - Uitvoerformaat:
sha256=<base64-encoded-hash>(Base64, geen hex)
Verifieer op de ruwe request-bytes (serialiseer de JSON niet opnieuw) en vergelijk in constante tijd.
import hmac, hashlib, base64
def verify(raw_body: bytes, timestamp: str, signature: str, secret: str) -> bool:
base = f"{timestamp}.{raw_body.decode('utf-8')}".encode('utf-8')
digest = hmac.new(secret.encode('utf-8'), base, hashlib.sha256).digest()
expected = "sha256=" + base64.b64encode(digest).decode('ascii')
return hmac.compare_digest(expected, signature)Replay-bescherming
Wijs de request af als |now - X-Webhook-Timestamp| > 60000 (venster van 60 s). De timestamp is in milliseconden sinds de Unix epoch. Vertrouw in productie nooit een ongesigneerde webhook.
Retry-gedrag
- Timeout per aflevering: 30 seconden
- Maximum aantal retries: 3
- Backoff: exponentieel, startend op 1 seconde, gemaximeerd op 10 seconden
- Totaal end-to-end-venster: ~120 seconden
Nadat alle retries zijn mislukt, blijft de job aan onze kant voltooid — jouw endpoint moet idempotent zijn (gebruik id als deduplicatiesleutel).