Employee Onboarding Process

Employee Onboarding adds a new employee to a company by walking them through a series of steps defined in an onboarding template. Some steps are completed by you (the employer) through the API, and some are completed by the employee themselves.

The employer-side flow through the API is:

  1. Start onboarding from a template.
  2. Enter the employee's basic information.
  3. Enter employment basic information.
  4. Enter employment details.
  5. (Optional) Prefill the employee's personal and contact information.

After that, the employee must complete their own onboarding steps before you can continue.

Before you start: create an onboarding template

Onboarding templates cannot be created through the API. Create them in BackOffice:

Client → Settings → Onboarding → Onboarding Templates

In the template, you choose which onboarding steps are included and who completes each one. Note the template's ID — you'll pass it as templateId when you start onboarding.

The steps returned by the API, and the fields it asks for, depend on how the template is configured.

Authentication and headers

All requests require a bearer access token (see How to Get API Access). CLIENT_ID is the company identifier returned as id when you create the company.

Every request in this guide uses these headers:

HeaderValue
authorizationBearer {TOKEN}
acceptapplication/json
content-typeapplication/merge-patch+json
api-version2.0
x-api-version2.0

Step 1: Start onboarding

Creates a new onboarding from a template and returns the list of steps it contains.

POST https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/start

Request body:

FieldTypeRequiredDescription
templateIdintegerRequiredID of the onboarding template created in BackOffice

Example request

require("dotenv").config();

const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/start`;

const payload = {
  templateId: 2094
};

async function startOnboarding() {
  const response = await fetch(url, {
    method: "POST",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    },
    body: JSON.stringify(payload)
  });

  console.log(response.status);
  console.log(await response.text());
}

startOnboarding();
curl -s -X POST "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/start" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0" \
  -d '{ "templateId": 2094 }' | jq .

Example response

The steps array depends on your template — yours may contain more or fewer steps.

{
  "templateId": 2094,
  "steps": [
    { "step": 1,  "num": 1, "name": "Employee Basic Information" },
    { "step": 2,  "num": 1, "name": "Employment Basic Information" },
    { "step": 3,  "num": 1, "name": "Employment Detail Information" },
    { "step": 0,  "num": 2, "name": "Employment Summary" },
    { "step": 0,  "num": 2, "name": "Personal Information" },
    { "step": 0,  "num": 2, "name": "Contact Information" },
    { "step": 0,  "num": 2, "name": "W-9 Form" },
    { "step": 0,  "num": 2, "name": "W-9 Review" },
    { "step": 0,  "num": 2, "name": "Documents Acknowledgment" },
    { "step": 0,  "num": 2, "name": "Signing Documents Summary" },
    { "step": 6,  "num": 3, "name": "Complete Net Pay Allocation" },
    { "step": 12, "num": 3, "name": "W-9 Form Review" },
    { "step": 10, "num": 3, "name": "Document Review/Signing" }
  ],
  "onboardingId": 2068,
  "nextStep": 1,
  "customRequiredFields": [
    "email",
    "ssn"
  ]
}
FieldDescription
templateIdThe template this onboarding was created from
stepsThe steps included in this onboarding, as configured in the template
onboardingIdUnique identifier of this onboarding. Save it — every following step uses it in the path.
nextStepThe next step to complete
customRequiredFieldsFields the template requires in the next step, in addition to the default required fields

Step 2: Enter employee basic information

Sets the employee's name and identifiers.

PUT https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/start/{ONBOARDING_ID}/eebasicinfo

ONBOARDING_ID is the onboardingId returned in Step 1.

Request body:

FieldTypeRequiredDescription
firstNamestringRequiredEmployee's first name
lastNamestringRequiredEmployee's last name
ssnstringRequired if listed in customRequiredFieldsSocial Security Number
emailstringRequired if listed in customRequiredFieldsEmployee's email address

Send every field listed in customRequiredFields from the Step 1 response.

Example request

require("dotenv").config();

const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/start/${ONBOARDING_ID}/eebasicinfo`;

const payload = {
  firstName: "Daniel",
  lastName: "Carter",
  ssn: "875-52-1231",
  email: "[email protected]"
};

