These docs are for v2023-09-01. Click to read the latest docs for v2024-04-01.

Complete a Regular Payroll

Your embedded payroll product allows the Company to pay the Employees the right amount at the right cadence, while accounting for tax implications.

We automatically pre-generate scheduled regular payrolls based on the companyโ€™s pay schedule and corresponding pay periods. Pay periods are the foundation of payroll. Compensation, time & attendance, taxes, and expense reports all rely on when they happened.

Before using this guide you need a fully onboarded Company and Employee.

1. Retrieve Upcoming Payroll

Upcoming regular payrolls can be retrieved using the GET companies/{company_uuid}/payrolls?processing_statuses=unprocessed endpoint. The next upcoming payroll will be the earliest unprocessed payroll. By default, the payrolls#index endpoint will return only processed payrolls so make sure to add the processing_statuses=unprocessed query param. You can use the optional query parameters start_date & end_date to narrow or expand the response.

The example cURL below is filtering to show unprocessed payrolls, including off cycle, the dates for payroll is between 2021-02-01 and 2021-03-01. To see all query values review our API Reference.

curl --request GET \
     --url 'https://api.gusto-demo.com/v1/companies/company_id/payrolls?processing_statuses=unprocessed&payroll_types=regular,off_cycle&start_date=2021-02-01&end_date=2021-03-31' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/companies/company_id/payrolls?processed=false&include_off_cycle=true&include=&include=deductions&start_date=2021-02-01&end_date=2021-03-31';
const options = {
  method: 'GET',
  headers: {accept: 'application/json', authorization: 'Bearer <<COMPANY_API_TOKEN>>'}
};

fetch(url, options)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error('error:' + err));

This query will return an JSON array of payroll objects:

[
  {
    "payroll_deadline": "2021-02-18T20:00:00Z",
    "check_date": "2021-02-22",
    "processed": false,
    "processed_date": null,
    "calculated_at": null,
    "payroll_uuid": "b50e611d-8f3d-4f24-b001-46675f7b5777",
    "company_uuid": "6bf7807c-a5a0-4f4d-b2e7-3fbb4b2299fb",
    "pay_period": {
      "start_date": "2021-02-01",
      "end_date": "2021-02-15",
      "pay_schedule_uuid": "00ebc4a4-ec88-4435-8f45-c505bb63e501"
    }
  }
]

2. Prepare & Update Payroll (Optional)

If there is information about the payroll that needs to be updated, start by using the PUT companies/{company_uuid}/payrolls/{payroll_uuid}/prepare endpoint. This endpoint will return the version and the employee_compensations data you need to update a payroll.

You can update Regular Hours, Overtime, and Double overtime for Salaried Nonexempt employees, but only Regular Hours can be updated for Exempt employees since they are not eligible for overtime.

curl --request PUT \
     --url https://api.gusto-demo.com/v1/companies/{company_uuid}/payrolls/{payroll_uuid}/prepare \
     --header 'accept: application/json' \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>' \
     --header 'content-type: application/json'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/companies/{company_uuid}/payrolls/2021-02-01/2021-02-15';
const options = {
  method: 'PUT',
  headers: {
    accept: 'application/json',
    'content-type': 'application/json',
    authorization: 'Bearer <<COMPANY_API_TOKEN>>'
  },
  body: JSON.stringify({
    version: '19289df18e6e20f797de4a585ea5a91535c7ddf7',
    employee_compensations: [
      {
        employee_id: 1123581321345589,
        employee_uuid: '187412e1-3dbe-491a-bb2f-2f40323a7067',
        excluded: false,
        payment_method: 'Direct Deposit',
        fixed_compensations: [
          {
            name: 'Bonus',
            amount: '200.00',
            job_id: 1,
            job_uuid: '94e0d15e-9ed2-4077-98f6-64554f242ba5'
          }
        ],
        hourly_compensations: [
          {
            name: 'Regular Hours',
            hours: '30.000',
            job_id: 1,
            job_uuid: '91bc3b43-ded0-4ee7-98fe-215499e909ba'
          },
          {
            name: 'Double overtime',
            hours: '20.000',
            job_id: 1,
            job_uuid: 'bd378298-3e0c-4145-904a-baadf8a91fa3'
          },
          {
            name: 'Overtime',
            hours: '10.000',
            job_id: 2,
            job_uuid: '9d3760f0-d1f9-4700-8817-0fe2dce5cf23'
          }
        ],
        paid_time_off: [
          {name: 'Vacation Hours', hours: '25.000'},
          {name: 'Sick Hours', hours: '10.000'},
          {name: 'Holiday Hours', hours: '8.000'}
        ]
      }
    ]
  })
};

