> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cadanapay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pay an Employee End-to-End

> From checking jurisdiction support to a completed payroll with payslips — the full employee lifecycle in one walkthrough

export const ApiExample = ({method = "GET", path, params, body, reference, tenantKey}) => {
  const baseUrl = "https://api.cadanapay.com";
  const query = params ? "?" + Object.entries(params).map(([k, v]) => `${k}=${v}`).join("&") : "";
  const url = `${baseUrl}${path}${query}`;
  const isPlatformPath = (/^\/(v1\/)?platform(\/|$)/).test(path || "");
  const includeTenantKey = tenantKey === undefined ? !isPlatformPath : tenantKey;
  let curl = `curl -X ${method} '${url}' \\\n  -H 'Authorization: Bearer YOUR_API_KEY'`;
  if (includeTenantKey) {
    curl += ` \\\n  -H 'X-MultiTenantKey: YOUR_BUSINESS_TENANT_KEY'`;
  }
  if (body) {
    const formatted = JSON.stringify(body, null, 2);
    curl += ` \\\n  -H 'Content-Type: application/json' \\\n  -d '${formatted}'`;
  }
  return <div>
      <CodeBlock language="bash" filename="bash" wrap>
        {curl}
      </CodeBlock>
      {reference && <div style={{
    marginTop: "-0.5rem",
    marginBottom: "1rem"
  }}>
          <a href={reference} style={{
    fontSize: "0.875rem"
  }}>
            Try it in the playground →
          </a>
        </div>}
    </div>;
};

This walkthrough takes one employee from "does Cadana support their country?" to a completed payroll with a downloadable payslip. (Paying an independent contractor instead? That flow skips statutory compliance and tax profiles — see [Pay a Contractor End-to-End](/workforce/pay-a-contractor).) The worked example uses India — one of the [supported jurisdictions](/api-reference/statutory/jurisdictions/list-supported-jurisdictions) — because it exercises every part of the flow: statutory documents, a tax profile, and the [EOR employment model](/workforce/employment-models). Every step is country-parameterized: the discovery endpoints in steps 1–2 tell you exactly what to collect for any other jurisdiction.

<Note>
  Profile writes are applied asynchronously — a read immediately after a write can return the previous state. When scripting this flow, poll or retry briefly between a write and its verification. See the notes in each step.
</Note>

## Step 1 — Confirm the jurisdiction is supported

List jurisdictions and confirm the worker's country is `live`. The response includes each country's filing types, payroll frequency, and currency.

<ApiExample method="GET" path="/v1/statutory/jurisdictions" reference="/api-reference/statutory/jurisdictions/list-supported-jurisdictions" />

## Step 2 — Discover what data the jurisdiction needs

The [required fields endpoint](/api-reference/statutory/jurisdictions/get-required-fields-for-tax-calculation-and-statutory-filing) returns every field the jurisdiction requires, tagged by `entity` (`person` or `business`) and `scope`:

* `scope: ["calculation"]` fields feed the tax engine — submit them to the person's tax profile (step 4)
* `scope: ["filing"]` fields go on statutory returns — submit them via the statutory fields endpoints (step 5)
* Fields with both scopes are submitted to both

<ApiExample method="GET" path="/v1/statutory/jurisdictions/IN/required-fields" params={{ worker_type: "employee" }} reference="/api-reference/statutory/jurisdictions/get-required-fields-for-tax-calculation-and-statutory-filing" />

## Step 3 — Create the employee

Create the person with their address (which determines the tax jurisdiction), compensation, and employment model. Set `compInfo.employmentModel: "eor"` to employ through Cadana's Employer of Record — this determines what the payroll debit collects; see [Employment Models](/workforce/employment-models).

<ApiExample method="POST" path="/v1/persons" body={{ type: "EMPLOYEE", firstName: "Vraj", lastName: "Shah", email: "vraj@example.com", address: { line1: "12 Alkapuri Main Road", city: "Vadodara", state: "GJ", postalCode: "390007", countryCode: "IN" }, compInfo: { type: "salaried", salary: { amount: 20090000, currency: "INR" }, frequency: "monthly", employmentModel: "eor" }, jobInfo: { title: "Software Engineer", startDate: "2026-08-01", employeeNumber: "EMP-101" } }} reference="/api-reference/workforce/persons/create" />

Then add identity details — date of birth, gender, and the identity documents the jurisdiction requires (for India: PAN as `taxId`, Aadhaar as `nationalId`, UAN as `socialSecurityId`):

<ApiExample method="PUT" path="/v1/persons/{personId}/personalInfo" body={{ dateOfBirth: "1994-05-12", gender: "male", nationality: "IN", nationalId: { fullId: "300000000099", type: "aadhaar" }, taxId: { fullId: "AAAPS1234V", type: "pan" }, socialSecurityId: { fullId: "100000000099", type: "uan" } }} reference="/api-reference/workforce/persons/update-personal-information" />

## Step 4 — Set the tax profile

Submit the `calculation`-scope fields from step 2 to the person's tax profile. These drive payroll tax computation — regime choice, employer size, year-to-date figures, and any declarations.

<ApiExample method="PUT" path="/v1/persons/{personId}/taxProfile" body={{ taxRegime: "new", employerSize: "large", workerType: "domestic", addressState: "GJ", payPeriodNumber: 4, totalPeriodsInYear: 12, ytdGrossIncome: 0, ytdTaxWithheld: 0 }} reference="/api-reference/workforce/persons/update-tax-profile" />