async function enterEmployeeBasicInfo() {
  const response = await fetch(url, {
    method: "PUT",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    },
    body: JSON.stringify(payload)
  });

  console.log(response.status);
  console.log(await response.text());
}

enterEmployeeBasicInfo();
curl -s -X PUT "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/start/2068/eebasicinfo" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0" \
  -d '{
    "firstName": "Daniel",
    "lastName": "Carter",
    "ssn": "875-52-1231",
    "email": "[email protected]"
  }' | jq .

Example response

{
  "onboardingId": 2068,
  "nextStep": 2,
  "customRequiredFields": []
}

Step 3: Enter employment basic information

Sets the employee's hire date.

PUT https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/start/{ONBOARDING_ID}/employmentbasic

Request body:

FieldTypeRequiredDescription
originalHireDatestring (ISO date, YYYY-MM-DD)RequiredThe employee's original hire date

Example request

require("dotenv").config();

const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/start/${ONBOARDING_ID}/employmentbasic`;

const payload = {
  originalHireDate: "2026-10-01"
};

async function enterEmploymentBasicInfo() {
  const response = await fetch(url, {
    method: "PUT",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    },
    body: JSON.stringify(payload)
  });

  console.log(response.status);
  console.log(await response.text());
}

enterEmploymentBasicInfo();
curl -s -X PUT "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/start/2068/employmentbasic" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0" \
  -d '{ "originalHireDate": "2026-10-01" }' | jq .
{
  onboardingId: 2068,
  previousStep: 1,
  nextStep: 3,
  customRequiredFields: []
}

Step 4: Enter employment details

Sets the employee's employment type, compensation, and work location.

PUT https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/start/{ONBOARDING_ID}/employmentdetail

Request body:

FieldTypeRequiredDescription
employmentTypeintegerRequiredEmployment type
compensationTypeintegerRequiredCompensation type
compensableHoursnumberRequiredHours the employee is expected to work per week
compensationAmountnumberRequiredCompensation amount
workLocationIdintegerRequiredID of the employee's work location. 0 = works from home.
highlyCompensatedintegerRequiredWhether the employee is highly compensated. 1 = yes, 0 = no.
keyEmployeeintegerRequiredWhether the employee is a key employee. 1 = yes, 0 = no.

Example request

require("dotenv").config();

const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/start/${ONBOARDING_ID}/employmentdetail`;

const payload = {
  employmentType: 2,
  compensationType: 5,
  compensableHours: 40,
  compensationAmount: 2000,
  workLocationId: 0, // works from home
  highlyCompensated: 1,
  keyEmployee: 0
};

async function enterEmploymentDetails() {
  const response = await fetch(url, {
    method: "PUT",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    },
    body: JSON.stringify(payload)
  });

  console.log(response.status);
  console.log(await response.text());
}

enterEmploymentDetails();
curl -s -X PUT "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/start/2068/employmentdetail" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0" \
  -d '{
    "employmentType": 2,
    "compensationType": 5,
    "compensableHours": 40,
    "compensationAmount": 2000,
    "workLocationId": 0,
    "highlyCompensated": 1,
    "keyEmployee": 0
  }' | jq .

Example response

{
  "onboardingId": 2068,
  "previousStep": 2,
  "nextStep": 0,
  "customRequiredFields": []
}

nextStep: 0 means you've completed the employer steps that come before the employee's part of onboarding.


Step 5 (optional): Prefill employee information

If you already have the employee's personal or contact information, you can prefill it. The employee then only needs to verify it during their part of onboarding instead of typing it in. The information can only be prefilled if employee didn't start his part of onboarding yet.

Both prefill requests have these requirements:

  • approveStep must be true.
  • For personalinfoee, w2ElectronicConsent must also be true.

Both return 204 No Content with no response body on success.

Prefill personal information

PUT https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/start/{ONBOARDING_ID}/personalinfoee

Request body:

FieldTypeRequiredDescription
genderintegerOptionalEmployee's gender
birthOnstring (ISO date, YYYY-MM-DD)OptionalDate of birth
citizenshipintegerOptionalCitizenship status
w2ElectronicConsentbooleanRequiredMust be true
approveStepbooleanRequiredMust be true
require("dotenv").config();