fetch(url, options)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error('error:' + err));

Here is a sample response from the prepare endpoint:

{
  "version": "19289df18e6e20f797de4a585ea5a91535c7ddf7",
  "payroll_deadline": "2022-02-18T22:00:00Z",
  "check_date": "2021-02-22",
  "processed": false,
  "processed_date": null,
  "calculated_at": null,
  "payroll_uuid": "b50e611d-8f3d-4f24-b001-46675f7b5777",
  "company_uuid": "6bf7807c-a5a0-4f4d-b2e7-3fbb4b2299fb",
  "pay_period": {
    "start_date": "2021-02-01",
    "end_date": "2021-02-15",
    "pay_schedule_uuid": "00ebc4a4-ec88-4435-8f45-c505bb63e501"
  },
  "payroll_status_meta": {
    "cancellable": false,
    "expected_check_date": "2022-02-22",
    "initial_check_date": "2022-02-22",
    "expected_debit_time": "2022-02-18T22:00:00Z",
    "payroll_late": false,
    "initial_debit_cutoff_time": "2022-02-18T22:00:00Z"
  },
  "employee_compensations": [
    {
      "employee_uuid": "187412e1-3dbe-491a-bb2f-2f40323a7067",
      "excluded": false,
      "payment_method": "Direct Deposit",
      "fixed_compensations": [
        {
          "name": "Bonus",
          "amount": "100.00",
          "job_uuid": "94e0d15e-9ed2-4077-98f6-64554f242ba5"
        },
        {
          "name": "Reimbursement",
          "amount": "100.00",
          "job_uuid": "91bc3b43-ded0-4ee7-98fe-215499e909ba"
        }
      ],
      "hourly_compensations": [
        {
          "name": "Regular Hours",
          "hours": "40.000",
          "job_uuid": "bd378298-3e0c-4145-904a-baadf8a91fa3",
          "compensation_multiplier": 1,
          "flsa_status": "Nonexempt"
        },
        {
          "name": "Overtime",
          "hours": "15.000",
          "job_uuid": "9d3760f0-d1f9-4700-8817-0fe2dce5cf23",
          "compensation_multiplier": 1.5,
          "flsa_status": "Nonexempt"
        },
        {
          "name": "Double overtime",
          "hours": "0.000",
          "job_uuid": "b5eef9a9-4a87-4649-a80d-14878c05f44e",
          "compensation_multiplier": 2,
          "flsa_status": "Nonexempt"
        },
        {
          "name": "Regular Hours",
          "hours": "40.000",
          "job_uuid": "332bd171-9efc-432b-abbb-a75c9dba706a",
          "compensation_multiplier": 1,
          "flsa_status": "Nonexempt"
        },
        {
          "name": "Overtime",
          "hours": "5.000",
          "job_uuid": "ca9b3dc1-57ac-4736-901a-9b1c9634b9d5",
          "compensation_multiplier": 1.5,
          "flsa_status": "Nonexempt"
        },
        {
          "name": "Double overtime",
          "hours": "0.000",
          "job_uuid": "1bad01e2-140c-49ed-9542-2388ce4a19b3",
          "compensation_multiplier": 2,
          "flsa_status": "Nonexempt"
        }
      ],
      "paid_time_off": [
        {
          "name": "Vacation Hours",
          "hours": "20.000"
        },
        {
          "name": "Sick Hours",
          "hours": "0.000"
        },
        {
          "name": "Holiday Hours",
          "hours": "0.000"
        }
      ],
      "benefits": [
        {
          "name": "Group Term Life",
          "employee_deduction": "100.00",
          "company_contribution": "50.00",
          "imputed": true
        },
        {
          "name": "401K",
          "employee_deduction": "100.00",
          "company_contribution": "50.00",
          "imputed": false
        }
      ],
      "deductions": [
        {
          "name": "Child Support",
          "amount": "80.00"
        }
      ],
      "taxes": [
        {
          "name": "Federal Income Tax",
          "employer": false,
          "amount": "646.69"
        },
        {
          "name": "Social Security",
          "employer": true,
          "amount": "191.25"
        }
      ]
    }
  ]
}

