# INDPayroll Payroll Engine API - OpenAPI 3.1 description.
#
# This is a split (multi-file) specification. This root document carries the
# info, servers, security schemes, tags and webhook events; every path item is
# referenced out to a file under ./paths/, and every schema, parameter and
# shared response to a file under ./components/.
#
# Bundle it into a single file with any OpenAPI tool, e.g.
#   npx @redocly/cli bundle openapi.yaml -o indpayroll-payroll-engine.yaml
#
# Human-readable documentation: https://www.indpayroll.com/docs/api
openapi: 3.1.0

info:
  title: INDPayroll Payroll Engine API
  version: "1.0.0"
  summary: Run Indian payroll - employees, structures, inputs, runs, payslips, statutory filings - from your own product.
  description: |
    The Payroll Engine API is the partner-facing HTTP surface of INDPayroll. It
    exposes the same engine the INDPayroll product runs on: employee masters,
    salary structures, period inputs, payroll runs, payslips, disbursement files
    and Indian statutory output (PF, ESI, PT, LWF, TDS).

    It is built for two kinds of integrator:

    - **Embedders**, who own their own UI and use INDPayroll only to calculate
      and persist payroll. They typically push employees and period inputs, call
      `POST /payroll-runs/{id}/calculate`, and read payslips back as JSON.
    - **Calculate-only partners**, who never persist anything here. A single
      `POST /payroll-runs/preview` call returns fully calculated slips - basic,
      HRA, PF, ESI, PT, TDS, LWF, LOP, overtime, reimbursements, gross,
      deductions, net payable and employer contributions - and writes nothing.

    ## Conventions

    - All requests and responses are JSON unless a `format` query parameter asks
      for a file (`xlsx`, `csv`, `txt`, `pdf`).
    - Money is a decimal number of Indian rupees with two decimal places. The
      organization's payroll currency is returned on every run.
    - Dates are ISO 8601 calendar dates (`2026-09-30`). Timestamps are RFC 3339
      in UTC (`2026-09-30T18:30:00Z`).
    - Employees may be addressed either by INDPayroll id or by your own
      `external_id` prefixed with `ext:` - `/employees/ext:emp-1001`.
    - Every mutating request accepts an `Idempotency-Key` header; a repeat with
      the same key returns the first response instead of acting twice.
    - List endpoints are paginated and return a `meta` envelope.

    ## Authentication

    Every call carries an API key. Either send it as a bearer token, or sign the
    request with your key secret (HMAC-SHA256) when your security review needs
    per-request signatures. See the `securitySchemes` section below and
    https://www.indpayroll.com/docs/api/authentication.
  termsOfService: https://www.indpayroll.com/terms-of-use
  contact:
    name: INDPayroll partner engineering
    url: https://www.indpayroll.com/contact
    email: sales@indpayroll.com
  license:
    name: Proprietary - use governed by the INDPayroll partner agreement
    url: https://www.indpayroll.com/terms-of-use

servers:
  - url: https://api.indpayroll.com/v1
    description: Production
  - url: https://sandbox-api.indpayroll.com/v1
    description: Sandbox - same engine, synthetic organizations, no filings submitted

externalDocs:
  description: Payroll Engine API documentation
  url: https://www.indpayroll.com/docs/api

security:
  - bearerAuth: []
  - hmacKey: []
    hmacTimestamp: []
    hmacSignature: []

tags:
  - name: Platform
    description: Health, tokens, key introspection, organizations and webhook subscriptions.
  - name: Org master
    description: Departments, designations, holiday calendar and shifts.
  - name: Employees
    description: The employee master the engine reads on every run.
  - name: Payroll configuration
    description: One-time per-organization setup - cycle, components, groups, TDS, PT, payment methods.
  - name: Employee salary
    description: Per-employee salary structure, increments and variable components.
  - name: Period inputs
    description: Attendance, leave, overtime, expenses and advances the run consumes.
  - name: Payroll runs
    description: The engine itself - preview, calculate, hold, lock, mark paid, reverse.
  - name: Payslips
    description: Generated slips as JSON, PDF and salary certificates.
  - name: Disbursement
    description: Bank files, disbursement summaries and Tally sync.
  - name: Statutory
    description: PF, ESI, PT, LWF and TDS returns in filing-ready formats.
  - name: FnF and gratuity
    description: Full and final settlements and gratuity projections.
  - name: Reports
    description: Payroll, attendance and compliance reports as JSON or file exports.
  - name: Imports
    description: Bulk file imports and their job status.
  - name: Webhooks
    description: Event subscriptions. The events themselves are described under `webhooks`.