const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/start/${ONBOARDING_ID}/personalinfoee`;

const payload = {
  gender: 1,
  birthOn: "2001-01-08",
  citizenship: 1,
  w2ElectronicConsent: true,
  approveStep: true
};

async function prefillPersonalInfo() {
  const response = await fetch(url, {
    method: "PUT",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    },
    body: JSON.stringify(payload)
  });

  console.log(response.status); // 204, no body
}

prefillPersonalInfo();
curl -s -o /dev/null -w "%{http_code}\n" -X PUT "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/start/2068/personalinfoee" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0" \
  -d '{
    "gender": 1,
    "birthOn": "2001-01-08",
    "citizenship": 1,
    "w2ElectronicConsent": true,
    "approveStep": true
  }'

Prefill contact information

PUT https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/start/{ONBOARDING_ID}/contactinfoee

Request body:

FieldTypeRequiredDescription
personalEmailstringOptionalEmployee's personal email address
residentialAddressobjectOptionalEmployee's home address (see below)
approveStepbooleanRequiredMust be true

Optional in this case just means it doesn't have to be in request body, but if it's not employee will still have to fill it in.

residentialAddress fields:

FieldTypeDescription
addressLine1stringStreet address
citystringCity
statestringTwo-letter state code
zipstringZIP code
countrystringTwo-letter country code
require("dotenv").config();

const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/start/${ONBOARDING_ID}/contactinfoee`;

const payload = {
  personalEmail: "[email protected]",
  residentialAddress: {
    addressLine1: "350 Fifth Avenue",
    city: "New York",
    state: "NY",
    zip: "10118",
    country: "US"
  },
  approveStep: true
};

async function prefillContactInfo() {
  const response = await fetch(url, {
    method: "PUT",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    },
    body: JSON.stringify(payload)
  });

  console.log(response.status); // 204, no body
}

prefillContactInfo();
curl -s -o /dev/null -w "%{http_code}\n" -X PUT "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/start/2068/contactinfoee" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0" \
  -d '{
    "personalEmail": "[email protected]",
    "residentialAddress": {
      "addressLine1": "350 Fifth Avenue",
      "city": "New York",
      "state": "NY",
      "zip": "10118",
      "country": "US"
    },
    "approveStep": true
  }'

Next: the employee completes their steps

The employee must now complete their own onboarding steps (in the example template above: personal information, contact information, W-9 form, and document acknowledgment). The remaining employer steps, which start at Step 6, are available only after the employee finishes.

Finish onboarding as the employer

The steps below match the example template used in this guide. As with the earlier steps, which steps you see and what they require depend on how your onboarding template is configured.

All finalize requests use the same base path and the same ONBOARDING_ID returned in Step 1:

https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/finalize/{ONBOARDING_ID}

Step 6: Approve net pay allocation

The employee chooses how their net pay is split during their part of onboarding. You review that allocation and approve it. This takes two requests: GET to read the allocation, then PUT to approve it.

Get the net pay allocation

GET https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/finalize/{ONBOARDING_ID}/netpay

The request has no body.

require("dotenv").config();
 
const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/finalize/${ONBOARDING_ID}/netpay`;
 
async function getNetPayAllocation() {
  const response = await fetch(url, {
    method: "GET",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    }
  });
 
  console.log(response.status);
  console.log(JSON.stringify(await response.json(), null, 2));
}
 
getNetPayAllocation();
curl -s -X GET "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/finalize/2068/netpay" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0" | jq .
[
  {
    "id": 1,
    "payMethod": 2,
    "accountHolderName": "Alex Smith",
    "accountType": 1,
    "routingNumber": "121000356",
    "accountNumber": "000123456791",
    "percent": 100,
    "startOn": "2026-10-13T00:00:00Z",
    "requirePreNote": false,
    "applyForAllPayStubs": true
  }
]

Approve the net pay allocation

PUT https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/finalize/{ONBOARDING_ID}/netpay

The request has no body. A successful request returns 204 No Content with no response body.

require("dotenv").config();
 
const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/finalize/${ONBOARDING_ID}/netpay`;
 
