Independence Day offer · ₹52/employee/moClaim offer
INDPayroll
API reference

Webhooks

Event subscriptions. The events themselves are described under `webhooks`.

Base URLhttps://api.indpayroll.com/v13 endpoints
GET /webhooks

List webhook subscriptions

Responses

  • 200 Subscriptions. Secrets are never returned after creation. object
  • 401 The key is missing, unknown, or the HMAC signature did not verify. ErrorResponse
POST /webhooks

Register a webhook subscription

Subscribe 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.

Headers

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.

Body application/json · required

url required
string (uri)
HTTPS endpoint to POST deliveries to. It must answer 2xx within 10 seconds.
events required
WebhookEventName[]
Events to subscribe to. At least one.
organization_ids
integer[]
Restrict deliveries to these entities. Omit for every entity the key can reach.

Responses

  • 201 Created. Store secret now - it is not shown again. Webhook
  • 400 The request was malformed or failed validation. ErrorResponse
  • 401 The key is missing, unknown, or the HMAC signature did not verify. ErrorResponse
  • 403 The key is valid but lacks the scope, or cannot reach this organization. ErrorResponse
Request
curl -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
  ]
}'
Response · 201
{
  "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"
}
DELETE /webhooks/{id}

Remove a webhook subscription

Deliveries stop immediately. Events already queued are dropped.

Path parameters

id required
string
INDPayroll id of the resource.

Responses

  • 204 Deleted. No body.
  • 401 The key is missing, unknown, or the HMAC signature did not verify. ErrorResponse
  • 404 No such resource, or it belongs to an organization this key cannot reach. ErrorResponse

Events

What INDPayroll POSTs to the URL you registered. Verify the signature before you act on a delivery - see the webhooks guide.

employee.upserted

Fires 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.

Delivery
{
  "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.calculated

The 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.

Delivery
{
  "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.paid

Salary has been disbursed and the run is closed. Held employees are listed so you can chase what is still outstanding.

Delivery
{
  "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.reversed

A generated or paid run has been voided. Anything you cached from it - slips, totals, statutory files - is now stale.

Delivery
{
  "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.ready

One event per employee, as each PDF finishes rendering. Fetch it from `GET /payslips/{id}.pdf` - the `pdf_url` in the payload expires.

Delivery
{
  "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.settled

A full and final settlement has been paid. The employee is fully closed out.

Delivery
{
  "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.ready

An ECR, ESI return, PT return, LWF return or TDS statement has finished generating and can be downloaded and filed.

Delivery
{
  "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.completed

A file import has finished, with or without rejected rows. Subscribing to this is the alternative to polling `GET /imports/{jobId}`.

Delivery
{
  "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"
  }
}