Skip to content

Australian payslip schema

Set schema to au.payslip.v2 and send the payslip data in payslip_non_pii. The schema is closed to undocumented fields, but some defined fields accept printed strings. Closed does not mean free-text-free or guarantee that values contain no personal data. Your integration must keep employee PII out of every non-PII field; the API does not detect PII in accepted string values.

  • ✓ Send each field that your payslip shows.
  • ✓ Send pay_frequency from the pay calendar in your payroll system. Refer to Pay frequency.
  • ✓ Send each amount, rate and quantity as a decimal string. Refer to Amounts, rates and quantities.
  • ✓ Send currency on each payslip. Refer to Currency.
  • ✓ Keep personal data out of every field. Send the employee name, the address and the bank details in the encrypted PII.
  • ✓ Write each date as YYYY-MM-DD.

A payslip in New Zealand uses nz.payslip.v2, which has a different field list. Refer to the nz.payslip.v2 schema.

The code sets below use the ATO Single Touch Payroll Phase 2 categories.

Send each amount, rate and quantity as a JSON string that contains a plain decimal number.

An amount
"gross": "3600.00"
  • ✓ Use digits and a decimal point. Put a minus sign at the start of a negative value.
  • ✓ Do not send a thousands separator, currency symbol, bracket, unit, plus sign or exponent.
  • ✓ Send a string, not a JSON number.
  • ✓ Any number of decimal places is valid. The API stores the string as you send it. "1.50" and "1.5" are different values.
  • ✓ Every field accepts a negative value, hours and quantities included.

The SDKs check the format of each number before they send the request.

From the printed payslip to the string
printed send
$3,600.00 "3600.00"
3,600.00 "3600.00"
(123.45) "-123.45"
NIL "0.00"
- "0.00"
76.00 hrs "76"
12% "12"
47.3684 "47.3684"

Send a nil line as zero, for example "0.00".

A rate of four decimal places, and a nil line
{
"period_start": "2026-03-21",
"period_end": "2026-04-03",
"payment_date": "2026-04-04",
"currency": "AUD",
"gross": "3600.00",
"paygw": "752.00",
"net": "2848.00",
"base_rate": {
"amount": "47.3684",
"per": "hour"
},
"earnings": [
{
"type": "ordinary",
"units": "76",
"rate": "47.3684",
"amount": "3600.00"
},
{
"type": "overtime",
"amount": "0.00"
}
]
}

Send currency on each payslip. Use the three-letter alphabetic code of the payslip currency, for example AUD or NZD. Write the code in capital letters.

The API accepts each current ISO 4217 currency code. It does not accept fund codes or codes that have no minor unit, for example XAU, XTS and XXX. SIX, the ISO 4217 maintenance agency, publishes the list on its data standards page.

The currency does not set the number of decimal places in an amount.

These mistakes put a correct figure in the wrong field.

Send PAYG withholding in paygw, also when the payslip prints it as “Tax” or under “Deductions”. deductions[] is for post-tax deductions only.

A payslip that prints tax under Deductions
Gross 3,600.00
Deductions
PAYG withholding 752.00 -> paygw: "752.00"
Health fund 45.50 -> deductions: [{ type: "other_post_tax", amount: "45.50" }]
Net 2,802.50

gross is the gross before salary sacrifice. Send the taxable earnings that the payslip shows as taxable_gross.

Which number goes where
Gross (before sacrifice) 3,600.00 -> gross: "3600.00"
Salary sacrifice to super 200.00 -> salary_sacrifice[].amount: "200.00"
Taxable gross (printed) 3,400.00 -> taxable_gross: "3400.00"

Send the number only. Remove the currency symbol, the separators and the unit.

Send a plain decimal string
"gross": "3600.00" valid
"gross": "$3,600.00" rejected: no currency symbol
"gross": "3,600.00" rejected: no thousands separator
"gross": 3600.00 rejected: send a string

