Webhooks
Receive a signed HTTP callback the moment an invoice is paid, an invoice expires, or a payment settles — so you can react in real time instead of polling.
A webhook is an HTTPS POST that Markgroup sends to a URL you control whenever a subscribed event happens. Each request is signed with a per-endpoint secret so you can verify it genuinely came from Markgroup and has not been tampered with or replayed.
Set up an endpoint
Webhook endpoints are managed in your dashboard, not through the public API:
- Open Business → Developers in the Markgroup app.
- Under Webhook endpoints, choose Add endpoint and enter your HTTPS URL (plain
httpis rejected). - Select the events you want delivered.
- Copy the signing secret (
whsec_…) that is shown once — you will use it to verify every delivery. Store it as a server-side secret.
The signing secret is shown only once
For your security the fullwhsec_… secret is displayed a single time at creation and never again. If you lose it, delete the endpoint and create a new one.Event catalog
Only the events below exist today. Subscribe to any combination when you create the endpoint.
| Event | When it fires |
|---|---|
invoice.paid | An invoice was fully paid. Fulfil the order or unlock the goods. |
invoice.expired | An invoice passed its expiry window without being paid. Release any held stock. |
payment.received | A payment settled on-chain and was credited to your balance. |
Delivery format
Every delivery is a JSON body with a stable envelope, plus two headers that identify and sign it:
X-Merchant-Event— the event type, e.g.invoice.paid.X-Merchant-Signature—t=<unix-seconds>,v1=<hex-hmac-sha256>.
The body carries the event name, a data object (the same shape you get from the matching API resource), and a unique delivery_id you can use for idempotency:
Verify the signature
Compute an HMAC-SHA256 of <timestamp>.<raw-body>using your endpoint's signing secret and compare it — in constant time — to the v1 value. Reject the request if the timestamp is more than 5 minutes (300s) from now, which stops replayed deliveries.
Verify the raw body
Compute the signature over the exact bytes you received, before JSON parsing or any framework middleware re-serializes them. Re-stringifying parsed JSON can reorder keys and break verification.Respond and stay idempotent
- Return any
2xxstatus to acknowledge receipt. Respond quickly — requests time out after 10 seconds, and a timeout counts as a failed attempt. - Do your slow work asynchronously; acknowledge first, then process.
- Deduplicate on
delivery_id. A delivery can arrive more than once (for example after a retry), so processing must be safe to repeat.
Retries & failure handling
A delivery is successful only when your endpoint returns a 2xx. Any other status, a connection error, or a timeout is retried with exponential backoff — roughly doubling each time and capped at 6 hours between attempts — until the maximum attempt count is reached, after which the delivery is marked failed and not retried again.
- Disabling or deleting an endpoint stops further attempts for its pending deliveries.
- You can review recent attempts, their HTTP status, and errors under Business → Developers.
Security checklist
- Always verify the signature; never trust the body alone.
- Enforce the timestamp tolerance to reject replays.
- Serve your endpoint over HTTPS only.
- Keep the signing secret server-side; never ship it to a browser or mobile client.
- Treat the webhook as a fast signal, then confirm critical state with a read call before releasing high-value goods.
Prefer to poll?
Webhooks are optional. You can achieve the same reconciliation by polling — see Reconcile payments.