GuidesAPI ReferenceChangelogAPI PolicyGusto Security
Guides

Onboard a contractor

Add a 1099 worker to your company payroll

Onboarding a Contractor is similar to onboarding a W2 Employee but requires less information. Jobs and compensations are simplified for contractors since these type of workers are not subject to the same minimum wage laws, tax calculations, and payroll rules.

Contractors may be added during a company’s onboarding for payroll, or at any time after this initial onboarding is complete. A contractor can be setup in one of two ways:

  1. By employer
  2. By contractor (β€œSelf-onboarding”)

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

To learn more about the difference between contractor onboarding methods, review the FAQ or see our guide on Contractor self-onboarding.

1. Create a contractor

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

The example below shows a contractor "self-onboarding".

Sample request

curl --request POST \
     --url https://api.gusto-demo.com/v1/companies/{company_uuid}/contractors \
     --header 'accept: application/json' \
     --header 'authorization: Bearer <<COMPANY_API_TOKEN>>' \
     --header 'content-type: application/json' \
     --data '
{
     "type": "Individual",
     "self_onboarding": true,
     "file_new_hire_report": "true",
     "wage_type": "Fixed",
     "first_name": "Jenna",
     "last_name": "Hunterson",
     "start_date": "2022-03-14",
     "email": "[email protected]",
     "work_state": "GA",
     "hourly_rate": "40.0"
}
'
const fetch = require('node-fetch');

const url = 'https://api.gusto-demo.com/v1/companies/{company_uuid}/contractors';
const options = {
  method: 'POST',
  headers: {
    accept: 'application/json',
    'content-type': 'application/json',
    authorization: 'Bearer <<COMPANY_API_TOKEN>>'
  },
  body: JSON.stringify({
    type: 'Individual',
    self_onboarding: true,
    file_new_hire_report: 'true,',
    wage_type: 'Fixed',
    first_name: 'Jenna',
    last_name: 'Hunterson',
    start_date: '2022-03-14',
    email: '[email protected]',
    work_state: 'GA',
    hourly_rate: '40.0'
  })
};

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

Sample response

{
  "uuid": "cd84ccd5-3acb-4783-a8fa-722a135d76e2",
  "company_uuid": "<company-uuid>",
  "wage_type": "Fixed",
  "version": "5c6dfbf5cae5ba55fe06034a903cd8e0",
  "type": "Individual",
  "first_name": "Jenna",
  "last_name": "Hunterson",
  "middle_initial": null,
  "business_name": null,
  "ein": null,
  "has_ein": false,
  "email": "[email protected]",
  "department_uuid": null,
  "dismissal_date": null,
  "department_title": null,
  "start_date": "2022-03-14",
  "is_active": false,
  "address": null,
  "file_new_hire_report": false,
  "work_state": "GA",
  "onboarded": false,
  "onboarding_status": "self_onboarding_started",
  "payment_method": null,
  "has_ssn": false,
  "hourly_rate": "40.00"
}

2. Update a contractor's information

Add a contractor's address

πŸ“˜

