Skip to main content
CMDashCybersecurity Maturity Dashboard
← Back to Workday

Workday — Integration Setup

Connecting Workday gives CMDash your worker roster — employees and contingent workers — with hire dates, termination dates, supervisory organization, and management chain. That roster is the evidence behind joiner/leaver controls, periodic access reviews, and the contractor population auditors ask about separately.

Prerequisites

  • A Workday tenant with REST API access enabled
  • A Workday Integration System Administrator able to register an API client
  • CMDash Enterprise tier (Workday deployments are enterprise by construction)

Workday has no central authorization server. Both the token endpoint and the API live on your Workday host (https://<your-host>/ccx/oauth2/<tenant>/token). CMDash builds the token endpoint from the hostname and tenant on your connection, and authenticates with the API client you registered; the client secret is stored encrypted and erased when you disconnect.

Step 1: Register an API Client in Workday

  1. In Workday, search for and run the Register API Client for Integrations task.
  2. Set:
    • Client Name: CMDash Integration
    • Non-Expiring Refresh Tokens: enabled
    • Scope (Functional Areas): Staffing only
  3. Save. Workday shows the Client ID and Client Secret once — copy both.
  4. Run View API Clients and note your Token Endpoint and REST API Endpoint; they contain your host and tenant name.
  5. Run Manage Refresh Tokens for Integrations for the integration system user and generate a refresh token.

Grant Staffing only. The security group backing this API client determines what the token can read. Do not include Compensation, Payroll, Benefits, Worker Data: Personal Data, or Performance functional areas — CMDash never calls those sub-resources, and excluding them makes that guarantee enforceable by Workday rather than by CMDash's code.

Step 2: Self-hosted licensed installs only (optional)

On CMDash Cloud skip this step: everything is entered on the connection (Step 3). On a licensed single-organization install (on-prem / customer-AWS) a connection made before per-connection clients existed keeps using these deployment settings, and only those installs read them: WORKDAY_HOST, WORKDAY_TENANT, WORKDAY_CLIENT_ID, WORKDAY_CLIENT_SECRET.

Step 3: Connect in CMDash

  1. Navigate to Integrations in your CMDash dashboard.
  2. Find Workday and click Connect.
  3. Enter:
    • API client ID and API client secret — from Step 1
    • Hostname — e.g. wd5-impl-services1.workday.com
    • Tenant — e.g. acme (letters, digits, hyphen, underscore)
    • (or) Base URL — a full override, which wins over the two above
  4. Click Save & Test Connection.

What Syncs

Data TypeWorkday APISync Frequency
Workforce rosterGET https://<host>/ccx/api/v1/<tenant>/workers?limit=100&offset=NEvery 4 hours by default (configurable per connection)

Pagination walks offset/limit against the response's reported total, and stops early on a short page.

Each roster row is normalized to: external id, work email, display name, employment status, employment type, hire date, termination date, department (supervisory organization), business title, manager reference, and isManager. CMDash also derives a summary alongside the rows — headcount, active/terminated counts, contractor count, joiners and leavers in the last 30 days, and terminatedButActiveCount.

Hire date is the earlier of originalHireDate (company tenure) and hireDate (current position), because an internal move resets the latter.

Employment type: Workday calls contractors "Contingent Worker", which maps to contractor; "Employee" maps to employee; anything else stays unknown.

Workday is the only one of the four HRIS integrations that states isManager directly, so it is the only one where CMDash records the negative ("manages nobody") as a fact rather than leaving it blank.

Limitations

Read this before deploying — Workday is the most tenant-configurable product of the four, and this adapter has not been validated against a live Workday tenant.

  • Field availability varies by tenant. The endpoint, the { total, data[] } envelope, the limit/offset pagination, and the client-credentials token flow are all from Workday's published REST documentation and are solid. Which of hireDate, originalHireDate, terminationDate, active, and workerType your /workers collection actually returns depends on your tenant's configuration and the security group backing the API client. CMDash reads every field defensively and reports unknown rather than guessing.
  • If your /workers collection omits the lifecycle dates, expose the roster as a RaaS (Reports-as-a-Service) custom report instead and point the connection's Base URL at it. Build a custom report on the Worker business object with the columns CMDash normalizes, enable Web Service as an output, and use its JSON URL.
  • One Workday tenant per connection, and the connection holds its own API client, host and tenant, so one CMDash deployment serves many Workday customers.
  • A status of unknown is not a bug. A Workday tenant that does not publish active must not read as "everyone is currently employed", so CMDash refuses to infer it.

What CMDash Deliberately Does Not Read

Workday's worker object is a hub: person, compensation, personalInformation, payslips, benefits, performance, and nationalIDs are all reachable as sub-resources or expansions off the same worker id CMDash already holds. None are requested:

  • No expansions are sent at all, so the response stays the summary collection. Compensation, pay, and payslip sub-resources are never called.
  • .../workers/{id}/personalInformation — date of birth, gender, marital status, ethnicity, home contact. Never called.
  • nationalIDs / government identifiers. Never called.
  • Performance and disciplinary sub-resources. Never called.

Only identity (name, work email) and employment lifecycle (status, type, dates, supervisory organization, manager reference) are normalized. A differently-configured Workday tenant that volunteers extra fields inline cannot leak them through — the roster record is constructed field by field from a fixed list, never copied wholesale.

Turning the roster into a control

Create a continuous-compliance rule against the roster:

  • Provider: Workday
  • Data type: Workforce
  • Condition: terminatedButActiveCount <= 0
  • Target status: Implemented

The control reads as Implemented while every departed worker is correctly marked inactive in Workday, and drifts to At Risk / Failing when one is not — with an auto-opened remediation task. Other useful fields on the same datum: headcount, activeCount, terminatedCount, contractorCount, joinersLast30d, leaversLast30d.

Training completion

Workday Learning is a separate product API, not the Staffing /workers collection this integration reads, so training completion is not synced. Security-awareness training completion in CMDash comes from KnowBe4.

Troubleshooting

  • 401 Unauthorized: The refresh token expired or was revoked in Manage Refresh Tokens for Integrations. Regenerate it.
  • 403 Forbidden: The security group backing the API client cannot read the Worker business object. Grant the Staffing functional area.
  • Everyone shows as unknown status: Your /workers collection is not returning active or terminationDate. Use the RaaS fallback above.
  • Department is blank: Your workers have no supervisoryOrganization, or the API client cannot see it.
  • Sync fails with "refusing to store a partial roster": Your roster exceeds 50,000 rows. CMDash fails rather than truncating, because a missing leaver reads as "no offboarding failure". Contact support.

Rate Limits & Sync Behavior

SettingValue
Sync frequencyEvery 4 hours by default (syncInterval is configurable per connection)
Page size100 workers per request (Workday's documented maximum; the default is 20)
Roster cap50,000 rows — exceeding it fails the sync rather than storing a partial roster
Rate limit handlingRequests are paced through CMDash's shared per-tenant token bucket
Circuit breakerRepeated failures open the breaker and skip the cycle rather than hammering Workday
Failed sync retryJob queue retries 3 times with exponential backoff (starting at 30s) — see docs/developers/dead-letter.md

Because the hostname and tenant are both supplied per connection, every request is routed through CMDash's SSRF guard, which validates and pins the resolved address before connecting and never auto-follows redirects.

Credential Rotation

Workday client secrets do not expire, but refresh tokens can be revoked at any time.

  1. In Workday: View API Clients → CMDash Integration → Generate New API Client Secret.
  2. In CMDash, edit the Workday connection and paste the new secret.
  3. Regenerate the refresh token via Manage Refresh Tokens for Integrations if it was also rotated.

Ready to see your posture?

Start a 30-day free trial of any paid plan — a card is required, and it converts unless you cancel — or sign up for Free with no card at all.