Webhooks
A webhook endpoint is a URL on your server that we send your account’s events to as they happen: a payment created, processing, succeeded, failed or expired, or given new payment details. You don’t have to ask for a payment’s status: the event tells you. Every event is also kept for 30 days and can be read with GET /events.
Add an endpoint
Section titled “Add an endpoint”Endpoints are set up in the merchant portal, under Settings › Developers › Webhooks, by your account’s admins and developers, or by your account manager. The API has no endpoints for them.
- The URL is an
httpsURL on a public host name: no IP address, no user name or password. Its path and query are yours; we send to it as it is. - Its event types are the ones it gets, or all of them, those we add later too. An endpoint gets only the events of the types it listened to when they happened: changing its types later does not send it earlier events.
- Each endpoint has its own signing secret,
whsec_…. Keep it: you need it to check that a request is ours (Signatures). Roll it in the portal (until its Webhooks page opens, ask your account manager) to get a new one; the old one can keep signing for up to 7 days while you deploy the new one. - An account has at most 16 endpoints in each mode.
- A test mode endpoint gets test mode’s events, a live one live mode’s: the modes never mix.
What we send
Section titled “What we send”Each event is one POST to the endpoint, its body the event object as JSON, shaped in the endpoint’s API version (its api_version, or your account’s default):
{ "id": "evt_in_01K7…", "object": "event", "type": "payment.succeeded", "api_version": "2026-10-14.acacia", "created": 1760000000, "livemode": false, "account": "macc_01K7…", "data": { "object": { "id": "pay_in_01K7…", "object": "payment", "status": "succeeded", "amount": 49900, … }, "previous_attributes": { "status": "processing" } }, "request": null, "pending_webhooks": 1}data.object is the object as its GET answers it, right after the event. A body larger than 256 KB is sent without data.object but its id and object: read the object with its GET.
The request’s headers:
| Header | What it is |
|---|---|
PSP-Signature |
t=<unix time>,v1=<signature>: proves the request is ours (Signatures) |
PSP-Event-Id |
The event’s id |
PSP-Delivery-Attempt |
Which attempt this is, from 1 |
Content-Type |
application/json |
User-Agent |
NovaroPay/1.0 (+https://docs…/webhooks) |
We send from our IP addresses for your market, if your firewall has to let us in.
Handle an event
Section titled “Handle an event”- Check the signature before anything else (Signatures), on the raw body as it came.
- Answer
2xxat once, within 10 seconds, and do the work after. Anything else, a redirect too, is a failure, and we try again. - Process each event once. We send an event at least once: a retry carries the same
idand is a duplicate if you processed thatidalready. - Don’t count on the order. Events can arrive in another order than they happened. Read
data.object.status, anddata.object.version, which grows with every change of a payment: ignore an event whose version is older than one you have. When in doubt, read the payment withGET /payments/{id}.
Turned off and failing
Section titled “Turned off and failing”An endpoint that is turned off, in the portal or by us, gets nothing, and its retries stop. Events while it is off are not sent once it is on again, and no delivery is made of them: read them with GET /events?created[gte]=<when you turned it off>. delivery_success=false lists only events whose delivery was tried, so it leaves these out.
An endpoint that answered no attempt successfully for 24 hours is failing, and your account’s admins and developers get an email; after 72 hours, and 10 failed attempts at least, we turn it off and email them again. A test mode endpoint is marked and turned off the same way, without the emails. Turn it on in the portal when your server is back (until its Webhooks page opens, ask your account manager), and read the events you missed: those whose delivery failed with GET /events?delivery_success=false, and those of the time it was off with created[gte].
When our support team adds, changes, turns off or on, rolls the signing secret of, rolls back or deletes a live endpoint for you, your account’s admins and developers get an email saying what changed.
Test mode
Section titled “Test mode”A test mode endpoint gets the events of your test payments, as live mode’s would come, so you can build against them: create a test payment and drive it to the status you need with a magic UTR, or a test payout with a magic amount. Test mode retries less: see Retries.