Webhooks
Ricevi notifiche di completamento dei batch job
Per i batch job che durano più di un minuto, i webhook sono più economici e veloci del polling. Thunderbit fa POST al tuo URL quando un job raggiunge uno stato terminale.
Configurazione all'invio
{
"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" }
}
}Il campo webhook.headers accetta una map di header personalizzati, ma è riservato per uso futuro e non viene ancora inviato dal server. Basa la verifica esclusivamente sugli header documentati qui sotto.
Header della richiesta
Ogni consegna include i seguenti 4 header:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Webhook-Event | batch.completed |
X-Webhook-Timestamp | Unix epoch in millisecondi |
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"
}jobTypevalebatch_distillobatch_extract.statusè uno traCOMPLETED,FAILEDoCANCELLED.
Il payload è volutamente compatto — recupera i risultati completi con GET /batch/distill/{id} (oppure /batch/extract/{id}) dopo aver ricevuto la callback.
Verifica della firma
Quando secret è impostato, ogni consegna include un header X-Webhook-Signature.
- Algoritmo: HMAC-SHA256
- Stringa da firmare:
<X-Webhook-Timestamp>.<raw-json-body>(un.letterale tra i due) - Formato di output:
sha256=<base64-encoded-hash>(Base64, non hex)
Verifica sui byte grezzi della richiesta (non riserializzare il JSON) e poi confronta a tempo costante.
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)Protezione contro replay
Rifiuta la richiesta se |now - X-Webhook-Timestamp| > 60000 (finestra di 60 s). Il timestamp è in millisecondi dall'epoch Unix. Non fidarti mai di un webhook non firmato in produzione.
Comportamento dei retry
- Timeout per consegna: 30 secondi
- Retry massimi: 3
- Backoff: esponenziale, partenza 1 secondo, tetto 10 secondi
- Finestra totale end-to-end: ~120 secondi
Esauriti i retry, il job sul nostro lato risulta comunque completato — il tuo endpoint deve essere idempotente (usa id come chiave di deduplica).