The schema has no field for a pay-code name. Map each pay code to its STP category, for example “Salary” to ordinary. If a pay code has no category, refer to Itemising earnings.

The record contains no pay-code names
"Salary" -> { type: "ordinary", amount: "3040.00" }
"Time and a half" -> { type: "overtime", amount: "240.00" }
"Tool allowance" -> { type: "allowance", allowance_type: "tools", amount: "40.00" }
"Annual Leave Taken" -> { type: "paid_leave", leave_type: "other_paid_leave", amount: "280.00" }

5. The STP Phase 2 Gross amount is not gross

Section titled “5. The STP Phase 2 Gross amount is not gross”

gross is the total of the earnings lines. Send each pay code in the STP Phase 2 Gross amount as an ordinary line. Send each other pay code as its own line, and include it in gross.

STP Phase 2 amounts on one payslip
Gross (STP Phase 2)
Ordinary hours 2,800.00 -> { type: "ordinary", amount: "2800.00" }
Shift penalty 240.00 -> { type: "ordinary", amount: "240.00" }
Overtime 240.00 -> { type: "overtime", amount: "240.00" }
Allowance (TD) 40.00 -> { type: "allowance", allowance_type: "tools", amount: "40.00" }
Paid leave (O) 280.00 -> { type: "paid_leave", leave_type: "other_paid_leave", amount: "280.00" }
Total 3,600.00 -> gross: "3600.00"

If the API rejects a payload, it returns VALIDATION_FAILED and lists the applicable fields in field_errors. The table shows each message from the payslip schema and the usual cause of that message.

MessageUsual cause
Unrecognized key: “…”The payload contains a field that is not in the schema. The schema is closed. Remove the field. A leave balance, a fund name and an award code each cause this message.
Invalid discriminator value. Expected ‘paid_leave’ | ‘allowance’ | …The payload contains a pay-code name in the earnings type field. Map the pay code to one of the ten types.
must be a decimal number, for example 1234.56An amount, rate or quantity is empty, or it contains a separator, a currency symbol, a bracket, a unit or a word. Send a plain decimal string.
Invalid input: expected string, received numberAn amount, rate or quantity is a JSON number. Send it as a string.
Invalid input: expected string, received objectAn amount, rate or quantity is an object. Send it as a string.
Invalid input: expected string, received undefinedA required amount is missing, for example net. Add it.
Too small: expected string to have >=1 charactersA text field, for example award, is an empty string. Send the printed text, or remove the field.
currency must be an ISO 4217 currency codeThe currency field is missing, or it does not contain an accepted ISO 4217 code. Send a code such as AUD or NZD.
USI must be a fund ABN plus 3-digit product suffix (14 digits) or a SPINThe usi field contains an 11-digit ABN. Send contributions to a self-managed fund with no fund identifier.
period_end must not be before period_startperiod_end is before period_start. Examine the two dates.
other_category is required on an ‘other’ allowanceAn allowance of the type other has no descriptor. Select one of the six other_category values.
other_category applies only to allowance_type ‘other’The payload contains a descriptor on an allowance that is not of the type other.
<field> must be YYYY-MM-DDThe date is not in ISO format, or the date does not exist. The API rejects 2026-02-31 and 31/03/2026.

Each payslip requires these fields.

FieldTypeMeaning
period_enddateLast day of the pay period (YYYY-MM-DD). Must not be before period_start when you send it.
payment_datedateThe day the employee is paid.
currencystringThe ISO 4217 code of the payslip currency, for example AUD. Refer to Currency.
grossdecimal stringTotal gross before salary sacrifice. The earnings lines add up to this amount.
paygwdecimal stringPAYG withholding, including the tax withheld from each ETP. Its own field, never also a deductions line.
netdecimal stringNet pay, as shown on the payslip.

A payload that contains only the core fields is valid. Use it only for a payslip that shows no other data.