paths:
  # 0. Platform
  /health:
    $ref: "./paths/platform.yaml#/health"
  /auth/token:
    $ref: "./paths/platform.yaml#/auth_token"
  /me:
    $ref: "./paths/platform.yaml#/me"
  /organizations:
    $ref: "./paths/platform.yaml#/organizations"
  /organizations/{id}:
    $ref: "./paths/platform.yaml#/organizations_id"
  /webhooks:
    $ref: "./paths/platform.yaml#/webhooks"
  /webhooks/{id}:
    $ref: "./paths/platform.yaml#/webhooks_id"

  # 1. Org master
  /departments:
    $ref: "./paths/org-master.yaml#/departments"
  /departments/{id}:
    $ref: "./paths/org-master.yaml#/departments_id"
  /designations:
    $ref: "./paths/org-master.yaml#/designations"
  /designations/{id}:
    $ref: "./paths/org-master.yaml#/designations_id"
  /holidays:
    $ref: "./paths/org-master.yaml#/holidays"
  /holidays/{id}:
    $ref: "./paths/org-master.yaml#/holidays_id"
  /shifts:
    $ref: "./paths/org-master.yaml#/shifts"
  /shifts/{id}:
    $ref: "./paths/org-master.yaml#/shifts_id"
  /shifts/{id}/assign:
    $ref: "./paths/org-master.yaml#/shifts_id_assign"

  # 2. Employees
  /employees:
    $ref: "./paths/employees.yaml#/employees"
  /employees/bulk:
    $ref: "./paths/employees.yaml#/employees_bulk"
  /employees/{id}:
    $ref: "./paths/employees.yaml#/employees_id"
  /employees/{id}/exit:
    $ref: "./paths/employees.yaml#/employees_id_exit"
  /employees/{id}/bank:
    $ref: "./paths/employees.yaml#/employees_id_bank"
  /employees/{id}/statutory:
    $ref: "./paths/employees.yaml#/employees_id_statutory"

  # 3. Payroll configuration
  /payroll-settings:
    $ref: "./paths/payroll-config.yaml#/payroll_settings"
  /payroll-settings/period:
    $ref: "./paths/payroll-config.yaml#/payroll_settings_period"
  /payroll-settings/salary-days:
    $ref: "./paths/payroll-config.yaml#/payroll_settings_salary_days"
  /payroll-settings/currency:
    $ref: "./paths/payroll-config.yaml#/payroll_settings_currency"
  /salary-components:
    $ref: "./paths/payroll-config.yaml#/salary_components"
  /salary-components/{id}:
    $ref: "./paths/payroll-config.yaml#/salary_components_id"
  /salary-groups:
    $ref: "./paths/payroll-config.yaml#/salary_groups"
  /salary-groups/{id}:
    $ref: "./paths/payroll-config.yaml#/salary_groups_id"
  /salary-groups/{id}/components:
    $ref: "./paths/payroll-config.yaml#/salary_groups_id_components"
  /salary-groups/{id}/employees:
    $ref: "./paths/payroll-config.yaml#/salary_groups_id_employees"
  /tds-slabs:
    $ref: "./paths/payroll-config.yaml#/tds_slabs"
  /tds-slabs/{id}:
    $ref: "./paths/payroll-config.yaml#/tds_slabs_id"
  /professional-tax/states:
    $ref: "./paths/payroll-config.yaml#/professional_tax_states"
  /employees/{id}/professional-tax:
    $ref: "./paths/payroll-config.yaml#/employees_id_professional_tax"
  /reimbursement-rates:
    $ref: "./paths/payroll-config.yaml#/reimbursement_rates"
  /payment-methods:
    $ref: "./paths/payroll-config.yaml#/payment_methods"
  /payroll-cycles:
    $ref: "./paths/payroll-config.yaml#/payroll_cycles"
  /employees/{id}/cycle:
    $ref: "./paths/payroll-config.yaml#/employees_id_cycle"

  # 4. Employee salary
  /employees/{id}/salary:
    $ref: "./paths/employee-salary.yaml#/employees_id_salary"
  /employees/{id}/salary/increments:
    $ref: "./paths/employee-salary.yaml#/employees_id_salary_increments"
  /employees/{id}/salary/history:
    $ref: "./paths/employee-salary.yaml#/employees_id_salary_history"
  /employees/{id}/salary/allow-payroll:
    $ref: "./paths/employee-salary.yaml#/employees_id_salary_allow_payroll"
  /employees/{id}/hourly-rate:
    $ref: "./paths/employee-salary.yaml#/employees_id_hourly_rate"
  /employees/{id}/variable-components:
    $ref: "./paths/employee-salary.yaml#/employees_id_variable_components"

  # 5. Period inputs
  /attendance:
    $ref: "./paths/period-inputs.yaml#/attendance"
  /attendance/bulk:
    $ref: "./paths/period-inputs.yaml#/attendance_bulk"
  /leaves:
    $ref: "./paths/period-inputs.yaml#/leaves"
  /leaves/{id}:
    $ref: "./paths/period-inputs.yaml#/leaves_id"
  /overtime-requests:
    $ref: "./paths/period-inputs.yaml#/overtime_requests"
  /overtime-requests/{id}/approve:
    $ref: "./paths/period-inputs.yaml#/overtime_requests_id_approve"
  /overtime-policies:
    $ref: "./paths/period-inputs.yaml#/overtime_policies"
  /overtime-policies/{id}/assign:
    $ref: "./paths/period-inputs.yaml#/overtime_policies_id_assign"
  /expenses:
    $ref: "./paths/period-inputs.yaml#/expenses"
  /expenses/{id}/approve:
    $ref: "./paths/period-inputs.yaml#/expenses_id_approve"
  /employees/{id}/advances:
    $ref: "./paths/period-inputs.yaml#/employees_id_advances"

  # 6. Payroll runs
  /payroll-runs/preview:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs_preview"
  /payroll-runs:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs"
  /payroll-runs/{id}:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs_id"
  /payroll-runs/{id}/calculate:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs_id_calculate"
  /payroll-runs/{id}/recalculate:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs_id_recalculate"
  /payroll-runs/{id}/employees:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs_id_employees"
  /payroll-runs/{id}/employees/{empId}:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs_id_employees_empId"
  /payroll-runs/{id}/hold:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs_id_hold"
  /payroll-runs/{id}/lock:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs_id_lock"
  /payroll-runs/{id}/unlock:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs_id_unlock"
  /payroll-runs/{id}/mark-paid:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs_id_mark_paid"
  /payroll-runs/{id}/email:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs_id_email"
  /payroll-runs/{id}/reverse:
    $ref: "./paths/payroll-runs.yaml#/payroll_runs_id_reverse"

  # 7. Payslips
  /payslips:
    $ref: "./paths/payslips.yaml#/payslips"
  /payslips/{id}:
    $ref: "./paths/payslips.yaml#/payslips_id"
  /payslips/{id}.pdf:
    $ref: "./paths/payslips.yaml#/payslips_id_pdf"
  /payslips/{id}/email:
    $ref: "./paths/payslips.yaml#/payslips_id_email"
  /payslips/{id}/salary-certificate:
    $ref: "./paths/payslips.yaml#/payslips_id_salary_certificate"
  /employees/{id}/payslips:
    $ref: "./paths/payslips.yaml#/employees_id_payslips"

  # 8. Disbursement
  /payroll-runs/{id}/bank-file:
    $ref: "./paths/disbursement.yaml#/payroll_runs_id_bank_file"
  /payroll-runs/{id}/disbursement-summary:
    $ref: "./paths/disbursement.yaml#/payroll_runs_id_disbursement_summary"
  /payroll-runs/{id}/sync-tally:
    $ref: "./paths/disbursement.yaml#/payroll_runs_id_sync_tally"

  # 9. India statutory
  /statutory/pf/ecr:
    $ref: "./paths/statutory.yaml#/statutory_pf_ecr"
  /statutory/pf/form-5:
    $ref: "./paths/statutory.yaml#/statutory_pf_form_5"
  /statutory/pf/form-10:
    $ref: "./paths/statutory.yaml#/statutory_pf_form_10"
  /statutory/pf/form-12a:
    $ref: "./paths/statutory.yaml#/statutory_pf_form_12a"
  /statutory/pf/form-3a:
    $ref: "./paths/statutory.yaml#/statutory_pf_form_3a"
  /statutory/pf/form-6a:
    $ref: "./paths/statutory.yaml#/statutory_pf_form_6a"
  /statutory/pf/statement:
    $ref: "./paths/statutory.yaml#/statutory_pf_statement"
  /statutory/esi/report:
    $ref: "./paths/statutory.yaml#/statutory_esi_report"
  /statutory/esi/return:
    $ref: "./paths/statutory.yaml#/statutory_esi_return"
  /statutory/esi/form-01:
    $ref: "./paths/statutory.yaml#/statutory_esi_form_01"
  /statutory/pt/report:
    $ref: "./paths/statutory.yaml#/statutory_pt_report"
  /statutory/pt/return:
    $ref: "./paths/statutory.yaml#/statutory_pt_return"
  /statutory/lwf/return:
    $ref: "./paths/statutory.yaml#/statutory_lwf_return"
  /statutory/lwf/mark-submitted:
    $ref: "./paths/statutory.yaml#/statutory_lwf_mark_submitted"
  /statutory/tds/24q:
    $ref: "./paths/statutory.yaml#/statutory_tds_24q"
  /statutory/tds/26q:
    $ref: "./paths/statutory.yaml#/statutory_tds_26q"
  /statutory/filings:
    $ref: "./paths/statutory.yaml#/statutory_filings"
  /government-forms:
    $ref: "./paths/statutory.yaml#/government_forms"
  /government-forms/{id}:
    $ref: "./paths/statutory.yaml#/government_forms_id"

  # 10. FnF and gratuity
  /fnf:
    $ref: "./paths/fnf-gratuity.yaml#/fnf"
  /fnf/{id}:
    $ref: "./paths/fnf-gratuity.yaml#/fnf_id"
  /fnf/{id}/recalculate:
    $ref: "./paths/fnf-gratuity.yaml#/fnf_id_recalculate"
  /fnf/{id}/approve:
    $ref: "./paths/fnf-gratuity.yaml#/fnf_id_approve"
  /fnf/{id}/cancel:
    $ref: "./paths/fnf-gratuity.yaml#/fnf_id_cancel"
  /fnf/{id}/mark-paid:
    $ref: "./paths/fnf-gratuity.yaml#/fnf_id_mark_paid"
  /gratuity:
    $ref: "./paths/fnf-gratuity.yaml#/gratuity"
  /gratuity/{employeeId}:
    $ref: "./paths/fnf-gratuity.yaml#/gratuity_employeeId"
  /gratuity/export:
    $ref: "./paths/fnf-gratuity.yaml#/gratuity_export"

  # 11. Reports
  /reports/payroll-register/summary:
    $ref: "./paths/reports.yaml#/reports_payroll_register_summary"
  /reports/payroll-register/detail:
    $ref: "./paths/reports.yaml#/reports_payroll_register_detail"
  /reports/salary-register:
    $ref: "./paths/reports.yaml#/reports_salary_register"
  /reports/attendance-summary:
    $ref: "./paths/reports.yaml#/reports_attendance_summary"
  /reports/overtime:
    $ref: "./paths/reports.yaml#/reports_overtime"
  /reports/ctc-annual:
    $ref: "./paths/reports.yaml#/reports_ctc_annual"
  /reports/pay-difference:
    $ref: "./paths/reports.yaml#/reports_pay_difference"
  /reports/monthly-tds:
    $ref: "./paths/reports.yaml#/reports_monthly_tds"
  /reports/esi-deduction:
    $ref: "./paths/reports.yaml#/reports_esi_deduction"
  /reports/salary-summary:
    $ref: "./paths/reports.yaml#/reports_salary_summary"
  /reports/gratuity:
    $ref: "./paths/reports.yaml#/reports_gratuity"
  /reports/leave:
    $ref: "./paths/reports.yaml#/reports_leave"
  /reports/expense:
    $ref: "./paths/reports.yaml#/reports_expense"

  # 12. Import
  /imports/template:
    $ref: "./paths/imports.yaml#/imports_template"
  /imports:
    $ref: "./paths/imports.yaml#/imports"
  /imports/{jobId}:
    $ref: "./paths/imports.yaml#/imports_jobId"

