Onboard a W-2 employee

Add a new employee or invite them to self-onboard

Employees may be added to a company’s payroll during onboarding or at any time after the initial onboarding. An employee can be set up in one of two ways:

Both can be done either using Gusto’s pre-built Flows using the Employee Management flow or the Add Employees flow), or using Employee APIs to build custom UI/UX.

If you use React on your front end, you can build through our React SDK.

1. Create a W-2 employee

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

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

Sample request

curl --request POST \
     --url https://api.gusto-demo.com/v1/companies/{company_uuid}/employees \
     --header 'X-Gusto-API-Version: 2024-04-01' \
     --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",
  "email": "1979-06-01",
  "date_of_birth": "[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));

Sample response

{
	"uuid": "EMPLOYEE_UUID",
	"first_name": "Alexander",
	"middle_initial": "A",
	"last_name": "Hamilton",
	"email": "[email protected]",
	"company_uuid": "COMPANY_UUID",
	"manager_uuid": null,
	"version": "VERSION",
	"current_employment_status": null,
	"onboarding_status": "self_onboarding_pending_invite",
	"preferred_first_name": null,
	"department_uuid": null,
	"employee_code": "d151f0",
	"payment_method": "Check",
	"department": null,
	"terminated": false,
	"two_percent_shareholder": null,
	"onboarded": false,
	"historical": false,
	"has_ssn": true,
	"onboarding_documents_config": {
		"uuid": null,
		"i9_document": false
	},
	"jobs": [],
	"eligible_paid_time_off": [],
	"terminations": [],
	"garnishments": [],
	"date_of_birth": "1979-06-01",
	"ssn": "",
	"phone": null,
	"work_email": null
}

2. Update an employee's work address

Assign an employee to an existing work address (that already exists at the company level) using the POST employees/{employee_id}/work_addresses endpoint.

Sample request

curl --request POST \
     --url https://api.gusto-demo.com/v1/employees/{company_uuid}/work_addresses \
     --header 'X-Gusto-API-Version: 2024-04-01' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer COMPANY_API_TOKEN' \
     --header 'content-type: application/json' \
     --data '
{
  "location_uuid": "LOCATION_UUID",
  "effective_date": "2025-05-15"
}'
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));

Sample response

{
  "uuid": "WORK_ADDRESS_UUID",
  "employee_uuid": "EMPLOYEE_UUID",
  "location_uuid": "LOCATION_UUID",
  "effective_date": "2020-01-31",
  "active": true,
  "version": "VERSION",
  "street_1": "977 Marks Viaduct",
  "street_2": "Apt. 958",
  "city": "Pink Hill",
  "state": "NC",
  "zip": "28572",
  "country": "USA"
}

3. Create an employee's job and compensation

To create a W-2 Employee's job and compensation, follow the Manage compensations and jobs guide.

4. Update the employee's bank account information

If using employee self-onboarding, use the employee_self_management flow to collect the following information. Otherwise, you can use the following steps.

1. Update employee’s home address

Follow the instructions in the Manage employee addresses guide.

2. Update employee’s tax information

Follow the instructions in our Configuring employee tax information guide, which covers both federal and state tax setup for an employee.

3. Create an employee’s bank account

To create a bank account for an Employee, use the POST employees/{employee_uuid}/bank_account endpoint as shown below, or you can

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, use the PUT employees/{employee_uuid}/payment_method endpoint.

Sample request

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));

Sample response

{
  "uuid": "BANK_ACCOUNT_UUID",
  "employee_uuid": "EMPLOYEEE_UUID",
  "name": "BoA Checking Account",
  "routing_number": "266905059",
  "hidden_account_number": "XXXX1207",
  "account_type": "Checking"
}

5. (Optional) Enroll the employee in a time off policy

If your 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.

Sample request

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": "EMPLOYEE_UUID",
               "balance": "40.0"
          },
          {
               "uuid": "EMPLOYEE_UUID",
               "balance": "40.0"
          },
          {
               "uuid": "EMPLOYEE_UUID",
               "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));

Sample response

{
  "uuid": "TIME_OFF_POLICY_UUID",
  "company_uuid": "COMPANY_UUID",
  "name": "test policy",
  "policy_type": "vacation",
  "accrual_method": "unlimited",
  "accrual_rate": null,
  "accrual_rate_unit": null,
  "paid_out_on_termination": false,
  "accrual_waiting_period_days": null,
  "carryover_limit_hours": null,
  "max_accrual_hours_per_year": null,
  "max_hours": null,
  "is_active": true,
  "complete": true,
  "employees": [
    {
      "uuid": "EMPLOYEE_UUID"
    },
    {
      "uuid": "EMPLOYEE_UUID"
    },
    {
      "uuid": "EMPLOYEE_UUID"
    }
  ]
}

6. (Optional) Add employee benefits and deductions

You can add benefits and deductions to your employee's compensation following the Manage employee benefits and Manage employee garnishments guides.

7. (Optional) Add recurring reimbursements for employees

Add recurring reimbursements for employees following the Manage recurring reimbursements guide.

8. Mark the employee as onboarded

With the basic details, address, payment method, and tax information complete, the final step is to mark the employee as onboarding_completed. Call the PUT employees/{employee_uuid}/onboarding_status endpoint, changing the "onboarding_status" to onboarding_completed.

Sample request

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));

Sample response

{
  "uuid": "EMPLOYEE_UUID",
  "onboarding_status": "onboarding_completed",
  "onboarding_steps": [
    {
      "title": "Personal details",
      "id": "personal_details",
      "required": true,
      "completed": true,
      "requirements": []
    },
    {
      "title": "Enter compensation details",
      "id": "compensation_details",
      "required": true,
      "completed": true,
      "requirements": []
    }
  ]
}

FAQ

What’s the difference between onboarding by employer and self-onboarding?

By employer

The employer is responsible for collecting and entering all employee information. This is typically done through paper or secure digital forms. Once entered, the employer completes the onboarding process on behalf of the employee.

Self-onboarding (by employee)

The employer starts the process by entering only basic details, then invites the employee to complete their own onboarding.

For more details, refer to the self-onboarding flow.

Self-onboarding offers clear advantages:

  • Employees manage their own sensitive data, reducing the need to share it over insecure channels (like email).
  • The process scales efficiently for organizations hiring many employees.
  • It saves time for both employers and employees.

πŸ“˜

Gusto does not send emails directly to employees. Any email or in-app invitations to self-onboard must be built into your Embedded Payroll implementation.