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:
- By employer
- By employee (βself-onboardingβ)
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.
Updated 6 days ago