GuidesAPI ReferenceChangelogAPI PolicyAPI StatusGusto Security

Onboard a W2 Employee

Employees are a critical part of payroll. Companies themselves are, naturally, a collection of one to hundreds of employees that must be paid on a regular cadence for work done.

Employees may be added during a company’s onboarding for payroll, or at any time after the initial onboarding is complete. An employee can be setup in one of two ways:

  1. By Employer
  2. By Employee (“Self-onboarding”)

Both can be done using Gusto’s pre-built Flows or using the Employee APIs to build custom UI/UX.

To learn more about the difference between Employee Onboarding by Employer vs Employee Self-Onboarding review the Onboarding Method Comparison Guide.

The steps for both of these processes is the same

1. Create a W2 Employee

You can create an employee using the POST companies/{company_id}/employees endpoint.

The example below shows an employee "self-onboarding".

curl --request POST \
     --url https://api.gusto-demo.com/v1/companies/{company_uuid}/employees \
     --header 'accept: application/json' \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>' \
     --header 'content-type: application/json' \
     --data '
{
     "first_name": "Alexander",
     "middle_initial": "A",
     "last_name": "Hamilton",
     "date_of_birth": "1979-06-01",
     "email": "[email protected]",
     "ssn": "123451776",
     "self_onboarding": true
}
'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/companies/{company_uuid}/employees';
const options = {
  method: 'POST',
  headers: {
    accept: 'application/json',
    'content-type': 'application/json',
    authorization: 'Bearer <<COMPANY_API_TOKEN>>'
  },
  body: JSON.stringify({
    first_name: 'Alexander',
    middle_initial: 'A',
    last_name: 'Hamilton',
    date_of_birth: '1979-06-01',
    email: '[email protected]',
    ssn: '123451776',
    self_onboarding: true
  })
};

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

2. Create an Employee's Job and Compensation

To create a W2 Employee's job and compensation follow the Create a Job and Compensation guide.

3. Update Employee's Information

If completing via Employee Self-Onboarding, you can use the employee_self_management Flow to collect this information or you can use the following steps.

a. Update An Employee's Home Address

To update an employee's home address, you will first need to get the version of the address using the GET employees/{employee_id}/home_addresses endpoint. Once you have the version you can update the home address using the PUT /employees/{employee_id}/home_addresses endpoint.

curl --request PUT \
     --url https://api.gusto-demo.com/v1/employees/employee_id/home_addresses \
     --header 'accept: application/json' \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>' \
     --header 'content-type: application/json' \
     --data '
[
  {
    "uuid": "56260b3d-c375-415c-b77a-75d99f717193",
    "employee_uuid": "7087a288-8349-4632-b92e-bc94fb79f29e",
    "street_1": "644 Fay Vista",
    "street_2": "Suite 842",
    "city": "Richmond",
    "state": "VA",
    "zip": "23218",
    "country": "USA",
    "active": false,
    "effective_date": "2021-01-01",
    "courtesy_withholding": true
  },
  {
    "uuid": "d9f74049-8769-4fba-8e0f-eceef2da4e6b",
    "employee_uuid": "7087a288-8349-4632-b92e-bc94fb79f29e",
    "street_1": "100 5th Ave",
    "street_2": "Suite 555",
    "city": "New York",
    "state": "NY",
    "zip": "10001",
    "country": "USA",
    "active": true,
    "effective_date": "2022-03-03",
    "courtesy_withholding": true
  }
]
'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/employees/employee_id/home_address';
const options = {
  method: 'PUT',
  headers: {
    accept: 'application/json',
    'content-type': 'application/json',
    authorization: 'Bearer <<COMPANY_API_TOKEN>>'
  },
  body: JSON.stringify({
    version: 'fe75bd065ff48b91c35fe8ff842f986c',
    street_1: '39057 Broderick Light',
    street_2: 'Apt. 612',
    city: 'San Francisco',
    state: 'CA',
    zip: '94107'
  })
};

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

b. Update an Employee's Federal Taxes

In order to file and pay taxes correctly, a employee needs to provide federal tax information. To update the Federal Tax Details, you will first need to get the version of the tax details using GET employees/{employee_uuid}/federal_taxes . Once you have the version you can update the federal tax details using PUT employees/{employee_uuid}/federal_taxes .

