List webhook subscriptions
Responses
200Subscriptions. Secrets are never returned after creation. object401The key is missing, unknown, or the HMAC signature did not verify. ErrorResponse
Event subscriptions. The events themselves are described under `webhooks`.
https://api.indpayroll.com/v13 endpoints200 Subscriptions. Secrets are never returned after creation. object401 The key is missing, unknown, or the HMAC signature did not verify. ErrorResponseSubscribe to one or more events. The response carries a secret - the only time it is returned - which signs every delivery as X-INDP-Signature: sha256=<hex> over {timestamp}.{raw body}.
Deliveries retry with exponential backoff for 24 hours. A subscription failing for seven consecutive days is deactivated and reported on GET /webhooks.
Idempotency-Key string | A unique key, at most 255 characters, that makes this request safe to retry. Replaying a key within 24 hours returns the original response and does not act again. |
url requiredstring (uri) | HTTPS endpoint to POST deliveries to. It must answer 2xx within 10 seconds. |
events requiredWebhookEventName[] | Events to subscribe to. At least one. |
organization_ids integer[] | Restrict deliveries to these entities. Omit for every entity the key can reach. |
201 Created. Store secret now - it is not shown again. Webhook400 The request was malformed or failed validation. ErrorResponse401 The key is missing, unknown, or the HMAC signature did not verify. ErrorResponse403 The key is valid but lacks the scope, or cannot reach this organization. ErrorResponsecurl -X POST https://api.indpayroll.com/v1/webhooks \
-H "Authorization: Bearer $INDP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.acme-hris.com/indpayroll",
"events": [
"payroll.run.calculated",
"payroll.run.paid",
"payslip.ready",
"fnf.settled"
],
"organization_ids": [
42
]
}'{
"id": "whk_01J9K7YR4H",
"url": "https://hooks.acme-hris.com/indpayroll",
"events": [
"payroll.run.calculated",
"payroll.run.paid",
"payslip.ready",
"fnf.settled"
],
"organization_ids": [
42
],
"secret": "whsec_8c1f2d4b9a7e...",
"active": true,
"created_at": "2026-09-30T09:20:11Z"
}Deliveries stop immediately. Events already queued are dropped.
id requiredstring | INDPayroll id of the resource. |
204 Deleted. No body. 401 The key is missing, unknown, or the HMAC signature did not verify. ErrorResponse404 No such resource, or it belongs to an organization this key cannot reach. ErrorResponseWhat INDPayroll POSTs to the URL you registered. Verify the signature before you act on a delivery - see the webhooks guide.
employee.upsertedFires on every create and update, whoever made it - your own call, an import, or a change made inside INDPayroll. Use it to keep your copy of the master in step without polling.
{
"id": "evt_01J9K8M2QF",
"type": "employee.upserted",
"created_at": "2026-09-30T10:12:49Z",
"organization_id": 42,
"api_version": "2026-09-01",
"data": {
"external_id": "emp-1001",
"employee_id": "1187",
"organization_id": 42,
"change": "updated",
"fields_changed": [
"department_id",
"statutory.uan"
]
}
}payroll.run.calculatedThe slips have been persisted and the run is `generated`. This is the signal to pull `GET /payroll-runs/{id}/employees` and show the numbers in your UI.
{
"id": "evt_01J9K8M2QF",
"type": "payroll.run.calculated",
"created_at": "2026-09-30T10:12:49Z",
"organization_id": 42,
"api_version": "2026-09-01",
"data": {
"payroll_run_id": "run_2026_09_0042",
"organization_id": 42,
"period_start": "2026-09-01",
"period_end": "2026-09-30",
"status": "generated",
"totals": {
"employee_count": 86,
"gross": 4820000,
"total_deductions": 612400,
"net_payable": 4207600
}
}
}payroll.run.paidSalary has been disbursed and the run is closed. Held employees are listed so you can chase what is still outstanding.
{
"id": "evt_01J9K8M2QF",
"type": "payroll.run.paid",
"created_at": "2026-09-30T10:12:49Z",
"organization_id": 42,
"api_version": "2026-09-01",
"data": {
"payroll_run_id": "run_2026_09_0042",
"organization_id": 42,
"paid_on": "2026-10-05",
"payment_method_id": 2,
"reference": "HDFC-BULK-20261005-0007",
"net_paid": 4207600,
"held_employee_ids": [
"ext:emp-1044"
]
}
}payroll.run.reversedA generated or paid run has been voided. Anything you cached from it - slips, totals, statutory files - is now stale.
{
"id": "evt_01J9K8M2QF",
"type": "payroll.run.reversed",
"created_at": "2026-09-30T10:12:49Z",
"organization_id": 42,
"api_version": "2026-09-01",
"data": {
"payroll_run_id": "run_2026_09_0042",
"organization_id": 42,
"reversed_at": "2026-10-06T07:15:00Z",
"reason": "Paid against the wrong bank account - reissuing.",
"payslips_voided": 86
}
}payslip.readyOne event per employee, as each PDF finishes rendering. Fetch it from `GET /payslips/{id}.pdf` - the `pdf_url` in the payload expires.
{
"id": "evt_01J9K8M2QF",
"type": "payslip.ready",
"created_at": "2026-09-30T10:12:49Z",
"organization_id": 42,
"api_version": "2026-09-01",
"data": {
"payslip_id": "slip_2026_09_1187",
"payroll_run_id": "run_2026_09_0042",
"employee_id": "1187",
"external_id": "emp-1001",
"month": 9,
"year": 2026,
"net_payable": 82654.67,
"pdf_url": "https://api.indpayroll.com/v1/payslips/slip_2026_09_1187.pdf"
}
}fnf.settledA full and final settlement has been paid. The employee is fully closed out.
{
"id": "evt_01J9K8M2QF",
"type": "fnf.settled",
"created_at": "2026-09-30T10:12:49Z",
"organization_id": 42,
"api_version": "2026-09-01",
"data": {
"fnf_id": "fnf_01J9K7YR4H",
"employee_id": "ext:emp-1044",
"organization_id": 42,
"date_of_leaving": "2026-09-18",
"paid_on": "2026-10-07",
"net_payable": 158315
}
}statutory.file.readyAn ECR, ESI return, PT return, LWF return or TDS statement has finished generating and can be downloaded and filed.
{
"id": "evt_01J9K8M2QF",
"type": "statutory.file.ready",
"created_at": "2026-09-30T10:12:49Z",
"organization_id": 42,
"api_version": "2026-09-01",
"data": {
"report": "pf_ecr",
"organization_id": 42,
"month": 9,
"year": 2026,
"download_url": "https://api.indpayroll.com/v1/statutory/pf/ecr?month=9&year=2026&format=txt",
"employee_count": 86,
"total_amount": 309600
}
}import.completedA file import has finished, with or without rejected rows. Subscribing to this is the alternative to polling `GET /imports/{jobId}`.
{
"id": "evt_01J9K8M2QF",
"type": "import.completed",
"created_at": "2026-09-30T10:12:49Z",
"organization_id": 42,
"api_version": "2026-09-01",
"data": {
"job_id": "job_01J9K8M2QF",
"type": "employees",
"organization_id": 42,
"status": "completed_with_errors",
"succeeded": 409,
"failed": 3,
"error_file_url": "https://api.indpayroll.com/v1/imports/job_01J9K8M2QF/errors.xlsx"
}
}