Integrations
Webhooks
Subscribe an HTTPS endpoint to Bytefuse events and receive signed, retried, at-least-once deliveries — with HMAC-SHA256 signatures, secret rotation, and a dead-letter queue for failures.
Which events fire today
The delivery system is fully live, but event emission is still rolling out. Today onlyinvoice.paid and the four team.member.* events actually fire. The job.* lifecycle events are in the catalog and subscribable, but nothing emits them yet — a job.completed subscription won’t deliver until job events go live. The table below marks which fire.Overview
Webhooks are org-scoped and managed by a member with the manage_webhooks permission. Delivery is a notification of already-metered work — webhooks themselves never cost credits.
Endpoints
https://api.bytefuse.in/api/v1/webhooks//api/v1/webhooks/event-typesEvents your plan can subscribe to/api/v1/webhooks/List endpoints/api/v1/webhooks/{id}Update url / events / active/api/v1/webhooks/{id}Delete an endpoint (204)/api/v1/webhooks/{id}/rotate-secretRotate the signing secret/api/v1/webhooks/{id}/testQueue a test delivery/api/v1/webhooks/{id}/deliveriesDelivery log (paginated)Create with a JSON body:
urlstringrequiredeventsstring[]requireddescriptionstringoptionalThe response includes secret — the signing secret — returned only on create and on rotate-secret.
Event catalog
invoice.paidbillingProYesteam.member.invitedteamProYesteam.member.joinedteamProYesteam.member.role_changedteamProYesteam.member.removedteamProYesjob.queuedjobFreenot yetjob.processingjobFreenot yetjob.completedjobFreenot yetjob.failedjobFreenot yetcredits.lowbillingFreenot yetinvoice.payment_failedbillingPronot yetsubscription.updatedbillingPronot yetapikey.createdkeyPronot yetapikey.revokedkeyPronot yetDelivery payload
Each delivery is a compact JSON POST to your URL:
{
"event": "invoice.paid",
"event_id": "3f1c…-uuid",
"timestamp": 1735689600,
"data": { "payment_id": "…", "amount_cents": 3000, "credits": 300000 }
}event_id is a stable UUID — use it as your idempotency key; retries and manual redeliveries reuse the same event_id. Headers sent with every delivery:
Webhook-IdThe event_id (idempotency key)Webhook-EventThe event keyWebhook-TimestampUnix seconds (also inside the signature)Webhook-Signaturev1,<base64 HMAC-SHA256>Verifying signatures
The signature is v1,base64(HMAC_SHA256(secret, "<event_id>.<timestamp>.<raw_body>")). Recompute it over the raw request body and constant-time compare. Reject if the timestamp is more than 300 seconds from now.
import hmac, hashlib, base64, time
def verify(secret: str, headers, raw_body: bytes) -> bool:
event_id = headers["Webhook-Id"]
timestamp = headers["Webhook-Timestamp"]
if abs(time.time() - int(timestamp)) > 300:
return False # replay window exceeded
signed = f"{event_id}.{timestamp}.".encode() + raw_body
digest = hmac.new(secret.encode(), signed, hashlib.sha256).digest()
expected = "v1," + base64.b64encode(digest).decode()
# header may carry multiple space-separated schemes
return any(hmac.compare_digest(expected, s) for s in headers["Webhook-Signature"].split())Secret rotation
POST /rotate-secret returns a new secret and keeps the previous one valid during a rotation window. Deliveries are always signed with the current secret; verify against current, falling back to previous, until you’ve switched.Delivery & retries
- At-least-once, durable — success is any HTTP
2xx; the send timeout is 10s. - Retries back off over up to 7 attempts: 1m, 5m, 30m, 2h, 5h, 12h. After the last failure the delivery is dead-lettered and can be manually redelivered.
- Auto-disable — after 15 consecutive failures the endpoint is disabled and the owner is alerted.
- SSRF-safe — the target URL is re-validated and DNS-pinned on every send.
- Redelivery re-sends the same payload and
event_id; it never re-runs the underlying work.
Inspect attempts via GET /api/v1/webhooks/{id}/deliveries — each entry has a status of pending, delivered or dead.