curl --request PUT \
     --url https://api.gusto-demo.com/v1/employees/{employee_uuid}/federal_taxes \
     --header 'accept: application/json' \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>' \
     --header 'content-type: application/json' \
     --data '
{
     "version": "56a489ce86ed6c1b0f0cecc4050a0b01",
     "filing_status": "Single",
     "extra_withholding": "0.0",
     "two_jobs": true,
     "dependents_amount": "0.0",
     "other_income": "0.0",
     "deductions": "0.0",
     "w4_data_type": "rev_2020_w4"
}
'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/employees/{employee_uuid}/federal_taxes';
const options = {
  method: 'PUT',
  headers: {
    accept: 'application/json',
    'content-type': 'application/json',
    authorization: 'Bearer <<COMPANY_API_TOKEN>>'
  },
  body: JSON.stringify({
    version: '56a489ce86ed6c1b0f0cecc4050a0b01',
    filing_status: 'Single',
    extra_withholding: '0.0',
    two_jobs: true,
    dependents_amount: '0.0',
    other_income: '0.0',
    deductions: '0.0',
    w4_data_type: 'rev_2020_w4'
  })
};

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

c. Update an Employee's State Taxes

The data required to correctly calculate an employee's state taxes varies by both home and work location.

To know which information is needed for an Employee's State Taxes, first use the GET employees/{employee_uuid}/state_taxes endpoint. This will return an array of questions that must be answered, and possible answers for each, and what we currently have marked as that answer. Below is an example for the state of CA with the following answers:
filing_status = S, withholding_allowance = 1, additional_withholding = 0.0 , file_new_hire_report = false

{
  "employee_uuid": "92fa4d30-e284-43d0-a26e-605619c04beb",
  "file_new_hire_report": false,
  "is_work_state": true,
  "state": "CA",
  "questions": [
    {
      "label": "Filing Status",
      "description": "The Head of Household status applies to unmarried individuals who have a relative living with them in their home. If unsure, read the <a target='_blank' data-bypass rel='noopener noreferrer' tabindex='99' href='https://www.ftb.ca.gov/file/personal/filing-status/index.html'>CA Filing Status explanation</a>.\n",
      "key": "filing_status",
      "input_question_format": {
        "type": "Select",
        "options": [
          {
            "value": "S",
            "label": "Single"
          },
          {
            "value": "M",
            "label": "Married one income"
          },
          {
            "value": "MD",
            "label": "Married dual income"
          },
          {
            "value": "H",
            "label": "Head of household"
          },
          {
            "value": "E",
            "label": "Do Not Withhold"
          }
        ]
      },
      "answers": [
        {
          "value": "S",
          "valid_from": "2010-01-01",
          "valid_up_to": null
        }
      ]
    },
    {
      "label": "Withholding Allowance",
      "description": "This value is needed to calculate the employee's CA income tax withholding. If unsure, use the <a target='_blank' data-bypass rel='noopener noreferrer' tabindex='99' href='http://www.edd.ca.gov/pdf_pub_ctr/de4.pdf'>CA DE-4 form</a> to calculate the value manually.\n",
      "key": "withholding_allowance",
      "input_question_format": {
        "type": "Number"
      },
      "answers": [
        {
          "value": 1,
          "valid_from": "2010-01-01",
          "valid_up_to": null
        }
      ]
    },
    {
      "label": "Additional Withholding",
      "description": "You can withhold an additional amount of California income taxes here.",
      "key": "additional_withholding",
      "input_question_format": {
        "type": "Currency"
      },
      "answers": [
        {
          "value": "0.0",
          "valid_from": "2010-01-01",
          "valid_up_to": null
        }
      ]
    },
    {
      "label": "File a New Hire Report?",
      "description": "State law requires you to file a new hire report within 20 days of hiring or re-hiring an employee.",
      "key": "file_new_hire_report",
      "input_question_format": {
        "type": "Select"
      },
      "answers": [
        {
          "value": false,
          "valid_from": "2010-01-01",
          "valid_up_to": null
        }
      ]
    }
  ]
}

Once you know which information needs to be added/updated, you can use the PUT employees/{employee_uuid}/state_taxes endpoint.

curl --request PUT \
     --url https://api.gusto-demo.com/v1/employees/{employee_uuid}/state_taxes \
     --header 'accept: application/json' \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>' \
     --header 'content-type: application/json' \
     --data '
{
     "employee_id": "87156178-62f8-46d4-a026-ed9de3e8f836",
     "states": [
          {
               "state": "CA",
               "questions": [
                    {
                         "key": "filing_status",
                         "answers": [
                              {
                                   "value": "M",
                                   "valid_from": "2010-01-01",
                                   "valid_up_to": null
                              }
                         ]
                    },
                    {
                         "key": "withholding_allowance",
                         "answers": [
                              {
                                   "value": 2,
                                   "valid_from": "2010-01-01",
                                   "valid_up_to": null
                              }
                         ]
                    },
                    {
                         "key": "additional_withholding",
                         "answers": [
                              {
                                   "value": "25.0",
                                   "valid_from": "2010-01-01",
                                   "valid_up_to": null
                              }
                         ]
                    },
                    {
                         "key": "file_new_hire_report",
                         "answers": [
                              {
                                   "value": true,
                                   "valid_from": "2010-01-01",
                                   "valid_up_to": null
                              }
                         ]
                    }
               ]
          }
     ]
}
'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/employees/{employee_uuid}/state_taxes';
const options = {
  method: 'PUT',
  headers: {
    accept: 'application/json',
    'content-type': 'application/json',
    authorization: 'Bearer <<COMPANY_API_TOKEN>>'
  },
  body: JSON.stringify({
    employee_id: '87156178-62f8-46d4-a026-ed9de3e8f836',
    states: [
      {
        state: 'CA',
        questions: [
          {
            key: 'filing_status',
            answers: [{value: 'M', valid_from: '2010-01-01', valid_up_to: null}]
          },
          {
            key: 'withholding_allowance',
            answers: [{value: 2, valid_from: '2010-01-01', valid_up_to: null}]
          },
          {
            key: 'additional_withholding',
            answers: [{value: '25.0', valid_from: '2010-01-01', valid_up_to: null}]
          },
          {
            key: 'file_new_hire_report',
            answers: [{value: true, valid_from: '2010-01-01', valid_up_to: null}]
          }
        ]
      }
    ]
  })
};

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

