Webhooks
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Flux POSTs a signed JSON payload to every registered webhook URL when a lifecycle event occurs.
Event types
| Event | When |
|---|---|
run.started | A pipeline run begins |
run.succeeded | A run completes without error |
run.failed | A run exits with a non-zero status |
step.failed | A step exhausts its retry budget |
pipeline.deployed | A new version of a pipeline is pushed live |
Payload shape
{
"id": "evt_01J8B1Q3VZ9Y7C...",
"type": "run.failed",
"created_at": "2026-04-24T09:14:02Z",
"data": {
"run_id": "01J8AZX4K5PQC3D7X5YJ6V",
"pipeline_id": "orders.ingest",
"error": {
"step": "normalize",
"message": "ZodError: total must be >= 0",
"stack": "..."
}
}
}
Verifying the signature
Flux signs each delivery with HMAC-SHA256 using the secret you provided when
registering the webhook. The signature is in the X-Flux-Signature header.
import crypto from 'node:crypto';
import type { IncomingMessage } from 'node:http';
export function verify(req: IncomingMessage, rawBody: Buffer, secret: string) {
const header = req.headers['x-flux-signature'];
if (typeof header !== 'string') return false;
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(header),
Buffer.from(`sha256=${expected}`),
);
}
Always verify
Unverified webhook handlers are a well-known foot-gun. If you skip the HMAC check, anyone who guesses the URL can replay events into your system.
Retries
Flux retries failed deliveries with exponential back-off up to 24 hours. A
delivery is considered successful if the endpoint returns 2xx within 10
seconds.