Skip to main content
Webhooks tell your server when an order’s payment outcome changes, so you don’t have to poll. They fire for every order in your organization, not only the ones your integration created; use attribution to tell them apart.

Set up an endpoint

Webhook endpoints are managed in the Laces app by an owner or admin. There are no webhook-management routes on the Partner API.
1

Open Webhooks

Settings → Developers (the tile at the bottom of Settings), then the Webhooks section (“Get told when an order is paid, fails or is refunded”).
The Laces Developers screen with the Webhooks section and its Add endpoint action

Developers, with the Webhooks section

2

Add an endpoint

Tap Add endpoint. Enter the Endpoint URL, a public https address such as https://shop.example.com/laces/webhook, and choose the Events to send.
The Add endpoint form with the URL https://example.com/laces/webhook and the order.paid, order.failed and order.refunded events switched on

The Add endpoint form

3

Copy the signing secret

The Your signing secret sheet shows the secret (LCSWHSEC-…-X) once, with a copy button and Copy all (an .env block with LACES_WEBHOOK_ENDPOINT_ID and LACES_WEBHOOK_SIGNING_SECRET). Store it on your server, next to your client secret.
The Your signing secret sheet showing the endpoint ID and a blurred signing secret, with Copy all and I've saved the secret buttons

The signing secret is shown once (secret value blurred here)

Your endpoint then appears in the list.
The Developers screen with a webhook endpoint card showing its endpoint ID, URL, subscribed events and No deliveries yet

A webhook endpoint in the list

4

Send a test event

Open the endpoint and tap Send test event. Laces posts a signed ping to your URL and shows the HTTP status your server returned. Aim for a 2xx.The example below was sent to a placeholder address that does not accept webhooks, so the app reports the status it got back (HTTP 405) instead of pretending it worked.
The Webhook endpoint screen with a message reading Your endpoint answered HTTP 405

Send test event reporting the status your endpoint returned

Open an endpoint to see its last delivery status. You can edit its URL and events at any time, and from Endpoint actions:
  • Send test event: see Test your endpoint.
  • Roll signing secret: issues a new secret, shown once, and the old one stops signing immediately (there is no overlap). Deliveries still queued are signed with the new secret at their next attempt, so be ready to accept the new secret before you roll, or deliveries fail until you deploy it.
  • Disable endpoint / Re-enable: a disabled endpoint receives nothing. Re-enabling resets the failure counter.
  • Delete endpoint: permanent; Laces stops sending to it.
An organization can have up to 5 webhook endpoints. URL rules. https only; no credentials in the URL; no localhost, internal or single-label host names; the name must resolve to public addresses only. Private, loopback, link-local and cloud-metadata addresses are refused, both when you save the URL and again at send time. Redirects are not followed, so register the final URL. Unlike allowed domains, a webhook URL can never be http or localhost. To receive webhooks on your laptop, see Test your endpoint.

Events

Every path that marks an order paid fires order.paid: mobile money settlements (and card, once it is available), however Laces learns of them, and free orders confirmed without payment.

Payload

order.paid
For order.failed, use failure_kind to choose what to tell the buyer: For order.refunded, data also has a refund object:
The ping payload:
Webhooks never contain buyer personal data: no name, email or phone. The webhook is a signal to act, plus enough to match your own record through attribution.partner_reference or attribution.checkout_session_id. If you need buyer details, read the order with GET /orders/{orderId}, which needs orders:read.

Headers

Verify the signature

Every request is signed with your endpoint’s signing secret, with a fresh timestamp on each attempt:
To verify:
  1. Parse t and every v1 from X-Laces-Signature.
  2. Reject if t is more than 300 seconds from your clock (replay protection).
  3. Compute the HMAC over `${t}.${rawBody}` using the whole secret, including the LCSWHSEC- prefix and the -X suffix, as the key.
  4. Compare to each v1 in constant time. Accept if any matches.
Verify against the raw bytes of the body, before any JSON parsing. Parsing and re-serialising changes whitespace and key order and breaks the signature. In Express that means express.raw() on this route, not express.json().

Delivery and retries

  • Acknowledge with any 2xx within 10 seconds. Anything else, a timeout, or a network error is a failed attempt. The response body is ignored.
  • Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours: 7 attempts in total over about 20.5 hours. Retries are sent by a job that runs every 5 minutes, so one can arrive up to 5 minutes after its nominal time.
  • Network errors, timeouts, 5xx, 408 and 429 are retried through the whole schedule. A 3xx (redirects are never followed) or any other 4xx is treated as permanent after 3 attempts.
  • After the last failed attempt the delivery is marked permanently failed and not retried again. Reconcile by reading orders or sessions.
  • Auto-disable: after 50 consecutive failed attempts on an endpoint, Laces switches it off, and deliveries still queued for it are dropped. Fix your receiver, then Re-enable it in the app (the counter resets). A successful delivery also resets the counter.

Test your endpoint

Payments are real (there is one environment), so test in two steps:
  1. Signature and plumbing: Send test event. In the app, open the endpoint and tap Send test event. Laces posts a signed ping to your URL right away and shows the result: “Test delivered (HTTP 200)”, or the status your server answered, or that it could not reach your endpoint. A ping is sent once and never retried, and it doesn’t count towards auto-disable. Use it to prove your signature check and your 2xx response.
  2. Real events: a test order. Buy a free or low-price ticket on an unlisted test event to receive order.paid, then refund it in the app to receive order.refunded.
To receive webhooks on your laptop, run a public https tunnel (for example ngrok or Cloudflare Tunnel) to your local server and register the tunnel’s https URL as the endpoint. Remember to point the endpoint back at your real server (or delete it) when you’re done.

Handle events idempotently

Delivery is at least once and unordered:
  • The same event can arrive more than once. Store each X-Laces-Delivery id when you process it, ignore repeats, and make the handler safe to run twice.
  • Events can arrive out of order, for example order.refunded before a retried order.paid. Always act on data.order.status, never on the order in which events arrived.
  • An order produces at most one order.paid, one order.failed, and one order.refunded per refund, per endpoint. If an order fails twice and then succeeds, you see a single order.failed and then order.paid.