Next you can update the payroll with the new employee_compensation data with the PUT companies/{company_uuid}/payrolls/{payroll_uuid} endpoint, passing in the version and updated employee_compensations params from the prepare endpoint.

curl --request PUT \
     --url https://api.gusto-demo.com/v1/companies/{company_uuid}/payrolls/{payroll_uuid} \
     --header 'accept: application/json' \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>' \
     --header 'content-type: application/json' \
     --data '
{
     "version": "19289df18e6e20f797de4a585ea5a91535c7ddf7",
     "employee_compensations": [
          {
               "employee_id": 1123581321345589,
               "employee_uuid": "187412e1-3dbe-491a-bb2f-2f40323a7067",
               "excluded": false,
               "payment_method": "Direct Deposit",
               "fixed_compensations": [
                    {
                         "name": "Bonus",
                         "amount": "200.00",
                         "job_id": 1,
                         "job_uuid": "94e0d15e-9ed2-4077-98f6-64554f242ba5"
                    }
               ],
               "hourly_compensations": [
                    {
                         "name": "Regular Hours",
                         "hours": "30.000",
                         "job_id": 1,
                         "job_uuid": "91bc3b43-ded0-4ee7-98fe-215499e909ba"
                    },
                    {
                         "name": "Double overtime",
                         "hours": "20.000",
                         "job_id": 1,
                         "job_uuid": "bd378298-3e0c-4145-904a-baadf8a91fa3"
                    },
                    {
                         "name": "Overtime",
                         "hours": "10.000",
                         "job_id": 2,
                         "job_uuid": "9d3760f0-d1f9-4700-8817-0fe2dce5cf23"
                    }
               ],
               "paid_time_off": [
                    {
                         "name": "Vacation Hours",
                         "hours": "25.000"
                    },
                    {
                         "name": "Sick Hours",
                         "hours": "10.000"
                    },
                    {
                         "name": "Holiday Hours",
                         "hours": "8.000"
                    }
               ]
          }
     ]
}
'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/companies/{company_uuid}/payrolls/2021-02-01/2021-02-15';
const options = {
  method: 'PUT',
  headers: {
    accept: 'application/json',
    'content-type': 'application/json',
    authorization: 'Bearer <<COMPANY_API_TOKEN>>'
  },
  body: JSON.stringify({
    version: '19289df18e6e20f797de4a585ea5a91535c7ddf7',
    employee_compensations: [
      {
        employee_id: 1123581321345589,
        employee_uuid: '187412e1-3dbe-491a-bb2f-2f40323a7067',
        excluded: false,
        payment_method: 'Direct Deposit',
        fixed_compensations: [
          {
            name: 'Bonus',
            amount: '200.00',
            job_id: 1,
            job_uuid: '94e0d15e-9ed2-4077-98f6-64554f242ba5'
          }
        ],
        hourly_compensations: [
          {
            name: 'Regular Hours',
            hours: '30.000',
            job_id: 1,
            job_uuid: '91bc3b43-ded0-4ee7-98fe-215499e909ba'
          },
          {
            name: 'Double overtime',
            hours: '20.000',
            job_id: 1,
            job_uuid: 'bd378298-3e0c-4145-904a-baadf8a91fa3'
          },
          {
            name: 'Overtime',
            hours: '10.000',
            job_id: 2,
            job_uuid: '9d3760f0-d1f9-4700-8817-0fe2dce5cf23'
          }
        ],
        paid_time_off: [
          {name: 'Vacation Hours', hours: '25.000'},
          {name: 'Sick Hours', hours: '10.000'},
          {name: 'Holiday Hours', hours: '8.000'}
        ]
      }
    ]
  })
};

