Webhooks overview
How MintCash notifies your server when state changes. Delivery semantics, retry policy, and what your endpoint needs to handle.
Webhooks are how MintCash tells you that something changed asynchronously — a payment succeeded, a subscription renewed, a refund cleared. Your endpoint receives an HMAC-signed POST; you verify the signature, dedupe the event, and act on it.
The contract
- Transport: HTTPS POST to a URL you register with your account manager
- Body: JSON, with the same envelope shape across all events
- Signature: HMAC-SHA256 in the
x-signatureheader, computed over the raw request body using a secret unique to your merchant - At-least-once delivery: we retry on non-2xx until we give up (see below). Your handler must be idempotent.
- Order: not guaranteed. A
succeededwebhook can arrive before apendingone. Handle out-of-order events defensively.
What you must do
In code form:
export async function POST(req: Request) {
const body = await req.text();
const signature = req.headers.get("x-signature");
if (!verifyWebhook(body, signature, signingSecret)) {
return new Response("invalid signature", { status: 401 });
}
const event = JSON.parse(body);
if (event.environment !== process.env.MINTCASH_ENV) {
return new Response("wrong environment", { status: 400 });
}
if (await alreadyProcessed(event.eventId)) {
return new Response("ok", { status: 200 });
}
await handleEvent(event);
await markProcessed(event.eventId);
return new Response("ok", { status: 200 });
}Retry policy
When your endpoint returns non-2xx (or times out), MintCash retries with backoff:
| Attempt | After |
|---|---|
| 1 | immediate |
| 2 | 30 seconds |
| 3 | 2 minutes |
| 4 | 10 minutes |
| 5 | 1 hour |
| 6 | 6 hours |
| — | give up after 24 hours |
After we give up, we'll surface the failure to your account manager and you can replay from the dashboard.
Latency budget
Respond inside 10 seconds. We treat anything longer as a timeout and retry.
If your handler does heavy work (e.g. sending emails, calling other APIs, rebuilding caches), defer that to a background job and return 200 immediately after persisting the event.
Idempotency is non-negotiable
Even if your endpoint is fast and reliable, you'll still see duplicate
events occasionally — a transient network blip on our side, a retry that
crossed paths with the original, a manual replay. Dedupe by eventId or
you'll double-fulfil orders.
Where to go next
- Event types — every event MintCash emits, with when it fires
- Payload reference — the JSON shape of each event
- Signature verification — code samples for verifying
x-signaturesafely