A minimal payslip_non_pii
{
"period_end": "2026-04-03",
"payment_date": "2026-04-04",
"currency": "AUD",
"gross": "3600.00",
"paygw": "752.00",
"net": "2848.00"
}

Send each of these fields that your payslip shows.

FieldTypeMeaning
period_startdateFirst day of the pay period (YYYY-MM-DD).
ytd_grossdecimal stringYear-to-date gross across the financial year, including this pay run.
ytd_paygwdecimal stringYear-to-date PAYG withholding, including this pay run.
pay_frequencyenumOne of the values in Pay frequency.
employment_basisenumSTP2 employment basis. Independent of engagement_type.
engagement_typeenumOne of permanent or fixed_term. A Fair Work notion with no STP2 counterpart.
awardstringThe award or agreement line as your payslip prints it.
base_rateobjectA base rate that is not on an earnings line. Refer to Base rate.
taxable_grossdecimal stringThe taxable earnings that the payslip shows.
stsl_withholdingdecimal stringThe HELP/STSL component printed on the payslip. Already inside paygw.
earningsarrayItemisation of gross.
salary_sacrificearraySalary sacrifice lines. Each line has a type and an amount, and can have a year-to-date amount.
deductionsarrayPost-tax deduction lines. Each line has a type and an amount, and can have a year-to-date amount.
superannuationarraySuperannuation contribution lines. Each line has a contribution type and amount. It can also have a rate, year-to-date amount and USI.
reimbursementsdecimal stringNon-taxable reimbursements. Not part of gross.
ytdobjectFurther year-to-date totals: taxable, super, non_taxable, post_tax_deductions, reimbursements.

Send the award or agreement line as the payslip prints it, with the classification, as one string.

Examples of a printed award line
"Clerks - Private Sector Award 2020 Level 3"
"Manufacturing and Associated Industries Award 2020 C10"
"Example Software Pty Ltd Enterprise Agreement 2024"
"Award free"

Keep personal data out of this field. An agreement title contains the name of the employer. It must not contain the name of the employee.

This table shows where to send each STP Phase 2 category in au.payslip.v2. Each earnings[] line is part of gross.

STP2 categorySend asRefer to
Grossearnings[] type ordinary. Send one line for each pay code.Mapping mistake 5
Paid leave (C, U, P, W, A, O)earnings[] type paid_leave, with leave_typePaid leave
Allowances (CD, AD, LD, MD, RD, TD, KN, QN, OD)earnings[] type allowance, with allowance_type. Add other_category for OD.Allowances
Overtimeearnings[] type overtimeEarnings line types
Bonuses and commissionsearnings[] type bonus_commissionEarnings line types
Directors’ feesearnings[] type directors_feesEarnings line types
Community Development Employment Projects (CDEP) paymentsearnings[] type otherEarnings line types
Exempt foreign employment incomeearnings[] type otherEarnings line types
Lump sum A (R, T), B, D, Eearnings[] type lump_sum, with lump_sum_typeLump sums
Lump sum Wearnings[] type return_to_workLump sums
ETP (R, O, S, P, D, N, B, T)earnings[] type etp, with etp_type and etp_component. Send one line for each component.Employment termination payments
Salary sacrifice (S, O)salary_sacrifice[] type super (S) or other (O)Salary sacrifice, Mapping mistake 2
PAYG withholdingpaygw. Include the tax withheld from each ETP.Core fields
Deductions (F, W, D, G)deductions[], with typeDeductions
Employer superannuationsuperannuation[], with contribution_typeSuperannuation contributions
Employment basis (F, P, C, L, V, D, N)employment_basisEmployment basis
A pay code with no STP2 categoryearnings[] type otherItemising earnings

Map your existing Single Touch Payroll Phase 2 categories to the accepted values below. PAYG withholding has its own field. deductions[] contains post-tax deductions only.

Each earnings[] line has a type and an amount. A line can also contain units, rate and ytd_amount. Four types have additional codes. A line cannot contain the codes of another type.

