Webhooks
Signed events for finished or failed batch jobs, usage thresholds and expiring API keys — endpoint registration, event subscriptions, HMAC-SHA256 signature verification, retries, and auto-disable rules.
Register an endpoint
Webhooks push metadata-only events to your HTTPS endpoint. Registration requires the opt-in webhooks:manage scope — or use the Webhooks page of the dashboard (workspace admins and owners):
curl -sS -X POST https://api.transmute.403fin.io/v1/webhooks \
-H "Authorization: Bearer $TRANSMUTE_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/hooks/transmute", "events": ["job.completed", "usage.threshold"]}'
# → {"id": "...", "events": [...], "secret": "whsec_..."} secret shown ONCE
GET /v1/webhooks lists endpoints; DELETE /v1/webhooks/{id} removes one. The whsec_ secret is displayed once at creation, like API keys. The URL must be public https://; hosts that resolve to private or loopback addresses are refused.
Choose your events
events is optional — omit it to receive job.completed only.
| Event | Sent when | Body |
|---|---|---|
job.completed | a batch job that names this endpoint ("webhook": "<id>") finishes — completed, failed or partial | type, jobId, status, counts, createdAt, completedAt |
job.failed | that job failed outright (every item failed). Sent instead of job.completed when you subscribe to it, never both | same shape, "type": "job.failed" |
usage.threshold | your live usage crosses 75%, 90% or 100% of your plan's included volume — once per threshold per billing period (paid plans) | type, threshold, periodStart, periodEnd, plan |
key.expiring | one of your API keys is within 7 days, then within 1 day, of its expiry | type, keyId, prefix, expiresAt |
usage.threshold deliberately carries no usage figures or amounts — read them from GET /v1/usage. key.expiring names the key by id and its non-secret prefix only.
Verify every delivery
Each delivery is signed:
X-Transmute-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256>
X-Transmute-Event: job.completed # or job.failed, usage.threshold, key.expiring
X-Transmute-Delivery: <id> # stable across retries — dedupe on it
Verification:
- Compute
HMAC-SHA256(key = the full secret string including the "whsec_" prefix, message = t + "." + raw request body bytes), lowercase hex. - Compare against
v1constant-time. - Reject timestamps more than 5 minutes from your clock (replay guard).
Verify over the raw received bytes — parse the JSON only after the signature checks out. The SDKs ship a verifier (Go, TypeScript, Python).
Retries and auto-disable
| Behavior | Value |
|---|---|
| Success criterion | Any 2xx from your endpoint |
| Retry ladder | 0s, 1m, 5m, 30m, 2h — 5 attempts max |
| Dedupe | X-Transmute-Delivery is stable across retries |
| Auto-disable | 10 consecutively exhausted deliveries disable the endpoint |
| Re-enable | Delete and re-create the endpoint |
The dashboard's Webhooks page shows the last 20 deliveries of each endpoint — event, attempts, last HTTP status and timing (never the payload).
Event contents
Every body carries ids, statuses, thresholds and timing — never message content, usage figures or money. Fetch results via GET /v1/jobs/{id} with your API key; the webhook is a doorbell, not a data channel.