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

# API

> Product updates and announcements

<Update label="Sep 28, 2026" tags={["Feature","Improvement","Fix"]}>
  ## Features

  * **Calendar feed for pay schedules.** Use `GET` and `POST /pay_schedules/{id}/calendar_url` to fetch, create or rotate a secret iCalendar subscription URL that lists a pay schedule's payroll approval deadlines and paydays in Google Calendar, Apple Calendar or Outlook.
  * **Business entity verification events.** Webhooks and Pusher now send `resource_created`, `resource_updated` and `resource_deleted` events for `business_entity_verification`, including approve and reject decisions.
  * **New `tips_direct` earning type.** Use it for tips a customer paid the worker directly. They're taxed, with the withholdings taken from the worker's other pay, but they're left out of gross pay, net pay, the deposit, the employer funding pull and the journal entry.
  * **See the amount actually deposited.** Pay stub and payroll responses now include `net_disbursement` in `employee_summary` and `contractor_summary`, plus `net_disbursement_ytd` on pay stubs; it's the part of `net` that the payroll run deposits to the worker.
  * **New warning for negative net disbursement.** The `negative_net_disbursement_on_pay_stub` warning blocks payroll approval when withholdings on direct tips are more than the pay run through payroll, which in practice happens only in Quebec.
  * **Split the payroll register by tags.** Pass `filters.split_by_tags` to `POST /reports/payroll-register` to get one row per employee per tag combination, with a column for each tag group; output is unchanged when you leave it off.

  ## Improvements

  * **Totals for pay types with several lines on the classic pay stub PDF.** When a pay type has more than one line item, such as 1.5x and 2x overtime, a total row for that pay type now shows the period sum and YTD, so the YTD no longer looks like it belongs to the last line.
  * **Unpaid hours appear on the Classic pay stub.** Rows with hours but no pay, such as unpaid leave, now show on the pay stub with no rate.
  * **Decimal places in the API reference.** The API reference now lists how many decimal places each numeric request field accepts (2 by default).
  * **Pay stub PDFs follow the reader's locale.** All three pay stub PDF formats now format money and hours for the reader's language, so French pay stubs read "1 234,56 \$" and "12,50".
  * **Deleting a remittance account cleans up its presets.** Deleting a remittance account now also deletes the withholding presets it created, and any deduction or benefit preset linked to it is kept but unlinked from the account.

  ## Fixes

  * **CPP credit for employees who move to Quebec mid-year.** QPP and federal tax withholdings now account for CPP already paid this year instead of restarting pension contributions from zero.
  * **Off-cycle payrolls remit to the right accounts.** Sibling off-cycle payrolls now remit to the same CRA and Revenu Québec accounts as their source pay stub, so businesses with more than one remittance account no longer see short remittances.
</Update>

<Update label="September 2026: Hours must match the pay rate type">
  ## Hours must match the pay rate type

  > 🚧 Behaviour Change
  > A `wage` row now requires an hourly pay rate, and a `salary` row a salary pay rate. A row
  > referencing the other type is rejected with a `422`.

  `POST /payrolls/{payroll}/hours` accepted a `wage` row that referenced a salary pay rate, and a `salary` row that referenced an hourly one. A pay stub has no line item for that combination, so the import dropped the row and still reported success. The hours never reached the pay stub, and nothing in the response said so.

  Both are now validation errors on the row's `pay_rate_id`:

  ```json theme={null}
  {
    "message": "The selected pay rate is of type salary, but these hours require a pay rate of type hourly.",
    "errors": {
      "data.0.wage.0.pay_rate_id": [
        "The selected pay rate is of type salary, but these hours require a pay rate of type hourly."
      ]
    },
    "error_details": {
      "data.0.wage.0.pay_rate_id": [{ "code": "pay_rate_matches_earning_type" }]
    }
  }
  ```

  Send a salaried employee's hours under `salary` and an hourly employee's under `wage`. Each pay rate reports its own `type`, so you can check before you send. Vacation, leave and overtime rows are unaffected: they pay from either rate type.

  ### Migrating

  Existing partners keep the old behaviour for now. New partners get the new behaviour. To force either one, pass a `Prefer` header with the request:

  * To reject mismatched rows: `Prefer: deprecate_mismatched_hours_pay_rate=true`
  * To keep accepting them, and dropping them during the import: `Prefer: deprecate_mismatched_hours_pay_rate=false`

  We recommend migrating as soon as possible by passing `Prefer: deprecate_mismatched_hours_pay_rate=true` on all hours requests. Until you do, a mismatched row is silently missing from the payroll it was meant for.

  <Note>
    Once your integration sends matching types, contact Nmbr support to make this the default
    behaviour for your partner. After that, you can remove the `Prefer` headers from your requests.
  </Note>
</Update>

<Update label="Sep 21, 2026" tags={["Breaking", "Feature", "Improvement", "Fix"]}>
  ## Behaviour changes

  * **Preview payrolls now require remittance accounts to be approved.** A payroll for a preview business can no longer be approved if any of its pay stubs has no CRA account, or sits in Quebec with no RQ account. Live businesses already follow this rule. Add the missing accounts before you approve.

  ## Features

  * **Search accounting codes.** Pass `search` to `GET /accounting_codes` to match part of a code, title, or description in the request's language.
  * **Split journal entry rows that share an accounting code.** Set `accounting_settings.journal_entry_row_grouping` on a business entity to `subtype` or `preset` to break those rows out by line item subtype or by business preset.
  * **Set an addressee on a remittance account.** When you set the optional `addressee`, employee pay stub PDFs show it as the employer name instead of the business name. The CRA account's addressee applies outside Québec and the Revenu Québec account's applies in Québec.

  ## Improvements

  * **Tax calculations scope year-to-dates by business number.** When calculating an employee's withholdings, Nmbr uses year-to-date amounts from the business entity's pay stubs under the same CRA business number or Revenu Québec identifier. Year-to-date figures shown on pay stubs are unchanged.
  * **ROEs can include more historical pay stubs.** Set `first_day_worked` back far enough and the ROE now picks up historical pay stubs whose payroll covers exactly one of the employee's pay periods.

  ## Fixes

  * **Business-number tax properties return `earliest_valid_effective_to`.** CRA and RQ business-number tax properties created from a remittance account now return `2020-01-01` instead of `null`, including existing ones.
  * **QPP paid earlier in the year now counts toward CPP.** For 2026, an employee who moves from Quebec to another province mid-year, or a Quebec employee taxed on another province's tables under a CRA letter of authority, gets credit for QPP already paid, so neither they nor their employer pays extra CPP.
  * **Correction payrolls keep the `accrued_vacation_pay` you send.** It's no longer replaced with a calculated amount, and you can now update it on allowance and employer benefit line items on correction payrolls.
</Update>

<Update label="Sep 14, 2026" tags={["Feature", "Improvement", "Fix"]}>
  ## Features

  * **Give statutory holiday pay a business preset.** Set `business_preset_id` on a `ca::statutory_holiday_pay` tax property and the statutory holiday pay line items it generates take their title and accounting codes from that preset, so accounting code rules that target the preset apply to them.
  * **Calculate federal tax against an authorized province.** Set a `ca::authorized_province` tax property on a work assignment to reflect a CRA letter of authority (form T1213), and that employee's federal tax is calculated against the authorized province's tables. Provincial tax stays with the province of employment.
  * **Estimate when an employee started and stopped working.** Post up to 100 employees to `/calculations/estimated_employment_dates` and get each one's estimated first and last day worked, derived from payroll data.

  ## Improvements

  * **Forms return advisories alongside errors.** Every form now carries an `advisories` list beside `errors`, and an `advisory_count`. Three ROE checks that Service Canada only flags are now advisories and no longer block saving or submission.

  ## Fixes

  * **A bulk pay stub create no longer fails the whole batch.** When a work assignment picks up a pay stub between validation and the queued write, `POST /pay_stubs/bulk/create` now skips that assignment and creates the rest.
  * **Employer EI stops at a business's reduced annual maximum.** A business granted a CRA EI premium reduction kept contributing up to the statutory maximum. Payroll calculations and both the `ei-cpp-balance` and `ei-qpp-qpip-balance` reports now use the reduced maximum. See [EI Premium Reduction](/guides/canada/ei-rate-reduction).
  * **Statutory withholdings on historical and correction payrolls carry a remittance account.** The account came back blank. It now follows the work assignment's enrollment, and falls back to the business entity's sole CRA or RQ account when the work assignment has no enrollment.
