Webhooks
Get notified when async jobs finish, when usage crosses a threshold, or when an incident affects your traffic.
Event types
| Event | When it fires |
|---|---|
usage.threshold | Spend crosses a configured % of cap. |
key.revoked | Any API key is revoked. |
model.deprecated | A model you've used is scheduled for retirement. |
incident.affecting_you | A region or component you use is degraded. |
batch.completed (Phase 2) | Batch job finishes. |
Setup
Console → Settings → Webhooks → New endpoint. Pick events, paste a URL.
Payload shape
{
"id": "evt_01HV...",
"type": "usage.threshold",
"created_at": "2026-05-13T09:11:02Z",
"data": {
"threshold_pct": 80,
"spend_usd": 400,
"cap_usd": 500
}
}
Signature verification
Every webhook carries a Tomoul-Signature header in the form
t=<unix>,v1=<hex>. Verify with HMAC-SHA256 over <unix>.<raw_body> using
the endpoint's signing secret.
import hmac, hashlib
def verify(payload: bytes, header: str, secret: str) -> bool:
parts = dict(kv.split("=", 1) for kv in header.split(","))
expected = hmac.new(
secret.encode(),
f"{parts['t']}.{payload.decode()}".encode(),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(parts["v1"], expected)
Reject any request whose signature doesn't validate. Also reject requests
older than 5 minutes (t field) — that's how we prevent replay.
Retry behaviour
Non-2xx responses retry with exponential backoff for 24 hours (10 attempts total). After that the event is dropped — but stays visible in the console's webhook log for 30 days so you can manually reprocess.
Last updated 13 May 2026Edit this page on GitHub