Request fields
Who this is for: Integrators building JSON bodies for POST /v1/deductions, batch pay runs, or year projection.
When you finish: You know which T4127 (Payroll Deductions Formulas) inputs are required, their defaults, and when to send each optional field.
A claim code is the TD1 box value that maps to federal and provincial personal amounts. Proration applies when Option 1 splits mid-year provincial rates (see effective-dates.md).
Money is always a quoted decimal string in dollars and cents (for example "1000.00"; "1000" and "1000.5" are read the same way as "1000.00" and "1000.50"). JSON numbers are rejected (HTTP 400 invalid_request naming the field). An amount with more than 2 decimal places (for example "1000.505") is refused with HTTP 400 invalid_request naming the field. Every money field is zero or more: a negative amount (for example "-50.00") is refused with HTTP 400 invalid_request whose message names the field, on POST /v1/deductions, on each item of a batch and on the year projection (-0.00 reads as zero). An amount may have at most 15 digits before the decimal point; a larger one is refused with HTTP 400 invalid_request naming the field. A year projection (POST /v1/deductions/year) also keeps running totals, so there gross_pay, pensionable_earnings, insurable_earnings, taxable_benefits and additional_tax_requested times the number of pay periods must stay within 15 digits too (HTTP 400 otherwise). The year projection takes only some of the fields below; see year-projection.md.
Six fields most callers need
| Field | Purpose |
|---|---|
as_of |
Effective date (calendar date) that selects which enacted rule set applies. |
province |
Two-letter jurisdiction code (ON, BC, AB, …). |
pay_period |
Pay periods per year: one of 1, 2, 4, 10, 12, 13, 22, 24, 26, 27, 52, 53, 240 (daily) or 2000 (hourly). |
gross_pay |
Taxable gross for this cheque. |
federal_claim_code |
Federal TD1 claim code: 0 to 10 (a whole number, or its digits as a string) or E (or e). |
provincial_claim_code |
Provincial TD1 claim code, the same values. |
Claim codes and claim amounts
Send both claim codes, or the total claim amounts from the TD1 forms as federal_tc / provincial_tcp instead. A code and an amount for the same side in one request are refused (HTTP 400 invalid_request, ambiguous claim), so exactly one row below sets TC (federal) and TCP (provincial):
| Sent | TC or TCP used |
|---|---|
federal_tc / provincial_tcp |
That amount |
Code 0 |
$0.00 (no claim amount) |
Code E |
$0.00, and that side's income tax is $0.00 (exempt; the Ontario Health Premium is still withheld) |
Code 1, or no code |
The basic personal amount from its T4127 formula; the federal, Manitoba and Yukon amounts fall as net income rises |
Codes 2 to 10 |
The TC or TCP the CRA prints for that code in the claim-code table (T4127 Chapter 8, Tables 8.9 to 8.21) of the edition as_of selects: the middle of the code's band of claim amounts |
Codes 2 to 10 use the printed amount at every income; the income phase-out applies to code 1 and to no code. The CRA prints the July 2026 Newfoundland and Labrador table for Option 1 only (its basic amount, $15,000.00, is prorated; Option 2 uses $13,094.00), so from 2026-07-01 an NL provincial_claim_code from 2 to 10 with calculation_option option2 is refused with HTTP 400 invalid_request naming the field: send provincial_tcp instead. The response's breakdown.TC and breakdown.TCP show the amounts used. The tables are the CRA's CSV files archived in data/sources/ with their SHA-256 and pinned in the engine by SHA-512; the 2027 preview reuses the July 2026 tables.
Identity and dates
| Field | Type | Default | Required | T4127 factor | When needed |
|---|---|---|---|---|---|
as_of |
date | Worker UTC date if omitted | Recommended always | Selects tables for R, K, V, YMPE (Year's Maximum Pensionable Earnings), BPAF (Basic Personal Amount Factor) |
Every production call; omit only when you accept the server clock |
province |
string | none | Yes | Table 8.1 jurisdiction | Always |
pay_period |
integer | none | Yes | P, PR |
Always; must be a legal T4127 period count |
prior_province |
string | none | No | Not read | Refused (HTTP 400 invalid_request naming the field) when sent with a value: a change of province during the year is not calculated; send the province of employment on the pay date as province |
date_of_birth |
date | none | No | Not read | Refused (HTTP 400 invalid_request naming the field) when sent with a value: no age-based CPP rule is applied from it; use cpp_exempt and cpp_months |
calculation_option |
string | option1 |
No | Option 1 vs Option 2 annualization | BC, NL, PE mid-year proration paths; see effective-dates.md |
cpp_months |
integer, 0 to 12 | 12 |
No | CPP contribution months | Short-year employment |
k2_method |
string | pdoc_observed |
No | K2 / K2P base |
PDOC reconciliation vs literal T4127 |
rounding_compat |
string | t4127 |
No | C (period CPP) |
pdoc reproduces PDOC's binary64 CPP line to the cent (ADR-007) |
Income
| Field | Type | Default | Required | T4127 factor | When needed |
|---|---|---|---|---|---|
gross_pay |
money | none | Yes | Feeds IE, annual A |
Always |
pensionable_earnings |
money | gross_pay |
No | CPP pensionable | Differs from gross (benefits, caps) |
insurable_earnings |
money | gross_pay |
No | EI insurable | Differs from gross |
taxable_benefits |
money | 0.00 |
No | Taxable allowances | Non-cash taxable items in the period |
retroactive_pay |
money | none | No | Retroactive lump | T4127 retro pay instructions |
Claims and personal amounts
| Field | Type | Default | Required | T4127 factor | When needed |
|---|---|---|---|---|---|
federal_claim_code |
int/string | none | One of code or TC | TC, K1 |
Default withholding setup |
provincial_claim_code |
int/string | none | One of code or TCP | TCP, K1P |
Default withholding setup |
federal_tc |
money | none | Mutually exclusive with code | TC |
Custom federal total claim amount |
provincial_tcp |
money | none | Mutually exclusive with code | TCP |
Custom provincial total claim amount |
dependants_under_19 |
integer, 0 to 99 | none | No | Provincial credits | MB and similar |
dependants_disabled |
integer, 0 to 99 | none | No | Provincial credits | Where TD1X applies |
Deductions from pay
| Field | Type | Default | Required | T4127 factor | When needed |
|---|---|---|---|---|---|
rpp |
money | none | No | Registered pension | Period RRSP/RPP |
union_dues |
money | none | No | Union dues credit | Period dues |
alimony |
money | none | No | Support payments | Ordered deductions |
child_care_expenses |
money | none | No | Child care | Provincial rules |
prescribed_zone_deduction |
money | none | No | Northern deduction | Zone allowances |
lcf_purchase |
money | none | No | LCF |
Labour-sponsored funds |
lcp_purchase |
money | none | No | LCP |
Provincial labour credit |
additional_tax_requested |
money | none | No | L extra tax |
TD1 “additional tax” box |
cpp_exempt |
boolean | false | No | CPP exemption | Valid exemption on file |
cpp_election_after_65 |
boolean | none | No | Not read | Refused (HTTP 400 invalid_request naming the field) when sent with a value, false included: the CPP election to stop contributing is not applied from it; use cpp_exempt and cpp_months |
Year-to-date (YTD)
| Field | Type | Default | Required | T4127 factor | When needed |
|---|---|---|---|---|---|
ytd_pensionable_earnings |
money | none | No | CPP YTD PE | Mid-year hire or CPP cap |
ytd_insurable_earnings |
money | none | No | EI YTD | EI cap tracking |
ytd_cpp |
money | none | No | D |
CPP paid YTD |
ytd_cpp2 |
money | none | No | D2, CPP2 (second additional CPP) |
YAMPE (Year's Additional Maximum Pensionable Earnings) band |
ytd_ei |
money | none | No | EI premiums YTD | EI cap |
ytd_federal_tax |
money | none | No | Federal tax paid | Bonus methods |
ytd_provincial_tax |
money | none | No | Provincial tax paid | Bonus methods |
ytd_income |
money | none | No | Cumulative income | Commission averaging |
ytd_rpp |
money | none | No | RPP YTD | Annualized RPP |
ytd_union_dues |
money | none | No | Dues YTD | Credits |
f5a_ytd |
money | none | No | F5A YTD |
CPP additional |
f5b_ytd |
money | none | No | F5B YTD |
CPP additional |
pay_periods_elapsed |
integer, 0 to pay_period |
none | No | Period index | Mid-year start |
ytd_qpp |
money | none | No | Quebec only | Refused (HTTP 400 invalid_request naming the field) when sent with a value: Quebec is not calculated (a QC request is HTTP 422 jurisdiction_not_supported) |
ytd_qpp2 |
money | none | No | Quebec only | Refused the same way |
ytd_qpip |
money | none | No | Quebec only | Refused the same way |
Bonus and lump sums
| Field | Type | Default | Required | T4127 factor | When needed |
|---|---|---|---|---|---|
bonus |
money | none | No | Bonus withholding | T4127 bonus method |
bonus_method |
string | regular |
No | Method selector | Alternate bonus treatment |
ytd_bonus |
money | none | No | YTD bonus | Cumulative bonus |
bonus_rrsp |
money | none | No | Bonus RRSP | Special RRSP bonus |
ytd_bonus_rrsp |
money | none | No | YTD bonus RRSP | Tracking |
most_recent_i |
money | none | No | Recent insurable | Bonus averaging |
Option 2 and commission
| Field | Type | Default | Required | T4127 factor | When needed |
|---|---|---|---|---|---|
calculation_option |
string | option1 |
No | Option 1 vs 2 | Set option2 when your software uses T4127 Option 2 |
commission_income |
money | none | No | Not read | Refused (HTTP 400 invalid_request naming the field) when sent with a value: the T4127 commission method (Form TD1X) is not calculated; send commission paid with the cheque in gross_pay or bonus |
commission_expenses |
money | none | No | Not read | Refused the same way |
estimated_annual_expenses |
money | none | No | Expense estimate | Annual expense election |
Unknown property names yield unknown_field (HTTP 400). The API strips no extra keys silently, on any route. In a batch, every field goes on each item: the envelope takes only requests, and a field next to it (for example federal_claim_code or as_of) is refused. A documented field the engine does not read (prior_province, date_of_birth, cpp_election_after_65, commission_income, commission_expenses, ytd_qpp, ytd_qpp2 and ytd_qpip) is refused by name whenever it is sent with a value; null is read as not sent. A value of the wrong type or outside the values its field takes (for example pay_period 3, a claim code 11, cpp_months 256), and a required field left out, are HTTP 400 invalid_request naming the field.
See api-reference.md for routes and errors.md for rejection behavior.
Last reviewed: 2026-10-24
Engine: 0.1.0