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 walletwallet.creditedCredits added (top-up, grant)wallet.debitedCredits deducted (metered usage)balance.updatedWallet balance changedExample 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