Gross-up Payments

Pay an employee an exact net bonus or commission by having Check calculate the gross.

🚧

Early preview

Gross-up is in early preview and will be released to all partners by November 19, 2026. Until then the endpoint is enabled per provider. Contact your Check representative to turn it on in your sandbox ahead of the release.

Employers often promise an employee a specific take-home amount: a $1,000 holiday bonus, a $5,000 signing bonus, a commission that must land as a round number. Check calculates gross-to-net, so without help an employer has to guess a gross amount, preview the payroll, compare the net, and iterate.

Gross-up solves for the gross. You tell Check the net amount the employee should receive, and Check calculates the gross earning that produces that net after the employee's taxes. The calculated gross is saved on the payroll item like any other earning, and the rest of the payroll flow is unchanged.

The example below pays an employee a bonus that nets exactly $1,000. The illustrative gross is $1,500; the real gross depends on the employee's tax situation.

What this release supports

Gross-up is limited to the simplest and most common case in its first release. Each limit is validated when you call the endpoint, so an unsupported request fails before anything is saved.

  • Off-cycle payrolls only, with apply_benefits and apply_post_tax_deductions both false. Benefit deductions and post-tax deductions are not part of the calculation.
  • Exactly one earning on the payroll item being grossed up, of type bonus or commission, or an earning code whose type is bonus or commission. The action replaces any earnings already saved on the item.
  • Tax settings come from the payroll item. The item's saved supplemental_tax_calc_method and tax_overrides are used in the calculation. Set them with an ordinary payroll item update before you gross up; the gross-up request does not accept them. See Overriding withholding taxes.
  • Reimbursements are paid on top of the target net. A $1,000 net bonus plus a $100 reimbursement produces $1,100 in net pay for the item.
  • Other payroll items on the same payroll can use ordinary fixed-gross earnings. Only the items you gross up are affected.
  • API only. Entering a gross-up in the Run Payroll Component is outside this release.

How it works

Gross-up is a single synchronous action on an existing payroll item:

POST /payroll_items/{payroll_item}/gross_up

You send the intended earning with its workplace, its type or earning code, and a net_amount. Check calculates the gross, replaces the item's earnings with that one earning and its calculated amount, and returns the ordinary payroll item. A successful response means the calculation is complete and saved. There is no new status to poll and no new field on the payroll item.

The saved gross is the smallest amount, to the cent, whose net wages are at least net_amount. Because each tax is withheld in whole cents, the employee's net may land a few cents above the target; it is never below it.

Check does not store net_amount. The saved earning is a fixed gross earning from that point on, so keep the target in your own application if you want to display it or recalculate from it later.

The end-to-end flow is:

  1. Create an off-cycle payroll.
  2. Create a payroll item for each employee receiving a gross-up.
  3. Call the gross-up action once per payroll item.
  4. Preview the payroll and review the results.
  5. Approve the payroll.

1. Create an off-cycle payroll

Create the payroll with type set to off_cycle and with benefits and post-tax deductions disabled. See Off-cycle Payrolls for the other available options.

curl -X POST 'https://sandbox.checkhq.com/payrolls' \
  -H 'Content-Type: application/json' \
  -H 'X-Idempotency-Key: 6f1c2b8e-3d6a-4d1e-9a7b-1f0c5d2e8a01' \
  -d '{
    "company": "com_1234567890abcdef",
    "period_start": "2026-11-01",
    "period_end": "2026-11-07",
    "payday": "2026-11-13",
    "type": "off_cycle",
    "off_cycle_options": {
      "apply_benefits": false,
      "apply_post_tax_deductions": false
    }
  }'

Use the returned payroll ID in the following requests. The examples below use pay_1234567890abcdef.

Every write request in this guide sends an X-Idempotency-Key header. Use a new key for each intended operation and reuse the key only when retrying that same operation after a timeout or lost response. See Idempotent requests.

2. Create the payroll item

Create a payroll item for the employee with the earning you intend to gross up. The item needs an earning with an amount to be created, so use the target net as a placeholder; the gross-up action replaces it with the calculated gross. Include any reimbursements now; they are kept by the gross-up action and paid in addition to the target net.

curl -X POST 'https://sandbox.checkhq.com/payroll_items' \
  -H 'Content-Type: application/json' \
  -H 'X-Idempotency-Key: 2c4a9d71-5b0e-4f3c-8e2d-7a6b1c9d0e02' \
  -d '{
    "payroll": "pay_1234567890abcdef",
    "employee": "emp_1234567890abcdef",
    "payment_method": "direct_deposit",
    "earnings": [
      {
        "type": "bonus",
        "workplace": "wrk_1234567890abcdef",
        "amount": "1000.00"
      }
    ],
    "reimbursements": [
      {
        "amount": "100.00",
        "description": "Travel expenses"
      }
    ]
  }'
📘

The placeholder is an ordinary earning until you gross it up