Send the number without the unit, for example "76" for 76.00 hrs. The schema has no unit field.

typeMeaning
ordinaryOrdinary hours.
overtimeOvertime.
bonus_commissionBonuses and commissions.
directors_feesDirectors’ fees.
return_to_workReturn-to-work payments.
otherA pay code that fits no other type.
paid_leavePaid absence. Carries a leave_type from the leave set below.
allowanceAllowance. Carries an allowance_type from the allowance set below.
lump_sumLump sum A, B, D or E. Carries a lump_sum_type from the lump sum set below.
etpEmployment termination payment. Carries an etp_type and an etp_component. Refer to the ETP section below.

Use these values in leave_type on a paid_leave line.

leave_typeSTP2 codeMeaning
cash_out_in_serviceCLeave cashed out while still employed.
unused_on_terminationUUnused leave paid out on termination.
paid_parentalPPaid parental leave.
workers_compensationWAbsence paid under workers compensation.
ancillary_defenceAAncillary and defence leave.
other_paid_leaveOThe ATO general code: annual, personal, long service, RDO, TOIL, and each other paid absence.

ATO reference: STP Phase 2 reporting quick reference guide

Send a lump_sum_type on each lump_sum line. Use one of these values. Send lump sum W as a return_to_work line.

lump_sum_typeSTP2 codeMeaning
a_redundancyRLump sum A, paid for genuine redundancy, invalidity or an early retirement scheme.
a_otherTLump sum A, paid for other reasons.
bBLump sum B.
dDLump sum D.
eELump sum E.

Send an etp_type on each etp line. Use one of these values.

etp_typeSTP2 codeMeaning
redundancyRGenuine redundancy, invalidity, an early retirement scheme, or compensation for personal injury, unfair dismissal, harassment or discrimination.
otherOA life benefit for other reasons.
redundancy_splitSA later payment of a type R ETP, in a later financial year.
other_splitPA later payment of a type O ETP, in a later financial year.
death_dependantDDeath benefit to a dependant.
death_non_dependantNDeath benefit to a non-dependant.
death_non_dependant_splitBA later payment of a type N ETP, in a later financial year.
death_trusteeTDeath benefit to the trustee of a deceased estate.

Send an etp_component on each etp line. Use taxable or tax_free. Send each component of an ETP as its own etp line.

Include the tax withheld from each ETP in paygw.

ATO reference: When an employee transfers or leaves

Use these values in allowance_type on an allowance line.

allowance_typeSTP2 codeMeaning
cents_per_kmCDCents-per-kilometre allowance.
award_transportADAward transport payments.
laundryLDLaundry allowance.
overtime_mealMDOvertime meal allowance.
travelRDTravel allowance.
toolsTDTools allowance.
tasksKNTask allowance.
qualificationsQNQualification allowance.
otherODThis type requires other_category. No other type accepts other_category.

Send other_category on each allowance of the type other. Use a value below. The API rejects other_category on other allowance types.

other_categorySTP2 codeMeaning
home_officeH1Home office costs.
non_deductibleNDNon-deductible amounts.
transport_faresT1Transport fares.
uniformU1Uniform costs.
private_vehicleV1Private vehicle use.
generalG1The general descriptor.

ATO reference: Allowances

An 'other' allowance line
{
"type": "allowance",
"allowance_type": "other",
"other_category": "home_office",
"amount": "80.00"
}

These types are for post-tax deductions only. PAYG withholding is not a deduction.

typeSTP2 codeMeaning
union_professional_feesFUnion and professional association fees.
workplace_givingWWorkplace giving.
child_support_deductionDChild support deduction.
child_support_garnisheeGChild support garnishee.
other_post_taxAny other post-tax deduction, for example a health fund or a social club.

ATO reference: Other components of your STP reporting

The type field accepts super or other.

Send each pre-tax deduction as a salary_sacrifice line. Use super for a contribution to superannuation and other for each other pre-tax deduction.