d. Create an Employee Bank Account

Creating an employee bank account will automatically update an employee’s payment method from Check to Direct Deposit. If you want to change the payment method you can use the PUT employees/{employee_uuid}/payment_method endpoint. To create a bank account for an Employee use the POST employees/{employee_uuid}/bank_accountendpoint.

curl --request POST \
     --url https://api.gusto-demo.com/v1/employees/{employee_uuid}/bank_accounts \
     --header 'accept: application/json' \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>' \
     --header 'content-type: application/json' \
     --data '
{
     "name": "BoA Checking Account",
     "routing_number": "266905059",
     "account_number": "5809431207",
     "account_type": "Checking"
}
'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/employees/{employee_uuid}/bank_accounts';
const options = {
  method: 'POST',
  headers: {
    accept: 'application/json',
    'content-type': 'application/json',
    authorization: 'Bearer <<COMPANY_API_TOKEN>>'
  },
  body: JSON.stringify({
    name: 'BoA Checking Account',
    routing_number: '266905059',
    account_number: '5809431207',
    account_type: 'Checking'
  })
};

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

4. (Optional) Enroll the employee in a Time Off policy (sick, vacation, etc)

If you company already has a Time Off Policy, you can enroll your employee after they are onboarded. To add an employee to a time off policy, use the PUT time_off_policies/{time_off_policy_uuid}/add_employees endpoint.

curl --request PUT \
     --url https://api.gusto-demo.com/v1/time_off_policies/time_off_policy_uuid/add_employees \
     --header 'accept: application/json' \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>' \
     --header 'content-type: application/json' \
     --data '
{
     "employee": [
          {
               "uuid": "56c672b4-3918-45cd-a3bb-a62ae0ff1307",
               "balance": "40.0"
          },
          {
               "uuid": "28e7a45d-32dd-4925-a82a-9a3ccc6d302c",
               "balance": "40.0"
          },
          {
               "uuid": "f60650da-ba18-417a-b2ab-3c9b6f0fe4f2",
               "balance": "20.0"
          }
     ]
}
'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/time_off_policies/time_off_policy_uuid/add_employees';
const options = {
  method: 'PUT',
  headers: {
    accept: 'application/json',
    'content-type': 'application/json',
    authorization: 'Bearer <<COMPANY_API_TOKEN>>'
  },
  body: JSON.stringify({
    employee: [
      {uuid: '56c672b4-3918-45cd-a3bb-a62ae0ff1307', balance: '40.0'},
      {uuid: '28e7a45d-32dd-4925-a82a-9a3ccc6d302c', balance: '40.0'},
      {uuid: 'f60650da-ba18-417a-b2ab-3c9b6f0fe4f2', balance: '20.0'}
    ]
  })
};

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

5. (Optional) Add employee benefits and deductions

You can add benefits and deductions to your employee's compensation following the Create Company Benefits guide.

6. Mark Employee as Onboarded

With the basic details, address, payment method, and tax information complete, the final step is to mark the employer as onboarding_completed. This is done using the PUT employees/{employee_uuid}/onboarding_status endpoint. You will want to change the status to onboarding_completed in the body.

curl --request PUT \
     --url https://api.gusto-demo.com/v1/employees/employee_id/onboarding_status \
     --header 'accept: application/json' \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>' \
     --header 'content-type: application/json' \
     --data '
{
     "onboarding_status": "onboarding_completed"
}
'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/employees/employee_id/onboarding_status';
const options = {
  method: 'PUT',
  headers: {
    accept: 'application/json',
    'content-type': 'application/json',
    authorization: 'Bearer <<COMPANY_API_TOKEN>>'
  },
  body: JSON.stringify({onboarding_status: 'onboarding_completed'})
};

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