webhooks:
  employee.upserted:
    $ref: "./paths/webhook-events.yaml#/employee_upserted"
  payroll.run.calculated:
    $ref: "./paths/webhook-events.yaml#/payroll_run_calculated"
  payroll.run.paid:
    $ref: "./paths/webhook-events.yaml#/payroll_run_paid"
  payroll.run.reversed:
    $ref: "./paths/webhook-events.yaml#/payroll_run_reversed"
  payslip.ready:
    $ref: "./paths/webhook-events.yaml#/payslip_ready"
  fnf.settled:
    $ref: "./paths/webhook-events.yaml#/fnf_settled"
  statutory.file.ready:
    $ref: "./paths/webhook-events.yaml#/statutory_file_ready"
  import.completed:
    $ref: "./paths/webhook-events.yaml#/import_completed"

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: INDPayroll API key, or a short-lived token from POST /auth/token
      description: |
        `Authorization: Bearer indp_live_...`. Use the API key directly, or
        exchange it once at `POST /auth/token` for a 60-minute access token and
        send that instead.
    hmacKey:
      type: apiKey
      in: header
      name: X-INDP-Key
      description: The public half of the key pair, e.g. `indp_live_9f2c...`.
    hmacTimestamp:
      type: apiKey
      in: header
      name: X-INDP-Timestamp
      description: |
        Unix seconds at the moment of signing. Requests more than 300 seconds
        old are rejected with `401 signature_expired`.
    hmacSignature:
      type: apiKey
      in: header
      name: X-INDP-Signature
      description: |
        Lowercase hex HMAC-SHA256 of
        `{timestamp}.{method}.{path}.{sha256(body)}`, keyed with your key
        secret. Send all three `X-INDP-*` headers together.
