Version

Webhooks & Events

Event-driven HTTPS notifications from Zuse Bifrost to your server, CI or anything that takes JSON over POST, with retries and replay.

Outbound webhooks let any HTTPS endpoint subscribe to the events Zuse Bifrost emits. When something happens, a project is created, a member joins a team, a role changes, an API token is minted, a budget runs low, Zuse Bifrost POSTs a signed JSON envelope to every endpoint subscribed to that event. The families grow with the product: planning, build, test, release and maintenance events join the catalogue as each module lands, and every one is documented on its own generated page.

Lifecycle

  1. Register an endpoint in Settings → Webhooks or through the REST API, with a name, an https:// URL and the events it should receive. The response shows the signing secret once.
  2. An event fires inside Zuse Bifrost. Every state change goes through one action, and the action announces it; a page, an API call and an MCP tool that make the same change raise the same event.
  3. Fan-out. The dispatcher finds every active endpoint subscribed to the event within the team the event happened in (and, for a project event, the endpoints narrowed to that project), writes one delivery row per endpoint in a single transaction, and queues each on the webhooks queue.
  4. Delivery. The worker posts the signed envelope. A 2xx response marks the delivery delivered; a non-2xx response or a timeout marks it failed and schedules the next attempt.
  5. Retry ladder. Up to 8 attempts over roughly four days: 10s, 30s, 2m, 10m, 1h, 6h, 24h, 72h.
  6. Dead letter. On the eighth failure the delivery is marked dead. After 20 consecutive failures the endpoint is switched off until somebody resumes it.

What lives in every payload

{
  "id": "evt_01HX...",
  "type": "project.created",
  "created_at": "2026-11-02T10:05:00+00:00",
  "api_version": "2026-11-01",
  "project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",
  "data": { "...event-specific..." }
}
  • id is a ULID, unique per event and shared by every delivery of that event, so a consumer subscribed through two endpoints can de-duplicate across them.
  • type matches one of the catalogue entries; the event reference has a page per event with its payload, field by field.
  • created_at is ISO 8601 server time, not the arrival time at your endpoint.
  • api_version locks the event shape. Additive fields keep the version; a field that changes meaning ships under a new one.
  • project_id is null for team-scoped events (client.*, team.*, role.*, token.*, a team-wide budget alert).
  • data is the event-specific payload. Its keys are declared on the event itself and held to the code by the application's own test suite, so the event pages never describe a field that does not arrive.

What lives in every request header

  • Content-Type: application/json
  • User-Agent: ZuseBifrost-Webhooks/1.0
  • ZG-Signature: t=<unix>,v1=<hex>, an HMAC-SHA256 over "<t>.<raw_body>". See signature verification.
  • ZG-Event-Id: <ulid>, the bare ULID. payload.id is the same ULID prefixed with evt_, so the two are not byte-equal; strip the prefix before comparing.
  • ZG-Event-Name: <type>, which mirrors payload.type.

Reject any request where |now - t| > 300. The five-minute tolerance window is the replay-attack defence. It is the consumer's job to enforce it; Zuse Bifrost enforces the same window in its own verify helper for parity.

Ordering guarantees

  • Deliveries are not strictly ordered. A project.status_changed may arrive before the project.updated that preceded it under retry.
  • Consumers should be idempotent on the event id and accept out-of-order delivery. Use the most recent event per resource when reconciling state, or read the resource back through the API.

Where to go next

How is this guide?

Last updated on

Version

On this page

What changed recentlyEvery release of Zuse Bifrost, with the pages it touched.

For AI agents: llms.txt · llms-full.txt