Company Verification (KYC)

Company Verification (KYC) is the regulatory check on a company's business legitimacy and ownership. It can run at any time, including alongside Company Bank Setup.

If KYC isn't enabled in your environment, you can skip this flow entirely.

Before you submit KYC for review

Three things must be in place before you can submit KYC for review:

  • A signatory. The company must have a signatory. See Company Signatories.
  • The required documents. Every required company and signatory document must be uploaded (Steps 4 and 5 below).
  • Bank Setup, if a bank provider is enabled. If a bank provider is enabled for the company, KYC can't be submitted for review until Bank Setup reports a Completed status.

Two ways to call these APIs

As with Bank Setup, every step below can be done either by calling a function from the @worklio/wep package or by calling the underlying REST endpoint directly.

@worklio/wep functions such as wepGetCompanyKYC don't make network requests themselves — they return a descriptor (method, url, and optionally createRequestBody/useResponseBody) that you execute yourself, for example with fetch. See Company Bank Setup: Two ways to call these APIs for the full explanation and a reusable callWepApi helper.

Prerequisites

RequirementDescription
FEINThe company's Federal Employer Identification Number must be valid and registered before onboarding begins.
Company IDRequired for every call below. Returned as id when you create the company.
SignatoryThe company must have a signatory before KYC can be submitted for review. See Company Signatories.
AuthorizationOnly administrative roles can call these endpoints.

Company Verification flow

Step 1: Get the KYC summary

Fetch the company's current KYC status along with the business and ownership fields already on file: business name, FEIN, address, ownership info, and KYC status.

  • wep: wepGetCompanyKYC(companyId)
  • Endpoint: GET /wep/companies/{companyId}/kyc
require("dotenv").config();

const TOKEN = process.env.API_KEY
const CLIENT_ID = process.env.CLIENT_ID

const URL = process.env.URL
const url = `${URL}/wep/companies/${CLIENT_ID}/kyc
`


async function getKYC() {
  const response = await fetch(url, {
    method: "GET",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/json",
      "x-api-version": "2.0"
    },

  });

  console.log(response.status);

  const raw = await response.text();
  console.log(JSON.stringify(JSON.parse(raw), null, 2));

}

getKYC();
{
  status: 0
}

This same call is used again at the end of the flow (Step 9) to confirm completion.

Step 2: List company KYC documents

Retrieve the documents already uploaded for the company, and their status.

  • wep: wepGetCompanyKYCDocuments(companyId)
  • Endpoint: /wep/companies/{clientId}/kyc/documents
require("dotenv").config();

const TOKEN = process.env.API_KEY
const CLIENT_ID = process.env.CLIENT_ID

const URL = process.env.URL
const url = `${URL}/wep/companies/${CLIENT_ID}/kyc/documents
`


async function listMyKYCDocuments() {
  const response = await fetch(url, {
    method: "GET",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/json",
      "x-api-version": "2.0"
    },

  });

  console.log(response.status);

  const raw = await response.text();
  console.log(JSON.stringify(JSON.parse(raw), null, 2));

}

listMyKYCDocuments();
[]

Step 3: Make sure the company has a signatory

KYC can't be submitted for review unless the company has a signatory. Check whether one exists.

  • wep: wepGetSignatories(companyId)
  • Endpoint: GET https://api.worklio.com/wep/companies/{companyId}/kyc/signatories
require("dotenv").config();

const TOKEN = process.env.API_KEY_BENEFIT;
const CLIENT_ID = process.env.CLIENT_ID_BENEFIT;
const URL = process.env.URL_BENEFIT;
const url = `${URL}/wep/companies/${CLIENT_ID}/kyc/signatories`;




async function getSignatories() {
  const response = await fetch(url, {
    method: "GET",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "x-api-version": "2.0",
      "content-type": "application/json",
    },
  });

  console.log(response.status);
  const raw = await response.text();
  console.log(JSON.stringify(JSON.parse(raw), null, 2));

getSignatories()
[
  {
    "id": 7,
    "type": 3,
    "status": 1,
    "gender": 2,
    "firstName": "John",
    "lastName": "Doe",
    "middleName": "",
    "ownership": 100
  }
]

