Webhooks
Receive real-time notifications when events happen in Holded.
Overview
Webhooks allow you to receive HTTP POST notifications when events occur in your Holded account, such as a new invoice being created or a payment being received.
Setting up webhooks
Configure webhooks in your Holded dashboard under Settings > Webhooks. You'll need to provide:
- A publicly accessible HTTPS URL
- The events you want to subscribe to
Common headers
Every webhook request includes these headers:
x-holded-webhook-eventstringEvent name, e.g. invoice.create
x-holded-webhook-datestring · ISO 8601When the event was sent, e.g. 2026-06-18T11:10:35Z
x-holded-webhook-signaturestringHMAC-SHA256 signature with the sha256= prefix (see below)
x-holded-webhook-idstringUnique event identifier; use it as an idempotency key
x-holded-webhook-account-idstringIdentifier of the Holded account that triggered the event
x-holded-webhook-versionstringWebhook system version (e.g. v1)
Available events
Webhooks are available for the following event groups. Click a group to see its events and payload details.
Invoices
4 eventsinvoice.createinvoice.updateinvoice.deleteinvoice.approveCredit Notes
4 eventscreditnote.createcreditnote.updatecreditnote.deletecreditnote.approveProforma Invoices
3 eventsproform.createproform.updateproform.deleteSales Receipts
4 eventssalesreceipt.createsalesreceipt.updatesalesreceipt.deletesalesreceipt.approveEstimates
3 eventsestimate.createestimate.updateestimate.deleteSales Orders
3 eventssalesorder.createsalesorder.updatesalesorder.deleteWaybills
3 eventswaybill.createwaybill.updatewaybill.deleteServices
3 eventsservice.createservice.updateservice.deletePurchases
4 eventspurchase.createpurchase.updatepurchase.deletepurchase.approvePurchase Refunds
4 eventspurchaserefund.createpurchaserefund.updatepurchaserefund.deletepurchaserefund.approveReceipts
3 eventsreceipt.createreceipt.updatereceipt.deletePurchase Orders
3 eventspurchaseorder.createpurchaseorder.updatepurchaseorder.deleteReceipt Notes
4 eventsreceiptnote.createreceiptnote.updatereceiptnote.deletereceiptnote.approvePayments
3 eventspayment.createpayment.updatepayment.deleteContacts
3 eventscontact.createcontact.updatecontact.deleteProducts
3 eventsproduct.createproduct.updateproduct.deleteStock
1 eventsstock.updateWarehouses
3 eventswarehouse.createwarehouse.updatewarehouse.deleteVerifying webhooks
Each webhook request includes a signature header (x-holded-webhook-signature) that you should verify to confirm the request came from Holded. The signature is an HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret and prefixed with sha256=.
const crypto = require('crypto');
// Use the raw request body, not the parsed JSON object
app.post('/holded-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const secret = process.env.HOLDED_WEBHOOK_SECRET; // whsec_...
// The header arrives with a "sha256=" prefix; strip it before comparing
const received = (req.header('x-holded-webhook-signature') || '').replace(/^sha256=/, '');
const expected = crypto.createHmac('sha256', secret).update(req.body).digest('hex');
const valid = received.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
if (!valid) return res.status(401).send('Invalid signature');
const event = req.header('x-holded-webhook-event');
const data = JSON.parse(req.body.toString('utf8'));
// ... process the event and acknowledge
res.sendStatus(200);
});Expected response
Your endpoint should return any 2xx status code to acknowledge receipt. If we don't receive a 2xx response, we will retry the delivery with exponential backoff.