fetch(url, options)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error('error:' + err));

The update endpoint will return the same response data as prepare, with the data reflecting your changes.

3. Calculate the Payroll

Once a payroll is updated, use the PUT companies/{company_id}/payrolls/{payroll_id}/calculate endpoint to calculate the taxes, benefits, and deductions for the unprocessed payroll. The calculated payroll details provide a preview of the actual values that will be used when the payroll is run. Any benefits or deductions - mandatory or voluntary - that are set up for the employee at the time payroll is calculated will automatically be factored in.

This calculation is asynchronous and a successful request responds with a 202 HTTP status. To view the details of the calculated payroll, poll the GET /v1/companies/{company_uuid}/payrolls/{payroll_uuid} endpoint with theinclude=taxes,benefits,deductions param, until the payroll returns with the calculated_at field populated with a timestamp. The payroll will include any submission_blockers and will also return a totals attribute once it is calculated.

๐Ÿ“˜

Due to the complex nature of the calculations, we recommend using our Webhooks to await the payroll.calculated response before trying to GET the calculations.

curl --request PUT \
     --url https://api.gusto-demo.com/v1/companies/{company_uuid}/payrolls/{payroll_uuid}/calculate \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/companies/{company_uuid}/payrolls/{payroll_uuid}/calculate';
const options = {method: 'PUT', headers: {authorization: 'Bearer <<COMPANY_API_TOKEN>>'}};

fetch(url, options)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error('error:' + err));

If you need to make further updates to the payroll after calculating, use the PUT companies/{company_uuid}/payrolls/{payroll_uuid}/prepare endpoint again, which will cancel out the calculations and return the payroll version used for updates.

4. Submit Payroll

๐Ÿ“˜

Preview UI

We recommend building a UI where the user can review their payroll before submitting. The displayed information can be customized to fit your unique business needs, but we highly recommend a preview step to provide the user with the payroll details before they finalize it. Typically this includes a breakdown of total payroll, taxes, and debits.

If everything looks accurate, a payroll can be processed using the PUT companies/{company_uuid}/payrolls/{payroll_uuid}/submit endpoint. Upon success, this request transitions the payroll to the processed state and initiates the transfer of funds. This is a critical step to process payroll. A payroll is not finalized without calling this endpoint.

This submission is asynchronous and a successful request responds with a 202 HTTP status. Upon success, the payroll status transitions to the processed state. You should poll to ensure that payroll is processed successfully, as async errors only occur after async processing is complete.

curl --request PUT \
     --url https://api.gusto-demo.com/v1/companies/{company_uuid}/payrolls/{payroll_uuid}/submit \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/companies/{company_uuid}/payrolls/{payroll_uuid}/submit';
const options = {method: 'PUT', headers: {authorization: 'Bearer <<COMPANY_API_TOKEN>>'}};

fetch(url, options)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error('error:' + err));

๐Ÿ“˜

Cancel a Payroll

You can revert a payroll to the unprocessed state using the PUT /companies/{company_uuid}/payrolls/{payroll_uuid}/cancel endpoint. A payroll cannot be canceled after 3:30pm PST on the payroll_deadline.

5. Receive Payroll Receipts and Paystubs

Once a payroll is submitted, we recommend including a summary of the payroll for the end user to view the debit date, check date, and payroll details. The payroll receipt should also be available on this final step. See Payroll Receipts for more information.

You can retrieve Payroll Receipt using the GET payrolls/{payroll_uuid}/receipt endpoint.

You can retrieve a W2 Employee's Paystub using the GET payrolls/{payroll_id}/employees/{employee_id}/pay_stub endpoint.

You can also use the pre-built UI Flow for a payroll receipt.

Architecture Diagram