Webhooks endpoints

The webhooks endpoints let you manage your company's webhook subscriptions programmatically: create a new endpoint, list existing ones, update events or URL, disable, and delete.

For the receiving side of webhooks (verifying signatures, handling retries, event payloads), see Webhooks.

GET /api/v1/webhooks

List all webhook endpoints configured for your company.

Required scope: webhooks:read

Example

curl https://distribu.app/api/v1/webhooks \
  -H "Authorization: Bearer dk_..."

Response

{
  "data": [
    {
      "id": "clxxwebhook1...",
      "url": "https://api.your-system.com/webhooks/distribu",
      "events": ["order.created", "order.status_changed"],
      "isActive": true,
      "secretRotationGraceExpiresAt": null,
      "createdAt": "2026-03-01T12:00:00.000Z",
      "updatedAt": "2026-04-10T09:30:00.000Z"
    }
  ]
}

Notes on fields

  • secret is never included in this response — it's only returned once, at creation time. Store it when you get it.
  • secretRotationGraceExpiresAt — Non-null when the webhook is currently in a rotation grace window (both the old and new secret are accepted). See Secret rotation below.
  • No pagination. There's a hard cap of 10 webhooks per company, so this endpoint always returns everything in one shot.

POST /api/v1/webhooks

Create a new webhook endpoint.

Required scope: webhooks:write

Request body

{
  "url": "https://api.your-system.com/webhooks/distribu",
  "events": ["order.created", "order.status_changed"],
  "isActive": true
}
FieldTypeRequiredNotes
urlstringyesHTTPS or HTTP URL. 1–2048 chars.
eventsstring[]yesAt least one valid event name. See Event types for the full catalog.
isActivebooleannoDefaults to true. Set to false to register the endpoint but not fire events yet.

Example

curl -X POST https://distribu.app/api/v1/webhooks \
  -H "Authorization: Bearer dk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.your-system.com/webhooks/distribu",
    "events": ["order.created", "order.status_changed"]
  }'

Response

201 Created. The secret field is returned only on this response — copy it somewhere safe immediately.

{
  "data": {
    "id": "clxxwebhook1...",
    "url": "https://api.your-system.com/webhooks/distribu",
    "events": ["order.created", "order.status_changed"],
    "isActive": true,
    "secretRotationGraceExpiresAt": null,
    "createdAt": "2026-04-17T14:22:00.000Z",
    "updatedAt": "2026-04-17T14:22:00.000Z",
    "secret": "whsec_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789-abcd"
  }
}

The secret is 24 random bytes, base64url-encoded, prefixed with whsec_ — around 37 characters total. Use it to verify incoming payloads via HMAC-SHA256; see Signature verification.

Errors

StatusMessage
400Invalid JSON body.
400URL is required.
400Invalid URL.
400At least one event is required.
400Unknown event: "{name}".
409Maximum of 10 webhooks per company.

GET /api/v1/webhooks/{id}

Fetch a single webhook's config.

Required scope: webhooks:read

Response

Same shape as one element of the list response. No secret.

{
  "data": {
    "id": "clxxwebhook1...",
    "url": "https://api.your-system.com/webhooks/distribu",
    "events": ["order.created", "order.status_changed"],
    "isActive": true,
    "secretRotationGraceExpiresAt": null,
    "createdAt": "2026-03-01T12:00:00.000Z",
    "updatedAt": "2026-04-10T09:30:00.000Z"
  }
}

Errors

  • 404Webhook not found. — No webhook with that ID in your company.

PATCH /api/v1/webhooks/{id}

Update a webhook's URL, events, or active state. Every field is optional.

Required scope: webhooks:write

Request body

{
  "url": "https://new-host.your-system.com/webhooks/distribu",
  "events": ["order.created"],
  "isActive": false
}
FieldTypeNotes
urlstring1–2048 chars.
eventsstring[]Replaces the full event list — not additive. Send the complete set you want subscribed.
isActivebooleanToggle the endpoint on/off.

Example

# Disable a webhook
curl -X PATCH https://distribu.app/api/v1/webhooks/clxxwebhook1... \
  -H "Authorization: Bearer dk_..." \
  -H "Content-Type: application/json" \
  -d '{ "isActive": false }'

Response

The full updated webhook, no secret.

Errors

StatusMessage
400Invalid URL.
400At least one event is required.
400Unknown event: "{name}".
404Webhook not found.

DELETE /api/v1/webhooks/{id}

Permanently delete a webhook. No confirmation, no undo — the row and its delivery log are removed.

Required scope: webhooks:write

Example

curl -X DELETE https://distribu.app/api/v1/webhooks/clxxwebhook1... \
  -H "Authorization: Bearer dk_..."

Response

{
  "data": {
    "id": "clxxwebhook1...",
    "deleted": true
  }
}

Errors

  • 404Webhook not found.

Secret rotation

Secret rotation — generating a new signing secret while keeping the old one valid during a grace window — is available in the dashboard (Settings → Webhooks → Rotate secret) but is not currently exposed over the API.

When a rotation is in progress:

  • The webhook's secretRotationGraceExpiresAt field is set to the cutover time.
  • Incoming webhook deliveries include both an X-Webhook-Signature header (new secret) and an X-Webhook-Signature-Old header (old secret) so your receiver can migrate without downtime.
  • After the grace window expires, only the new secret is used.

If rotating secrets over the API is blocking you, email support@distribu.app — it's on the roadmap.

Event catalog

events[] must contain only valid event names. The full list:

EventFires when…
order.createdA new order is placed (storefront or API).
order.status_changedAn order transitions between statuses.
order.shippedAn order transitions specifically to SHIPPED.
order.cancelledAn order transitions specifically to CANCELLED.
customer.createdA new customer account is created.
customer.updatedA customer's details change.
product.updatedA product is edited.
product.low_stockA product's stock crosses below its low-stock threshold.
invoice.paidA Stripe invoice succeeds (subscription billing).
return.createdA return is opened.
return.approvedA return transitions to APPROVED.
refund.processedA refund is issued.

See Event types for the payload shape of each.

What's not here (yet)

  • POST /api/v1/webhooks/{id}/rotate-secret — Secret rotation is dashboard-only today.
  • Delivery log access. The per-delivery history (status codes, response times) visible in the dashboard isn't exposed over the API. Use the dashboard for forensics.
  • Manual redelivery. The "Resend" button in the dashboard doesn't have an API equivalent.

Related: