Workday

This guide covers everything a developer needs to deploy the Incode x Workday Identity Verification (IDV) middleware integration (v1 Lite).

Overview

When a new hire is created in Workday, this integration automatically sends them an Incode identity verification link. When they complete the scan, the result is written back to Workday as a Government ID record.

Integration flow:

  1. Workday Hire business process fires an HTTP callout → POST /trigger
  2. Middleware authenticates with Incode, creates an IDV session, returns the verification URL
  3. Workday delivers the URL to the employee
  4. Employee completes ID scan in Incode
  5. Incode fires a webhook → POST /webhook
  6. Middleware exchanges refresh token for a Workday OAuth token, then writes the IDV result via SOAP Change_Government_IDs

Tech stack:

  • Node.js / Express (3 endpoints: POST /trigger, POST /webhook, GET /health)
  • Incode OAuth2 client_credentials flow
  • Workday OAuth2 refresh_token grant (ISU machine-to-machine)
  • Workday SOAP API v43.0 — Human_Resources web service

Prerequisites

Incode

ItemNotes
B2B onboarding enabledRequired on your Incode tenant
OAuth2 API Clientclient_credentials grant — save Client ID and Secret
API KeyFrom Incode Dashboard
Integration Reference IDFrom Incode Dashboard — identifies the workflow
Auth URLhttps://auth.demo.incode.com/oauth2/token (demo)
API URLhttps://demo-api.incodesmile.com (demo)
⚠️

All session creation calls require the header api-version: 1.0. Without it, the API returns 406 Not Acceptable.

Workday

Step 1 — Create an Integration System User (ISU)

  1. Workday search → Create Integration System User
  2. Name: e.g. Incode_IDV_ISU
  3. Check Do Not Allow UI Sessions
  4. Save

Step 2 — Create a Security Group and assign domain permissions

  1. Workday search → Create Security Group → type: Integration System Security Group
  2. Name: e.g. Incode IDV Integration
  3. Add Incode_IDV_ISU as a member
  4. Workday search → Maintain Permissions for Security Group → select Incode IDV Integration
  5. Under Domain Security Policy Permissions, add:
DomainAccess
National ID IdentificationGet and Put
  1. Under Business Process Security Policy Permissions, add:
Business ProcessPermission Type
Change Government IDsInitiating Action
  1. Save → Workday search → Activate Pending Security Policy Changes → submit

Step 3 — Register an API Client for Integrations

  1. Workday search → Register API Client for Integrations
  2. Client Name: e.g. Incode IDV Client
  3. Non-Expiring Refresh Tokens: Yes
  4. Save → copy Client ID and Client Secret

Step 4 — Generate a Refresh Token

  1. Workday search → Manage Refresh Tokens for Integrations
  2. Select the ISU → Generate New Refresh Token
  3. Copy the token value

Step 5 — Configure the Hire Business Process HTTP Callout

In the Hire business process, add an Integration step that POSTs to /trigger with this body:

{
  "workerID": "{{Employee_ID}}",
  "fullName": "{{Legal_Name}}",
  "personalEmail": "{{Personal_Email}}",
  "tenantURL": "https://impl.wd12.myworkday.com/ccx/service/your_tenant",
  "workdayClientId": "{{Client_ID}}",
  "workdayClientSecret": "{{Client_Secret}}",
  "workdayRefreshToken": "{{Refresh_Token}}",
  "integrationReference": "{{Integration_Reference_ID}}",
  "linkValidityMinutes": 1440
}

Environment variables

# Incode
INCODE_CLIENT_ID=
INCODE_CLIENT_SECRET=
INCODE_AUTH_URL=https://auth.demo.incode.com/oauth2/token
INCODE_API_URL=https://demo-api.incodesmile.com
INCODE_API_KEY=
INCODE_INTEGRATION_REFERENCE=
LINK_VALIDITY_MINUTES=1440

# Workday
WORKDAY_TENANT=your_tenant_name
WORKDAY_CLIENT_ID=
WORKDAY_CLIENT_SECRET=
WORKDAY_REFRESH_TOKEN=
WORKDAY_ISU_USERNAME=Incode_IDV_ISU
AUTO_COMPLETE=true

# Middleware
PORT=3000
WEBHOOK_SECRET=       # optional — for HMAC signature validation

API endpoints

POST /trigger

Receives the Workday callout. Creates an Incode IDV session and returns the verification URL.

Request body:

{
  "workerID": "12345",
  "fullName": "Jane Doe",
  "personalEmail": "[email protected]",
  "tenantURL": "https://impl.wd12.myworkday.com/ccx/service/mytenant",
  "workdayClientId": "...",
  "workdayClientSecret": "...",
  "workdayRefreshToken": "...",
  "integrationReference": "...",
  "linkValidityMinutes": 1440
}

Response:

{ "url": "https://incode.me/verify/..." }

POST /webhook

Receives Incode SESSION_SUCCEEDED events. Validates HMAC-SHA256 signature (header: x-incode-signature) if WEBHOOK_SECRET is set. Looks up the session context by externalCustomerId, fetches a fresh Workday OAuth token, then writes Change_Government_IDs via SOAP.

GET /health

Returns { "status": "ok" }. Use for load balancer health checks.


Deployment

Local testing with ngrok:

npm install
cp .env.example .env    # fill in all values
node src/index.js

# In a second terminal:
ngrok http 3000
# Use the ngrok HTTPS URL as your Incode webhook endpoint

Production:

npm install --production
NODE_ENV=production node src/index.js

Recommend deploying behind a reverse proxy (nginx / AWS ALB) with TLS termination. The service is stateless except for the in-memory session store — for production, replace sessionStore with Redis.


National ID type code mapping

Workday National_ID_Type_Code values are country-specific, formatted as {ISO3166Alpha3}-{Suffix}. The middleware auto-derives codes from the issuing country and document type returned by Incode OCR. Common mappings:

Incode document typeIssuing countryWorkday code
passportIN (India)IND-PAS
passportJP (Japan)JPN-PAS
passportBY (Belarus)BLR-PAS
national_idUSUSA-SSN
national_idMXMEX-CURP
passportUSUSA-SSN

For country-specific exceptions, add entries to COUNTRY_DOC_OVERRIDES in src/utils/documentTypeMap.js.


Workday SOAP — critical notes

TopicDetail
OAuth token placementBearer token in HTTP Authorization header — not in WS-Security SOAP header
Services hostSandbox: impl-services1.wd12.myworkday.com (different from UI host impl.wd12.myworkday.com)
SOAP endpointhttps://{services-host}/ccx/service/{tenant}/Human_Resources/v43.0
Person referenceUse Person_Reference (not Worker_Reference) inside Change_Government_IDs_Data
Verification datexsd:date format — YYYY-MM-DD only, no time component
Country reference typeISO_3166-1_Alpha-2_Code
ID type referenceNational_ID_Type_Code
Replace_AllSet to false to preserve existing IDs

Troubleshooting

ErrorCauseFix
Incode invalid_clientWrong client secretRe-paste INCODE_CLIENT_SECRET directly — do not retype
Incode 406 Not AcceptableMissing api-version headerAdd api-version: 1.0 to session creation request
Incode Employee by login factor cannot be foundEmail not in IncodeUse an email that exists in the Incode tenant
Workday 404 on token endpointWrong hostUse services host, not UI host
Workday invalid_client (OAuth)Misread client ID/secretPaste directly from Workday — don't retype
Workday invalid username or passwordTenant has disabled SOAP Basic AuthSwitch to OAuth refresh_token grant
SOAP The task submitted is not authorizedMissing BP security policyAdd ISU group to Change Government IDs BP Initiating Actions
SOAP Invalid Subelement Worker_ReferenceWrong SOAP schemaUse Person_Reference inside Change_Government_IDs_Data
SOAP PASSPORT is not a valid National_ID_Type_CodeGeneric codes don't existUse country-specific code e.g. IND-PAS
SOAP Invalid ID type Country_IDWrong type attributeUse ISO_3166-1_Alpha-2_Code

Go-live checklist

Incode

  • OAuth client created, credentials saved
  • API key configured
  • Integration Reference ID confirmed
  • Webhook URL registered pointing to /webhook
  • WEBHOOK_SECRET set and matches Incode dashboard

Workday

  • ISU created with "Do Not Allow UI Sessions"
  • Security group created, ISU assigned
  • Domain permission: National ID Identification — Get and Put
  • BP policy: Change Government IDs — Initiating Action
  • Security policy changes activated
  • API Client registered, Client ID + Secret saved
  • Refresh token generated and saved
  • Hire BP HTTP callout configured with correct field mapping

Middleware

  • All env vars populated
  • /health returns { status: 'ok' }
  • /trigger tested manually — returns IDV URL
  • /webhook tested with sample payload — writes to Workday
  • TLS enabled on public endpoint
  • Session store replaced with Redis for production