Payroll XML
Turn a certified payroll CSV into the XML file your state DOT imports. Check the file before you upload it.
Your file stays on this computer. The page runs in your browser and uploads nothing, SSNs included.
Starting from a payroll export with its own column names? Match your columns on this page and skip the layout below.
New York public work? The state DOL's bulk upload has its own XML. Use the New York page.
How to lay out the CSV
One row per employee, per labor class, per day worked. The first row holds the column names below. Column order does not matter. Excel's "CSV UTF-8" save works best.
Excel rewrites some cells when it saves. Dates like 2026-10-04 come back as 10/4/2026, and this tool reads both. A project number that looks like a date can turn into a date, so format that column as Text before you save.
| Column | What goes in it |
|---|---|
vendor_id | Your vendor ID with the state. Same on every row. |
contract_id | The contract number, no dashes or spaces. Same on every row. |
payroll_number | This week's payroll number, a whole number starting at 1. Same on every row. |
period_begin, period_end | First and last day of the pay week, as 2026-10-04 or 10/4/2026. Seven days. Same on every row. |
fringe_type | Cash, Plan Funds, or Plan Funds with Exceptions. Same on every row. |
last_name, first_name | The worker's name. |
middle_initial | One letter, or blank. |
ssn | All 9 digits, or the last 4. Dashes are taken out for you. |
employee_id | Your own ID for the worker. Can be blank. |
gender | Male or Female. M and F work too. |
ethnicity | The code from your state's list. |
craft_code, labor_class | Codes from your state's list. |
project_id | The project number on the contract. |
apprentice | Y or N. For Y, fill apprentice_id and apprentice_pct too. |
st_rate, ot_rate | Straight time and overtime hourly rates. Up to 3 decimals for straight time and 4 for overtime are kept. |
work_date | The day worked, as 2026-10-05 or 10/5/2026. |
st_hours, ot_hours | Hours that day. A blank counts as 0. |
gross_pay, total_deductions, net_pay, fica, federal_tax, state_tax, medicare | The week's totals for this worker and labor class. Repeat them on each of that worker's day rows. If they differ between rows, the tool stops and names the row. |
other_deductions | Pairs like Union dues:38.37, split by semicolons. Blank if none. |
project_gross | Optional. Pay for this contract and project only. Leave it out and the tool uses the rates times the hours, plus lump_sum. |
lump_sum | Optional. Cash fringe or other cash paid on this line. |
apprentice_id, apprentice_pct | Needed when apprentice is Y: the apprentice ID and the wage percentage, like 60. |
health_rate, vacation_rate, apprenticeship_rate, pension_rate, other1_rate, other2_rate | Optional, for Plan Funds. The hourly rate paid into each kind of plan for this worker and labor class, in dollars and cents. Blank or 0 leaves it out. |
fringe_benefits | Optional. The fringe paid on this line for the week. |
health_program, vacation_program, apprenticeship_program, pension_program, other1_program, other2_program | Optional, one per kind of plan, same on every row: name | account ID | trustee | phone. Other 1 and Other 2 also need | class at the end. |
The sample uses Wisconsin's codes as they appear on the WisDOT spreadsheet template. Your state's codes may differ. This tool passes codes through as you typed them. It cannot tell you if a code is wrong.
What the check looks for
Problems are things the official AASHTO field definitions require or a state import rejects. Fix them before you upload.
- The pay period is seven days, and every day worked falls inside it.
- The payroll number is a whole number starting at 1.
- The contract ID has no dashes or spaces.
- Every day has straight time filled in, 0.00 when there was none. An overtime field that is there holds a number.
- Daily hours add up to the straight time, overtime and total hours for the week.
- Gross pay minus deductions equals net pay.
- Gender, when filled in, is the word Male or Female.
- A full SSN is 9 digits with no dashes. A middle initial is one character.
- Every employee has a change indicator.
- No field runs past its length limit.
- The fringe type is Cash, Plan Funds, or Plan Funds with Exceptions.
- An hourly worker has a straight time rate above 0 and an overtime rate, above 0 when there is overtime. A salaried worker has neither.
- Every labor line has its total hours, straight time hours, gross pay, deductions, net pay, and the gross pay for this project.
- An apprentice line has the apprentice ID and wage percentage. An OJT line has its wage percentage.
- Every worker has an ID: the full SSN, the last 4, or your own employee ID, whichever your state asks for.
- No hours below 0. Any fringe rate in the file is above 0.
- Each other deduction on a line has its own description. Each fringe benefit program has a valid type and all of its fields.
Warnings are things a state reviewer would ask about. Some states also reject them, depending on how they set up their system.
- Pay below the hours times the rates.
- Taxes and other deductions that do not add up to the total deductions.
- The same day twice on one labor line, or more than 24 hours in one day.
- An overtime rate of 0.00, or one lower than the straight time rate.
- A day with no overtime field. Wisconsin wants 0.00 there.
- A worker with no hours, or one name listed as two workers.
- Plan Funds with no fringe rates.
What it does not check
Your state's code lists, whether the project is on the contract, and the rates in the wage decision. The check compares the file's own numbers with each other, not with the prevailing wage. Convert writes fringe programs and rates, but not fringe benefit exceptions, so a Plan Funds with Exceptions payroll still needs those from your payroll system. Convert also takes one straight time and overtime rate pair per worker and labor class. A double time or shift rate needs its own labor line, which the CSV cannot hold yet, so convert stops and names the row.
The full Payroll XML schema. That check needs AASHTO's schema files, which this page does not carry. Run your file through the official online validator before you upload, or use the Python version of this tool, which checks the schema and the rules together.