async function approveNetPayAllocation() {
  const response = await fetch(url, {
    method: "PUT",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    }
  });
 
  console.log(response.status); // 204, no body
}
 
approveNetPayAllocation();
curl -s -o /dev/null -w "%{http_code}\n" -X PUT "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/finalize/2068/netpay" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0"

Step 7: Review documents

Returns the documents prepared during onboarding so you can review them before signing.

GET https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/finalize/{ONBOARDING_ID}/docreview

The request has no body.

require("dotenv").config();
 
const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/finalize/${ONBOARDING_ID}/docreview`;
 
async function getDocumentsForReview() {
  const response = await fetch(url, {
    method: "GET",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    }
  });
 
  console.log(response.status);
  console.log(JSON.stringify(await response.json(), null, 2));
}
 
getDocumentsForReview();
curl -s -X GET "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/finalize/2068/docreview" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0" | jq .

The response contains documents encoded in base64.

{
  "signatureRequired": false,
  "documents": [
    {
      "title": "Onboarding Template",
      "preview": [
        "data:data:application/octet-stream;base64;base64....",
        "data:data:application/octet-stream;base64;base64....",
        "data:data:application/octet-stream;base64;base64....",
        "data:data:application/octet-stream;base64;base64...."

      ]
    },
    {
      "title": "1099-MISC.docx",
      "preview": [
        "data:data:application/octet-stream;base64;base64....",
        "data:data:application/octet-stream;base64;base64....",
        "data:data:application/octet-stream;base64;base64....",
        "data:data:application/octet-stream;base64;base64...."
      ]
    }
  ]
}

Step 8: Finish document review and signing

Confirms that you reviewed the documents and signs them. Choose whether the signature is electronic or wet ink.

PUT https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/finalize/{ONBOARDING_ID}/docreview/finish

Request body:

FieldTypeRequiredDescription
declarePenaltiesboolean | nullRequiredWhether you accepted the declaration required to finish the review or signing. Send true to accept.
erTypeSignatureintegerRequiredHow you want to sign the documents. 1 = electronically: the system generates the signature from the name in erSignatureName. 2 = wet ink: you sign the documents manually.
erSignatureNamestring | nullRequired if erTypeSignature is 1Name used for the electronic signature.

A successful request returns 204 No Content with no response body.

Example request

require("dotenv").config();
 
const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/finalize/${ONBOARDING_ID}/docreview/finish`;
 
const payload = {
  declarePenalties: true,
  erTypeSignature: 1, // 1 = electronic, 2 = wet ink
  erSignatureName: "Admin"
};
 
async function finishDocumentReview() {
  const response = await fetch(url, {
    method: "PUT",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    },
    body: JSON.stringify(payload)
  });
 
  console.log(response.status); // 204, no body
}
 
finishDocumentReview();
curl -s -o /dev/null -w "%{http_code}\n" -X PUT "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/finalize/2068/docreview/finish" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0" \
  -d '{
    "declarePenalties": true,
    "erTypeSignature": 1,
    "erSignatureName": "Admin"
  }'

Step 9: Check which documents need to be uploaded

Some documents cannot be signed electronically. If the employee chose to sign a document manually (wet ink) during their part of onboarding, you must print the document, have it signed, and upload the signed copy. In this example, the employee chose to sign the W-9 manually, so a W-9 upload is required.

This request lists the documents that still need an upload.

GET https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/finalize/{ONBOARDING_ID}/docupload

The request has no body.

require("dotenv").config();
 
const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/finalize/${ONBOARDING_ID}/docupload`;
 
async function getRequiredUploads() {
  const response = await fetch(url, {
    method: "GET",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    }
  });
 
  console.log(response.status);
  console.log(JSON.stringify(await response.json(), null, 2));
}
 
getRequiredUploads();
curl -s -X GET "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/finalize/2068/docupload" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0" | jq .

Example response

{
  "documents": [
    {
      "type": 7,
      "uploaded": false
    }
  ]
}
FieldTypeDescription
documentsarrayDocuments that require an upload
documents[].typeintegerDocument type. Use this value in the upload path in Step 11. 7 = W-9.
documents[].uploadedbooleantrue if the document has already been uploaded

Step 10: Finish the W-9 review

Confirms that you reviewed the employee's W-9. Complete this step before you upload the signed W-9, so you can review the form before it is printed, signed, and uploaded.

PUT https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/finalize/{ONBOARDING_ID}/w9/finish

The request has no body. A successful request returns 204 No Content with no response body.

require("dotenv").config();
 
const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/finalize/${ONBOARDING_ID}/w9/finish`;
 
