> ## 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.

# Getting Started

> Discover supported jurisdictions, required fields, and outstanding compliance requirements

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>;
};

## Prerequisites

<Steps>
  <Step title="API key from Dashboard">
    Get your API key from the [Cadana Dashboard](https://app.cadanapay.com). See [Authentication](/authentication) for details.
  </Step>

  <Step title="Business with workers in supported jurisdictions">
    At least one business must be created with workers [onboarded](/workforce/onboarding-workers) in a supported jurisdiction.
  </Step>
</Steps>

***

## Core Concepts

The Statutory Compliance API handles the post-payroll compliance lifecycle — filing statutory returns with government authorities and remitting payments. Depending on which [mode](/statutory/overview#choose-what-you-need) you're using, you'll engage different parts of the workflow below:

* **Full loop & Filing-only** — You go through the whole discover → check → submit → file workflow because Cadana needs complete worker data to prepare the return on your behalf.
* **Remittance-only** — You file with the authority yourself, so you can skip most of the discovery flow. The minimum you need is a [business](/workforce/manage-contracts) and the filing totals + Línea de Captura (or equivalent reference) to submit per period. Jump straight to the [Remittance-only walkthrough](/statutory/remittance#remittance-only-walkthrough-external-filing).

For the filing-data path, the statutory API acts as the **single front door**: you submit all required data through the statutory endpoints, and the API handles the rest.

```mermaid theme={null}
flowchart LR
    A[Discover required fields] --> B[Check what's missing]
    B --> C[Submit missing data]
    C --> D[Cadana files & remits]
```

The developer workflow is:

1. **Discover** — Call the required-fields endpoint to see what's needed for each jurisdiction
2. **Check** — Call the requirements endpoint to see what's still missing for a specific person or business
3. **Submit** — Send the missing data through the statutory PUT endpoints
4. **File & Remit** — Cadana handles filing with authorities and government payments

***

## Step 1: Discover Jurisdictions

List every jurisdiction the Statutory API supports, along with the filing types available in each.

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

**Response:**

```json theme={null}
{
  "data": [
    {
      "countryCode": "MX",
      "countryName": "Mexico",
      "currency": "MXN",
      "filingTypesCount": 4,
      "payrollFrequency": "MONTHLY",
      "status": "live"
    },
    {
      "countryCode": "BR",
      "countryName": "Brazil",
      "currency": "BRL",
      "filingTypesCount": 4,
      "payrollFrequency": "MONTHLY",
      "status": "live"
    }
  ]
}
```

***

## Step 2: Understand a Jurisdiction

Once you know which jurisdictions are available, drill into a specific country to see its filing types, deadlines, and cadence.

<ApiExample method="GET" path="/v1/statutory/jurisdictions/MX/filing-types" reference="/api-reference/statutory/jurisdictions/list-filing-types-for-a-jurisdiction" />

**Response:**

```json theme={null}
{
  "data": [
    {
      "id": "ft_mx_isr_monthly",
      "name": "ISR Withholding",
      "description": "Monthly income tax withholding return",
      "governmentFormName": "Declaración Provisional ISR",
      "authority": { "id": "auth_mx_sat", "name": "Servicio de Administración Tributaria", "abbreviation": "SAT" },
      "frequency": "MONTHLY",
      "dueDay": 17,
      "includesRemittance": true,
      "legislativeReference": "Ley del Impuesto Sobre la Renta (LISR), Artículo 96"
    },
    {
      "id": "ft_mx_imss_bimonthly",
      "name": "IMSS Contributions",
      "description": "Social security contributions",
      "authority": { "id": "auth_mx_imss", "name": "Instituto Mexicano del Seguro Social", "abbreviation": "IMSS" },
      "frequency": "BIMONTHLY",
      "dueDay": 17,
      "includesRemittance": true,
      "legislativeReference": "Ley del Seguro Social (LSS), Artículo 39"
    },
    {
      "id": "ft_mx_infonavit_bimonthly",
      "name": "INFONAVIT Contributions",
      "description": "Housing fund contributions",
      "authority": { "id": "auth_mx_infonavit", "name": "Instituto del Fondo Nacional de la Vivienda para los Trabajadores", "abbreviation": "INFONAVIT" },
      "frequency": "BIMONTHLY",
      "dueDay": 17,
      "includesRemittance": true,
      "legislativeReference": "Ley del INFONAVIT, Artículo 29"
    },
    {
      "id": "ft_mx_isn_monthly",
      "name": "ISN Payroll Tax",
      "description": "State-level payroll tax",
      "authority": { "id": "auth_mx_state", "name": "State Tax Authority", "abbreviation": "State" },
      "frequency": "MONTHLY",
      "dueDay": 17,
      "includesRemittance": true,
      "legislativeReference": "Ley de Hacienda del Estado (varies by state)"
    }
  ]
}
```

### What Fields Are Required?

Before filing, you need to know which fields are required for tax calculation and statutory filing. The required-fields endpoint returns all fields needed in a given jurisdiction — each field includes an `entity` (who the field belongs to) and a `scope` (what it's used for).

<Note>
  Use this response to build onboarding forms for each jurisdiction. Each field includes an `entity` value of `"person"` or `"business"`, so you can render separate forms for worker data and employer data.
</Note>

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

**Response:**

```json theme={null}
{
  "countryCode": "MX",
  "workerType": "employee",
  "version": 3,
  "fields": [
    {
      "key": "firstName",
      "name": "First Name",
      "description": "Worker's legal first name, required on all statutory returns",
      "type": "string",
      "required": true,
      "entity": "person",
      "scope": ["filing"]
    },
    {
      "key": "lastName",
      "name": "Last Name",
      "description": "Worker's legal last name, required on all statutory returns",
      "type": "string",
      "required": true,
      "entity": "person",
      "scope": ["filing"]
    },
    {
      "key": "jobTitle",
      "name": "Job Title",
      "description": "Worker's job title, required for statutory classification",
      "type": "string",
      "required": true,
      "entity": "person",
      "scope": ["filing"]
    },
    {
      "key": "startDate",
      "name": "Employment Start Date",
      "description": "Date the worker started employment, required for social security and filing",
      "type": "string",
      "required": true,
      "entity": "person",
      "scope": ["filing"]
    },
    {
      "key": "taxId",
      "name": "RFC (Registro Federal de Contribuyentes)",
      "description": "13-character tax ID for individuals",
      "type": "string",
      "pattern": "^[A-Z&Ñ]{4}\\d{6}[A-Z0-9]{3}$",
      "required": true,
      "entity": "person",
      "scope": ["calculation", "filing"]
    },
    {
      "key": "imssNumber",
      "name": "IMSS Social Security Number (NSS)",
      "description": "11-digit social security number",
      "type": "string",
      "required": true,
      "entity": "person",
      "scope": ["filing"]
    },
    {
      "key": "maritalStatus",
      "name": "Marital Status",
      "description": "Used for tax deduction eligibility under LISR",
      "type": "string",
      "required": true,
      "entity": "person",
      "scope": ["calculation"],
      "enum": ["single", "married", "divorced", "widowed"]
    },
    {
      "key": "state",
      "name": "State",
      "description": "ISO 3166-2 state code (e.g. MX-JAL)",
      "type": "string",
      "required": true,
      "entity": "person",
      "scope": ["calculation", "filing"]
    },
    {
      "key": "employerRfc",
      "name": "Employer RFC",
      "description": "Business tax ID for SAT filings",
      "type": "string",
      "required": true,
      "entity": "business",
      "scope": ["filing"]
    }
  ]
}
```

<Note>
  Each field has two key attributes:

  * **`entity`** — Who the field belongs to: `person` or `business`. This tells you which PUT endpoint to use.
  * **`scope`** — What it's used for: `filing` fields go to the statutory PUT endpoint. `calculation` fields go to [`PUT /v1/persons/{id}/taxProfile`](/api-reference/workforce/persons/update-tax-profile).
</Note>

### How to Submit the Fields

The statutory service is the **single front door** for all filing data. You only need two endpoints:

| Scope         | Endpoint                                                                                                                                                                                                                                                                                                |
| :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `filing`      | [`PUT /v1/statutory/persons/{personId}/fields/{countryCode}`](/api-reference/statutory/statutory-fields/submit-person-filing-fields) or [`PUT /v1/statutory/businesses/{businessId}/fields/{countryCode}`](/api-reference/statutory/statutory-fields/submit-business-filing-fields) (based on `entity`) |
| `calculation` | [`PUT /v1/persons/{personId}/taxProfile`](/api-reference/workforce/persons/update-tax-profile)                                                                                                                                                                                                          |

The workflow is:

1. **Discover** what's needed → `GET /v1/statutory/jurisdictions/{cc}/required-fields`
2. **Check** what's missing → `GET /v1/statutory/requirements?personId=...&countryCode=...`
3. **Submit** what's missing → each requirement's `resolutionGuide` tells you which endpoint to call

The statutory PUT endpoint accepts **all** filing-scope fields — including fields like `firstName` or `taxId` that may already exist on the person record. Data already on the person record auto-populates and won't appear as an outstanding requirement, so in practice you're only submitting the fields that are actually missing.

<ApiExample method="PUT" path="/v1/statutory/persons/per_xyz789/fields/MX" body={{ fields: { imssNumber: "12345678901", state: "MX-JAL" } }} reference="/api-reference/statutory/statutory-fields/submit-person-filing-fields" />

<ApiExample method="PUT" path="/v1/statutory/businesses/biz_01ABC/fields/MX" body={{ fields: { employerRfc: "XYZ010101ABC" } }} reference="/api-reference/statutory/statutory-fields/submit-business-filing-fields" />

Both return `204 No Content` on success.

#### Document Fields

Some jurisdictions require supporting documents (e.g. an identity card or a signed offer letter) alongside data fields. These appear in the required-fields response with `"type": "document"`, and their value is a **file ID** rather than a raw string:

1. **Upload the document** using the [two-step file upload flow](/reference/working-with-files) with purpose `statutory-document`:

<ApiExample method="POST" path="/v1/files/upload-url" body={{ purpose: "statutory-document", contentType: "application/pdf" }} reference="/api-reference/workforce/files/create-upload-url" />

2. **PUT the file bytes** to the returned `putUrl`.

3. **Submit the `fileId`** as the field value:

<ApiExample method="PUT" path="/v1/statutory/persons/per_xyz789/fields/MX" body={{ fields: { idCard: "f1e2d3c4-b5a6-4789-9abc-def012345678" } }} reference="/api-reference/statutory/statutory-fields/submit-person-filing-fields" />

The service verifies the file exists, was uploaded with purpose `statutory-document`, and belongs to your account — otherwise the request is rejected with `400`.

When you read the data back, document fields are returned as an object with a fresh presigned download link (valid for \~15 minutes — request again when you need a new one):

```json theme={null}
{
  "idCard": {
    "fileId": "f1e2d3c4-b5a6-4789-9abc-def012345678",
    "url": "https://...presigned download URL...",
    "fileUploaded": true
  }
}
```

#### Read Back Filing Data

The GET endpoint returns a merged view — fields you submitted via PUT combined with data already on the person or business record:

<ApiExample method="GET" path="/v1/statutory/persons/per_xyz789/fields/MX" reference="/api-reference/statutory/statutory-fields/get-person-filing-data" />

**Response:**

```json theme={null}
{
  "entityType": "person",
  "entityId": "per_xyz789",
  "countryCode": "MX",
  "fields": {
    "firstName": "María",
    "lastName": "García",
    "taxId": "GAPM900101ABC",
    "imssNumber": "12345678901",
    "jobTitle": "Software Engineer",
    "startDate": "2026-03-01",
    "maritalStatus": "single",
    "state": "MX-JAL"
  }
}
```

<Note>
  Fields like `firstName`, `taxId`, and `jobTitle` were already on the person record from onboarding — they appear automatically in the merged response without needing to be submitted again.
</Note>

***

## Step 3: Check Requirements

After onboarding, use the requirements endpoint to see what's still missing. This is the most reliable way to check whether a person or business is ready for statutory filing — you don't need to manually cross-reference the required-fields list.

Each requirement's `affectedField` is the same required-field key returned by the required-fields endpoint and accepted by the statutory PUT endpoint, so you can join the required-fields and requirements responses directly on this shared key — no manual mapping needed. The human-readable label is the top-level `name`, and the affected worker or business is identified by the top-level `resourceType` and `resourceId`.

<Note>
  **How requirements are scoped:**

  * To check a worker's missing data, query by `personId` and `countryCode` (both required). The response covers that one worker.
  * A `businessId` query returns employer/business-level requirements — registrations, business tax IDs, and similar.
</Note>

### Business-Level Requirements

<Note>
  If a person is linked to a sub-entity via `jobInfo.entityId`, the entity's registration details (taxId, registrationNumber, address) are used for statutory requirements instead of the parent business's. See [Entity Management](/workforce/entity-management).
</Note>

<ApiExample method="GET" path="/v1/statutory/requirements" params={{ businessId: "bus_abc123", status: "outstanding" }} reference="/api-reference/statutory/requirements/list-outstanding-requirements" />

**Response:**

```json theme={null}
{
  "data": [
    {
      "category": "business_registration",
      "status": "outstanding",
      "name": "IMSS Employer Registration",
      "description": "IMSS employer registration number is required for social security filings",
      "affectedField": "imssRegistration",
      "resourceType": "business",
      "resourceId": "bus_abc123",
      "resolutionGuide": "Submit via PUT /v1/statutory/businesses/{id}/fields/{countryCode}"
    }
  ]
}
```

Each requirement includes a `resolutionGuide` that points to the statutory service's field submission endpoint — [`PUT /v1/statutory/persons/{id}/fields/{cc}`](/api-reference/statutory/statutory-fields/submit-person-filing-fields) for person fields or [`PUT /v1/statutory/businesses/{id}/fields/{cc}`](/api-reference/statutory/statutory-fields/submit-business-filing-fields) for business fields. Once you submit the data, the requirement is resolved automatically.

<Warning>
  Filings that depend on outstanding requirements will be in `blocked` status until the requirements are resolved. You do not need to manually retry — the Statutory API detects when missing data is submitted and unblocks filings automatically.
</Warning>

### Per-Worker Requirements

You can also check requirements for a specific worker to see what's missing before their next payroll run.

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

**Response:**

```json theme={null}
{
  "data": [
    {
      "category": "worker_data",
      "status": "outstanding",
      "name": "Job Title",
      "description": "Worker's job title is required for statutory classification",
      "affectedField": "jobTitle",
      "resourceType": "person",
      "resourceId": "per_xyz789",
      "resolutionGuide": "Submit via PUT /v1/statutory/persons/{id}/fields/{countryCode}"
    },
    {
      "category": "worker_data",
      "status": "outstanding",
      "name": "Social Security Number (NSS)",
      "description": "Worker NSS is required for IMSS and INFONAVIT filings",
      "affectedField": "imssNumber",
      "resourceType": "person",
      "resourceId": "per_xyz789",
      "resolutionGuide": "Submit via PUT /v1/statutory/persons/{id}/fields/{countryCode}"
    }
  ]
}
```

<Tip>
  Filter by `status=outstanding` to find what still needs attention. Once you submit the missing data via the statutory PUT endpoint, requirements resolve automatically.
</Tip>

***

## Step 4: Check the Compliance Calendar

Once requirements are met, list upcoming and active filings for a business. This gives you a view of the compliance calendar across all jurisdictions.

<ApiExample method="GET" path="/v1/statutory/filings" params={{ businessId: "bus_abc123" }} reference="/api-reference/statutory/filings/list-filings" />

**Response:**

```json theme={null}
{
  "data": [
    {
      "id": "fil_001",
      "businessId": "bus_abc123",
      "countryCode": "MX",
      "source": "cadana",
      "filingTypeId": "ft_mx_isr_monthly",
      "filingTypeName": "ISR Withholding",
      "authorityName": "SAT",
      "status": "pending",
      "period": { "start": "2024-06-01", "end": "2024-06-30", "label": "June 2024" },
      "dueAt": "2024-07-17",
      "workerCount": 12,
      "createdAt": "2024-07-01T00:00:00Z"
    },
    {
      "id": "fil_002",
      "businessId": "bus_abc123",
      "countryCode": "MX",
      "source": "cadana",
      "filingTypeId": "ft_mx_imss_bimonthly",
      "filingTypeName": "IMSS Contributions",
      "authorityName": "IMSS",
      "status": "blocked",
      "period": { "start": "2024-04-01", "end": "2024-06-30", "label": "Q2 2024" },
      "dueAt": "2024-07-17",
      "workerCount": 12,
      "createdAt": "2024-07-01T00:00:00Z"
    }
  ],
  "summary": {
    "total": 4,
    "pending": 2,
    "inReview": 0,
    "approved": 0,
    "submitted": 0,
    "accepted": 0,
    "blocked": 1,
    "cancelled": 0,
    "rejected": 0
  }
}
```

<Note>
  The `summary` object provides a quick overview of your compliance posture. Use `blocked` to surface filings that need attention.
</Note>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="The Full Loop" icon="arrows-rotate" href="/statutory/the-full-loop">
    Walk through the complete calculate, file, remit, prove cycle
  </Card>

  <Card title="Statutory API Reference" icon="code" href="/api-reference/statutory">
    Full endpoint documentation
  </Card>
</CardGroup>