After creating the contractor, it has an address by default. To "create" an address for the contractor, you need to update the existing address, which replaces it because a contractor can only have one address.

  1. To update a contractor's address, you first need to get the version of the address using the GET contractors/{contractor_uuid}/address endpoint.
    Sample request

    curl --request GET \
         --url https://api.gusto-demo.com/v1/contractors/cd84ccd5-3acb-4783-a8fa-722a135d76e2/address \
         --header 'X-Gusto-API-Version: 2025-06-15' \
         --header 'accept: application/json' \
         --header 'authorization: Bearer <token>'
    

    Sample response

    {
      "version": "d41d8cd98f00b204e9800998ecf8427e",
      "contractor_uuid": "cd84ccd5-3acb-4783-a8fa-722a135d76e2",
      "street_1": null,
      "street_2": null,
      "city": null,
      "state": null,
      "zip": null,
      "country": null,
      "active": true
    }
    
  2. Use the version to update the contractor’s address details using the PUT contractors/{contractor_uuid}/address endpoint.
    Sample request

    curl --request PUT \
         --url https://api.gusto-demo.com/v1/contractors/{contractor_uuid}/address \
         --header 'accept: application/json' \
         --header 'authorization: Bearer <<COMPANY_API_TOKEN>>' \
         --header 'content-type: application/json' \
         --data '
    {
         "version": "fe75bd065ff48b91c35fe8ff842f986c",
         "street_1": "39057 Broderick Light",
         "street_2": "Apt. 612",
         "city": "San Francisco",
         "state": "CA",
         "zip": "94107"
    }
    '
    
    const fetch = require('node-fetch');
    
    const url = 'https://api.gusto-demo.com/v1/contractors/{contractor_uuid}/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

    {
      "version": "fe75bd065ff48b91c35fe8ff842f986c",
      "contractor_uuid": "cd84ccd5-3acb-4783-a8fa-722a135d76e2",
      "street_1": "39057 Broderick Light",
      "street_2": "Apt. 612",
      "city": "San Francisco",
      "state": "CA",
      "zip": "94107",
      "country": "USA",
      "active": true
    }
    

Create a contractor's bank account

Creating a contractor bank account will automatically update an contractor's payment method from Check to Direct Deposit. If you want to change the payment method, you can use the PUT contractors/{contractor_uuid}/payment_method endpoint.

To create a bank account for a contractor use the POST contractors/{contractor_uuid}/bank_accounts endpoint.

πŸ“˜

We only support one bank account per contractor. Using this endpoint on a contractor who already has a bank account will replace the existing bank account.

Sample request

curl --request POST \
     --url https://api.gusto-demo.com/v1/contractors/{contractor_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/contractors/{contractor_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": "ede6bca0-b717-4c0b-a029-20d4f23ce3c4",
  "contractor_uuid": "cd84ccd5-3acb-4783-a8fa-722a135d76e2",
  "name": "BoA Checking Account",
  "routing_number": "266905059",
  "hidden_account_number": "XXXX1207",
  "account_type": "Checking"
}

3. Mark the contractor as onboarded

With the basic details, address, and payment method complete, the final step is to mark the contractor as onboarding_completed using the PUT contractors/{contractor_uuid}/onboarding_status endpoint.

Sample request

curl --request PUT \
     --url https://api.gusto-demo.com/v1/contractors/{contractor_uuid}/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/contractors/{contractor_uuid}/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": "cd84ccd5-3acb-4783-a8fa-722a135d76e2",
  "onboarding_status": "onboarding_completed",
  "onboarding_steps": [
    {
      "title": "Basic details",
      "id": "basic_details",
      "required": true,
      "completed": true,
      "requirements": []
    },
    {
      "title": "Enter compensation details",
      "id": "compensation_details",
      "required": true,
      "completed": true,
      "requirements": []
    },
    {
      "title": "Add an address",
      "id": "add_address",
      "required": true,
      "completed": true,
      "requirements": []
    },
    {
      "title": "Payment details",
      "id": "payment_details",
      "required": true,
      "completed": true,
      "requirements": []
    },
    {
      "title": "Sign and acknowledge documents",
      "id": "sign_documents",
      "required": false,
      "completed": false,
      "requirements": [
        "basic_details,",
        "add_address"
      ]
    },
    {
      "title": "File new hire report",
      "id": "file_new_hire_report",
      "required": false,
      "completed": false,
      "requirements": [
        "basic_details"
      ]
    }
  ]
}

FAQ

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

By employer

The employer is responsible for collecting and entering all contractors’ 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 contractor)

The employer starts the process by entering only basic details, then invites the contractor to complete their own onboarding using the self-onboarding flow. Read more in Contractor self-onboarding.

Self-onboarding offers clear advantages:

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

πŸ“˜

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