Docs / Webhooks

Webhooks

Receive real-time events when purchases complete, balances update, and credit activity changes.

Why webhooks matter

Webhooks keep your app in sync without polling. Your backend can react to purchases, wallet updates, and credit activity — useful for updating product access, balances, and internal state.

Common webhook events

Initial Chargly webhook set for MVP:

checkout.completedPurchase finished; credits added to wallet
wallet.creditedCredits added (top-up, grant)
wallet.debitedCredits deducted (metered usage)
balance.updatedWallet balance changed

Example payload

webhook-payload.json
{
  "id": "evt_123",
  "type": "wallet.credited",
  "customerId": "user_123",
  "walletId": "wallet_123",
  "credits": 500,
  "balance": 1240,
  "timestamp": "2026-03-11T21:00:00Z"
}

Handling a webhook

Receive the request, verify the signature, parse the event, and process by type.

webhooks.ts
// Receive raw body for signature verification
export async function POST(req: Request) {
  const body = await req.text();
  const sig = req.headers.get("x-chargly-signature");
  if (!verifySignature(body, sig)) return new Response(null, { status: 401});
  const event = JSON.parse(body);
  switch (event.type) {
    case "wallet.credited":
      await updateProductAccess(event.customerId, event.balance);
      break;
    case "wallet.debited":
    case "balance.updated":
      await syncBalance(event.walletId, event.balance);
      break;
  }
  return new Response(null, { status: 200});
}

Signature verification

Always verify webhook signatures before processing. Reject invalid requests. Store the webhook secret securely (e.g. in environment variables).

Best practices

  • Make handlers idempotent — same event twice should be safe
  • Log processed events for debugging
  • Handle retries safely — Chargly may resend on failure
  • Keep processing fast — return 200 quickly
  • Offload heavy work to a queue when needed

Next