Nothing marks the item as pending a gross-up. If the gross-up call never happens, the payroll pays the placeholder amount as the gross. Make the gross-up call part of the same workflow as creating the item.

If the employee already has a payroll item on this payroll, you can gross it up directly. The action replaces whatever earnings the item currently has.

If the employer wants to block or fix income tax withholding on this bonus, or change the supplemental tax calculation method, set tax_overrides or supplemental_tax_calc_method on the item now with an ordinary payroll item update. The gross-up calculation reads them from the item.

3. Gross up the payroll item

Send the complete intended earning. Supply workplace, either type or earning_code, and net_amount. Do not send amount or earning_rate; Check calculates the amount. The request accepts only earnings; any other payroll item field is rejected.

curl -X POST 'https://sandbox.checkhq.com/payroll_items/itm_1234567890abcdef/gross_up' \
  -H 'Content-Type: application/json' \
  -H 'X-Idempotency-Key: 9b7e5c23-1a4f-4c8d-b2e6-3d0f7a1c5e03' \
  -d '{
    "earnings": [
      {
        "type": "bonus",
        "workplace": "wrk_1234567890abcdef",
        "net_amount": "1000.00"
      }
    ]
  }'

The response is the ordinary payroll item object. The earning's amount is the calculated gross, and the payroll's current preview has been invalidated:

{
  "id": "itm_1234567890abcdef",
  "payroll": "pay_1234567890abcdef",
  "employee": "emp_1234567890abcdef",
  "status": "draft",
  "payment_method": "direct_deposit",
  "earnings": [
    {
      "amount": "1500.00",
      "hours": null,
      "type": "bonus",
      "workplace": "wrk_1234567890abcdef",
      "earning_code": null,
      "description": null,
      "metadata": {}
    }
  ],
  "reimbursements": [
    {
      "amount": "100.00",
      "description": "Travel expenses",
      "code": null,
      "metadata": {}
    }
  ],
  "net_pay": null,
  "tax_overrides": null,
  ...
}

Refresh your cached copy of the item from this response before making further edits. net_pay is populated by the payroll preview in the next step.

Inputs to the calculation

InputWhere it comes from
Target net wagesearnings[0].net_amount in the request. This is the net for the earning only; reimbursements are added on top.
Workplace and earning typeearnings[0].workplace and either earnings[0].type or earnings[0].earning_code in the request.
Item tax settingsThe item's saved supplemental_tax_calc_method and tax_overrides. Set them with a payroll item update before calling the action.
Payroll setupThe parent payroll's payday, pay period, pay frequency, and off-cycle options.
Employee and company tax contextThe employee's residence and workplace, tax elections and exemptions, the company's tax setup, and year-to-date wages.
ReimbursementsThe item's saved reimbursements, paid in addition to the target net.

Check saves the earning with its calculated gross and invalidates the payroll's preview. Other edits to the same item wait until the gross-up request completes, so the saved gross always reflects the inputs it was calculated from. A validation or calculation failure leaves the item exactly as it was.

Grossing up several employees

Call the action once per payroll item, each with its own idempotency key. Each call commits independently: one failure does not undo another item's successful calculation. Keep the number of concurrent requests small and respect 429 responses as described in Rate limiting. Wait for every intended item to succeed before previewing the payroll.

4. Preview the payroll and review

After the gross-up calls succeed, preview the payroll as usual. Both synchronous and asynchronous preview work; asynchronous preview is recommended. See Payroll Preview and Approval.

curl -X GET 'https://sandbox.checkhq.com/payrolls/pay_1234567890abcdef/preview?async=true'

Save the returned preview.started_at, then poll the payroll, or listen for the payroll updated webhook event, until preview.status is succeeded:

curl -X GET 'https://sandbox.checkhq.com/payrolls/pay_1234567890abcdef'
{
  "id": "pay_1234567890abcdef",
  "status": "draft",
  "type": "off_cycle",
  "preview": {
    "status": "succeeded",
    "started_at": "2026-11-09T14:30:00.000000Z"
  },
  ...
}

Show the employer the previewed gross earnings, employee taxes, net pay, employer taxes, and the payroll's funding requirement. For each grossed-up item, net_pay equals the target you sent plus the item's reimbursements, possibly a few cents higher because of rounding. In this example, a $1,000 target plus a $100 reimbursement produces net_pay of 1100.00.

If the preview fails, resolve the reported errors. If preview.started_at changes, another preview replaced the one you requested; fetch and review the new result before approving.

5. Approve the payroll

Approve the payroll, passing the preview.started_at of the preview the employer reviewed so a stale preview cannot be approved by mistake.

curl -X POST 'https://sandbox.checkhq.com/payrolls/pay_1234567890abcdef/approve' \
  -H 'Content-Type: application/json' \
  -H 'X-Idempotency-Key: 4e8d2f60-7c1b-4a9e-95d3-0b6f8c2a1d04' \
  -d '{
    "preview_started_at": "2026-11-09T14:30:00.000000Z"
  }'

