API reference

Webhook endpoints

Register the URLs that receive events, rotate their signing secrets, send test events and work with the delivery log. Organization admins can do the same in the Flux app under Settings → Developers.

EndpointWhat it does
GET /api/v1/webhook-endpointsList endpoints
POST /api/v1/webhook-endpointsRegister an endpoint
GET /api/v1/webhook-endpoints/{id}Get an endpoint
PATCH /api/v1/webhook-endpoints/{id}Update an endpoint
DELETE /api/v1/webhook-endpoints/{id}Delete an endpoint
POST /api/v1/webhook-endpoints/{id}/rotate-secretRotate the signing secret
POST /api/v1/webhook-endpoints/{id}/testSend a test event
GET /api/v1/webhook-endpoints/{id}/deliveriesList deliveries
POST /api/v1/webhook-deliveries/{id}/redeliverRedeliver

All these endpoints need the webhooks:manage scope. To receive and verify events, see Receiving webhooks.

The endpoint object#

A URL that receives events.

FieldDescription
idintegerEndpoint id.
urlstringWhere events are sent.
descriptionstring · nullableYour label.
eventsarray of stringsEvent types it receives, or ["*"] for all.
activebooleanWhether it receives events.
disabledReasonstring · nullableWhy Flux switched it off, if it did.
consecutiveFailuresintegerFailed delivery attempts in a row. At 50, Flux switches the endpoint off.
createdAttimestampWhen it was created. UTC, no offset.
secretstring · nullableThe signing secret, whsec_…. Only on create and rotate, shown once; null otherwise.
Webhook endpoint
{
  "id": 3,
  "url": "https://hooks.example.com/flux",
  "description": "Billing sync",
  "events": ["invoice.paid", "receipt.issued"],
  "active": true,
  "disabledReason": null,
  "consecutiveFailures": 0,
  "createdAt": "2026-11-01T10:00:00",
  "secret": "whsec_Xq3vT8mKp2Lr7Ns1Wd5Yb9Hc4Fj6Ga0Z"
}

The delivery object#

One event sent to one endpoint, with its attempt history.

Delivery status: pending (waiting to be sent or retried), delivered (your endpoint returned 2xx), dead (all 10 attempts failed) or cancelled (the endpoint was deleted).

FieldDescription
idinteger (64-bit)Delivery id. Use it to redeliver.
eventIdstringThe event, evt_…. Same as the Flux-Event-Id header.
eventTypestringThe event type.
statusstringDelivery state. One of pending, delivered, dead, cancelled.
attemptsintegerAttempts so far. At most 10 before dead, plus any redeliveries.
lastStatusCodeinteger · nullableHTTP status of the last attempt.
lastErrorstring · nullableWhat went wrong on the last attempt, up to 500 characters.
nextAttemptAttimestamp · nullableWhen the next retry is due. UTC, no offset.
lastAttemptAttimestamp · nullableUTC, no offset.
deliveredAttimestamp · nullableWhen your endpoint returned 2xx. UTC, no offset.
createdAttimestampWhen the delivery was queued. UTC, no offset.
Delivery
{
  "id": 99001,
  "eventId": "evt_01JC8Z5T6Q1W3M4N5P6R7S8T9V",
  "eventType": "invoice.paid",
  "status": "delivered",
  "attempts": 1,
  "lastStatusCode": 200,
  "lastError": null,
  "nextAttemptAt": null,
  "lastAttemptAt": "2026-11-03T14:02:05",
  "deliveredAt": "2026-11-03T14:02:05",
  "createdAt": "2026-11-03T14:02:04"
}

List endpoints#

GET/api/v1/webhook-endpointswebhooks:manage

All the organization's webhook endpoints, as an array, oldest first.

Request

curl "$FLUX_BASE_URL/api/v1/webhook-endpoints" \
  -H "Authorization: ApiKey $FLUX_API_KEY"

Responses

200 OK
200 OK
[
  {
    "id": 3,
    "url": "https://hooks.example.com/flux",
    "description": "Billing sync",
    "events": ["invoice.paid", "receipt.issued"],
    "active": true,
    "disabledReason": null,
    "consecutiveFailures": 0,
    "createdAt": "2026-11-01T10:00:00",
    "secret": null
  }
]
3 possible errors
StatusCodeWhen
401unauthorizedNo API key, an unknown key or a revoked key.
403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }.
429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.

Register an endpoint#

POST/api/v1/webhook-endpointswebhooks:manageIdempotency-Key