If the list is empty, add a signatory before you continue. Adding, updating, and removing signatories, and managing the documents on file for each signatory, are covered in Company Signatories.

Step 4: Get required document types

Get the list of required document types. The endpoint returns documents for one scope at a time, so call it once for the company and once for its signatories:

  • wep: wepGetCompanyKYCDocumentTypes({ companyId, documentScope })

  • Endpoint: GET https://api.worklio.com/wep/companies/{companyId}/kyc/documenttypes

require("dotenv").config();

const TOKEN = process.env.API_KEY
const CLIENT_ID = process.env.CLIENT_ID

const URL = process.env.URL
const url = `${URL}/wep/companies/${CLIENT_ID}/kyc/documenttypes
`


async function getRequiredDocumentTypes() {
  const response = await fetch(url, {
    method: "GET",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "content-type": "application/json",
      "x-api-version": "2.0"
    },

  });

  console.log(response.status);

  const raw = await response.text();
  console.log(JSON.stringify(JSON.parse(raw), null, 2));

}

getRequiredDocumentTypes();
[
  {
    "key": "EIN_DOC",
    "name": "EIN Document - Form 147C",
    "description": "Image of Form 147C issued to the business by the IRS.",
    "scope": 1,
    "isFile": true,
    "fileTypes": "png,jpg,jpeg,pdf",
    "isSecondary": false,
    "trustLevel": 0,
    "isRequired": true
  },
  {
    "key": "SS4_DOC",
    "name": "EIN Application - Form SS4",
    "description": "Image of Form SS4 that is used by a business to apply for a TIN number.",
    "scope": 1,
    "isFile": true,
    "fileTypes": "png,jpg,jpeg,pdf",
    "isSecondary": false,
    "trustLevel": 0,
    "isRequired": true
  }
]

Without scope, the response contains company documents only. It never includes signatory documents. For the signatory response and the rules for choosing which documents to upload, see Company Signatories.

Compare the company list against the documents from Step 2, and the signatory list against the signatory's documents, to determine which documents are still missing.

Same as with signatories only one of the documents needs to be uploaded.

Step 5: Upload required documents

Upload each required document. Uploading documents for a signatory is covered in Company Signatories.

  • wep: wepCreateCompanyKYCDocument(companyId)
  • Endpoint: POST /wep/companies/{clientId}/kyc/documents
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 URL = process.env.URL;
const url = `${URL}/wep/companies/${CLIENT_ID}/kyc/documents`;

const filePath = path.join(__dirname, "SS4_DOC_MY_COMPANY.pdf");

async function uploadKYC() {
  const fileBuffer = fs.readFileSync(filePath);
  const fileName = path.basename(filePath);

  const form = new FormData();
  form.append("Document[0].Key", "SS4_DOC");
  form.append(
    "Document[0].File",
    new Blob([fileBuffer], { type: "application/pdf" }),
    fileName
  );
  form.append("Key", "SS4_DOC")

  const response = await fetch(url, {
    method: "POST",
    headers: {
      accept: "application/json",
      "api-version": "2.0",
      authorization: `Bearer ${TOKEN}`,
      "x-api-version": "2.0",
      // do NOT set content-type manually — fetch adds the correct
      // multipart/form-data boundary for you
    },
    body: form,
  });

  console.log(response.status);
  const raw = await response.text();
  console.log(JSON.stringify(JSON.parse(raw), null, 2));

}

uploadKYC()
true

Step 6: Save or update KYC information

Save the business and ownership information collected in Steps 1–5.

  • wep: wepSaveCompanyKYC(companyId)
  • Endpoint: /wep/companies/{clientId}/kyc
require("dotenv").config();

const TOKEN = process.env.API_KEY
const CLIENT_ID = process.env.CLIENT_ID
const SIGNATORY_ID = process.env.SIGNATORY_ID
const URL = process.env.URL
const url = `${URL}/wep/companies/${CLIENT_ID}/kyc
`

