Payroll by Workweek
This guide covers the API behavior your integration will encounter once Payroll by Workweek is enabled—new fields, allowed values, immutability rules, and validation errors.
Company earning types: category and included_in_overtime_pay
category and included_in_overtime_payWhen creating a custom earning type with POST earning_types, you specify a category that tells Gusto how that earning should be treated for overtime purposes under the Fair Labor Standards Act (FLSA).
Here's what the response from GET earning_types looks like with the new fields:
// GET /v1/companies/:company_id/earning_types
{
"custom": [
{
"name": "Sales Commission",
"uuid": "d614c0e2-395f-4251-bb2e-2255ea8385ac",
"active": true,
"category": "Commission",
"included_in_overtime_pay": true
},
{
"name": "Cash Tips",
"uuid": "08a9b469-02e9-4754-a9e5-5be6aa6b3cb4",
"active": true,
"category": "Other",
"included_in_overtime_pay": false
}
]
}Allowed category values
category accepts one of the following exact string values:
category value | Default included_in_overtime_pay | Overtime treatment |
|---|---|---|
Commission | true | Determined automatically by FLSA rules |
DiscretionaryBonus | false | Determined automatically by FLSA rules |
ServiceCharges | true | Determined automatically by FLSA rules |
OnCallPay | true | Determined automatically by FLSA rules |
Other | You set it (defaults to true) | You set included_in_overtime_pay explicitly |
For every category except Other, the default shown above is fixed and returned on reads—you can't override it. For Other, you set included_in_overtime_pay yourself at creation. If you omit it, it defaults to true.
These are the literal strings the API expects and returns (for example,
DiscretionaryBonus, not "Discretionary bonus"). Sending any other value is rejected—see Validation and error responses below.
Setting included_in_overtime_pay
included_in_overtime_payFor every category except Other, overtime treatment is fixed by FLSA rules and Gusto sets included_in_overtime_pay for you automatically—omit it from your request. Passing included_in_overtime_pay alongside a non-Other category is rejected—see Validation and error responses below.
If none of the standard categories fit your use case, use category Other and set included_in_overtime_pay explicitly yourself when calling POST earning_types:
// POST /v1/companies/:company_id/earning_types
{
"name": "Custom Incentive",
"category": "Other",
"included_in_overtime_pay": true
}category and included_in_overtime_pay are set once at creation with POST earning_types and cannot be changed afterward on an existing earning type. See Immutability and reactivation for the details and the exact rules around reactivating a previously deactivated earning type.
Example: an included overtime calculation
Here's how a single workweek's gross pay is calculated when a non-discretionary earning is included in overtime pay:
| Item | Value |
|---|---|
| Regular hours | 40 |
| Overtime hours | 5 |
| Double overtime hours | 2 |
| Base hourly rate | $25.00 |
| Earning marked as included in overtime | $100.00 |
- Total qualifying pay: (47 hrs × $25.00) + $100.00 included earning = $1,275.00
- Weighted hourly rate: $1,275.00 ÷ 47 hrs = $27.13/hr
- Overtime premium: $27.13 × 5 hrs × 0.5 = $67.82
- Double overtime premium: $27.13 × 2 hrs × 1 = $54.26
- Total gross pay for the week: $1,175.00 (straight-time) + $100.00 (included earning) + $67.82 (overtime premium) + $54.26 (double overtime premium) = $1,397.08
Pay schedules: workweek_start_day
workweek_start_dayBecause overtime is calculated per workweek, pay schedules now support a workweek_start_day field on both reads and writes. Here's what you'll see on GET pay_schedules:
// GET /v1/companies/:company_id/pay_schedules/:pay_schedule_id
{
"uuid": "3124ec28-18bd-43cd-808a-60e2200256a9",
"frequency": "Every other week",
"workweek_start_day": "Monday"
}We recommend that nonexempt employees on weekly or biweekly schedules have their workweek start day aligned with their pay period start date.
Change constraints
workweek_start_daymust be a valid day of the week. An unrecognized value is rejected with"is not a valid day of the week".- Once set, the workweek start day can only be changed once per pay period. Attempting to change it again before the window opens is rejected with
"can only be changed once per pay period. Please wait until {date} to change it again.", where{date}is the ISO-8601 date on which the next change becomes available.
Draft payroll responses include gross_pay
gross_payEmployee compensation entries in unprocessed (draft) payroll responses—from prepare, update, and create actions—now include an estimated gross_pay. Previously this was only available once a payroll was calculated or processed. You no longer need to calculate it yourself or make an extra request per employee just to get an estimate while building a payroll.
Here's an excerpt from a draft payroll response:
// excerpt from a draft payroll response
{
"employee_uuid": "85a6fd1c-d691-41b9-a4f0-ce95870fc673",
"gross_pay": "2500.12"
// ...
}Workweek breakdowns on the payroll read and write API
On payrolls where per-workweek data applies, payroll endpoints support reading and writing pay data broken out by workweek—so you can specify exactly how much was earned in each workweek within a pay period and see that same breakdown reflected back. Workweek breakdowns are what Gusto uses to calculate overtime pay, so when to collect them from your end users is driven by whether the earning type counts toward overtime.
Items in hourly_compensations do not report compensation_multiplier under Payroll by Workweek—overtime premiums are calculated per workweek instead.
When workweek breakdowns are required
Use included_in_overtime_pay on each earning type as the switch for whether your UI needs to collect a workweek breakdown. The rules:
- Regular hours need a workweek breakdown only when the payroll includes overtime (or double overtime). No OT, no breakdown required—regular hours drive the FQOT calculation
- Additional earnings with
included_in_overtime_pay: true—Bonus, Commission, Correction Payments, and any custom earning the company has flagged this way—need a workweek breakdown. These drive the overtime pay calculation. - Earnings with
included_in_overtime_pay: false—Cash Tips, Paycheck Tips, Reimbursements, and custom "not included" earnings—do not require a workweek breakdown. Your UI should not prompt end users to supply one. The API will accept breakdowns on these earnings if you send them, but they're informational—they don't affect gross pay or the overtime calculation. - Overtime hours are reported in their own entry in
hourly_compensations(alongsideRegular Hours), just like any other pay type
To determine which earnings require breakdowns for a given company, call GET earning_types and read included_in_overtime_pay on each returned earning type. This covers both standard and custom earnings and handles per-company configuration automatically.
What you'll see in responses
- A top-level
workweeksarray, listing each workweek's date range within the pay period. This appears only on the unprocessed payroll responses fromcreate,update, andprepare—it is not returned by theshowendpoint. When per-workweek data can't be determined for a payroll, this resolves to an empty array. - A
pay_adjustmentsarray on each employee's compensation block, reflecting overtime premium corrections carried over from a previous workweek—for example, when earnings from an earlier pay period are adjusted retroactively, or an off-cycle payroll covers the workweeks and includes additional pay. Each entry has anameand anamount. This appears only on theshowpayroll response for calculated or processed payrolls—not oncreate,update, orprepare. Because of this,workweeksandpay_adjustmentsnever appear in the same response. - A
breakdownsarray on eachfixed_compensationsandhourly_compensationsentry that has per-workweek detail to report. In the example below,Bonus(included_in_overtime_pay: true) is broken out across both workweeks—this is the kind of breakdown your UI should be collecting. An earning entered as a single pay-period total has no per-workweek detail and is returned as a single amount with nobreakdownsarray.
Exempt employee breakdownsBreakdowns can be written for—and may appear on—exempt employees' compensation entries. Because exempt employees are not eligible for overtime, any breakdowns on their earnings are informational only and do not affect their pay.
Here's what an unprocessed payroll response from PUT payrolls/prepare looks like—note the top-level workweeks array and the absence of pay_adjustments:
// PUT /v1/companies/:company_id/payrolls/:payroll_id/prepare
{
"payroll_uuid": "11bd39d8-af3f-4020-9ea4-067ae62ad8bd",
"pay_period": {
"start_date": "2026-10-01",
"end_date": "2026-10-14"
},
"workweeks": [
{ "start_date": "2026-10-01", "end_date": "2026-10-07" },
{ "start_date": "2026-10-08", "end_date": "2026-10-14" }
],
"employee_compensations": [
{
"employee_uuid": "85a6fd1c-d691-41b9-a4f0-ce95870fc673",
"gross_pay": "2500.12",
"fixed_compensations": [
{
"name": "Commission",
"amount": "500.00",
"breakdowns": [
{ "start_date": "2026-10-01", "end_date": "2026-10-07", "amount": "300.00" },
{ "start_date": "2026-10-08", "end_date": "2026-10-14", "amount": "200.00" }
]
},
{
"name": "Bonus",
"amount": "100.00",
"breakdowns": [
{ "start_date": "2026-10-01", "end_date": "2026-10-07", "amount": "60.00" },
{ "start_date": "2026-10-08", "end_date": "2026-10-14", "amount": "40.00" }
]
}
],
"hourly_compensations": [
{
"name": "Regular Hours",
"flsa_status": "Nonexempt",
"hours": "40.000",
"breakdowns": [
{ "start_date": "2026-10-01", "end_date": "2026-10-07", "hours": "20.000" },
{ "start_date": "2026-10-08", "end_date": "2026-10-14", "hours": "20.000" }
]
},
{
"name": "Overtime",
"flsa_status": "Nonexempt",
"hours": "5.000",
"breakdowns": [
{ "start_date": "2026-10-01", "end_date": "2026-10-07", "hours": "3.000" },
{ "start_date": "2026-10-08", "end_date": "2026-10-14", "hours": "2.000" }
]
}
]
}
]
}By contrast, GET a payroll on a processed payroll omits the top-level workweeks array and instead includes pay_adjustments on each employee. Per-workweek breakdowns still appear on compensation entries:
// GET /v1/companies/:company_id/payrolls/:payroll_id
{
"payroll_uuid": "11bd39d8-af3f-4020-9ea4-067ae62ad8bd",
"pay_period": {
"start_date": "2026-10-01",
"end_date": "2026-10-14"
},
"employee_compensations": [
{
"employee_uuid": "85a6fd1c-d691-41b9-a4f0-ce95870fc673",
"gross_pay": "2500.12",
"pay_adjustments": [
{ "name": "Overtime", "amount": "13.65" }
],
"fixed_compensations": [
{
"name": "Commission",
"amount": "500.00",
"breakdowns": [
{ "start_date": "2026-10-01", "end_date": "2026-10-07", "amount": "300.00" },
{ "start_date": "2026-10-08", "end_date": "2026-10-14", "amount": "200.00" }
]
},
{
"name": "Bonus",
"amount": "100.00",
"breakdowns": [
{ "start_date": "2026-10-01", "end_date": "2026-10-07", "amount": "60.00" },
{ "start_date": "2026-10-08", "end_date": "2026-10-14", "amount": "40.00" }
]
}
]
}
]
}Writing workweek breakdowns
When you update a payroll with PUT payrolls, you can include a breakdowns array on each fixed_compensations or hourly_compensations entry to specify how much of that earning applies to each workweek:
// PUT /v1/companies/:company_id/payrolls/:payroll_id
{
"employee_compensations": [
{
"employee_uuid": "85a6fd1c-d691-41b9-a4f0-ce95870fc673",
"fixed_compensations": [
{
"name": "Commission",
"amount": "500.00",
"breakdowns": [
{ "start_date": "2026-10-01", "end_date": "2026-10-07", "amount": "300.00" },
{ "start_date": "2026-10-08", "end_date": "2026-10-14", "amount": "200.00" }
]
}
],
"hourly_compensations": [
{
"name": "Regular Hours",
"flsa_status": "Nonexempt",
"hours": "40.000",
"breakdowns": [
{ "start_date": "2026-10-01", "end_date": "2026-10-07", "hours": "20.000" },
{ "start_date": "2026-10-08", "end_date": "2026-10-14", "hours": "20.000" }
]
},
{
"name": "Overtime",
"flsa_status": "Nonexempt",
"hours": "5.000",
"breakdowns": [
{ "start_date": "2026-10-01", "end_date": "2026-10-07", "hours": "3.000" },
{ "start_date": "2026-10-08", "end_date": "2026-10-14", "hours": "2.000" }
]
}
]
}
]
}For the most accurate overtime calculation, we recommend providing explicit breakdowns wherever you have the underlying data. Breakdowns written on a fixed compensation that is not included in overtime pay (included_in_overtime_pay: false) do not affect gross pay—they are recorded for reference only. The exact rules a breakdowns array must satisfy are covered in Validation and error responses below.
Immutability and reactivation
category and included_in_overtime_pay are set at creation and cannot be changed on an existing earning type.
- Sending a different
categoryon an update is rejected with"Category is not modifiable" - Sending a different
included_in_overtime_payon an update is rejected with"Included in overtime pay is not modifiable"
Reactivating a deactivated earning type
Earning types are matched on name and category. To reactivate a previously deactivated earning type, submit a create request by calling POST earning_types with the same name and the same category it originally had:
- If you reuse a name but pair it with a different category, the request is rejected with
"Name already in use on an earning type with a different category. To reactivate, use the same name and category." - For a category
Otherearning type,included_in_overtime_payis immutable, so it must match the original value on reactivation. You can either omit it (Gusto reuses the stored value) or pass the original value. Passing a different value is rejected with"included_in_overtime_pay does not match the existing earning type. Omit it or use the original value to reactivate.".
Validation and error responses
Company earning types
| Condition | Message |
|---|---|
category not one of the allowed values | Category is not valid |
included_in_overtime_pay set with a non-Other category | Category must be 'Other' to set included_in_overtime_pay |
category changed on update | Category is not modifiable |
included_in_overtime_pay changed on update | Included in overtime pay is not modifiable |
| Name reused with a different category | Name already in use on an earning type with a different category. To reactivate, use the same name and category. |
included_in_overtime_pay mismatch on reactivation | included_in_overtime_pay does not match the existing earning type. Omit it or use the original value to reactivate. |
Pay schedule workweek_start_day
| Condition | Message |
|---|---|
| Not a valid weekday | Workweek start day is not a valid day of the week |
| Changed more than once per pay period | Workweek start day can only be changed once per pay period. Please wait until {date} to change it again. |
Compensation breakdowns
Each breakdowns array you submit must satisfy all of the following. Errors are reported against employee_compensations.hourly_compensations.breakdowns or employee_compensations.fixed_compensations.breakdowns.
| Rule | Message on failure |
|---|---|
Every entry has a start_date and end_date | breakdowns must include a start_date and end_date for every entry |
| Dates are valid ISO 8601 (YYYY-MM-DD) | breakdowns must use valid ISO 8601 dates (YYYY-MM-DD) for start_date and end_date |
start_date is on or before end_date | breakdowns must have start_date on or before end_date |
Each value (hours or amount) is present and non-negative | breakdowns must have a non-negative value |
| Entry values sum to the parent total | breakdowns must sum to the reported total ({parent_total}); got {breakdown_total} |
| Entries cover the full pay period with no gaps or overlaps | breakdowns must cover the payroll's pay period ({period_start} - {period_end}) exactly, with no gaps or overlaps |
| All hourly earnings for the same job include breakdowns if any one does | all hourly_compensations for the same job must include breakdowns when any one does |
Two details worth calling out:
- Sum tolerance. The sum check allows a small rounding tolerance:
0.0001for hourlyhoursand$0.01for fixedamount - Tiling, not calendar alignment. The date ranges you submit must tile the pay period—chain contiguously from the pay period start to the pay period end with no gaps and no overlaps. They do not have to correspond one-to-one to the calendar workweeks. A single range spanning the entire pay period is valid, as is any contiguous partition of it.
Updated 9 minutes ago