GuidesAPI ReferenceChangelogAPI StatusAPI PolicyGusto Security
Guides

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

When 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 valueDefault included_in_overtime_payOvertime treatment
CommissiontrueDetermined automatically by FLSA rules
DiscretionaryBonusfalseDetermined automatically by FLSA rules
ServiceChargestrueDetermined automatically by FLSA rules
OnCallPaytrueDetermined automatically by FLSA rules
OtherYou 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

For 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:

ItemValue
Regular hours40
Overtime hours5
Double overtime hours2
Base hourly rate$25.00
Earning marked as included in overtime$100.00
  1. Total qualifying pay: (47 hrs × $25.00) + $100.00 included earning = $1,275.00
  2. Weighted hourly rate: $1,275.00 ÷ 47 hrs = $27.13/hr
  3. Overtime premium: $27.13 × 5 hrs × 0.5 = $67.82
  4. Double overtime premium: $27.13 × 2 hrs × 1 = $54.26
  5. 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

Because 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_day must 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

Employee 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 (alongside Regular 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 workweeks array, listing each workweek's date range within the pay period. This appears only on the unprocessed payroll responses from create, update, and prepare—it is not returned by the show endpoint. When per-workweek data can't be determined for a payroll, this resolves to an empty array.
  • A pay_adjustments array 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 a name and an amount. This appears only on the show payroll response for calculated or processed payrolls—not on create, update, or prepare. Because of this, workweeks and pay_adjustments never appear in the same response.
  • A breakdowns array on each fixed_compensations and hourly_compensations entry 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 no breakdowns array.
📘

Exempt employee breakdowns

Breakdowns 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 category on an update is rejected with "Category is not modifiable"
  • Sending a different included_in_overtime_pay on 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 Other earning type, included_in_overtime_pay is 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
ConditionMessage
category not one of the allowed valuesCategory is not valid
included_in_overtime_pay set with a non-Other categoryCategory must be 'Other' to set included_in_overtime_pay
category changed on updateCategory is not modifiable
included_in_overtime_pay changed on updateIncluded in overtime pay is not modifiable
Name reused with a different categoryName 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 reactivationincluded_in_overtime_pay does not match the existing earning type. Omit it or use the original value to reactivate.
Pay schedule workweek_start_day
ConditionMessage
Not a valid weekdayWorkweek start day is not a valid day of the week
Changed more than once per pay periodWorkweek 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.

RuleMessage on failure
Every entry has a start_date and end_datebreakdowns 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_datebreakdowns must have start_date on or before end_date
Each value (hours or amount) is present and non-negativebreakdowns must have a non-negative value
Entry values sum to the parent totalbreakdowns must sum to the reported total ({parent_total}); got {breakdown_total}
Entries cover the full pay period with no gaps or overlapsbreakdowns 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 doesall 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.0001 for hourly hours and $0.01 for fixed amount
  • 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.

Did this page help you?