Skip to main content
Macropay is your Merchant of Record: we’re the seller on the customer’s statement, we collect and remit sales tax and VAT worldwide, and we run the billing ledger. Webhooks are how your application stays in lockstep with that ledger. Every time money moves, a subscription changes state, or a customer’s entitlements shift, we POST a signed event to your endpoint — so you can provision access, update your database, and react in real time without polling our API. This guide takes you from zero to a verified, production-ready handler.

What you’ll need

Register an endpoint

Create the endpoint from the dashboard or the API. Either way, you’ll get back a signing secret (whsec_...) — store it like a password; you’ll use it to verify every delivery.

In the dashboard

  1. Open your organization SettingsWebhooks
  2. Click Add Endpoint
  3. Paste your handler URL, e.g. https://your-app.com/api/webhooks/macropay
  4. Tick the events you care about
  5. Create, then copy the signing secret

Via the API

Pick your events

“The customer just paid” → listen for order.paid. It is the single signal for money cleared, across every flow:checkout.updated → status: succeeded is a funnel signal, not a fulfillment one — it can arrive before the order is fully paid. Grant access on order.paid, not on a checkout event. Distinguish one-time vs. subscription orders with order.subscription_id / order.billing_reason.
Subscribe to only what your integration acts on. A few of the most common:
If you only want a single source of truth for “what does this customer have access to right now,” subscribe to customer.state_changed and read the entitlement set from the payload — it folds order and subscription changes into one signal. You can also query it on demand; see Customer State.
The full catalog — including events for usage-based meters and AI/agent billing — lives in the event reference.

Verify, then handle

Every delivery is signed using the Standard Webhooks spec and carries three headers: webhook-id, webhook-timestamp, and webhook-signature. Always verify against the raw request body before you trust a payload — never parse JSON first, or you’ll break the signature check.

Next.js (App Router)

FastAPI (Python)

Express.js

For a line-by-line walkthrough of the signing scheme, see Signature verification.

Test before you ship

Tunnel to localhost

Use the public ngrok URL as your endpoint, e.g. https://abc123.ngrok-free.app/api/webhooks/macropay. Point your sandbox endpoint at it so test events never touch production.

Fire a real event

  1. Spin up a checkout link for one of your products
  2. Pay with the sandbox test card 4242 4242 4242 4242
  3. Confirm the order.paid event lands at your handler and your fulfillment logic runs
Prefer to stay in the terminal? The Macropay CLI can replay and forward events to your local handler — see local webhook testing.

Harden for production

Deduplicate with webhook-id

The same event may be delivered more than once (that’s how at-least-once delivery guarantees nothing is lost). Treat webhook-id as an idempotency key:

Acknowledge fast, work later

Verify the signature, hand the payload to a queue, and return 200 right away. Long-running fulfillment (emails, provisioning, third-party calls) belongs in a worker, not the request cycle:
Respond with a 2xx within 30 seconds. Anything else (or a timeout) is treated as a failure and retried with exponential backoff. See delivery & retries for the schedule and guarantees.

Where to go next

Event reference

Every event type and payload shape, including usage and agent-billing events

Signature verification

How Standard Webhooks signing works under the hood

Delivery & retries

Retry backoff, ordering, and delivery guarantees

Customer state

Query a customer’s live entitlements on demand