Independence Day offer · ₹52/employee/moClaim offer
INDPayroll
Payroll Engine API

Quickstart

The shortest path to a number you can show a customer. Every call below runs against the sandbox, which uses the same engine as production with synthetic organizations and nothing filed to any portal.

1. Confirm the key

GET /me tells you which organizations the key can reach, what it may do and how hard you may push. Start here - it is the cheapest way to prove your credentials work.

Request
curl https://sandbox-api.indpayroll.com/v1/me \
  -H "Authorization: Bearer $INDP_API_KEY"
Response
{
  "key_id": "indp_test_9f2c41ab",
  "mode": "sandbox",
  "scopes": ["org:read", "employees:write", "payroll:write"],
  "organization_ids": [42],
  "rate_limits": { "requests_per_minute": 600, "payroll_runs_per_hour": 60 }
}

2. Push the employees

Employees are matched on your own external_id, so the same call creates the first time and updates every time after. Send up to 500 at once to /employees/bulk.

bash
curl -X POST https://sandbox-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,
        "statutory": { "pan": "ABCPS1234D", "uan": "101234567890", "pt_state": "Odisha" }
      }
    ]
  }'
What the engine needs per employeeexternal_id, name, date of joining, department, a salary group or explicit components, a CTC or daily wage, bank details, PAN, UAN, ESI number and a PT state. Anything else can follow later.

3. Push the period inputs

Attendance, leave, overtime and expenses are what turn a structure into a payslip. Push them with POST /attendance/bulk and friends - or skip this step entirely and pass the same numbers inline in the preview call below.

4. Preview the run

POST /payroll-runs/preview calculates every employee and returns the slips without writing anything. For calculate-only partners this single call is the whole integration.

bash
curl -X POST https://sandbox-api.indpayroll.com/v1/payroll-runs/preview \
  -H "Authorization: Bearer $INDP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_id": 42,
    "cycle": "monthly",
    "period_start": "2026-09-01",
    "period_end": "2026-09-30",
    "employee_ids": ["ext-1001"],
    "options": {
      "use_attendance": true,
      "mark_leaves_paid": true,
      "mark_absent_unpaid": true,
      "include_expense_claims": true,
      "add_timelogs": false
    }
  }'

Each employee comes back with basic, HRA, other earnings, PF, ESI, PT, TDS, LWF, loss of pay, overtime, reimbursements, gross, total deductions, net payable, employer PF and ESI, and payable days. That object is the contract you embed in your own UI.

5. Commit it

  1. 1Create: POST /payroll-runs opens a draft for the period.
  2. 2Calculate: POST /payroll-runs/{id}/calculate persists the slips and moves the run to generated.
  3. 3Lock: POST /payroll-runs/{id}/lock freezes it, so the statutory files you generate next cannot drift from what you filed.
  4. 4Pay: POST /payroll-runs/{id}/mark-paid records the disbursement date, method and bank reference.

6. Take the output

  • Payslips as JSON from GET /payslips, or as PDF from GET /payslips/{id}.pdf.
  • The bank upload file from GET /payroll-runs/{id}/bank-file?format=csv.
  • The PF ECR from GET /statutory/pf/ecr?month=9&year=2026&format=txt, and the rest of the statutory set beside it.
Do not pollSubscribe to payroll.run.calculated, payslip.ready and statutory.file.ready instead. See Webhooks.