Webhooks
Configure signed webhook delivery for challan search and payment-link events.
What webhooks are for
Webhooks let your integration receive lifecycle updates without polling every workflow step. They are particularly useful for:
- challan search completion or failure monitoring
- payment-link creation notifications
- payment success, failure, and expiry handling
Configure your webhook
From the organization API settings area, your admin can:
- Save a webhook URL.
- Activate or deactivate delivery.
- Rotate the webhook secret.
- Send a test event.
Store the webhook secret securely
When the webhook secret is rotated, update your receiver before relying on new deliveries. Use the latest secret to verify every signed payload.
Delivery headers
Webhook headers
| Field | Type | Required | Description |
|---|---|---|---|
| content-type | string | Required | Always sent as application/json. |
| x-cms-event | string | Required | Event type, such as payment_link.paid or challan_search.completed. |
| x-cms-timestamp | string | Required | Unix timestamp used in signature verification. |
| x-cms-signature | string | Required | HMAC SHA-256 signature in the format sha256=HEX_DIGEST. |
Payload envelope
Every webhook body uses the same top-level shape:
Webhook envelope
{"id": "a8e97bbf-0ba5-4ea7-b7cb-11b29efdf641","type": "payment_link.paid","createdAt": "2026-05-13T11:42:08.000Z","data": { "...": "event-specific fields"}}Verify the signature
Use the raw request body exactly as received and compute the signature over:
${x-cms-timestamp}.${rawRequestBody}Example in Node.js:
import crypto from 'node:crypto';
function verifyWebhook({
timestamp,
rawBody,
signatureHeader,
secret,
}: {
timestamp: string;
rawBody: string;
signatureHeader: string;
secret: string;
}) {
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
const received = signatureHeader.replace(/^sha256=/, '');
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(received, 'hex'),
);
}Supported event types
| Status | Meaning | Notes |
|---|---|---|
| webhook.test | Test event queued from the CMS settings screen. | Use this to verify connectivity and signature handling. |
| challan_search.completed | A challan search completed successfully. | Includes rcNumber, challanCount, and newChallansCount. |
| challan_search.failed | A challan search failed. | Includes rcNumber, statusCode, and errorMessage. |
| payment_link.created | A payment link was created. | Includes paymentLinkId, vehicleNumber, amount summary, and expiresAt. |
| payment_link.paid | A payment link was paid successfully. | Includes paymentLinkId, transactionId, paymentMode, and amount. |
| payment_link.failed | A payment attempt failed. | Includes paymentLinkId, orderId, and errorMessage. |
| payment_link.expired | A payment link expired before payment. | Includes paymentLinkId and expiredAt metadata. |
Example event payloads
webhook.test
{"id": "8803f57d-6814-41e5-a3da-97f349acf698","type": "webhook.test","createdAt": "2026-05-13T09:02:21.000Z","data": { "orgId": "bf4cc07b-d395-456a-8aac-e419d9ef10ff", "sentAt": "2026-05-13T09:02:21.000Z"}}challan_search.completed
{"id": "418b94b6-74e8-4d82-a474-773e3aeae0c6","type": "challan_search.completed","createdAt": "2026-05-13T09:10:11.000Z","data": { "rcNumber": "DL01AB1234", "apiKeyId": "4e4984c0-16c2-4b33-b831-fcbf5485f95d", "challanCount": 2, "newChallansCount": 2}}payment_link.created
{"id": "e398c607-d52d-4828-9755-9351fef99871","type": "payment_link.created","createdAt": "2026-05-13T10:20:00.000Z","data": { "paymentLinkId": "17de2557-58d6-4f8d-aa2b-fb60074c6a6d", "vehicleNumber": "DL01AB1234", "status": "ACTIVE", "challanCount": 2, "amount": { "baseAmount": 1500, "legalFee": 100, "gst": 18, "convenienceFee": 0, "totalAmount": 1618 }, "expiresAt": "2026-05-15T14:20:00.000Z"}}payment_link.paid
{"id": "b58bc6f3-8384-4db9-aa7b-40c418ccb4b1","type": "payment_link.paid","createdAt": "2026-05-13T11:42:08.000Z","data": { "paymentLinkId": "17de2557-58d6-4f8d-aa2b-fb60074c6a6d", "status": "PAID", "orderId": "ORD-20260513-0011", "transactionId": "TXN-845012193", "paymentMode": "UPI", "amount": 1618}}Delivery expectations
- Treat any non-2xx response as a failed delivery attempt.
- Validate the signature before trusting the payload.
- Store webhook event IDs if you want an idempotency layer in your own system.
