Skip to main content
Your endpoint is a public URL on the internet, which means anyone can POST to it. A fabricated order.paid could provision an entitlement that was never bought; a spoofed subscription.canceled could revoke access a customer is still paying for. Signature verification closes that gap: every payload Macropay sends is cryptographically signed with a secret only you and Macropay share, so your handler can prove an event is authentic before acting on it.
Treat verification as mandatory, not optional. An unverified endpoint lets an attacker forge any event — granting licenses, flipping subscription state, or triggering downstream automation — without ever touching your account.
Macropay follows the Standard Webhooks specification, so any compliant library works out of the box and the scheme is identical across every event type — orders, subscriptions, refunds, payouts, and agent Signals API activity alike.

What gets signed

Every delivery carries three headers: The signature is an HMAC-SHA256 over the concatenation:
keyed by your endpoint’s signing secret. Three rules fall out of this:
  • Use the raw body. Sign the exact bytes you received. Re-serializing parsed JSON reorders keys and breaks the digest.
  • The ID and timestamp are part of the message. They aren’t decoration — they’re folded into what’s signed, which is what makes replay protection possible.
  • Multiple signatures can appear. During secret rotation, Macropay sends signatures for both the old and new secret so you never miss a beat.

Get your signing secret

A unique secret is minted the moment you create an endpoint. Retrieve it from either place:
  1. DashboardSettings → Webhooks, then open the endpoint.
  2. API — the response when creating a webhook endpoint.
It looks like whsec_ followed by a Base64-encoded key. Store it as a secret (environment variable, secrets manager) and never commit it. Reach for an official Standard Webhooks library first. It handles Base64 decoding, the timestamp window, multi-signature comparison, and constant-time matching — all the places a hand-rolled check tends to go wrong.
Install:

Verify manually

If you’d rather not add a dependency, reproduce the four steps yourself: enforce the timestamp window, rebuild the signed string, recompute the HMAC, and compare in constant time.

Hardening checklist

Compare signatures with timingSafeEqual (Node) or hmac.compare_digest (Python). Plain === leaks timing information that can be exploited to forge a match.
Enforce the 5-minute timestamp window so a captured-but-valid payload can’t be replayed later.
Serve the endpoint over HTTPS only.
Acknowledge fast — return a 2xx within 30 seconds and offload heavy work to a queue.
Make handlers idempotent. Macropay retries with exponential backoff, so key your processing on webhook-id and ignore IDs you’ve already handled. See Webhook Delivery for the full retry schedule.
Idempotency matters most for money-touching events. Because Macropay is your Merchant of Record — the seller of record that collects and remits sales tax and VAT on your behalf — events like order.refunded and payout.paid are the source of truth for reconciliation. Process each one exactly once and your books stay clean.

Skip it entirely with an adapter

Our framework adapters verify signatures for you, then hand you a typed, already-trusted event. Drop in the route and move straight to your business logic.

Next.js

Route handler with verification baked in.

Express

Middleware for your webhook routes.

Fastify

Plugin that validates before your handler runs.

Hono

Edge-ready middleware for Hono.