Mintcash
ConceptsWebhooks

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-signature header, 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 succeeded webhook can arrive before a pending one. Handle out-of-order events defensively.

What you must do

Rendering diagram…

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:

AttemptAfter
1immediate
230 seconds
32 minutes
410 minutes
51 hour
66 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