Agent skill
Xendit Webhooks Skill
Receive and verify Xendit webhooks. Use when setting up Xendit webhook handlers, checking the x-callback-token verification token, debugging 401s or failed verification, or handling payment.capture, payment.failure, payment_token.activation, refund.succeeded and invoice PAID callbacks.
Install this skill
npx skills add hookdeck/webhook-skills --skill xendit-webhooks
Xendit is a Southeast Asia payments platform (Indonesia, Philippines, Vietnam, Thailand, Malaysia and more). It notifies your server about payments, payment tokens, refunds, invoices and payouts with webhooks. Xendit does not sign the payload: there is no HMAC. It sends your webhook verification token, unchanged, in the x-callback-token header, and you verify the sender by comparing that header with the token from the dashboard.
When to Use This Skill
- How do I receive Xendit webhooks?
- How do I verify the
x-callback-tokenheader on a Xendit webhook? - Does Xendit sign webhooks with HMAC? (No: shared verification token)
- How do I handle
payment.capture,payment.failureorpayment_token.activation? - How do I handle an invoice callback with status
PAID? - Why is my Xendit webhook endpoint rejecting requests?
Verification (core)
const crypto = require('crypto');
// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
const ha = crypto.createHash('sha256').update(String(a)).digest();
const hb = crypto.createHash('sha256').update(String(b)).digest();
return crypto.timingSafeEqual(ha, hb);
}
function verifyXenditToken(headers, token = process.env.XENDIT_CALLBACK_TOKEN) {
if (!token) return false; // never accept when unconfigured
const received = headers['x-callback-token']; // raw token, not a signature
return Boolean(received) && !Array.isArray(received) && safeEqual(received, token);
}
Return 401 when this fails, 400 for a body that isn't a JSON object, and 200 quickly once handled. Nothing is signed, so the raw body isn't needed for verification; read it raw (Express: express.raw({ type: '*/*' })) so any content type is accepted, check the token first, then parse. Python uses hmac.compare_digest on the same SHA-256 digests; see references/verification.md.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Types
Payload shape varies by product. Payments API webhooks carry an event field and the resource under data; dispatch on event. Invoice callbacks have no event: the invoice is the body and you check status.
| Event | Triggered When |
|---|---|
payment.capture | A payment is collected and confirmed by the payment method |
payment.authorization | A payment is reserved on the customer's account |
payment.failure | A payment fails to be collected |
payment.expiry | A successful payment authorization expires |
payment_request.expiry | A payment request expires unpaid |
payment_token.activation | A payment token is authenticated and confirmed |
payment_token.failure | Payment token creation fails authentication |
payment_token.expiry | A payment token reaches its expiry |
refund.succeeded | A refund is confirmed by the payment method |
refund.failed | A refund fails |
Invoice callback, status: PAID | An invoice is paid (no event field) |
Invoice callback, status: EXPIRED | An invoice expires unpaid (no event field) |
Other products (Payouts, Subscriptions, Disputes, xenPlatform, Payment Sessions) use their own payloads; see references/overview.md. Bill Payments callbacks are the exception that is signed (X-Callback-Signature, HMAC SHA256), not covered by the token check here.
Environment Variables
XENDIT_CALLBACK_TOKEN=your_xendit_webhook_verification_token # Dashboard > Settings > Webhooks
Local Development
For local webhook testing, run the Hookdeck CLI via npx (no account required):
npx hookdeck-cli listen 3000 xendit --path /webhooks/xendit
Paste the printed URL into Xendit Dashboard > Settings > Webhooks.
Resources
- references/overview.md - What Xendit webhooks are, event types, payload shapes, retries
- references/setup.md - Find the verification token and register your endpoint
- references/verification.md - Token check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests