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

Employees

The employee master the engine reads on every run.

Base URLhttps://api.indpayroll.com/v110 endpoints
GET /employees

List employees

Filterable by organization, status, department and last change. Use updated_since to keep your own copy in step without re-reading everyone.

Query parameters

organization_id
integer
Organization to act on. Required when the API key can reach more than one organization; optional - and defaulted - when it can reach exactly one.
status
enum: active, on_notice, exited, on_hold
Restrict to one lifecycle state.
department_id
integer
external_id
string
Look one employee up by your own identifier.
updated_since
string (date-time)
Only records changed at or after this timestamp. Use it to sync incrementally.
page
integer
1-based page number.
per_page
integer
Items per page. The engine caps this at 200.

Responses

  • 200 Employees. object
  • 401 The key is missing, unknown, or the HMAC signature did not verify. ErrorResponse
  • 429 Rate limit exceeded. Retry after the number of seconds in Retry-After. ErrorResponse
POST /employees

Create or upsert an employee

Matches on external_id within the organization: a new one creates, a known one updates. That makes the call safe to repeat when your own system replays a sync.

Sending monthly_ctc or daily_wage here sets the opening salary structure in the same call, which is the same thing PUT /employees/{id}/salary would do afterwards.

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

external_id required
string
organization_id
integer
The entity the employee is paid by.
employee_code
string
The code printed on the payslip. Generated when omitted.
first_name required
string
Given name, as it should read on the payslip and in statutory returns.
last_name
string
Family name.
email
string (email)
Used for emailed payslips. Employees without one are skipped when a run is emailed.
phone
string
Mobile number in E.164 form.
gender
enum: female, male, other, undisclosed
Used where a professional tax schedule sets a different threshold by gender.
date_of_birth
string (date)
Drives ESI and gratuity edge cases.
date_of_joining required
string (date)
Drives pro-rating in the joining month, gratuity eligibility and PF Form 5.
employment_type
enum: full_time, part_time, contract, intern, daily_wage
Full time, part time, contract, intern or daily wage.
department_id
integer
INDPayroll department id.
department_external_id
string
Use instead of department_id when you only hold your own identifiers.
designation_id
integer
INDPayroll designation id.
location
string
Work location. Location-scoped holidays are matched against it.
salary_group_id
integer
The structure template the employee inherits components from.
payroll_cycle
enum: monthly, weekly, bi-weekly
Pay frequency for this employee.
monthly_ctc
number (double)
Monthly cost to company. Sets the opening structure when sent on a create.
daily_wage
number (double)
Rate per day, for daily-wage employees. Send instead of a CTC.
allow_generate_payroll
boolean
False keeps the employee out of every run without exiting them.
bank
EmployeeBank
Where the net pay goes.
statutory
EmployeeStatutory
PAN, UAN, ESI, PT state and the elected tax regime.

Responses

  • 200 An existing employee was updated. Employee
  • 201 Created. Employee
  • 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
  • 409 The resource is in a state that forbids the change - a locked or paid run, a duplicate external_id, or a settlement already approved. ErrorResponse
Request
curl -X POST https://api.indpayroll.com/v1/employees \
  -H "Authorization: Bearer $INDP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "external_id": "emp-1001",
  "organization_id": 42,
  "first_name": "Aditi",
  "last_name": "Sharma",
  "date_of_joining": "2024-06-01",
  "department_external_id": "dept-eng",
  "salary_group_id": 3,
  "monthly_ctc": 85000,
  "bank": {
    "account_holder_name": "Aditi Sharma",
    "account_number": "50100234567890",
    "ifsc": "HDFC0000123"
  },
  "statutory": {
    "pan": "ABCPS1234D",
    "uan": "101234567890",
    "esi_number": "4400012345",
    "pt_state": "Odisha",
    "tax_regime": "new"
  }
}'
POST /employees/bulk

Bulk upsert employees

Up to 500 employees per call, matched on external_id. This is the onboarding path from an existing HRIS - prefer it over an Excel import.

Rows are independent: a row that fails is reported in results and skipped, and the rest still land. Nothing is rolled back, so a retry of the whole batch is safe.

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

organization_id
integer
The entity every employee in the batch belongs to.
employees required
EmployeeWrite[]
Up to 500 employees, upserted independently by external_id.

Responses

  • 200 Per-row outcome. EmployeeBulkResult
  • 400 The request was malformed or failed validation. ErrorResponse
  • 401 The key is missing, unknown, or the HMAC signature did not verify. ErrorResponse
  • 429 Rate limit exceeded. Retry after the number of seconds in Retry-After. ErrorResponse
Request
curl -X POST https://api.indpayroll.com/v1/employees/bulk \
  -H "Authorization: Bearer $INDP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "organization_id": 42,
  "employees": [
    {
      "external_id": "emp-1001",
      "first_name": "Aditi",
      "last_name": "Sharma",
      "date_of_joining": "2024-06-01",
      "monthly_ctc": 85000,
      "salary_group_id": 3
    },
    {
      "external_id": "emp-1002",
      "first_name": "Rahul",
      "last_name": "Mohanty",
      "date_of_joining": "2025-01-13",
      "monthly_ctc": 62000,
      "salary_group_id": 3
    }
  ]
}'
Response · 200
{
  "created": 1,
  "updated": 1,
  "failed": 0,
  "results": [
    {
      "external_id": "emp-1001",
      "status": "updated",
      "id": "1187"
    },
    {
      "external_id": "emp-1002",
      "status": "created",
      "id": "1188"
    }
  ]
}
GET /employees/{id}

Retrieve an employee

The full HR snapshot payroll reads - identity, dates, department, group, bank and statutory identifiers.

Path parameters

id required
string
Employee id, or your own identifier prefixed with ext: - /employees/ext:emp-1001.

Responses

  • 200 The employee. Employee
  • 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
Request
curl -X GET https://api.indpayroll.com/v1/employees/{id} \
  -H "Authorization: Bearer $INDP_API_KEY"
Response · 200
{
  "id": "1187",
  "external_id": "emp-1001",
  "organization_id": 42,
  "employee_code": "ACM-0142",
  "first_name": "Aditi",
  "last_name": "Sharma",
  "email": "aditi@acme.in",
  "date_of_joining": "2024-06-01",
  "date_of_leaving": null,
  "status": "active",
  "employment_type": "full_time",
  "department_id": 7,
  "designation_id": 12,
  "location": "Bhubaneswar",
  "salary_group_id": 3,
  "payroll_cycle": "monthly",
  "allow_generate_payroll": true,
  "bank": {
    "payment_method": "bank_transfer",
    "account_holder_name": "Aditi Sharma",
    "account_number": "50100234567890",
    "ifsc": "HDFC0000123",
    "bank_name": "HDFC Bank"
  },
  "statutory": {
    "pan": "ABCPS1234D",
    "uan": "101234567890",
    "pf_applicable": true,
    "pf_wage_ceiling_applied": true,
    "esi_number": "4400012345",
    "esi_applicable": false,
    "pt_state": "Odisha",
    "tax_regime": "new"
  }
}
PATCH /employees/{id}

Update an employee

Send only what changed. Statutory identifiers - PAN, UAN, PF number, ESI number, PT state, LWF - can be patched here or through the dedicated /statutory sub-resource; both write the same fields.

Changing the date of joining after a run has been calculated does not retouch it. Recalculate, or reverse and re-run.

Path parameters

id required
string
Employee id, or your own identifier prefixed with ext: - /employees/ext:emp-1001.

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

external_id required
string
organization_id
integer
The entity the employee is paid by.
employee_code
string
The code printed on the payslip. Generated when omitted.
first_name required
string
Given name, as it should read on the payslip and in statutory returns.
last_name
string
Family name.
email
string (email)
Used for emailed payslips. Employees without one are skipped when a run is emailed.
phone
string
Mobile number in E.164 form.
gender
enum: female, male, other, undisclosed
Used where a professional tax schedule sets a different threshold by gender.
date_of_birth
string (date)
Drives ESI and gratuity edge cases.
date_of_joining required
string (date)
Drives pro-rating in the joining month, gratuity eligibility and PF Form 5.
employment_type
enum: full_time, part_time, contract, intern, daily_wage
Full time, part time, contract, intern or daily wage.
department_id
integer
INDPayroll department id.
department_external_id
string
Use instead of department_id when you only hold your own identifiers.
designation_id
integer
INDPayroll designation id.
location
string
Work location. Location-scoped holidays are matched against it.
salary_group_id
integer
The structure template the employee inherits components from.
payroll_cycle
enum: monthly, weekly, bi-weekly
Pay frequency for this employee.
monthly_ctc
number (double)
Monthly cost to company. Sets the opening structure when sent on a create.
daily_wage
number (double)
Rate per day, for daily-wage employees. Send instead of a CTC.
allow_generate_payroll
boolean
False keeps the employee out of every run without exiting them.
bank
EmployeeBank
Where the net pay goes.
statutory
EmployeeStatutory
PAN, UAN, ESI, PT state and the elected tax regime.

Responses

  • 200 Updated. Employee
  • 400 The request was malformed or failed validation. ErrorResponse
  • 404 No such resource, or it belongs to an organization this key cannot reach. ErrorResponse
  • 409 The resource is in a state that forbids the change - a locked or paid run, a duplicate external_id, or a settlement already approved. ErrorResponse
Request
curl -X PATCH https://api.indpayroll.com/v1/employees/{id} \
  -H "Authorization: Bearer $INDP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "external_id": "emp-1001",
  "first_name": "Aditi",
  "date_of_joining": "2024-06-01",
  "department_id": 9,
  "statutory": {
    "uan": "101234567899"
  }
}'
POST /employees/{id}/exit

Mark an employee's last working day

Sets the date of leaving and moves the employee to exited. The final month is pro-rated to that date, the employee drops out of later runs, and the exit feeds PF Form 10 and the full and final settlement.

Set create_fnf: true to open the settlement in the same call.

Path parameters

id required
string
Employee id, or your own identifier prefixed with ext: - /employees/ext:emp-1001.

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

date_of_leaving required
string (date)
Last working day. The final month is pro-rated to it.
reason
enum: resignation, termination, retirement, end_of_contract, death, absconding
notes
string
create_fnf
boolean
Open a full and final settlement immediately. Equivalent to calling POST /fnf afterwards.

Responses

  • 200 Exited. Employee
  • 400 The request was malformed or failed validation. ErrorResponse
  • 404 No such resource, or it belongs to an organization this key cannot reach. ErrorResponse
  • 409 The resource is in a state that forbids the change - a locked or paid run, a duplicate external_id, or a settlement already approved. ErrorResponse
Request
curl -X POST https://api.indpayroll.com/v1/employees/{id}/exit \
  -H "Authorization: Bearer $INDP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "date_of_leaving": "2026-09-18",
  "reason": "resignation",
  "create_fnf": true
}'
GET /employees/{id}/bank

Retrieve bank details

Path parameters

id required
string
Employee id, or your own identifier prefixed with ext: - /employees/ext:emp-1001.

Responses

  • 200 Bank details. EmployeeBank
  • 404 No such resource, or it belongs to an organization this key cannot reach. ErrorResponse
PUT /employees/{id}/bank

Replace bank details

The IFSC is checked for shape, not against the bank. An employee with incomplete details still gets a payslip; they are left out of the bank file and listed under excluded in the disbursement summary.

Path parameters

id required
string
Employee id, or your own identifier prefixed with ext: - /employees/ext:emp-1001.

Body application/json · required

payment_method
enum: bank_transfer, cash, cheque, upi
account_holder_name
string
account_number
string
ifsc
string
bank_name
string
branch
string
upi_id
string

Responses

  • 200 Updated. EmployeeBank
  • 400 The request was malformed or failed validation. ErrorResponse
  • 404 No such resource, or it belongs to an organization this key cannot reach. ErrorResponse
Request
curl -X PUT https://api.indpayroll.com/v1/employees/{id}/bank \
  -H "Authorization: Bearer $INDP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "payment_method": "bank_transfer",
  "account_holder_name": "Aditi Sharma",
  "account_number": "50100234567890",
  "ifsc": "HDFC0000123",
  "bank_name": "HDFC Bank",
  "branch": "Saheed Nagar, Bhubaneswar"
}'
GET /employees/{id}/statutory

Retrieve statutory identifiers

Path parameters

id required
string
Employee id, or your own identifier prefixed with ext: - /employees/ext:emp-1001.

Responses

  • 200 Statutory identifiers and flags. EmployeeStatutory
  • 404 No such resource, or it belongs to an organization this key cannot reach. ErrorResponse
PUT /employees/{id}/statutory

Replace statutory identifiers

PAN, UAN, PF and ESI numbers, PT state, LWF applicability and the elected tax regime. Changing the regime mid-year re-spreads the remaining TDS over the rest of the financial year rather than restating what was deducted.

Path parameters

id required
string
Employee id, or your own identifier prefixed with ext: - /employees/ext:emp-1001.

Body application/json · required

pan
string
aadhaar_last4
string
Only the last four digits are stored, for ECR matching.
uan
string
Universal account number. Required for the PF ECR.
pf_number
string
pf_applicable
boolean
pf_wage_ceiling_applied
boolean
True to cap PF wages at the statutory ceiling (currently 15,000/month); false to compute PF on actual PF wages.
vpf_percent
number
Voluntary PF on top of the statutory 12%, as a percentage of PF wages.
esi_number
string
esi_applicable
boolean
The engine still re-tests the wage threshold each period and honours the contribution-period rule.
pt_state
string
State whose professional tax schedule applies to this employee.
pt_applicable
boolean
lwf_applicable
boolean
tax_regime
enum: old, new
Regime the employee elected for the financial year. Drives TDS.

Responses

  • 200 Updated. EmployeeStatutory
  • 400 The request was malformed or failed validation. ErrorResponse
  • 404 No such resource, or it belongs to an organization this key cannot reach. ErrorResponse
Request
curl -X PUT https://api.indpayroll.com/v1/employees/{id}/statutory \
  -H "Authorization: Bearer $INDP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "pan": "ABCPS1234D",
  "uan": "101234567890",
  "pf_applicable": true,
  "pf_wage_ceiling_applied": true,
  "vpf_percent": 0,
  "esi_number": "4400012345",
  "esi_applicable": true,
  "pt_state": "Odisha",
  "pt_applicable": true,
  "lwf_applicable": true,
  "tax_regime": "old"
}'