Bytefusedocs

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 only invoice.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

POSThttps://api.bytefuse.in/api/v1/webhooks/
GET/api/v1/webhooks/event-typesEvents your plan can subscribe to
GET/api/v1/webhooks/List endpoints
PATCH/api/v1/webhooks/{id}Update url / events / active
DELETE/api/v1/webhooks/{id}Delete an endpoint (204)
POST/api/v1/webhooks/{id}/rotate-secretRotate the signing secret
POST/api/v1/webhooks/{id}/testQueue a test delivery
GET/api/v1/webhooks/{id}/deliveriesDelivery log (paginated)

Create with a JSON body:

urlstringrequired
Your subscriber URL (SSRF-validated on every send; HTTPS required in production).
eventsstring[]required
At least one event key to subscribe to (see the catalog below).
descriptionstringoptional
Up to 300 characters.

The response includes secret — the signing secret — returned only on create and on rotate-secret.

Event catalog

EventCategoryMin tierFires today
invoice.paidbillingProYes
team.member.invitedteamProYes
team.member.joinedteamProYes
team.member.role_changedteamProYes
team.member.removedteamProYes
job.queuedjobFreenot yet
job.processingjobFreenot yet
job.completedjobFreenot yet
job.failedjobFreenot yet
credits.lowbillingFreenot yet
invoice.payment_failedbillingPronot yet
subscription.updatedbillingPronot yet
apikey.createdkeyPronot yet
apikey.revokedkeyPronot yet

Delivery payload

Each delivery is a compact JSON POST to your URL:

POST to your endpoint
{
  "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 key
Webhook-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.

Verify (Python)
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.