How to register a Webhook
To receive webhooks, you register a receiving URL and the events you want on a developer app. You can do this in the developer portal or through the API. This article covers both, plus the test send, changing subscribed events, and pausing and deleting a webhook.
Before you start
- An app registered in the developer portal. The developer API is available on the Starter plan and above
- An
https://receiving URL reachable from the internet.localhostand private IP addresses cannot be registered - To register in the portal: you are the owner of the business account that owns the app
- To register through the API: an access token bound to a business account (obtained through the business-account authorization flow). The webhook API does not require any particular scope
Portal or API
| Operation | Developer portal | API |
|---|---|---|
| Register | Yes (secret is generated) | Yes (give a secret or have one generated) |
| Whose events it receives | The business account that owns the app | The business account the token is bound to |
| List and check status | Yes (active/inactive, last delivered) | Yes |
| Pause and resume | Yes | Yes |
| Change subscribed events | No | Yes |
| Test send | No | Yes |
| Delivery log | No | Yes (latest 50) |
| Delete | Yes | Yes |
The receiving URL cannot be changed later. To move to a new URL, register a new webhook and delete the old one.
Register in the portal
- Open the app from the developer portal's app list (
/en/organizations/{organizationId}/developer/apps) - In the Webhooks card, select Register
- Enter the URL, tick the events you want, and select Register Webhook
- The signing secret is shown at the top of the page. It is shown only this once, so copy it now and set it on your receiving server
The form lists every event name, but only receipt.created, order.created and order.updated are actually sent (see below).
Register through the API
curl -X POST https://receiptroller.io/api/v1/webhooks \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/rr-webhook",
"eventTypes": ["receipt.created"],
"secret": "{webhook_secret}"
}'
| Field | Required | Contents |
|---|---|---|
url | Yes | An absolute URL starting with https://. Not localhost, 127.0.0.1 or a private IP address |
eventTypes | Yes | An array of event names to subscribe to (at least one). The name is eventTypes, not event_types |
secret | No | The signing secret (16 to 64 characters). If omitted, a 32-character alphanumeric secret is generated |
On success you get status 201 and the response below. secret appears only in this response; no endpoint returns it later.
{
"id": "wh_xxxxxxxxxxxxxxxx",
"clientId": "{client_id}",
"organizationId": "{organizationId}",
"url": "https://example.com/rr-webhook",
"eventTypes": ["receipt.created"],
"active": true,
"createdAt": "2026-10-26T03:00:00.0000000Z",
"lastDeliveredAt": null,
"secret": "{webhook_secret}"
}
Registration errors
| Status | Message | Cause |
|---|---|---|
| 401 | (no body) | The token is missing, expired or invalid |
| 400 | organization_id not found in token. | The token is not bound to a business account |
| 400 | url is required. | url is empty |
| 400 | url must be a valid HTTPS address. | Not an absolute https:// URL |
| 400 | url must not point to localhost or private IP ranges. | A local or private-IP URL |
| 400 | event_types must contain at least one event. | No events. You also get this if you name the field event_types |
| 400 | Unknown event types: ... | An event name that does not exist. The message goes on to list the valid names |
| 400 | secret must be 16 to 64 characters, or omitted to have one generated. | secret is too short or too long |
Choosing events
| Event | Sent |
|---|---|
receipt.created | When a new sale is saved for the first time |
order.created | When an order is created |
order.updated | When an order is edited or its status changes |
receipt.updated, receipt.deleted, coupon.redeemed, coupon.expired, customer.visited | Can be registered, but are not sent |
For what each event contains, see Webhook overview.
Test send
curl -X POST https://receiptroller.io/api/v1/webhooks/test/{webhook_id} \
-H "Authorization: Bearer {access_token}"
One test.ping event is sent to the receiving URL and the result is returned.
{
"webhookId": "wh_xxxxxxxxxxxxxxxx",
"httpStatus": 200,
"success": true,
"deliveredAt": "2026-10-26T03:01:00.0000000Z"
}
- The
datain the body your URL receives is{ "message": "This is a test ping from Receipt Roller." }. It is signed with the same secret as real events - The API answers
200whenever the webhook exists. Whether it arrived is insuccess(your server returned2xx) andhttpStatus. AnhttpStatusof0means the connection itself failed - The test is sent regardless of subscribed events or the active flag, is not retried, and is not written to the delivery log
404means the ID is wrong or the webhook belongs to another app
Change, pause and delete
Change subscribed events, and pause or resume, with PATCH. Only the fields you send change, and eventTypes replaces the whole list.
curl -X PATCH https://receiptroller.io/api/v1/webhooks/{webhook_id} \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{ "eventTypes": ["receipt.created", "order.created"], "active": true }'
"active": falsepauses,"active": trueresumes. The portal's pause and resume buttons do the same- Events that happen while paused are not held back and do not arrive after you resume
- A webhook that fails four delivery attempts in a row is switched to
active: falseautomatically. Resume it the same way once your server is fixed - Delete with
DELETE /api/v1/webhooks/{webhook_id}(204on success). Deleting cannot be undone - List registered webhooks with
GET /api/v1/webhooksand fetch one withGET /api/v1/webhooks/{webhook_id}. Neither includessecret
Using several webhooks
- One app can have several webhooks. Every webhook subscribed to an event receives it, all with the same
X-RR-Delivery - To receive events from several business accounts, register with each business account's token
- List, get, change, delete and test cover every webhook the app has registered (the list is the same whichever business account's token you call it with)
Checklist after registering
- The receiving URL is
https://and reachable from outside - The secret is stored safely and set on the receiving server
- The receiving server verifies the signature (Signature verification and security)
- The test send returns
success: true - Duplicates are dropped by
X-RR-Delivery - Your server returns
2xxwithin 10 seconds
Related guides
-
Receive new sales by webhook and post them to SlackRegister a receipt.created webhook, verify its signature and drop duplicates in Node.js, and post each new sale to a Slack channel.
-
Designing resends, ordering, and idempotencyWhat counts as success, the retry rules (1 min, 10 min, 1 h, four attempts), ordering and lost events, and dropping duplicates by X-RR-Delivery.
-
Webhook is not being deliveredWhen a webhook does not arrive: narrow it down with a test send and the delivery log, then check the active flag, subscribed events, connection and signature.
-
Developer Help IndexThe index of ReceiptRoller developer help. Covers the developer application, app registration, OAuth authentication and scopes, implementation guides (wallet apps, store-facing Webhooks, the Survey API), guides by data domain, operations and security, the community, and troubleshooting.
-
Monitoring and handling failuresRead a webhook's active flag, last delivery time and delivery log, notice when it has switched itself off, recover, and fill the gap from the API.