Webhooks
Receive real-time notifications when invoices, quotes, items, or customers change.
Overview
Webhooks let your services react to changes in Corebill without polling. When a subscribed event fires, Corebill sends an HTTP POST to your endpoint with a JSON payload and a signature header you can verify.
Webhooks fire for changes made through the REST API and through the dashboard UI -- the source is recorded on each delivery so you can distinguish them.
Manage endpoints from Developers > Webhooks in the dashboard.
Available Events
| Event | When it fires |
|---|---|
customer.created | A new customer was created |
customer.updated | A customer was updated |
customer.deleted | A customer was deleted (soft delete) |
item.created | A catalog item was created |
item.updated | A catalog item was updated |
item.deleted | A catalog item was deleted |
quote.created | A quote was created |
quote.updated | A quote was updated |
quote.deleted | A quote was deleted |
quote.sent | A quote was sent to the customer |
quote.approved | A quote was approved |
quote.rejected | A quote was rejected |
quote.converted | A quote was converted to an invoice |
invoice.created | An invoice was created |
invoice.updated | An invoice was updated |
invoice.deleted | An invoice was deleted |
invoice.sent | An invoice was sent to the customer |
invoice.payment_recorded | A partial payment was recorded |
invoice.paid | The invoice was fully paid |
invoice.cancelled | An invoice was cancelled |
invoice.refunded | An invoice was refunded |
Payload
1{2 "id": "evt_a1b2c3d4e5f6",3 "event": "invoice.paid",4 "created_at": "2026-04-15T14:30:00Z",5 "webhook_id": "wh_xyz",6 "data": {7 "id": "inv_abc123",8 "object": "invoice",9 "invoice_number": "INV-2026-000001",10 "status": "paid",11 "total": 5800,12 "amount_paid": 5800,13 "amount_due": 0,14 "customer_id": "cus_abc123",15 "..."16 },17 "metadata": {}18}
The data object is the same shape returned by the REST API for that resource.
Headers
| Header | Description |
|---|---|
X-Webhook-Signature | HMAC-SHA256 hex digest of the raw body, signed with your endpoint secret |
X-Webhook-Event | Event type (e.g. invoice.paid) |
X-Webhook-Id | The webhook endpoint ID |
X-Event-Id | The system event ID |
X-Webhook-Delivery | The delivery ID (unique per attempt batch) |
X-Webhook-Attempt | Attempt number (1, 2, ...) |
User-Agent | Corebill-Webhooks/1.0 |
Verifying the Signature
Always verify the signature before trusting a webhook. Use the raw request body (do not parse and re-stringify it).
1import crypto from 'crypto'23function verifyWebhook(rawBody, signature, secret) {4 const expected = crypto5 .createHmac('sha256', secret)6 .update(rawBody)7 .digest('hex')89 return crypto.timingSafeEqual(10 Buffer.from(expected),11 Buffer.from(signature)12 )13}1415// Express example16app.post('/webhooks/corebill', express.raw({ type: 'application/json' }), (req, res) => {17 const signature = req.headers['x-webhook-signature']18 const valid = verifyWebhook(req.body, signature, process.env.COREBILL_WEBHOOK_SECRET)1920 if (!valid) return res.status(401).send('invalid signature')2122 const event = JSON.parse(req.body.toString())23 // handle event.event and event.data24 res.status(200).send('ok')25})
Responding
Return a 2xx status code within 10 seconds. Anything else (or a timeout) is treated as a failure and Corebill will retry.
Keep handlers fast: enqueue work to a background job and respond immediately.
Retries
Failed deliveries are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 25 minutes |
| 5 | 2 hours |
After 5 failed attempts the delivery is marked failed. You can replay it manually from Developers > Webhooks > Deliveries in the dashboard.
Idempotency
The same event can be delivered more than once (for example after a retry or a manual replay). Use the id field of the event envelope as an idempotency key on your side -- if you've already processed evt_xyz, ignore subsequent deliveries with the same id.
Testing
You can replay any past delivery from the dashboard to debug your handler without waiting for a real event:
- Go to Developers > Webhooks > Deliveries
- Pick a delivery
- Click Replay
The replay produces a fresh X-Webhook-Delivery and a new attempt counter, but the event payload is identical to the original.