Approval moves the payroll to pending. The saved gross earnings are final. See Approve a payroll.

Changing the target or other inputs

Because Check saves the calculated gross as an ordinary earning, the gross-up action is the way to change anything about a grossed-up earning.

To change the net target, call the action again with the complete earning and the new net_amount, using a new idempotency key:

{
  "earnings": [
    {
      "type": "bonus",
      "workplace": "wrk_1234567890abcdef",
      "net_amount": "1200.00"
    }
  ]
}

To change the workplace or earning type, include the new values in the same request alongside the net target. Check recalculates and saves everything together.

To change the item's tax overrides or supplemental tax calculation method, update the payroll item first, then call the action again with the net target so the gross reflects the new settings.

When the employee, company, or payroll changes (for example, a new tax election or a different payday), update that resource first, then call the action again with the original net_amount if the employer still wants that take-home amount. Running an ordinary preview instead keeps the saved gross, and the employee's net pay may change.

Repeating a request with net_amount: "1000.00" always targets $1,000 net. It never treats the previously calculated gross as a new target.

If you reopen an eligible approved payroll, decide which number the employer wants to preserve, the gross or the net, before recalculating.

Retries and failures

SituationWhat to do
The action returns a validation or calculation errorThe item is unchanged, including its earnings and tax settings. Fix the reported issue and send a new request with a new idempotency key.
The request times out or the response is lostThe calculation may already have completed and been saved. Retry the same request with the same idempotency key. Do not assume a timeout means the old earnings are still saved.
The payroll was approved before the requestThe action returns payroll_already_approved and saves nothing. Reopen the payroll, or create a new one, before retrying.
Some items succeed and others failSuccessful items stay saved. Resolve the failed items, then preview.
The payroll preview fails afterwardSuccessful gross-ups remain saved. Fix the preview errors; recalculate affected items only if their inputs changed.

Idempotency keys expire after 24 hours. After that window, fetch the payroll item to see what is saved before sending another request.

Errors

Validation failures return the standard validation_error envelope with one entry per problem in input_errors:

{
  "error": {
    "type": "validation_error",
    "message": "Please correct the required fields and try again.",
    "input_errors": [
      {
        "field": "type",
        "field_path": ["earnings", 0, "type"],
        "message": "Gross-up earnings must be of type bonus or commission."
      }
    ]
  }
}
ConditionField pathMessage
Payroll is not an off-cycle payrollpayrollOnly items on off-cycle payrolls can be grossed up.
Payroll has apply_benefits enabledpayrollGross-up requires the payroll's off_cycle_options.apply_benefits to be false.
Payroll has apply_post_tax_deductions enabledpayrollGross-up requires the payroll's off_cycle_options.apply_post_tax_deductions to be false.
earnings is missing, empty, or has more than one entryearningsProvide exactly one earning.
Earning type is not bonus or commissionearnings[0].typeGross-up supports bonus and commission earnings.
Earning code is not of type bonus or commissionearnings[0].earning_codeGross-up supports bonus and commission earnings.
net_amount is missing, zero, or negativeearnings[0].net_amountStandard field validation, for example "Ensure this value is greater than or equal to 0.01."
amount or earning_rate is sentearnings[0].amount or earnings[0].earning_rateOmit this field. Gross-up calculates the amount.
Any field other than earnings is sent, including tax_overrides and supplemental_tax_calc_methodThat fieldThis field is not supported by gross-up.

Branch on type and field_path, not on message text.

Other outcomes:

  • An approved payroll returns 400 with type set to payroll_already_approved, and a payroll managed by a correction returns payroll_managed_by_correction, as on any other payroll item write.
  • Missing employee or company tax setup that would also block a preview returns the same errors a preview does, such as entity_missing_required_value.
  • If gross-up is not enabled for your provider, the action returns 404, the same response as for an item that does not exist.
  • If Check cannot produce a gross whose net reaches net_amount, the action returns a 5xx error and the item is unchanged. This should not happen in practice; retry with a new idempotency key, and contact Check if it recurs.

Updating an existing integration

  1. Add the item action. Where your product lets an employer enter a bonus, add a "gross up" option that collects the net amount and calls POST /payroll_items/{payroll_item}/gross_up with the complete earning. Treat a 200 response as confirmation that the gross is calculated and saved. Set any tax overrides on the item before the call, not in it.
  2. Keep the net target yourself. Check does not return net_amount. Store it with your own record of the payroll item if you want to display it or recalculate from it after other edits.
  3. Handle independent outcomes. Send one request per item with bounded concurrency, refresh each item from its response, and resolve individual failures before moving on to preview.
  4. Preview once after all conversions. Reuse your existing preview, review, and approval flow. Nothing about it changes.
  5. Keep the ordinary earning shape. amount stays a non-null numeric string. No new field, status, or polling resource is introduced on the payroll item.

Related references: Gross up a payroll item, Create a payroll item, Update a payroll item, Preview a payroll, Approve a payroll, and Idempotent requests.


Did this page help you?