const payload = {
  startOn: "2027-01-01",
  primaryControllingContactSignatoryId: SIGNATORY_ID,
  complianceContactSignatoryId: SIGNATORY_ID,
  bankingSignatoryId: SIGNATORY_ID,
  moneyTransmissionLicense: true,
  involvedWithCryptocurrency: false,
  gamblingBusiness: false,
  registeredWithSecurities: true,
  regularFinanacialCompany: true,
  ownedByPublicCompany: false,
  publicCompany: true
}



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

  });

  console.log(response.status);

  const raw = await response.text();
  console.log(JSON.stringify(JSON.parse(raw), null, 2));

}

saveKYC();
{
  "status": 1,
  "trustLevel": 0,
  "message": "",
  "publicCompany": true,
  "ownedByPublicCompany": false,
  "regularFinanacialCompany": true,
  "registeredWithSecurities": true,
  "gamblingBusiness": false,
  "involvedWithCryptocurrency": false,
  "moneyTransmissionLicense": true,
  "bankingSignatoryId": 4,
  "complianceContactSignatoryId": 4,
  "primaryControllingContactSignatoryId": 4,
  "startOn": "2027-01-01T00:00:00Z"
}

Step

Step 7: Submit KYC for review

Once every required field and document is in place and the company has a signatory, submit for review.

  • wep: wepSaveCompanyKYC(companyId)
  • Endpoint: /wep/companies/{clientId}/kyc/process
require("dotenv").config();

const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const URL = process.env.URL;
const url = `${URL}/wep/companies/${CLIENT_ID}/kyc/process
`

const payload = {
 trustLevel: 3,
}



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

  });

  console.log(response.status);

  const raw = await response.text();
  console.log(JSON.stringify(JSON.parse(raw), null, 2));

}

submitForReview();
true

Step 8: Verify Company

Verify the company.

  • wep: wepSaveCompanyKYC(companyId)
  • Endpoint: /wep/companies/{clientId/kyc/verify
require("dotenv").config();

const TOKEN = process.env.API_KEY_BENEFIT;
const CLIENT_ID = process.env.CLIENT_ID_BENEFIT;
const URL = process.env.URL_BENEFIT;
const url = `${URL}/wep/companies/${CLIENT_ID}/kyc/verify
`

const payload = {
  status: 3,
  verifyAllDocuments: true,
  verifyAllSignatories: true
}



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

  });

  console.log(response.status);

  const raw = await response.text();
  console.log(JSON.stringify(JSON.parse(raw), null, 2));

}

verifyKYC();
true

Step 9: Confirm completion

Check that KYC and document statuses are both complete before enabling payroll.

  • wep: wepGetCompanyKYC(companyId), wepGetCompanyKYCDocuments(companyId)
  • Endpoint: same calls as Steps 1 and 2.

KYC verification status

ValueMeaning
NoneNo verification has started.
PendingVerification has been submitted and is awaiting a decision.
CompletedVerification is complete.

Function reference

DomainPurposewep functionMethod
Company KYCGet KYC summarywepGetCompanyKYC(companyId)GET
Company KYCSave/update KYC datawepSaveCompanyKYC(companyId)POST
KYC DocumentsList company docswepGetCompanyKYCDocuments(companyId)GET
KYC DocumentsGet document detailwepGetCompanyKYCDocumentDetail({ companyId, documentId })GET
KYC DocumentsGet required doc typeswepGetCompanyKYCDocumentTypes({ companyId, documentScope })GET
KYC DocumentsGet doc configurationwepGetCompanyKYCDocumentConfigurations({ companyId, documentKey })GET
KYC DocumentsUpload documentswepCreateCompanyKYCDocument(companyId)POST

Signatory functions are listed in Company Signatories.

Glossary

TermDefinition
KYCThe regulatory process that verifies a business's legitimacy and ownership.
SignatoryA person who signs for the company. A company needs one before KYC can be submitted for review. See Company Signatories.
Beneficial OwnerAn individual with significant control or authority over the company — collected as part of ownership info in Steps 1 and 6.

Enabling payroll

Payroll can only be enabled once KYC reports Completed (or is disabled for this environment) and Company Bank Setup reports Completed. Record the timestamp each track completes — you'll want it for audits and support.

Error handling and edge cases

  • Only users with administrative privileges should call these endpoints.
  • GET /kyc/documenttypes returns company documents unless you pass scope=2. Call it for both scopes to get the full list.

Did this page help you?