async function finishW9Review() {
  const response = await fetch(url, {
    method: "PUT",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    }
  });
 
  console.log(response.status); // 204, no body
}
 
finishW9Review();
curl -s -o /dev/null -w "%{http_code}\n" -X PUT "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/finalize/2068/w9/finish" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0"

Step 11: Upload the signed documents

Uploads a signed document for each entry returned in Step 9. Send one request per document.

POST https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/finalize/{ONBOARDING_ID}/docupload/upload/{DOCUMENT_TYPE}

DOCUMENT_TYPE is the type value from the Step 9 response. For the W-9, use 7.

The request body is multipart/form-data with one part:

FieldTypeRequiredDescription
filefileRequiredThe signed document as a PDF

Do not set the content-type header yourself. Your HTTP client sets it, including the multipart boundary. A successful request returns 204 No Content with no response body.

Example request

require("dotenv").config();
const fs = require("fs");
const path = require("path");
 
const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const DOCUMENT_TYPE = 7; // 7 = W-9
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/finalize/${ONBOARDING_ID}/docupload/upload/${DOCUMENT_TYPE}`;
 
const filePath = path.join(__dirname, "w9-daniel-carter.pdf");
 
async function uploadDocument() {
  const fileBuffer = fs.readFileSync(filePath);
 
  const form = new FormData();
  form.append(
    "file",
    new Blob([fileBuffer], { type: "application/pdf" }),
    path.basename(filePath)
  );
 
  const response = await fetch(url, {
    method: "POST",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "x-api-version": "2.0"
    },
    body: form
  });
 
  console.log(response.status); // 204, no body
}
 
uploadDocument();
curl -s -o /dev/null -w "%{http_code}\n" -X POST "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/finalize/2068/docupload/upload/7" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "x-api-version: 2.0" \
  -F "[email protected];type=application/pdf"

To confirm the upload, call the Step 9 request again. The document's uploaded value is true after a successful upload.


Step 12: Finish onboarding

Completes onboarding and starts the employee's employment. Call it after all previous steps are done.

PUT https://api.worklio.com/wep/companies/{CLIENT_ID}/onboarding/finalize/{ONBOARDING_ID}/finish

The request has no body.

require("dotenv").config();
 
const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const ONBOARDING_ID = 2068;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/onboarding/finalize/${ONBOARDING_ID}/finish`;
 
async function finishOnboarding() {
  const response = await fetch(url, {
    method: "PUT",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/merge-patch+json",
      "x-api-version": "2.0"
    }
  });
 
  console.log(response.status);
  console.log(JSON.stringify(await response.json(), null, 2));
}
 
finishOnboarding();
curl -s -X PUT "https://api.worklio.com/wep/companies/$CLIENT_ID/onboarding/finalize/2068/finish" \
  -H "accept: application/json" \
  -H "api-version: 2.0" \
  -H "authorization: Bearer $API_KEY" \
  -H "content-type: application/merge-patch+json" \
  -H "x-api-version: 2.0" | jq .

Example response

{
  "id": 4382,
  "firstName": "Daniel",
  "lastName": "Carter",
  "middleName": "",
  "birthDate": "2001-01-08T00:00:00Z",
  "hireDate": "2026-10-01T00:00:00Z",
  "email": "[email protected]",
  "phone": ""
}
FieldTypeDescription
idintegerID of the new employee
firstNamestringEmployee's first name
lastNamestringEmployee's last name
middleNamestringEmployee's middle name. Empty if not set.
birthDatestring (ISO 8601 date-time)Employee's date of birth
hireDatestring (ISO 8601 date-time)Employee's hire date
emailstringEmployee's email address
phonestringEmployee's phone number. Empty if not set.

The employee now exists as an active employee of the company, with the hire date you set in Step 3.



Did this page help you?