<Warning>
  The tax profile PUT replaces the whole profile — send every field you want kept, not just the one you're changing.
</Warning>

## Step 5 — Submit statutory filing data

Submit the `filing`-scope person fields. Fields are validated against the jurisdiction's definitions — bad formats and unknown keys are rejected with per-field errors.

<ApiExample method="PUT" path="/v1/statutory/persons/{personId}/fields/IN" body={{ fields: { pan: "AAAPS1234V", uan: "100000000099", aadhaar: "300000000099", esicIpNumber: "6000000099", gender: "M", fatherOrHusbandName: "Rajesh Shah", employeeNumber: "EMP-101" } }} reference="/api-reference/statutory/statutory-fields/submit-person-filing-fields" />

Document-type fields (like India's Aadhaar and PAN card images) take a `fileId`: reserve one with [create upload URL](/api-reference/workforce/files/create-upload-url) (purpose `statutory-document`), PUT the file bytes to the returned URL, then submit the `fileId` as the field value. See [document fields](/statutory/getting-started#document-fields).

Business-side fields (for India: TAN, employer PAN, EPFO establishment ID, ESIC employer code, LIN) go to the [business fields endpoint](/api-reference/statutory/statutory-fields/submit-business-filing-fields) once per jurisdiction.

## Step 6 — Verify nothing is missing

The requirements endpoint returns per-field `satisfied`/`outstanding` status. Loop until nothing is outstanding — each entry names the exact field and endpoint to fix it.

<ApiExample method="GET" path="/v1/statutory/requirements" params={{ personId: "{personId}", countryCode: "IN" }} reference="/api-reference/statutory/requirements/list-outstanding-requirements" />

## Step 7 — Add payment details

Fetch the corridor's requirements schema — it specifies every field, format, and conditional rule for the worker's country, currency, and payment method — then build the payment info from it.

<ApiExample method="GET" path="/v1/payment-requirements" params={{ countryCode: "IN", paymentMethod: "bank", currency: "INR" }} reference="/api-reference/payments/resources/payment-corridor-requirements" />

<ApiExample method="PUT" path="/v1/persons/{personId}/paymentInfo" body={{ preferredMethod: "bank", bank: { accountName: "Vraj Shah", ownerType: "Individual", accountNumber: "12345678901234", bankCode: "HDFC", bankName: "HDFC Bank", sortCode: "HDFC0000123", address: { line1: "12 Alkapuri Main Road", city: "Vadodara", postalCode: "390007", state: "GJ", countryCode: "IN" }, email: "vraj@example.com", phoneNumber: { countryCode: "91", number: "9876543210" } } }} reference="/api-reference/workforce/persons/update-payment-information" />

A person must have payment details before they can be added to a payroll.

## Step 8 — Run payroll

Create a payroll, save it with the worker's entry, then approve. Entries carry the base salary plus any allowances, bonuses, or voluntary deductions; the engine computes taxes and statutory contributions on save.

<ApiExample method="POST" path="/v1/payrolls" body={{ workerType: "EMPLOYEE", type: "ONE_OFF" }} reference="/api-reference/workforce/payrolls/create" />

<ApiExample method="POST" path="/v1/payrolls/{payrollId}/save" body={{ payrollDate: "2026-08-31", payPeriod: { fromDate: "2026-08-01", toDate: "2026-08-31" }, entries: [{ personId: "{personId}", salary: { amount: 20090000, currency: "INR" }, allowances: [{ name: "HRA", isTaxable: true, amount: { amount: 10045000, currency: "INR" } }] }] }} reference="/api-reference/workforce/payrolls/save" />

Fetch the saved payroll to review the computed gross, deductions, employer contributions, net, and the `debit` that will be collected from your balance (what the debit covers depends on the [employment model](/workforce/employment-models)):

<ApiExample method="GET" path="/v1/payrolls/{payrollId}" reference="/api-reference/workforce/payrolls/get" />

Then approve it:

<ApiExample method="POST" path="/v1/payrolls/{payrollId}/approve" reference="/api-reference/workforce/payrolls/approve" />

A payroll dated in the future is `Scheduled` and disburses on the payroll date. In sandbox, use a past `payrollDate` to complete the payroll immediately on approval — see [testing payrolls](/reference/sandbox#testing-payrolls).

## Step 9 — Payslips and the invoice

Once the payroll is `Completed`, download payslips and reconcile the invoice:

<ApiExample method="GET" path="/v1/payrolls/{payrollId}/payslip-links" reference="/api-reference/workforce/payrolls/get-payslip-links" />

The payroll's `invoiceId` links to an [itemized invoice](/api-reference/workforce/invoices/get-invoice) breaking the debit into line categories with per-line FX rates.

## What's next

<CardGroup cols={2}>
  <Card title="Employment Models" icon="building-user" href="/workforce/employment-models">
    What the debit collects under direct employment vs EOR
  </Card>

  <Card title="Payroll Lifecycle" icon="arrows-rotate" href="/workforce/payroll-lifecycle">
    Status flow, FX handling, and webhooks
  </Card>

  <Card title="Statutory Compliance" icon="scale-balanced" href="/statutory/getting-started">
    Filing data, documents, and the requirements loop in depth
  </Card>

  <Card title="Tax Engine" icon="calculator" href="/tax/getting-started">
    Standalone tax calculations with full formula transparency
  </Card>
</CardGroup>
