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

Authentication

Every call except GET /health carries credentials. You can send the API key as a bearer token, or sign each request with HMAC-SHA256 when your security review asks for per-request signatures. Both reach the same engine with the same scopes.

Keys

A key pair is issued per environment from the INDPayroll partner console: a key id (indp_live_... or indp_test_...) and a secret shown once. Live and sandbox keys are separate and cannot reach each other's data.

The secret never belongs in a browserThe Payroll Engine API is server-to-server. A key in front-end code exposes every employee's salary and every statutory identifier you hold.

Bearer tokens

The simplest form: send the key itself. Optionally exchange it once at POST /auth/token for a 60-minute access token and send that instead, so the key itself stays out of most requests.

cURL
curl https://api.indpayroll.com/v1/organizations \
  -H "Authorization: Bearer $INDP_API_KEY" \
  -H "INDP-Version: 2026-09-01"

HMAC signing

Send three headers instead of an Authorization header. The signature is a lowercase hex HMAC-SHA256 of {timestamp}.{method}.{path}.{sha256(body)}, keyed with your key secret.

HeaderValue
X-INDP-KeyThe key id - the public half of the pair.
X-INDP-TimestampUnix seconds at the moment of signing.
X-INDP-SignatureHex HMAC-SHA256 of the canonical string below.
Signing a request
import { createHash, createHmac } from 'node:crypto';

function sign({ method, path, body, keyId, keySecret }) {
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const bodyHash = createHash('sha256').update(body ?? '').digest('hex');
  const canonical = [timestamp, method.toUpperCase(), path, bodyHash].join('.');
  const signature = createHmac('sha256', keySecret).update(canonical).digest('hex');
  return {
    'X-INDP-Key': keyId,
    'X-INDP-Timestamp': timestamp,
    'X-INDP-Signature': signature,
  };
}

path is the path and query string as sent, without the host - /v1/payroll-runs/preview. Requests signed more than 300 seconds ago are rejected with 401 signature_expired, so sign at the moment you send, not when you build the payload.

Scopes

Keys are issued with the narrowest set that does the job. A call outside them returns 403 missing_scope naming the scope it wanted.

ScopeGrants
org:readOrganizations, departments, designations, holidays, shifts.
org:writeChanging statutory identifiers, the pay calendar and org master data.
employees:readThe employee master, salary structures and history.
employees:writeCreating and updating employees, structures, increments and exits.
payroll:readRuns, payslips, disbursement summaries and reports.
payroll:writePreview, calculate, recalculate, hold, lock, mark paid.
payroll:adminUnlocking a locked run and reversing a paid one.
statutory:readPF, ESI, PT, LWF and TDS files, and the filing dashboard.

Sandbox

Point at https://sandbox-api.indpayroll.com/v1 with a indp_test_ key. Same engine, same statutory rules, synthetic organizations - and nothing is ever submitted to EPFO, ESIC or TRACES. Build and test everything there first.