</Update>

<Update label="September 2026: Deprecating the sole-account remittance fallback">
  ## Deprecating the sole-account remittance fallback

  > 🚧 Breaking Change
  > On October 19, 2026, a Work Assignment with no enrollment will stop defaulting to the Business Entity's only CRA or RQ Remittance Account. Every employee Work Assignment needs a CRA enrollment that covers the pay date, and a Quebec Work Assignment also needs an RQ enrollment, before its Payroll can be approved.

  ### Background

  Today, when an employee Work Assignment has no enrollment in a CRA or RQ Remittance Account and the Business Entity has exactly one Remittance Account for that agency, Nmbr uses that account for the Work Assignment's Pay Stubs. No enrollment is created or required. The same applies when an enrollment has ended or been deleted.

  The fallback hides missing configuration. When an enrollment is ended or deleted, the Remittance Account may be used anyway, so a Work Assignment that was meant to stop remitting to an account keeps doing so.

  ### What's Changing

  From October 19, 2026, the enrollment alone decides which account a Work Assignment remits to. A Work Assignment with no enrollment in effect on the pay date gets a `missing_cra_remittance_account_enrollment` or `missing_rq_remittance_account_enrollment` warning on its next Payroll, and the Payroll cannot be approved until the enrollment exists.

  Work Assignments that already have an enrollment covering the pay date are not affected. Auto-enrollment is unchanged: the first CRA or RQ Remittance Account created for a Business Entity is marked as the default, and new Work Assignments are enrolled in the default account when they are created.

  ### What To Do

  Before October 19, enroll every employee Work Assignment that has no CRA enrollment in effect, and every Quebec Work Assignment that has no RQ enrollment in effect. Either:

  * Create an enrollment with the [Remittance Account Enrollments](/api-reference/remittance-account-enrollments/create-a-remittance-account-enrollment) endpoint.
  * Set `is_default` to `true` on the Remittance Account, which enrolls every employee Work Assignment in the Business Entity that has no enrollment for that provider.

  A Work Assignment whose enrollment has ended already has one, so setting a default does not re-enroll it. Create a new enrollment instead.

  After the release, any affected Payroll shows the warning above, and creating the enrollment clears it. See [Deprecated Sole-Account Fallback](/guides/payroll-fundamentals/tax-agency-configuration#deprecated-sole-account-fallback).
</Update>

<Update label="Sep 7, 2026" tags={["Feature", "Improvement"]}>
  ## Features

  * **Import cost of sales accounts as expense GL codes.** Xero Direct Costs and QuickBooks Cost of Goods Sold accounts now import as expense GL codes, so a business that books payroll as cost of sales can export its journals.
  * **Every `422` carries machine-readable error codes.** Validation responses now include an `error_details` object alongside `errors`. Each failure has a stable code, so you can branch on the reason instead of matching localized text.
  * **Give a vacation pay setting a business preset.** Set `business_preset_id` on a vacation pay setting and the vacation payout line item it generates takes its title and accounting codes from that preset. A preset in use by a vacation pay setting can no longer be deleted.

  ## Improvements

  * **Recurring earnings accept amounts up to 99,999.** Anything above 9,999 used to be rejected.
  * **An annual deduction at source can reduce CPP pensionable earnings.** Flag the deduction and it lowers pensionable earnings alongside the withholding.
  * **A mid-year deduction at source withholds its full authorized amount by year end.** `prorated_amount` reads higher than before for properties created part way through the year.
  * **Name a successor remittance account before you pick a changeover date.** A remittance account can now name its successor with no date set, and nobody moves until you set one. Clearing either the date or the successor no longer moves enrolled employees back.
</Update>

<Update label="Aug 31, 2026" tags={["Breaking", "Feature", "Improvement", "Fix"]}>
  ## Behaviour changes

  * **Ending a preview now requires remittance accounts.** Ending a business entity's preview returns a `400` if a pay stub that would go live has no CRA account, no jurisdiction, or sits in Quebec with no RQ account. Add the missing accounts and jurisdictions before you end the preview.

  ## Features

  * **Import paid leave hours against a pay rate.** The hours endpoints now accept the 14 paid leave earning types, including `leave_sick`, `leave_general` and `leave_parental`, against a pay rate and pay them at that rate, hourly or salary equivalent.
  * **New non-legislated sick leave earning type.** `GET /earning_types` now returns `leave_sick_discretionary` ("Leave (sick, non-legislated)") for every partner, and `leave_sick` is now labelled "Leave (sick, legislated)".

  ## Improvements

  * **`leave_general` accrues vacation pay in every province.** Earnings on `leave_general` accrued vacation pay in Quebec and PEI only. They now accrue it everywhere, so amounts change on draft payrolls that use the type outside those two provinces.
  * **Historical and correction payrolls check remittance accounts.** Both types now carry the missing remittance account warning and are blocked at approval, the same as every other payroll type.
  * **Statutory holiday pay splits by tag like the pay behind it.** If you use tags, managed statutory holiday pay calculated from provincial rules now takes its tag allocation from the line items behind the entitlement, so journal entries split the way the underlying pay does.

  ## Fixes

  * **EI insurability follows the province of employment.** A Quebec transit pass reimbursement is now excluded from EI premiums, YTD insurable earnings and ROE Block 15, matching what T4 box 24 already reported. Only Quebec pay stubs carrying that reimbursement subtype change.
  * **Errors you can fix return a `400`.** A client-fixable error raised with context, such as saving a semi-monthly pay schedule with no anchor start date, now returns a `400` with a translated message instead of a `500`.
  * **Nested arrays in query parameters return a validation error.** Passing a nested array to a filter, id or sort parameter, for example `?type[][]=debit`, now returns a normal validation error instead of a `500`.
</Update>

<Update label="August 2026: Report dates are ISO 8601">
  ## Report dates are ISO 8601

  > 🚧 Behaviour Change
  > The WCB and Vacation Accrual reports now write dates as `YYYY-MM-DD`. Update any parsing
  > that expects the old formats.

  Every other report already emitted `YYYY-MM-DD`. These two did not:

  | Report | Fields | Was | Now |
  | - | - | - | - |
  | WCB | `Pay Period`, `Pay Date` | `06-30-2025` | `2025-06-30` |
  | Vacation Accrual | `Approval Date`, `Pay Date` | `Jun 30, 2025` | `2025-06-30` |

  The `Pay Period` column keeps its `start to end` shape, so a June period now reads
  `2025-06-01 to 2025-06-15`.

  Nothing else about either report changes. The columns, their order, and every other value
  stay as they are.
</Update>

<Update label="August 2026: Integration sync returns an async task">
  ## Integration sync returns an async task

  `POST /integrations/{integration}/sync` still answers `202 Accepted`, but the response now carries an [async task](/api-reference/async-tasks/retrieve-an-async-task) instead of an empty body:

  ```json theme={null}
  {
    "id": "asnct_01KS0G8Z2YD3T9KQNFW1XEA7HB",
    "object": "async_task",
    "data": {
      "type": "integration_sync",
      "status": "processing",
      "completed_at": null,
      "results": [],
      "created_at": "2026-08-19T14:02:11.000000Z",
      "updated_at": "2026-08-19T14:02:11.000000Z"
    },
    "links": {
      "self": "/async_tasks/asnct_01KS0G8Z2YD3T9KQNFW1XEA7HB"
    }
  }
  ```

  All three sync types report the same `integration_sync` task type: the Chart of Accounts import, the tracking dimension imports, and the journal entry export. Poll `GET /async_tasks/{id}` for the task to reach `completed` or `error`, or subscribe to the `async_task_completed` webhook, which fires on both. A sync task reports status only, so `results` stays empty.

  The `integration_sync_account_codes_*`, `integration_sync_tags_*` and `integration_sync_journal_entries_*` webhooks are unchanged. Nothing else about a sync changes, only how you observe it. See [Accounting Integrations](/guides/accounting/integrations#tracking-a-sync).
</Update>

<Update label="July 2026: async_task_completed now fires on failure">
  ## `async_task_completed` now fires on failure

  > 🚧 Behaviour Change
  > The `async_task_completed` webhook now fires when an async task fails, not only when it
  > succeeds. Check the task's `status` before you act on the event.

  ### Background

  An async task ends in one of two states: `completed` when the work succeeded, or `error`
  when it failed. Until now the `async_task_completed` webhook only fired on `completed`, so
  a failed task produced no event at all. The only way to notice a failure was to keep
  polling a task you had otherwise stopped watching.

  ### What's Changing

  The webhook now fires as soon as the task reaches either state. It reports that the task
  is done, not that it worked.

  The payload is unchanged, and it does not carry the status. Fetch the task with the ID from
  the webhook and read `data.status`:

  * `completed`: the work succeeded. `completed_at` is set, and `data.results` lists the
    affected entities.
  * `error`: the work failed. `completed_at` stays `null`. For batch operations,
    `data.results` names the entities that could not be written, each with `status` set to
    `error` and a message in `error`.

  If your integration treats an `async_task_completed` event as proof of success, add a
  `data.status` check before it goes any further. See
  [Failed Tasks](https://docs.nmbr.co/api/bulk-and-batch-operations/bulk-and-batch-operations#failed-tasks).
</Update>

<Update label="July 2026: Deprecating root-level data properties on bulk endpoints">
  ## Deprecating root-level data properties on bulk endpoints

  > 🚧 Breaking Change
  > Bulk endpoints will stop accepting data properties on the root of the request in a
  > future release. Data properties should be nested under a `data` key. The old format
  > continues to work for now but is deprecated.

  ### Background

  Bulk create and update endpoints accept two types of properties in the request body:

  * **Filter properties**: Properties used to select the entities to operate on
    (e.g. `payroll_id`, `pay_stubs`).
  * **Data properties**: Properties for the created or updated entities (e.g.
    `deduction_type`, `custom_amount`).

  When we first introduced bulk endpoints, both types of properties were placed on the
  root of the request body. However, some bulk endpoints need filter and data properties
  with the same name but different values. To support this, we're moving data properties
  into a nested `data` key.

  ### What's Changing

  Data properties should be nested under a `data` key in the request body. For example,
  instead of sending data properties on the root of the request:

  <Warning>Legacy request with data properties on root</Warning>

  ```
  POST /deduction_line_items/bulk/create

  {
      "payroll_id": "payrl_01JBC4F2XKMP7R9QHN3DSWV6YA",
      "pay_stubs": {
          "include": {
              "ids": [
                  "payst_01JBC4F8GT5YNWZ0CRHQ6DMXKE",
                  "payst_01JBC4FBMS3AP4VD7TXJW2HN9R"
              ]
          }
      },
      "deduction_type": "union_dues",
      "title": "Union Dues",
      "custom_amount": 50,
      "custom_hours": null
  }
  ```

  nest the data properties under a `data` key:

  <Check>Modern request with data properties nested under `data`</Check>

  ```
  POST /deduction_line_items/bulk/create

  {
      "payroll_id": "payrl_01JBC4F2XKMP7R9QHN3DSWV6YA",
      "pay_stubs": {
          "include": {
              "ids": [
                  "payst_01JBC4F8GT5YNWZ0CRHQ6DMXKE",
                  "payst_01JBC4FBMS3AP4VD7TXJW2HN9R"
              ]
          }
      },
      "data": {
          "deduction_type": "union_dues",
          "title": "Union Dues",
          "custom_amount": 50,
          "custom_hours": null
      }
  }
  ```

  ### Migrating to the `data` Key

  To help preexisting partners migrate their integrations, you can force either behavior by
  passing a `Prefer` header with the request:

  * To require data properties under `data`: `Prefer: deprecate_flat_bulk_shape=true`
  * To keep accepting data properties on the root: `Prefer: deprecate_flat_bulk_shape=false`

  When the `data` key is required, any data property sent on the root is rejected with a `422`,
  with one validation error per misplaced property.

  We recommend migrating as soon as possible by passing `Prefer: deprecate_flat_bulk_shape=true`
  on all bulk requests.

  <Note>
    Once your integration is fully migrated to the `data` key, contact Nmbr
    support to make this the default behavior for your partner. After that, you
    can remove the `Prefer` headers from your requests.
  </Note>

  ### Affected Endpoints

  Recurrence bulk endpoints:

  * `POST /allowances/bulk/create`
  * `POST /allowances/bulk/update`
  * `POST /deductions/bulk/create`
  * `POST /deductions/bulk/update`
  * `POST /earnings/bulk/create`
  * `POST /earnings/bulk/update`
  * `POST /employee_benefits/bulk/create`
  * `POST /employee_benefits/bulk/update`
  * `POST /employer_benefits/bulk/create`
  * `POST /employer_benefits/bulk/update`
  * `POST /reimbursements/bulk/create`
  * `POST /reimbursements/bulk/update`
  * `POST /pay_rates/bulk/create`
  * `POST /pay_rates/bulk/update`
  * `POST /overtime_rates/bulk/create`
  * `POST /overtime_rates/bulk/update`

  Line item bulk endpoints:

  * `POST /allowance_line_items/bulk/create`
  * `POST /allowance_line_items/bulk/update`
  * `POST /deduction_line_items/bulk/create`
  * `POST /deduction_line_items/bulk/update`
  * `POST /earning_line_items/bulk/create`
  * `POST /earning_line_items/bulk/update`
  * `POST /employee_benefit_line_items/bulk/create`
  * `POST /employee_benefit_line_items/bulk/update`
  * `POST /employer_benefit_line_items/bulk/create`
  * `POST /employer_benefit_line_items/bulk/update`
  * `POST /reimbursement_line_items/bulk/create`
  * `POST /reimbursement_line_items/bulk/update`

  Other bulk endpoints:

  * `POST /remittance_account_enrollments/bulk/create`
  * `POST /work_assignments/bulk/create`
  * `POST /work_assignments/bulk/update`

  ### What's Not Changing

  Filter properties (`payroll_id`, `pay_stubs`, etc.) remain on the root of the request
  body. Only data properties are moving into the `data` key.

  The old format with data properties on the root of the request continues to work for
  preexisting partners, but will be removed in a future release. The nested `data` shape is
  the default for new partners going forward.
</Update>

<Update label="July 2026: Deprecating the legacy percent field on benefits">
  ## Deprecating the legacy `percent` field on benefits

  > 🚧 Breaking Change
  > The `percent` field on employee and employer benefits will be removed in a future
  > release. Percentage-based benefits are represented with `amount` and an `amount_type` of
  > `percent`. The old `percent` field continues to work for now but is deprecated.

  ### Background

  A benefit's contribution can be either a fixed dollar amount or a percentage. Historically
  these were represented with two separate, mutually exclusive fields:

  * **`amount`**: The fixed dollar amount. Set on fixed benefits, `null` on percentage
    benefits.
  * **`percent`**: The percentage. Set on percentage benefits, `null` on fixed benefits.

  Having two fields where only one is ever populated made benefits awkward to work with:
  callers had to choose the right field and always handle a `null` for the other. It was also
  inconsistent with other recurrence types that support percentage values - deductions, for
  example, already use a single `amount` field paired with an `amount_type` discriminator.
  We're bringing benefits in line by replacing `percent` with the same `amount` +
  `amount_type` shape.

  ### What's Changing

  Percentage-based benefits are now represented with a single `amount` value and an
  `amount_type` of `percent`, instead of the separate `percent` field. Fixed benefits use the
  same `amount` value with an `amount_type` of `fixed`.

  Instead of sending a `percent` field when creating or updating a percentage-based benefit:

  <Warning>Legacy request using the `percent` field</Warning>

  ```
  POST /employee_benefits

  {
      "work_assignment_id": "wrkas_01JBC4F2XKMP7R9QHN3DSWV6YA",
      "employee_benefit_type": "pension_rrsp",
      "frequency": "per_pay_period",
      "effective_from": "2026-01-01",
      "percent": 5
  }
  ```

  send the value in `amount` with an `amount_type` of `percent`:

  <Check>Modern request using `amount` and `amount_type`</Check>

  ```
  POST /employee_benefits

  {
      "work_assignment_id": "wrkas_01JBC4F2XKMP7R9QHN3DSWV6YA",
      "employee_benefit_type": "pension_rrsp",
      "frequency": "per_pay_period",
      "effective_from": "2026-01-01",
      "amount": 5,
      "amount_type": "percent"
  }
  ```

  Fixed benefits set `amount_type` to `fixed`:

  ```
  {
      "amount": 100,
      "amount_type": "fixed"
  }
  ```

  Benefit responses change to match. Instead of returning both `amount` and `percent` with one
  always `null`:

  <Warning>Legacy response</Warning>

  ```
  {
      ...
      "amount": null,
      "percent": 5
  }
  ```

  responses return a single `amount` alongside `amount_type` and omit `percent`:

  <Check>Modern response</Check>

  ```
  {
      ...
      "amount": 5,
      "amount_type": "percent"
  }
  ```

  In the modern shape `amount` holds the value for **both** types: it is a dollar amount when
  `amount_type` is `fixed` and a percentage when `amount_type` is `percent`. Use `amount_type`
  to interpret it.

  `amount_type` itself is a new field introduced by this change; benefit responses did not
  previously return it. It now appears in both shapes, so partners who stay on the legacy shape
  will start receiving `amount_type` alongside the existing `amount` and `percent` fields. This
  is additive - no existing field is removed or changed for the legacy shape.

  ### Migrating to `amount` and `amount_type`

  To help preexisting partners migrate their integrations, you can force either shape by
  passing a `Prefer` header with the request:

  * To use the modern shape: `Prefer: deprecate_benefit_percent=true`
  * To use the legacy shape: `Prefer: deprecate_benefit_percent=false`

  The header controls both how the request body is interpreted and how the response is shaped.
  We recommend migrating as soon as possible by passing `Prefer: deprecate_benefit_percent=true`
  on all benefit requests.

  The modern shape is the default for new partners going forward. The legacy shape remains the
  default for preexisting partners until they migrate.

  <Note>
    Once your integration is fully migrated to the modern shape, contact Nmbr
    support to make it the default behavior for your partner. After that, you can
    remove the `Prefer` headers from your requests.
  </Note>

  ### Affected Endpoints

  The change applies to employee and employer benefit resources wherever they are created or
  updated:

  * `POST /employee_benefits`
  * `PUT /employee_benefits/:id`
  * `POST /employee_benefits/bulk/create`
  * `POST /employee_benefits/bulk/update`
  * `POST /employee_benefits/batch/upsert`
  * `POST /employer_benefits`
  * `PUT /employer_benefits/:id`
  * `POST /employer_benefits/bulk/create`
  * `POST /employer_benefits/bulk/update`
  * `POST /employer_benefits/batch/upsert`

  It also applies to the corresponding read endpoints (`GET /employee_benefits`,
  `GET /employee_benefits/:id`, `GET /employer_benefits`, `GET /employer_benefits/:id`) and to
  `employee_benefit` and `employer_benefit` fields expanded in other resources.

  ### What's Not Changing

  Benefit line items are unaffected: they never carried a `percent` field.
</Update>

<Update label="July 2026: Deprecating synchronous mode on bulk endpoints">
  ## Deprecating synchronous mode on bulk endpoints

  > 🚧 Breaking Change
  > Bulk endpoints will default to asynchronous mode for all partners in a future release.
  > Synchronous mode is deprecated.

  ### Background

  Bulk create, update, and delete endpoints currently support two modes of operation:

  **Synchronous mode** (deprecated):

  * Processes the operation synchronously
  * Returns HTTP 201 Created, HTTP 200 OK, or HTTP 204 No Content
  * Returns the created or updated models in the response body

  **Asynchronous mode**:

  * Returns HTTP 202 Accepted
  * Returns an [async task](https://docs.nmbr.co/api-reference/async-tasks/retrieve-an-async-task)
    in the response body
  * Processes the operation asynchronously
  * Returns the created or updated models via the async task result endpoint

  Synchronous mode is the current default for preexisting partners but will be deprecated
  in a future release. Asynchronous mode is the default for new partners going forward.

  For more details on asynchronous bulk operations, see the
  [Bulk and Batch Operations](https://docs.nmbr.co/api/bulk-and-batch-operations/bulk-line-item-operations)
  documentation.

  ### Migrating to Asynchronous Mode

  To help preexisting partners migrate their integrations, you can force either mode by
  passing a `Prefer` header with the request:

  * To force asynchronous mode: `Prefer: deprecate_sync_bulk=true`
  * To force synchronous mode: `Prefer: deprecate_sync_bulk=false`

  We recommend migrating to asynchronous mode as soon as possible by passing
  `Prefer: deprecate_sync_bulk=true` on all bulk requests.

  <Note>
    Once your integration is fully migrated to asynchronous mode, contact Nmbr
    support to make this the default behavior for your partner. After that, you
    can remove the `Prefer` headers from your requests.
  </Note>

  ### Affected Endpoints

  Recurrence bulk endpoints:

  * `POST /allowances/bulk/create`
  * `POST /allowances/bulk/update`
  * `POST /allowances/bulk/delete`
  * `POST /deductions/bulk/create`
  * `POST /deductions/bulk/update`
  * `POST /deductions/bulk/delete`
  * `POST /earnings/bulk/create`
  * `POST /earnings/bulk/update`
  * `POST /earnings/bulk/delete`
  * `POST /employee_benefits/bulk/create`
  * `POST /employee_benefits/bulk/update`
  * `POST /employee_benefits/bulk/delete`
  * `POST /employer_benefits/bulk/create`
  * `POST /employer_benefits/bulk/update`
  * `POST /employer_benefits/bulk/delete`
  * `POST /reimbursements/bulk/create`
  * `POST /reimbursements/bulk/update`
  * `POST /reimbursements/bulk/delete`
  * `POST /pay_rates/bulk/create`
  * `POST /pay_rates/bulk/update`
  * `POST /pay_rates/bulk/delete`
  * `POST /overtime_rates/bulk/create`
  * `POST /overtime_rates/bulk/update`
  * `POST /overtime_rates/bulk/delete`

  Line item bulk endpoints:

  * `POST /allowance_line_items/bulk/create`
  * `POST /allowance_line_items/bulk/update`
  * `POST /allowance_line_items/bulk/delete`
  * `POST /deduction_line_items/bulk/create`
  * `POST /deduction_line_items/bulk/update`
  * `POST /deduction_line_items/bulk/delete`
  * `POST /earning_line_items/bulk/create`
  * `POST /earning_line_items/bulk/update`
  * `POST /earning_line_items/bulk/delete`
  * `POST /employee_benefit_line_items/bulk/create`
  * `POST /employee_benefit_line_items/bulk/update`
  * `POST /employee_benefit_line_items/bulk/delete`
  * `POST /employer_benefit_line_items/bulk/create`
  * `POST /employer_benefit_line_items/bulk/update`
  * `POST /employer_benefit_line_items/bulk/delete`
  * `POST /reimbursement_line_items/bulk/create`
  * `POST /reimbursement_line_items/bulk/update`
  * `POST /reimbursement_line_items/bulk/delete`

  Other bulk endpoints:

  * `POST /remittance_account_enrollments/bulk/create`
  * `POST /remittance_account_enrollments/bulk/delete`
  * `POST /work_assignments/bulk/create`
  * `POST /work_assignments/bulk/update`
</Update>

<Update label="July 2026: Deprecating synchronous execution of batch endpoints">
  ## Deprecating synchronous execution of batch endpoints

  > 🚧 Breaking Change
  > Batch endpoints will execute asynchronously for all partners in a future release.
  > Synchronous execution is deprecated.

  ### Background

  Batch upsert and delete endpoints already return the asynchronous response shape (HTTP
  202 Accepted with an
  [async task](https://docs.nmbr.co/api-reference/async-tasks/retrieve-an-async-task) in
  the response body). However, for preexisting partners, the operation is currently
  executed synchronously before returning the response.

  In a future release, batch endpoints will execute asynchronously for all partners.
  Asynchronous execution is already the default for new partners going forward.

  ### Migrating to Asynchronous Execution

  To help preexisting partners migrate their integrations, you can force either mode by
  passing a `Prefer` header with the request:

  * To force asynchronous execution: `Prefer: deprecate_sync_batch=true`
  * To force synchronous execution: `Prefer: deprecate_sync_batch=false`

  We recommend migrating to asynchronous execution as soon as possible by passing
  `Prefer: deprecate_sync_batch=true` on all batch requests.

  <Note>
    Once your integration is fully migrated to asynchronous execution, contact
    Nmbr support to make this the default behavior for your partner. After that,
    you can remove the `Prefer` headers from your requests.
  </Note>

  ### Affected Endpoints

  Recurrence batch endpoints:

  * `POST /allowances/batch/upsert`
  * `POST /allowances/batch/delete`
  * `POST /deductions/batch/upsert`
  * `POST /deductions/batch/delete`
  * `POST /earnings/batch/upsert`
  * `POST /earnings/batch/delete`
  * `POST /employee_benefits/batch/upsert`
  * `POST /employee_benefits/batch/delete`
  * `POST /employer_benefits/batch/upsert`
  * `POST /employer_benefits/batch/delete`
  * `POST /reimbursements/batch/upsert`
  * `POST /reimbursements/batch/delete`
  * `POST /pay_rates/batch/upsert`
  * `POST /pay_rates/batch/delete`
  * `POST /overtime_rates/batch/upsert`
  * `POST /overtime_rates/batch/delete`

  Line item batch endpoints:

  * `POST /allowance_line_items/batch/upsert`
  * `POST /allowance_line_items/batch/delete`
  * `POST /deduction_line_items/batch/upsert`
  * `POST /deduction_line_items/batch/delete`
  * `POST /earning_line_items/batch/upsert`
  * `POST /earning_line_items/batch/delete`
  * `POST /employee_benefit_line_items/batch/upsert`
  * `POST /employee_benefit_line_items/batch/delete`
  * `POST /employer_benefit_line_items/batch/upsert`
  * `POST /employer_benefit_line_items/batch/delete`
  * `POST /reimbursement_line_items/batch/upsert`
  * `POST /reimbursement_line_items/batch/delete`

  Other batch endpoints:

  * `POST /contractors/batch/upsert`
  * `POST /contractors/batch/delete`
  * `POST /employees/batch/upsert`
  * `POST /employees/batch/delete`
  * `POST /forms/batch/upsert`
  * `POST /forms/batch/delete`
  * `POST /tax_properties/batch/upsert`
  * `POST /tax_properties/batch/delete`
  * `POST /vacation_pay_settings/batch/upsert`
  * `POST /vacation_pay_settings/batch/delete`
  * `POST /work_assignments/batch/upsert`
  * `POST /work_assignments/batch/delete`
</Update>

<Update label="March 2026">
  ## Fields and features being removed after May 15, 2026

  > 🚧 Breaking Change
  > Several fields and features will be removed after **May 15, 2026**. Most of these are older, legacy fields and features that were repaced with newer ones in 2025, but without a formal deprecation notice. Most partners will already be using the newer fields and features and will not need to make changes as a result. However, it's important to review each change to be sure.

  ### Removing `employer_ei_multiplier` from business entity resources

  We're removing the `employer_ei_multiplier` read-only field from business entity resources (`/business_entities`, `/business_entities/:id`, and `business_entity` fields expanded in other resources).

  We now support having multiple CRA RP account numbers within a single business entity. As part of that feature, we now support managing a reduced EI rate through tax properties on the relevant CRA remittance account. For more information, see the [EI Rate Reduction](https://docs.nmbr.co/guides/canada/ei-rate-reduction) documentation.

  ### Removing `tax_jurisdiction` from work assignment resources

  We're removing the `tax_jurisdiction` field from work assignment resources (`/work_assignments`, `/work_assignments/:id`, and `work_assignment` fields expanded in other resources).

  A work assignment may work in different tax jurisdictions in different pay periods. Having a single `tax_jurisdiction` value prevented tracking the tax jurisdiction over time or scheduling changes to it.

  Instead of setting `tax_jurisdiction`, manage a work assignment's Province of Employment (POE) through its `ca::province_of_employment` tax properties.

  ℹ️ We are **not** removing the read-only `current_tax_jurisdiction` field as part of these changes. This field returns the value of the work assignment's current `ca::province_of_employment` tax property for convenience.

  ### Removing `is_cpp_exempt` and `is_ei_exempt` from work assignment resources

  We're removing the `is_cpp_exempt`and `is_ei_exempt` fields from work assignment resources (`/work_assignments`, `/work_assignments/:id`, and `work_assignment` fields expanded in other resources).

  A work assignment may be CPP-exempt or EI-exempt in some pay periods but not others. Having single `is_cpp_exempt` and `is_ei_exempt` values prevented tracking or scheduling changes to CPP-exempt and EI-exempt status over time.

  Instead of setting `is_cpp_exempt` or `is_ei_exempt`, manage a work assignment's CPP-exempt and EI-exempt statuses through its `ca::cpp_exempt` and `ca::ei_exempt` tax properties.

  ℹ️ QPP- and QPIP-exempt status is already managed through tax properties.

  ### Removing `employee_summary.deductions` and `employee_summary.deductions_ytd` from pay stub resources

  We're removing the `employee_summary.deductions` and `employee_summary.deductions_ytd` fields from pay stub resources (`/pay_stubs`, `/pay_stubs/:id`, and `pay_stub` fields expanded in other resources).

  These fields are poorly-named. Their names suggest they're the sum of deduction line items on the pay stub (or pay stubs year-to-date), but they're actually the sum of any line item subtracted from the employee's pay, i.e. deduction, employee benefit, and employee statutory withholding line items.

  You can get the same information from the `employee_summary.subtractions` and `employee_summary.subtractions_ytd` fields respectively.

  ### Removing support for creating forms identifying owner with `work_assignment_id`

  We're removing support for creating a form (`POST /forms`) using a `work_assignment_id` field to identify the owner of the form.

  When we first added support for forms, we only supported work assignment-level forms (e.g. TD1 and TP-1015.3-V), and so only ever needed `work_asignment_id`. Now that we support employee-level forms (e.g. T4 and ROE), we need different identifiers for different forms.

  Instead of sending a `work_assignment_id` on the request

  ```
  POST /forms

  {
    "work_assignment_id": "wrkas_...",
    ...
  }
  ```

  send the appropriate value in `owner_id`:

  ```
  POST /forms

  {
    "owner_id": "wrkas_...", // work assignment identifier for work assignment-level forms, etc
    ...
  }
  ```

  ### Removing `work_assignment` from form resources

  We're removing the `work_assignment` field from form resources (`/forms`, `/forms/:id`, and `form` fields expanded in other resources).

  Use the `owner` field to determine the owner of the form.

  ### Removing support for expanding all line items on a pay stub with `?expand=line_items`

  We're removing support for expanding all line items on a pay stub in a single `line_items` field using `/pay_stubs?expand=line_items` or `/pay_stub/:id?expand=line_items`.

  Each type of line item has a unique shape, and adding new types of line items could break consumers who aren't expecting their shapes. Additionally, some use cases only require certain types of line items, and returning all line items is unnecessarily expensive. Instead of using `expand=line_items` to expand the line items, use `expand=<type>_line_items,<type>_line_items,...` for each type of line item you want returned.

  ### Removing `t4_code` from allowance type resources

  We're removing the `t4_code` field from allowance type resources (`/allowance_types`, `/allowance_types/:id`, and `allowance_type` fields expanded in other resources).

  Where an allowance is reported on a T4 isn't a simple 1:1 mapping. This field was always previously returned with the value `null`. The `form_mappings` field contains more nuanced information about how types map to forms.

  ### Removing `is_taxable`, `is_insurable`, and `is_pensionable` from allowance type resources

  We're removing the `is_taxable`, `is_insurable`, and `is_pensionable` fields from allowance type resources (`/allowance_types`, `/allowance_types/:id`, and `allowance_type` fields expanded in other resources).

  Whether or not an allowance type is taxable, insurable, or pensionable isn't a simple boolean value; it varies by jurisdiction. We've replaced these fields with a new [Line Item Type Features](https://docs.nmbr.co/guides/payroll-fundamentals/line-item-type-features) system that will return more nuanced information about line item types in each jurisdiction we support.

  ### Removing `t4_code` from deduction type resources

  We're removing the `t4_code` field from deduction type resources (`/deduction_types`, `/deduction_types/:id`, and `deduction_type` fields expanded in other resources).

  Where a deduction is reported on a T4 isn't a simple 1:1 mapping. This field was always previously returned with the value `null`. The `form_mappings` field contains more nuanced information about how types map to forms.

  ### Removing `t4_code` from reimbursement type resources

  We're removing the `t4_code` field from reimbursement type resources (`/reimbursement_types`, `/reimbursement_types/:id`, and `reimbursement_type` fields expanded in other resources).

  Where a reimbursement is reported on a T4 isn't a simple 1:1 mapping. This field was always previously returned with the value `null`. The `form_mappings` field contains more nuanced information about how types map to forms.

  ### Removing the `is_taxable`, `is_insurable`, and `is_pensionable` from reimbursement type resources

  We're removing the `is_taxable`, `is_insurable`, and `is_pensionable` fields from reimbursement type resources (`/reimbursement_types`, `/reimbursement_types/:id`, and `reimbursement_type` fields expanded in other resources).

  Whether or not a reimbursement type is taxable, insurable, or pensionable isn't a simple boolean value; it varies by jurisdiction. We've replaced these fields with a new [Line Item Type Features](https://docs.nmbr.co/guides/payroll-fundamentals/line-item-type-features) system that will return more nuanced information about line item types in each jurisdiction we support.

  ### Removing journal entry line item report

  We're removing the journal entry *line item* report (`POST /reports/journal-entry-line-item`).

  It has been replaced by the [Journal Entry Report](https://docs.nmbr.co/api-reference/reports/create-payroll-register-report).

  ### Removing amount\_override from line item resources

  We're removing the `amount_override` field from all line item resources:

  * `/allowance_line_items/:id`
  * `/deduction_line_items/:id`
  * `/earning_line_items/:id`
  * `/employer_statutory_withholding_line_items/:id`
  * `/reimbursement_line_items/:id`
  * `/statutory_withholding_line_items/:id`

  These fields were for backward-compatibility during the transition from `amount` and `amount_override` to `managed_amount`, `custom_amount`, and `amount` in early 2025.

  Use the `custom_amount` field instead.
</Update>

<Update label="January 2026">
  ## name Read-Only amount and hours Properties on Line Items

  > 🚧 Breaking Change
  > The `amount` and `hours` properties on Line Items will be read-only as of **March 1, 2026**.

  ## Read-Only `amount` and `hours` Properties on Line items

  ### Introduction

  In Nmbr, each type of line item has 3 "amount" properties:

  * `managed_amount`: The managed amount for the line item. The value is either calculated by the system (e.g. statutory holiday pay or vacation pay payout) or configured on the line item's recurrence (e.g. a recurring allowance's `amount`). This property is read-only.

  * `custom_amount`: The custom amount for the line item.

  * `amount`: The final amount for the line item. This is the amount that will be paid, deducted, withheld, etc. The value will be `custom_amount`, if set, and `managed_amount` otherwise.

  Similarly, the earning line item type has 3 "hours" properties:

  * `managed_hours`: The managed hours for the line item. This amount is the default number of hours on the line item's Pay Rate. This property is read-only.

  * `custom_hours`: The custom hours for the line item. `custom_hours` can be used to prorate the dollar `amount` on salary line items with a Pay Rate.

  * `hours`: The final hours for the line item. The value will be `custom_hours`, if set, and `managed_hours` otherwise.

  Currently, the `amount` and `hours` properties are read/write. If you create or update a line item with an `amount` or `hours` value, the line item's `custom_amount` or `custom_hours` values will be set.

  ### What's Changing

  On March 1, 2026 we will be changing the `amount` and `hours` properties to be read-only. If you create or update a line item with `amount` or `hours` values, these values will be ignored.

  If your application sends `amount` or `hours` properties in requests to create or update line items, you will need to rename `amount` to `custom_amount` and `hours` to `custom_hours` in requests.

  For example, if you make the following request to create an earning line item

  `POST /earning_line_items`

  ```
  {
      ...
      "amount": 500,
      "hours": 10,
      ...
  }
  ```

  you would need to rename the properties as follows

  `POST /earning_line_items`

  ```
  {
      ...
      "custom_amount": 500,
      "custom_hours": 10,
      ...
  }
  ```

  ### What's Not Changing

  We will continue sending the computed `amount` and `hours` values in responses. Your application can continue to rely on these values.

  `GET /earning_line_items/ernli_01KBG99GP45784S1PVZTAGFE6X`

  ```
  {
      ...
      "managed_amount": 1000,
      "managed_hours": 20,
      ...
      "custom_amount": 500,
      "custom_hours": 10,
      ...
      "amount": 500,           // computed from managed_amount and custom_amount
      "hours": 10,             // computed from managed_hours and custom_hours
      ...
  }
  ```
</Update>

<Update label="September 2025">
  ## name Property Required on Business Presets

  > 🚧 Breaking Change
  > The `name` property on Business Presets will be required as of **November 15th, 2025**.

  ## `name` Property Required on Business Presets

  Business Presets currently require a value for *either* the `title` property or the `name` property.

  * `title` is copied to recurrences (pay rates, allowances, employee and employer benefits, deductions, and reimbursements) and line items, and used as their `title`.

  * `name` is used to identify the preset in the component or portal, or when grouping line items by preset on pay stubs or reports. If a Business Preset doesn't have a `name`, we currently fall back on `title`.

  We will be making the `name` property required as of November 15th, 2025 and removing the logic that currently falls back to `title`. In order to create new Business Presets, you will need to supply a value for `name`.

  The `title` property will continue to be optional.

  ## Business Entity-level templates deprecated

  > 🚧 **Breaking Change**
  > Business Entity-level templates for Allowances, Benefits, Deductions, and Reimbursements have been deprecated and will be removed on **November 15, 2025**. Please migrate to the new [Business Presets](/guides/advanced/presets) feature. Legacy endpoints will continue to function until this date but will not receive updates or bug fixes.

  ## Deprecation of Business Entity-Level Templates for Allowances, Benefits, Deductions, and Reimbursements

  Nmbr previously supported defining templates for Allowances, Benefits, Deductions, and Reimbursements at the Business Entity level. As part of improving our API and providing a more consistent experience, we are replacing this feature with the new [Business Presets](/guides/advanced/presets) feature, which supports a larger set of use cases with fewer endpoints.

  ## Deprecated Endpoints

  The following endpoints are now deprecated and have been replaced by the new Business Presets endpoints:

  * `/business_entity_allowances`
  * `/business_entity_benefits`
  * `/business_entity_deductions`
  * `/business_entity_reimbursements`

  # Migration

  ## Data

  You do NOT need to perform a data migration from legacy templates to Business Presets.

  When Business Presets were released, legacy templates were migrated to Business Presets of the appropriate `type`. For example, every legacy template for Allowances was migrated to a Business Preset of type `allowance`.

  To maintain backward compatibility, the APIs for these features were changed to manage Business Presets of the appropriate `type`. For example, creating/updating/deleting a Business Preset of type `allowance` will *appear* to create/update/delete a legacy template for Allowances, and vice versa. However, behind the scenes there is actually only one record in the Nmbr system: the Business Preset.

  During this migration, the ULIDs for legacy templates were retained as the ULID for the corresponding Business Preset, so partners will **not** need to store two ULID values (legacy template ULID and Business Preset ULID) or map between them.

  ## API

  You **DO** need to perform a code migration to change any code that is currently calling the legacy template APIs to instead call the Business Presets APIs.

  ### Allowances

  Change any code calling the `/business_entity_allowances` endpoints with the property `"allowance_type": "T"` to instead call the corresponding `/business_presets` endpoints with the properties `"type": "allowance"` and `"subtype": "T"`.

  For example, if you're working with taxable meals allowances, `"allowance_type": "meals_taxable"` will become `"type": "allowance"` and `"subtype": "meals_taxable"`.

  As mentioned in the data section above, you can continue to use the ULIDs for legacy templates.

  \| Action | Old Endpoint | New Endpoint |
  ||||
  \| | `name` | New required property. All Business Presets must have a name. The name is used when listing Business Presets and when grouping line items by Business Preset. |
  \| | `type` | New required property. For allowances, the type is always `allowance`. |
  \| `allowance_type` | `subtype` | Renamed. |
  \| `business_entity_id` | `business_entity_id` | Unchanged. |
  \| `title` | `title` | Unchanged. |

  ### Benefits

  Since Business Presets were released, Benefits were split into separate Employer Benefits and Employee Benefits.

  For Employer Benefits, change any code calling the `/business_entity_benefits` endpoints with the property `"benefit_type": "T"` to instead call the corresponding `/business_presets` endpoints with the properties `"type": "employer_benefit"` and `"subtype": "T"`.

  \| Action | Old Endpoint | New Endpoint |
  ||||
  \| Create | `POST /business_entity_benefits` with body `{ "benefit_type": "T", ... }` | `POST /business_presets` with body `{ "type": "employee_benefit", "subtype": "T", ... }` |
  \| List | `GET /business_entity_benefits` | `GET /business_presets?type=employee_benefit` |
  \| Retrieve | `GET /business_entity_benefits/:ulid` | `GET /business_presets/:ulid` |
  \| Update | `PUT /business_entity_benefits/:ulid` | `PUT /business_presets/:ulid` |
  \| Delete | `DELETE /business_entity_benefits/:ulid` | `DELETE /business_presets/:ulid` |

  The legacy benefit template properties map to the Business Presets properties as follows:

  \| Old Property | New Property | Notes |
  ||||
  \| Create | `POST /business_entity_deductions` with body `{ "deduction_type": "T", ... }` | `POST /business_presets` with body `{ "type": "deduction", "subtype": "T", ... }` |
  \| List | `GET /business_entity_deductions` | `GET /business_presets?type=deduction` |
  \| Retrieve | `GET /business_entity_deductions/:ulid` | `GET /business_presets/:ulid` |
  \| Update | `PUT /business_entity_deductions/:ulid` | `PUT /business_presets/:ulid` |
  \| Delete | `DELETE /business_entity_deductions/:ulid` | `DELETE /business_presets/:ulid` |

  The legacy deduction template properties map to the Business Presets properties as follows:

  \| Old Property | New Property | Notes |
  ||||
  \| Create | `POST /business_entity_reimbursements` with body `{ "reimbursement_type": "T", ... }` | `POST /business_presets` with body `{ "type": "reimbursement", "subtype": "T", ... }` |
  \| List | `GET /business_entity_reimbursements` | `GET /business_presets?type=reimbursement` |
  \| Retrieve | `GET /business_entity_reimbursements/:ulid` | `GET /business_presets/:ulid` |
  \| Update | `PUT /business_entity_reimbursements/:ulid` | `PUT /business_presets/:ulid` |
  \| Delete | `DELETE /business_entity_reimbursements/:ulid` | `DELETE /business_presets/:ulid` |

  The legacy reimbursement template properties map to the Business Presets properties as follows:

  \| Old Property | New Property | Notes |
  ||---|
  \| | `name` | New required property. All Business Presets must have a name. The name is used when listing Business Presets and when grouping line items by Business Preset. |
  \| | `type` | New required property. For reimbursements, the type is always `reimbursement`. |
  \| `deduction_type` | `subtype` | Renamed. |
  \| `business_entity_id` | `business_entity_id` | Unchanged. |
  \| `title` | `title` | Unchanged. |

  ## Deprecation of Legacy Benefits API

  > 🚧 **Breaking Change**\
  > The legacy Benefits API has been deprecated and will be removed on **August 31, 2025**.\
  > Please use the new Benefits API endpoints for all benefit-related operations. Legacy endpoints will continue to function until this date but will not receive updates or bug fixes.

  ## Deprecation of Legacy Benefits API

  As part of improving our API and providing a more consistent experience, we are deprecating the legacy Benefits endpoints. We have split the functionality into two distinct sets of API endpoints: one for employers and one for employees. This allows us to better align the Benefits functionality with our existing API structure.

  All existing data has been migrated to the new endpoints, and you can continue to access your employee and employer benefits data without interruption. All existing Benefit and Benefit Line Item ULIDs have been preserved on the corresponding Employee Benefit and Employee Benefit Line Item records.

  More information can be found in the [Configuring Benefits documentation](/guides/payroll-fundamentals/configuring-benefits).

  As well as providing a more consistent API experience, these changes are required to offer improved functionality for reporting and GL code mapping.

  ## Legacy Benefits API Deprecation

  The following endpoints are now deprecated and have been replaced by the new Employee and Employer Benefits API endpoints:

  * `/benefits` → replaced by:
    * `/employee_benefits`
    * `/employer_benefits`

  * `/benefit_line_items` → replaced by:
    * `/employee_benefit_line_items`
    * `/employer_benefit_line_items`

  * `/benefit_types` → replaced by:
    * `/employee_benefit_types`
    * `/employer_benefit_types`

  ## Benefit Types

  Previously, a single `benefit_type` was used to categorize benefits. This did not correctly represent benefits as they are used in the real world. Some benefits are contributed to by both the employer and employee, while others are only contributed to by one party. This ambiguity made accurate reporting and accounting more difficult.

  To address this, we have introduced two new benefit types: `employee_benefit_type` and `employer_benefit_type`. The two benefit types now contain different values, reflecting the distinct contributions made by employees and employers. For example, `pension_rrsp` may appear in both `employee_benefit_type` and `employer_benefit_type`, whereas `gift_near_cash` will only appear in `employer_benefit_type`.

  These changes not only provide a more consistent API experience but also enable improved reporting and GL code mapping.
</Update>

<Update label="May 2025">
  ## Updates to Line Item Type Filters

  > 🚧 **Breaking Change**
  > Type filters in Line Item listing endpoints have been replaced with `include_types`.
  > The deprecated filters listed below will be removed on **June 30, 2025**.

  ## Renamed Type Filters for Line Item Listings

  The following query parameters have been renamed to `include_types` for consistency across line item listing endpoints:

  * `allowance_type` → `include_types` ([Allowances](/api-reference/allowances/list-allowances))
  * `benefit_type` → `include_types` ([Benefits](/api-reference/employee-benefits/list-employee-benefits))
  * `deduction_type` → `include_types` ([Deductions](/api-reference/deductions/list-deductions))
  * `earning_type` → `include_types` ([Earnings](/api-reference/earnings/list-earnings))
  * `employer_statutory_withholding_type` → `include_types` ([Employer Statutory Withholdings](/api-reference/employer-statutory-withholding-line-items/list-employer-statutory-withholding-line-items))
  * `reimbursement_type` → `include_types` ([Reimbursements](/api-reference/reimbursements/list-reimbursements))
  * `statutory_withholding_type` → `include_types` ([Statutory Withholdings](/api-reference/statutory-withholding-line-items/list-statutory-withholding-line-items))

  ## New Filtering Options

  All line item listing endpoints now support both:

  * **`include_types`**: Return only items matching one or more specified types.
    *Example*: Retrieve only `wage` and `overtime` earnings.

  * **`exclude_types`**: Exclude items of one or more specified types from the results.
    *Example*: Omit `union_dues` from deduction results.

  ***

  These changes promote consistency and improve filtering flexibility across the API.
</Update>

<Update label="April 2025">
  ## Additional Validation on Date Fields

  > 🚧 Breaking Change
  > Additional validation will be performed on date fields as of **May 1st, 2025**.

  ## Additional Validation on Date Fields

  Our API accepts and returns date-related fields following the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) standard:

  * `date` fields are formatted `YYYY-MM-DD` without a time component (e.g. `2023-01-01`)
  * `date-time` fields are formatted `YYYY-MM-DDThh:mm:ss.sssZ` (e.g. `2023-01-01T06:30:00.000000Z`)

  However, some endpoints are not currently validating the format of some `date` fields, and are accepting the `date-time` format or other formats.

  We will be introducing validation on these fields beginning May 1, 2025, and accepting only the `date` format after that.

  The following key fields have been identified as changing:

  * When creating or updating [employees](/api-reference/employees/create-an-employee), the `date_of_birth` field must be formatted `YYYY-MM-DD`.

  * When creating or updating [allowances](/api-reference/allowances/create-an-allowance), [benefits](/api-reference/employee-benefits/create-an-employee-benefit), [deductions](/api-reference/deductions/create-a-deduction), [reimbursements](/api-reference/reimbursements/create-a-reimbursement), or [pay rates](/api-reference/pay-rates/create-a-pay-rate), the `effective_from` and `effective_to` fields must be formatted `YYYY-MM-DD`.

  * When creating or updating [pay schedules](/api-reference/pay-schedules/create-a-pay-schedule), the `anchor_pay_date` and `anchor_end_of_pay_period` fields must be formatted `YYYY-MM-DD`.

  * When [terminating work assignments](/api-reference/work-assignments/terminate-a-work-assignment), the `effective_date` field must be formatted `YYYY-MM-DD`.

  * When filtering reports by pay date, the `pay_date_from` and `pay_date_to` fields must be formatted `YYYY-MM-DD`.

  However, this change will apply to **all** fields documented as `date` in our documentation. Please ensure that `date` fields are properly formatted.

  ## amount Required on Reimbursements

  > 🚧 Breaking Change
  > `amount` field required on reimbursements as of **May 30th, 2025**.

  ## `amount` Required on Reimbursements

  When [creating reimbursements](/api-reference/reimbursements/create-a-reimbursement), the `amount` field is not currently a required field.

  We will be making the `amount` field a required field beginning May 30, 2025, and requiring a value for the field after that.

  Please ensure the `amount` field is supplied when creating reimbursements.
</Update>

<Update label="March 2025">
  ## Deprecating Vacation Pay on Pay Rates

  > 🚧 Deprecation Notice
  > Vacation Pay on Pay Rates is being deprecated and will be removed on **May 15th, 2025**.

  ## Deprecating Vacation Pay on Pay Rates

  As part of improvements to functionality for Vacation Pay, we have deprecated the Vacation Pay attributes on the Pay Rate entity.

  * `vacation_pay_percentage` - this attribute is deprecated and has been removed.
  * `vacation_pay_method` - this attribute is deprecated and has been removed.

  These changes will take effect on May 15th, 2025.

  ## Migrating to Vacation Pay Settings

  To manage the vacation pay percentage and payout methods for a Work Assignment, you can use the `vacation_pay_settings` endpoint. This entity will allow you to set the percentage, the effective dates, and method of vacation pay for a specific Work Assignment.

  All eligible earning line items will have vacation pay calculated based on the active Vacation Pay Setting and the Province of Employment for the Work Assignment.

  To read more, please see the [Vacation Pay Guide](/guides/payroll-fundamentals/vacation-pay).
</Update>

<Update label="July 2024">
  ## Standardizing Historical Payroll Terminology

  We've standardized on the term "historical payroll" for payrolls run with a different provider in the current calendar year. To create a historical payroll, a post request should use the `type: historical`. View [docs](/guides/advanced/historical-payrolls).
</Update>

<Update label="May 2024">
  ## Updates to Future PayStub Line Items

  **Enhanced Line Item Generation:** We’ve refined the generation process for recurring line items on future Pay Stubs. Now, line items for recurring Earnings, Benefits, Reimbursements, Allowances, and Deductions will automatically populate once the preceding payroll changes status to paid. This ensures that your upcoming Pay Stubs reflect the most current and accurate information.

  ## Changes to Validation on Pay Rates

  **Required `expected_hours_per_week` Field**: To improve the accuracy of Statutory Withholdings for off-cycle Payrolls, the `expected_hours_per_week field` is now mandatory when creating a new Pay Rate. This update helps minimizing discrepancies and potential errors in payroll processing.
</Update>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.