Webhooks
Generated page, from the OpenAPI specification the gateway serves (
GET /v1/openapi.json). Do not edit it by hand: it is rewritten on every build.
| Method | Path | Description |
|---|---|---|
GET |
/v1/webhooks |
List your notification endpoints |
POST |
/v1/webhooks |
Register an endpoint |
POST |
/v1/webhooks/test |
Trigger a test delivery |
GET |
/v1/webhooks/{id} |
Retrieve an endpoint |
PATCH |
/v1/webhooks/{id} |
Update an endpoint |
DELETE |
/v1/webhooks/{id} |
Delete an endpoint |
GET |
/v1/webhooks/{id}/secret |
Read the signing secret again |
GET /v1/webhooks
List your notification endpoints
Your endpoints, with their delivery counters. consecutive_failures is the field to look at when notifications stop: twenty consecutive failures suspend an endpoint for an hour, and a single success restores it.
ⓘ Endpoints are NOT separated between test and live: a test key and a live key see the same ones.
Responses
200— OK (WebhookList)401— API key missing or rejected (Error)403— The key does not carry the can_manage_webhooks scope (Error)429— The key's request budget is exhausted (300/min) — honour Retry-After (Error)
POST /v1/webhooks
Register an endpoint
⚠️ THIS CALL GUARANTEES, IT DOES NOT ALWAYS CREATE. A URL already registered comes back as 200 rather than being duplicated, so a provisioning script run twice leaves ONE endpoint and not two receiving every event in duplicate. The secret is returned in both cases, because the legitimate reason to call again is having lost it.
Ten endpoints per account at most. An empty subscribed_events means all events; otherwise, from: payment.initiated, payment.pending, payment.success, payment.failed, payout.success, payout.failed, payout.cancelled, payout.refunded, refund.success, refund.failed, and the subscription.* family.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | yes | HTTPS required. |
description |
string | no | So you can find it again in the list. |
subscribed_events |
string[] | no | Empty = all of them. An event outside the catalogue is refused rather than stored: it would wait for a delivery nothing emits. |
Responses
200— The endpoint already existed for this URL (WebhookCreated)201— Endpoint created (WebhookCreated)401— API key missing or rejected (Error)403— The key does not carry the can_manage_webhooks scope (Error)422— Non-HTTPS URL, event outside the catalogue, or ten endpoints already registered (Error)429— The key's request budget is exhausted (300/min) — honour Retry-After (Error)
POST /v1/webhooks/test
Trigger a test delivery
Sends a payment.test event to ALL your active endpoints and returns the result of each delivery. Nothing is persisted and no money is at stake — this is how you check your signature verification before the first real payment.
ⓘ Scope can_read_transactions, not can_manage_webhooks: the call changes no configuration.
Responses
200— The result for each endpoint400— No active endpoint to deliver to (Error)401— API key missing or rejected (Error)403— The key does not carry the can_manage_webhooks scope (Error)429— The key's request budget is exhausted (300/min) — honour Retry-After (Error)
GET /v1/webhooks/{id}
Retrieve an endpoint
The same object as in the list.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
id |
path | yes | The endpoint's number, as returned by GET /v1/webhooks. |
Responses
200— OK (object)401— API key missing or rejected (Error)403— The key does not carry the can_manage_webhooks scope (Error)404— No endpoint with that number on this account (Error)429— The key's request budget is exhausted (300/min) — honour Retry-After (Error)
PATCH /v1/webhooks/{id}
Update an endpoint
A PARTIAL update: only the fields sent change. Omitting is_active does not deactivate the endpoint, and omitting description does not clear it — which is not a subtlety, since the opposite would mean a call renaming an endpoint silently stopped its own deliveries.
is_active: false suspends deliveries without losing the endpoint or its secret.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
id |
path | yes | The endpoint's number, as returned by GET /v1/webhooks. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | no | HTTPS required. |
description |
string | no | |
subscribed_events |
string[] | no | |
is_active |
boolean | no |
Responses
200— OK (object)401— API key missing or rejected (Error)403— The key does not carry the can_manage_webhooks scope (Error)404— No endpoint with that number on this account (Error)422— Non-HTTPS URL or event outside the catalogue (Error)429— The key's request budget is exhausted (300/min) — honour Retry-After (Error)
DELETE /v1/webhooks/{id}
Delete an endpoint
Permanent. To stop deliveries without losing the endpoint and its secret, prefer PATCH with is_active: false.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
id |
path | yes | The endpoint's number, as returned by GET /v1/webhooks. |
Responses
204— Deleted401— API key missing or rejected (Error)403— The key does not carry the can_manage_webhooks scope (Error)404— No endpoint with that number on this account (Error)429— The key's request budget is exhausted (300/min) — honour Retry-After (Error)
GET /v1/webhooks/{id}/secret
Read the signing secret again
ⓘ YES, THIS ONE CAN BE READ AGAIN, unlike an API key's secret. The difference is not an inconsistency: the platform must hold the webhook secret in order to SIGN, so it is stored recoverably. An API key is stored only as a fingerprint, and therefore cannot be read back.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
id |
path | yes | The endpoint's number, as returned by GET /v1/webhooks. |
Responses
200— OK (WebhookSecret)401— API key missing or rejected (Error)403— The key does not carry the can_manage_webhooks scope (Error)404— No endpoint with that number on this account (Error)429— The key's request budget is exhausted (300/min) — honour Retry-After (Error)
Schemas
The shapes the operations above return.
WebhookList
| Field | Type | Description |
|---|---|---|
data |
Webhook[] | |
data[].id |
integer | |
data[].url |
string | |
data[].description |
string | |
data[].subscribed_events |
string[] | |
data[].is_active |
boolean | |
data[].created_at |
string | |
data[].total_sent |
integer | |
data[].total_success |
integer | |
data[].total_failed |
integer | |
data[].consecutive_failures |
integer | Twenty consecutive failures suspend the endpoint for an hour; a single success reinstates it. |
data[].last_success_at |
string | |
data[].last_failure_at |
string |
Error
| Field | Type | Description |
|---|---|---|
error |
object | |
error.code |
string | |
error.message |
string |
WebhookCreated
| Field | Type | Description |
|---|---|---|
data |
object | |
data.webhook |
Webhook | A notification endpoint, with the delivery counters the platform keeps. |
data.webhook.id |
integer | |
data.webhook.url |
string | |
data.webhook.description |
string | |
data.webhook.subscribed_events |
string[] | |
data.webhook.is_active |
boolean | |
data.webhook.created_at |
string | |
data.webhook.total_sent |
integer | |
data.webhook.total_success |
integer | |
data.webhook.total_failed |
integer | |
data.webhook.consecutive_failures |
integer | Twenty consecutive failures suspend the endpoint for an hour; a single success reinstates it. |
data.webhook.last_success_at |
string | |
data.webhook.last_failure_at |
string | |
data.secret |
string | The signing secret. It appears here and on GET /v1/webhooks/{id}/secret, nowhere else. |
data.created |
boolean | false when the endpoint already existed for this URL. |
WebhookSecret
| Field | Type | Description |
|---|---|---|
data |
object | |
data.id |
integer | |
data.url |
string | |
data.secret |
string |