Registers a URL to receive events. The response includes the signing secret. It's shown once: store it in your secret manager straight away.

Request body

FieldDescription
urlstringrequired An absolute https URL of at most 500 characters, publicly reachable. Required on create.
descriptionstringoptional At most 200 characters.
eventsarray of stringsrequired At least one known event type, or ["*"] for all. Required on create.
activebooleanoptional Default true. Setting true re-enables a disabled endpoint and resets its failure count.

The URL must be an absolute https URL of at most 500 characters, with no credentials in it. It can't be or resolve to a loopback, private, link-local or carrier-grade NAT address; Flux checks this again on every delivery. Flux doesn't follow redirects. events must contain at least one known event type, or "*" for all.

Request

curl -X POST "$FLUX_BASE_URL/api/v1/webhook-endpoints" \
  -H "Authorization: ApiKey $FLUX_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: endpoint-billing-sync" \
  -d '{
  "url": "https://hooks.example.com/flux",
  "description": "Billing sync",
  "events": ["invoice.paid", "receipt.issued"]
}'

Responses

201 Created
201 Created
{
  "id": 3,
  "url": "https://hooks.example.com/flux",
  "description": "Billing sync",
  "events": ["invoice.paid", "receipt.issued"],
  "active": true,
  "disabledReason": null,
  "consecutiveFailures": 0,
  "createdAt": "2026-11-01T10:00:00",
  "secret": "whsec_Xq3vT8mKp2Lr7Ns1Wd5Yb9Hc4Fj6Ga0Z"
}
7 possible errors
StatusCodeWhen
400invalid_requestValidation failed. The message says what, such as Add at least one invoice item.
400invalid_idempotency_keyIdempotency-Key is longer than 255 characters.
409idempotency_in_progressA request with the same Idempotency-Key is still running.
422idempotency_key_reusedThe same Idempotency-Key was sent with a different request.
401unauthorizedNo API key, an unknown key or a revoked key.
403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }.
429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.

Get an endpoint#

GET/api/v1/webhook-endpoints/{id}webhooks:manage

Returns one endpoint. secret is null.

Path parameters

FieldDescription
idintegerrequired The webhook endpoint's id.

Request

curl "$FLUX_BASE_URL/api/v1/webhook-endpoints/3" \
  -H "Authorization: ApiKey $FLUX_API_KEY"

Responses

200 OK
200 OK
{
  "id": 3,
  "url": "https://hooks.example.com/flux",
  "description": "Billing sync",
  "events": ["invoice.paid", "receipt.issued"],
  "active": true,
  "disabledReason": null,
  "consecutiveFailures": 0,
  "createdAt": "2026-11-01T10:00:00",
  "secret": null
}
4 possible errors
StatusCodeWhen
404not_foundUnknown id, or an id that belongs to another organization.
401unauthorizedNo API key, an unknown key or a revoked key.
403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }.
429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.

Update an endpoint#

PATCH/api/v1/webhook-endpoints/{id}webhooks:manage

Changes the URL, events, description or active state. Setting active: true re-enables an endpoint Flux switched off, and resets its failure count.

Path parameters

FieldDescription
idintegerrequired The webhook endpoint's id.

Request body

FieldDescription
urlstringoptional An absolute https URL of at most 500 characters, publicly reachable. Required on create.
descriptionstringoptional At most 200 characters.
eventsarray of stringsoptional At least one known event type, or ["*"] for all. Required on create.
activebooleanoptional Default true. Setting true re-enables a disabled endpoint and resets its failure count.

Request

curl -X PATCH "$FLUX_BASE_URL/api/v1/webhook-endpoints/3" \
  -H "Authorization: ApiKey $FLUX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "active": true
}'

Responses

200 OK
200 OK
{
  "id": 3,
  "url": "https://hooks.example.com/flux",
  "description": "Billing sync",
  "events": ["invoice.paid", "receipt.issued"],
  "active": true,
  "disabledReason": null,
  "consecutiveFailures": 0,
  "createdAt": "2026-11-01T10:00:00",
  "secret": null
}
5 possible errors
StatusCodeWhen
400invalid_requestValidation failed. The message says what, such as Add at least one invoice item.
404not_foundUnknown id, or an id that belongs to another organization.
401unauthorizedNo API key, an unknown key or a revoked key.
403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }.
429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.

Delete an endpoint#

DELETE/api/v1/webhook-endpoints/{id}webhooks:manage

Deletes the endpoint and cancels its pending deliveries.

Path parameters

FieldDescription
idintegerrequired The webhook endpoint's id.

Request

curl -X DELETE "$FLUX_BASE_URL/api/v1/webhook-endpoints/3" \
  -H "Authorization: ApiKey $FLUX_API_KEY"

Responses

204 No Content

No body.

4 possible errors
StatusCodeWhen
404not_foundUnknown id, or an id that belongs to another organization.
401unauthorizedNo API key, an unknown key or a revoked key.
403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }.
429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.

Rotate the signing secret#

POST/api/v1/webhook-endpoints/{id}/rotate-secretwebhooks:manage

Creates a new signing secret and returns it once. The old secret stops working immediately, so deploy the new one promptly. See Rotate keys and secrets.

Path parameters

FieldDescription
idintegerrequired The webhook endpoint's id.

Request

curl -X POST "$FLUX_BASE_URL/api/v1/webhook-endpoints/3/rotate-secret" \
  -H "Authorization: ApiKey $FLUX_API_KEY"

Responses

200 OK
200 OK
{
  "id": 3,
  "url": "https://hooks.example.com/flux",
  "description": "Billing sync",
  "events": ["invoice.paid", "receipt.issued"],
  "active": true,
  "disabledReason": null,
  "consecutiveFailures": 0,
  "createdAt": "2026-11-01T10:00:00",
  "secret": "whsec_P7nR2kV9cX4mT1bQ8wL5yJ3hD6gF0sZa"
}
4 possible errors
StatusCodeWhen
404not_foundUnknown id, or an id that belongs to another organization.
401unauthorizedNo API key, an unknown key or a revoked key.
403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }.
429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.

Send a test event#

POST/api/v1/webhook-endpoints/{id}/testwebhooks:manage

Queues a webhook.test event to this endpoint only. It's signed and retried like any other event.

Path parameters

FieldDescription
idintegerrequired The webhook endpoint's id.

Request

curl -X POST "$FLUX_BASE_URL/api/v1/webhook-endpoints/3/test" \
  -H "Authorization: ApiKey $FLUX_API_KEY"

Responses

202 Accepted

No body. The delivery appears in the delivery log.

4 possible errors
StatusCodeWhen
404not_foundUnknown id, or an id that belongs to another organization.
401unauthorizedNo API key, an unknown key or a revoked key.
403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }.
429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.

List deliveries#

GET/api/v1/webhook-endpoints/{id}/deliverieswebhooks:manage

The endpoint's delivery log, newest first.

Path parameters

FieldDescription
idintegerrequired The webhook endpoint's id.

Query parameters

FieldDescription
limitintegerItems per page, 1 to 100. Default 25.
cursorstringThe nextCursor from the previous page. Leave it out for the first page.

Request

curl "$FLUX_BASE_URL/api/v1/webhook-endpoints/3/deliveries" \
  -H "Authorization: ApiKey $FLUX_API_KEY"

Responses

200 OK
200 OK
{
  "data": [
    {
      "id": 99001,
      "eventId": "evt_01JC8Z5T6Q1W3M4N5P6R7S8T9V",
      "eventType": "invoice.paid",
      "status": "delivered",
      "attempts": 1,
      "lastStatusCode": 200,
      "lastError": null,
      "nextAttemptAt": null,
      "lastAttemptAt": "2026-11-03T14:02:05",
      "deliveredAt": "2026-11-03T14:02:05",
      "createdAt": "2026-11-03T14:02:04"
    }
  ],
  "nextCursor": null,
  "hasMore": false
}
4 possible errors
StatusCodeWhen
404not_foundUnknown id, or an id that belongs to another organization.
401unauthorizedNo API key, an unknown key or a revoked key.
403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }.
429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.

Redeliver#

POST/api/v1/webhook-deliveries/{id}/redeliverwebhooks:manage

Sends a delivery again now, whatever its status, keeping its attempt history. Use it for dead deliveries once your endpoint is fixed.

Path parameters

FieldDescription
idinteger (64-bit)required The delivery's id, from the delivery log.

Request

curl -X POST "$FLUX_BASE_URL/api/v1/webhook-deliveries/99001/redeliver" \
  -H "Authorization: ApiKey $FLUX_API_KEY"

Responses

202 Accepted

No body.

4 possible errors
StatusCodeWhen
404not_foundUnknown id, or an id that belongs to another organization.
401unauthorizedNo API key, an unknown key or a revoked key.
403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }.
429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.