Also send salary-sacrificed superannuation as a superannuation line. Refer to Superannuation contributions.

These types are for employer contributions only. Send a post-tax contribution from the employee as a deduction (other_post_tax), and not as a superannuation line.

Send one line for each contribution type. Use the ATO category of the contribution, also when the payslip prints it under a different heading.

contribution_typeMeaning
superannuation_guaranteeThe superannuation guarantee amount. If the payroll pays a higher compulsory rate as one pay code, send one superannuation_guarantee line with that rate.
rescReportable employer superannuation contributions (RESC), other than salary sacrifice. Do not include salary sacrifice in resc.
salary_sacrificeSalary-sacrificed superannuation contributions.
otherAn employer contribution that is not superannuation guarantee, RESC or salary sacrifice. For example, an amount above the superannuation guarantee under an award or enterprise agreement.

Send salary-sacrificed superannuation on two lines:

  • ✓ A salary_sacrifice line with type super. This line is the pre-tax deduction.
  • ✓ A superannuation line with contribution type salary_sacrifice. This line is the contribution to the fund.

ATO references: Other components of your STP reporting, Reportable employer super contribution types

Send engagement_type (permanent or fixed_term) in its own field.

employment_basisSTP2 code
full_timeF
part_timeP
casualC
labour_hireL
voluntary_agreementV
death_beneficiaryD
non_employeeN

ATO reference: How to report employment and tax information

The pay_frequency field accepts weekly, fortnightly, four_weekly, semi_monthly, monthly or quarterly.

Send semi_monthly for a half-monthly or twice-monthly pay calendar.

If a pay is not in a pay calendar, do not send pay_frequency.

Send an earnings line for each pay code on the payslip. The lines add up to gross.

If a pay code has no category, send it as an other line.

Send each lump sum with its lump_sum_type, and each etp line with its etp_type and etp_component. Refer to Lump sums and Employment termination payments.

The earnings on a payslip for a genuine redundancy
[
{
"type": "ordinary",
"amount": "3000.00"
},
{
"type": "lump_sum",
"lump_sum_type": "a_redundancy",
"amount": "4000.00"
},
{
"type": "lump_sum",
"lump_sum_type": "d",
"amount": "5000.00"
},
{
"type": "etp",
"etp_type": "redundancy",
"etp_component": "taxable",
"amount": "8000.00"
}
]

The gross of this payslip is 20000.00, the total of the four lines.

Send base_rate if the payslip shows a base rate that is not on an earnings line, for example “Base Pay Rate: $450.00 Per Day”.

  • ✓ amount is the rate, as a decimal string.
  • ✓ per is the period of the rate. It accepts hour, day, week, fortnight, month or year.
  • ✓ Send units, rate and amount on the ordinary earnings lines.
A daily base rate and its earnings line
"base_rate": { "amount": "450.00", "per": "day" },
"earnings": [
{ "type": "ordinary", "units": "10", "rate": "450.00", "amount": "4500.00" }
]

base_rate replaces hourly and annual_rate. If you send them, change to base_rate.

A superannuation line can contain usi, or no identifier. The schema has no field for a fund ABN and no field for a fund name.

  • ✓ usi is an APRA-product Unique Superannuation Identifier: the fund ABN with a 3-digit product suffix (14 digits), or a SPIN such as STA0100AU. It does not accept an 11-digit ABN.
  • ✓ A 14-digit usi starts with the fund ABN, so the schema has no separate fund_abn field. A record that contains one is rejected.
  • ✓ A self-managed fund has no USI. Send its contributions with no fund identifier.

Send each year-to-date figure that your payslip shows. There are three places for a year-to-date figure:

  • ✓ ytd_gross and ytd_paygw hold the two principal totals.
  • ✓ ytd_amount on an earnings, salary_sacrifice, deductions or superannuation line holds the figure for that line.
  • ✓ The ytd object holds the further totals: taxable, super, non_taxable, post_tax_deductions and reimbursements.

