Skip to main content
A webhook is a POST we send to your server when something happens — money arrived, a payment settled, a refund went through. You need them. There is no polling endpoint that tells you a credit landed, so without a webhook URL you will not know your buyer has paid.

Events we send

Payload for each is in the API Reference.
LRS payments get LRS_VERIFICATION_NEEDED, not VERIFICATION_NEEDED. If you collect education fees and you only handle VERIFICATION_NEEDED, your handler will never fire.
A verified payment sends nothing. VERIFIED means nothing is outstanding, so there is no event — do not wait on one to confirm a payment passed.

Set it up

1. Build the endpoint. A public HTTPS URL that accepts POST with application/json and returns 200 OK. 2. Register it. In your EximPe Dashboard, go to Developer and enter the URL. If you cannot use the dashboard, send the URL and your server IP to EximPe Support instead. 3. Whitelist our IPs. The ranges are in the same Developer section. Configure your firewall to accept webhooks only from those addresses. 4. Test it. Make a sandbox payment, watch it arrive, confirm you return 200.

Driving every event in sandbox

You do not have to wait for real money, or a real settlement date, to exercise your handlers. Sandbox lets you fire each event on demand. Set Refund Status is the useful one for coverage: force a single refund through REFUNDED, FAILED, PROCESSING, REVERSED, ON_HOLD and BANK_REJECTED_REFUND in turn, and one refund exercises every branch of your handler. INITIATED fires nothing — it is the starting state, not a transition.
Both need the Simulator gateway. They only work on transactions created through it, and return 400 in production. If your sandbox account is not on the Simulator gateway, ask your EximPe integration manager to assign it.
Mark as Settled applies a simulated fee schedule, not your contracted rates. Use it to check that your reconciliation arithmetic holds, not to predict what a production settlement will pay you.

Check the message is really from us

Every webhook carries an HMAC-SHA256 signature. Verify it before you act on anything — otherwise anyone who finds your URL can tell you a payment succeeded. There are two headers, and you get both on every delivery:
Prefer V2. Its timestamp is signed along with the body, so a captured delivery can’t be replayed later with a fresh one. Split the header on ,, take t and v2, rebuild the hash over t + "." + raw_body, and reject anything whose t is too old. Both headers are sent on every delivery, so you can verify either. The walk-through below covers V1.
1

Take the signature from the header

Read X-Webhook-Signature off the request.
2

Get your Encryption Key

It is in the Developer section of the dashboard.
Get your Encryption Key from Developer Section
3

Rebuild the signature

HMAC-SHA256, using your Encryption Key as the secret and the raw request body as the message. Output as a hex string.
4

Compare

They must match exactly. Use a timing-safe comparison.
Use the raw body, not a re-serialised object. If you parse the JSON and stringify it again, your bytes will differ from ours and the signature will never match.

Code

Delivery and duplicates

Delivery is at-least-once, so the same event can reach you more than once.
  • Every event carries a unique sequence_number. It is a UUID, not an ordering counter. A retry resends the same value, so de-duplicate on it.
  • Do not rely on arrival order. Order events by event_time, and when it matters call Get Verification Requirements for where the payment stands now.
  • event_time is when we created the event, not when we delivered it.

If your endpoint is down

We retry. A delivery fails when your endpoint answers with anything other than a 2xx, does not answer within 10 seconds, or cannot be reached. After a failed attempt we try again 1 minute later, then 5 minutes, 15 minutes and 1 hour after each further failure, 5 attempts in all. After the fifth failure we stop sending that event. Return 200 as soon as you have stored the event, and do your processing afterwards.
Verification asks are batched. VERIFICATION_NEEDED and LRS_VERIFICATION_NEEDED are queued per merchant and sent in batches every 2 minutes, so the first attempt can come a couple of minutes later, and longer behind a burst. Their retries ride the same batches. Every other event is sent as soon as it is created.
Four things to get right.
  • Never expose your encryption key.
  • Verify every signature.
  • HTTPS only.
  • Log what you receive — you will need it to reconcile.