Webhook endpoints
Add, list and remove webhook endpoints from your own code, so automation tools such as n8n and Zapier can subscribe to events by themselves. The same endpoints you manage in Developers.
On this page
The webhook endpoint object
A webhook endpoint is an address that receives your events. Endpoints you add here are the same ones you see and manage in Developers, with the same rules: each mode has its own endpoints, up to 10 of them, each at a different address.
| Field | Type | Description |
|---|---|---|
id | string | The endpoint's id, we_… |
object | string | "webhook_endpoint" |
url | string | Where events are sent |
description | string or null | Your label for it, up to 80 characters |
events | array of strings | The event types it receives |
livemode | boolean | false for Test mode, true for Live mode |
mode | string | "test" or "live": the same fact as livemode, in words |
previous_secret_expires_at | integer or null | After someone rolls the secret in Developers, when the previous secret stops signing, in Unix seconds. null the rest of the time |
created | integer | When it was added, in Unix seconds |
secret | string | The signing secret, whsec_…. Only in the response to Create |
{
"id": "we_N8nQ4vRx7KtZ2mLp9WcB",
"object": "webhook_endpoint",
"url": "https://automation.example.com/webhook/railbed-orders",
"description": "n8n: Fulfil paid orders",
"events": [
"payment.paid",
"payment.held"
],
"livemode": false,
"mode": "test",
"previous_secret_expires_at": null,
"created": 1790380525
}Create a webhook endpoint
POST/v1/webhook_endpoints
Adds an endpoint in the key's mode and returns it with its signing secret. Events start arriving with the next payment change.
| Parameter | Type | Description |
|---|---|---|
url | string | Required. A full https:// address with no username or password. Live endpoints must be on the public internet. Test endpoints also accept http://localhost, though Railbed's servers can't reach your computer that way |
events | array of strings | The event types to receive, at least one. Default: every type |
description | string | Optional label, up to 80 characters, shown on the endpoint in Developers. Name the tool and the job, such as n8n: Fulfil paid orders |
curl https://pay.railbed.io/v1/webhook_endpoints \
-H "Authorization: Bearer $RAILBED_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://automation.example.com/webhook/railbed-orders",
"description": "n8n: Fulfil paid orders",
"events": ["payment.paid", "payment.held"]
}'const endpoint = await fetch('https://pay.railbed.io/v1/webhook_endpoints', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.RAILBED_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://automation.example.com/webhook/railbed-orders',
description: 'n8n: Fulfil paid orders',
events: ['payment.paid', 'payment.held'],
}),
}).then((r) => r.json());
// Save endpoint.id and endpoint.secret now: the secret isn't returned again.endpoint = requests.post(
"https://pay.railbed.io/v1/webhook_endpoints",
headers={"Authorization": f"Bearer {os.environ['RAILBED_SECRET_KEY']}"},
json={
"url": "https://automation.example.com/webhook/railbed-orders",
"description": "n8n: Fulfil paid orders",
"events": ["payment.paid", "payment.held"],
},
timeout=15,
).json()
# Save endpoint["id"] and endpoint["secret"] now: the secret isn't returned again.{
"id": "we_N8nQ4vRx7KtZ2mLp9WcB",
"object": "webhook_endpoint",
"url": "https://automation.example.com/webhook/railbed-orders",
"description": "n8n: Fulfil paid orders",
"events": [
"payment.paid",
"payment.held"
],
"livemode": false,
"mode": "test",
"previous_secret_expires_at": null,
"created": 1790380525,
"secret": "whsec_Tq7mVx2RkL9pZc4WbN8yHd3Jf6Gs1Ae0Ku5Xo-Pi_Mr"
}The endpoint appears in Developers straight away, credited to the person who created the key (or approved the agent), and your account's activity records it with the key that added it. Keys created before October 9, 2026 don't record a creator, so their endpoints show no name.
| Error | Cause |
|---|---|
400 invalid_url | The address isn't a full https:// address, has a username or password, or (in Live) isn't on the public internet. field is url |
400 invalid_events | events is empty or names a type that doesn't exist. field is events |
400 too_long | description is over 80 characters |
409 duplicate_url | That address is already one of your endpoints in this mode |
409 too_many | This mode already has 10 endpoints. Remove one first |
403 merchant_read_only | The account is suspended or archived |
Retrieve a webhook endpoint
GET/v1/webhook_endpoints/:id
Returns one of your endpoints in the key's mode, without its secret. A removed endpoint, another account's, or one in the other mode is 404 not_found, so an automation tool can tell whether its subscription still exists.
curl https://pay.railbed.io/v1/webhook_endpoints/we_N8nQ4vRx7KtZ2mLp9WcB \
-H "Authorization: Bearer $RAILBED_SECRET_KEY"List webhook endpoints
GET/v1/webhook_endpoints
Returns your endpoints in the key's mode, newest first, without their secrets. Removed endpoints aren't listed.
| Parameter | Type | Description |
|---|---|---|
limit | integer | 1–100. Default 20 |
starting_after | string | An endpoint id from the previous page's next_cursor |
curl https://pay.railbed.io/v1/webhook_endpoints \
-H "Authorization: Bearer $RAILBED_SECRET_KEY"{
"data": [
{
"id": "we_N8nQ4vRx7KtZ2mLp9WcB",
"object": "webhook_endpoint",
"url": "https://automation.example.com/webhook/railbed-orders",
"description": "n8n: Fulfil paid orders",
"events": [
"payment.paid",
"payment.held"
],
"livemode": false,
"mode": "test",
"previous_secret_expires_at": null,
"created": 1790380525
}
],
"has_more": false,
"next_cursor": null
}Delete a webhook endpoint
DELETE/v1/webhook_endpoints/:id
Removes the endpoint: it stops receiving events and its pending retries stop. Its delivery history stays readable under Removed endpoints in Developers for 30 days. Deleting works even when the account is suspended or archived, as removing an endpoint in the dashboard does.
curl -X DELETE https://pay.railbed.io/v1/webhook_endpoints/we_N8nQ4vRx7KtZ2mLp9WcB \
-H "Authorization: Bearer $RAILBED_SECRET_KEY"{
"id": "we_N8nQ4vRx7KtZ2mLp9WcB",
"object": "webhook_endpoint",
"deleted": true
}Deleting an endpoint that was already removed is 404 not_found; treat that as done.
Access
These endpoints need the webhooks:read (retrieve, list) and webhooks:write (create, delete) scopes. Keys with Full access have both. Read only keys have neither, because an endpoint's address can carry its receiver's own token. Connected AI agents don't have them yet, so use a secret key.
For automation tools
A tool that subscribes on a user's behalf (an n8n trigger, a Zapier REST hook, your own job runner) follows the same pattern:
- When the user turns the automation on, create an endpoint with the tool's receiving address, the events it needs and a description naming the tool and the automation. Store the returned
idandsecretwith the automation. - Verify every delivery's
Railbed-Signaturewith that secret, as in Verify signatures, and answer2xxquickly. - Before reusing a stored subscription, retrieve it by
id. A404means it was removed (in Developers, or by another tool): create a new one. - When the user turns the automation off, delete the endpoint. A
404means it's already gone.
Every endpoint counts toward the 10 per mode, including ones a tool forgot to delete. Remove stale ones in Developers, or list them here and delete the ones your tool created (its description tells you which).