Each year-to-date figure includes the pay run that you register.

The schema does not contain these fields. The API rejects them. Remove them from your payload.

Not in the schemaWhy
Leave balancesThe schema has no leave-balance field.
Super fund namesIdentify a fund with a usi, or send no identifier.
Employee names, addresses and bank detailsSend personal data in the encrypted PII payload.
Award codes and classification levelsSend the printed line as award.
Income stream types, tax treatment codesThe schema has no field for these.

Send the personal details on the payslip in the encrypted PII, not in payslip_non_pii. Australian payslips use the AU2 format. For the string format, the rules for every format and encryption, refer to PII and encryption.

AU2: all fields populated
AU2|Ana Leota|Senior Developer|Engineering|12 345 678 901|062-000|****5678|A Leota|12 Example St, Sydney NSW 2000
AU2: some fields omitted
AU2|Ana Leota|||Example Software Pty Ltd|062-000|****5678||
#FieldExample
1employee_nameAna Leota
2positionSenior Developer
3departmentEngineering
4employer_identity12 345 678 901
5bsb062-000
6account_number****5678
7account_nameA Leota
8address12 Example St, Sydney NSW 2000

For employer_identity, send the employer ABN. If the payslip shows no ABN, send the employer name.

The SDKs write employer_identity for you. Give the employer name and the employer ABN. The SDK writes the ABN if it is present, and the name if not.

The encrypted PII contains one bank account. Cash and cheque are not bank accounts.

  • ✓ Send the bank account that receives the largest part of the pay. For example, if the payslip shows 75% of the pay in cash and 25% to a bank account, send the bank account.
  • ✓ If two or more bank accounts receive equal quantities of the pay, send the first of these accounts that the payslip shows.
  • ✓ If no bank account receives pay, send empty bank fields.

This fortnightly payslip shows hourly pay, an allowance, paid leave, salary sacrifice to superannuation, a post-tax deduction and a reimbursement.

payslip_non_pii
{
"period_start": "2026-03-21",
"period_end": "2026-04-03",
"payment_date": "2026-04-04",
"currency": "AUD",
"pay_frequency": "fortnightly",
"gross": "3600.00",
"taxable_gross": "3400.00",
"paygw": "752.00",
"net": "2682.50",
"ytd_gross": "43200.00",
"ytd_paygw": "9024.00",
"earnings": [
{
"type": "ordinary",
"units": "76",
"rate": "40.00",
"amount": "3040.00",
"ytd_amount": "36480.00"
},
{
"type": "overtime",
"units": "4",
"rate": "60.00",
"amount": "240.00"
},
{
"type": "allowance",
"allowance_type": "tools",
"amount": "40.00"
},
{
"type": "paid_leave",
"leave_type": "other_paid_leave",
"amount": "280.00"
}
],
"salary_sacrifice": [
{
"type": "super",
"amount": "200.00"
}
],
"deductions": [
{
"type": "other_post_tax",
"amount": "45.50"
}
],
"superannuation": [
{
"contribution_type": "superannuation_guarantee",
"amount": "398.40",
"rate": "12",
"usi": "STA0100AU"
},
{
"contribution_type": "salary_sacrifice",
"amount": "200.00"
}
],
"reimbursements": "80.00",
"employment_basis": "full_time",
"engagement_type": "permanent",
"award": "Clerks - Private Sector Award 2020 Level 3",
"stsl_withholding": "120.00",
"base_rate": {
"amount": "40.00",
"per": "hour"
},
"ytd": {
"taxable": "40800.00",
"super": "4780.80",
"non_taxable": "960.00",
"post_tax_deductions": "546.00",
"reimbursements": "960.00"
}
}

Records that you register with v2 stay valid, and a verifier can still verify them.

The single registration endpoints accept each supported schema id. The schema field selects which schema applies to your payload. Batch registration examines the schema id of each record. An unsupported version fails that record only, and not the full batch.