---
name: workday-oauth-app
description: Obtains working Workday OAuth 2.0 credentials for a multi-tenant connector — where the client ID and secret are registered by each customer's own Workday administrator inside their own tenant, not by you in a central developer portal. Covers the Register API Client and Register API Client for Integrations tasks, the tenant-specific authorize/token/API hosts, functional-area scopes and the domain security policies underneath them, the Integration System User and security group model that decides whether a valid token returns data or nothing, non-expiring refresh tokens, tenant refreshes, RaaS, and the REST-vs-SOAP fork. Use when asked to get Workday OAuth credentials, write the Workday onboarding runbook for a customer admin, pick between Workday's REST and SOAP integration paths, or fix a Workday error like a 401 Invalid Access Token, a 403 Not Authorized, an S21 404 on a worker sub-resource, or a sync that returns an empty tenant. For any other vendor's developer portal, use that vendor's skill instead.
---

# Workday OAuth 2.0 Credentials

**Read the next four lines before anything else, because they are the whole shape of this task.**

There is no Workday developer portal that issues you one client ID and secret for all your customers. For a
connector that reaches other organizations' Workday tenants, **every customer's own Workday administrator
registers an API client inside that customer's tenant and hands you a separate client ID and client secret**
(§1). Your deliverable is therefore not a registration form you fill in — it is an **admin runbook you hand to
each customer**, plus a connect flow that can accept per-customer endpoints (§3, §5).

Three more things decide whether this works, and none of them is on the registration form.

**Every host is per-customer, and there are two of them.** The authorization host and the web-service host are
different machines. Workday's own worked example shows an authorization endpoint of
`https://wd3.myworkday.com/tenant_name/authorize` and a REST endpoint of
`https://wd3-services1.myworkday.com/api/v1/tenant_name` — same customer, different hostnames. Your connect
flow has to collect both, or derive them, before it can build an authorize URL (§3).

**A scope is not enough.** Workday has two independent permission layers: the functional-area *scopes* on the
API client, and the *domain security policies* granted to the security group behind the authorizing account.
Get the scope right and the domain wrong and Workday does not error — it returns fewer rows, or none. This is
the single most misdiagnosed Workday failure and it looks like an empty tenant (§4, §7).

**Security changes do nothing until they are activated.** A Workday admin can do every step correctly and still
have a dead integration because nobody ran **Activate Pending Security Policy Changes** (§4).

## Inputs to collect before you start

Ask in one batch. Never invent.

| Input | Notes |
| --- | --- |
| **Which customer tenant**, and which type | Production, Sandbox, Sandbox Preview or Implementation — each is a separate tenant with its own hosts and its own registration (§3) |
| **Who the Workday-side admin is** | Only they can run the tasks. You cannot do any of this yourself (§1) |
| **REST or SOAP** | Decides the whole integration, not just the credential (§2) |
| **Auth host, webservice host, tenant name** | Three distinct values (§3) |
| **Which functional areas** the integration needs | Drives the Scope prompt on the API client (§7) |
| **Which domain security policies** the endpoints need | Per endpoint, from Workday's published endpoint descriptions (§7) |
| **Redirect URIs** | Every callback host you serve, exact strings (§6) |
| **Authorization-code grant or ISU refresh-token grant** | Two different registration tasks, two different runbooks (§5) |
| **Dedicated ISU, or a named human** | Decides what the token can see, and whether it dies when somebody leaves (§4) |
| **Is a Workday sandbox/demo tenant needed?** | Not self-serve; a commercial conversation (§11) |

## Quick Start

1. Confirm the premise with the user: **credentials are per-customer**, so this is a runbook, not a registration (§1).
2. Decide **REST or SOAP** before writing a line of the runbook (§2).
3. Establish the customer's **auth host, webservice host and tenant name**, and which tenant type (§3).
4. Have the admin create an **ISU**, an **integration system security group**, grant **Report/Task View** on the
   right domains, and run **Activate Pending Security Policy Changes** (§4).
5. Have the admin enable **OAuth 2.0 Clients** in tenant setup, then run **Register API Client** (authorization
   code grant) *or* **Register API Client for Integrations** (ISU refresh-token grant) (§5).
6. Give them the exact **redirect URI** list to paste (§6).
7. Give them the exact **functional areas** to select, and the **domains** to grant (§7).
8. Collect client ID, client secret, authorization endpoint, token endpoint and REST endpoint — the secret is
   shown once (§8).
9. Work out the **token lifetimes** and whether non-expiring refresh tokens were enabled (§9).
10. Warn them, in writing, about **tenant refreshes and scope changes** (§10).
11. Verify end to end against the customer's real tenant, then hand off (§13, §14).

## Platform state (verified 2026-09-20 — re-verify before trusting)

**A large part of Workday's integration documentation sits behind a Workday Community sign-in.** Everything in
this skill that is stated as fact was read on a page a signed-out reader can open. Where a claim could not be
verified that way it is labelled **[unverified]** and you should treat it as a hypothesis to test, not a fact to
assert. The honest list of what is walled is in §15.

- **There is no self-serve Workday developer program that issues production API credentials for other
  companies' tenants.** Workday's own instruction for connecting a third-party product is a sequence of tasks
  the customer's administrator runs inside their tenant: `Edit Tenant Setup – Security` → `Register API Client`.
  Workday publishes this pattern in its own product integration guides.
- **Workday does operate a Developer Site Console that issues API clients** (`Create API Client`, with a client
  name, redirect URI, authorized CORS domains and a scope picker). That path is **Workday Extend / Orchestrate**,
  it routes through regional API gateways rather than the customer's tenant host, and — crucially — *"When you
  migrate your client to a nondevelopment tenant such as an implementation or production tenant, you must allow
  the client ID on the target tenant. Only a Company Administrator can add or remove tenants from an API
  client's allowlist."* So even the closest thing to a central client still needs per-customer consent by that
  customer's own administrator. See §1.
- **REST API security is documented as acting on behalf of a user.** Workday: *"REST APIs act on behalf of the
  individual using the client. The user's security profile affects the REST API access to Workday resources."*
  And: *"If the current user can't access the domain or business process, the REST API returns an error.
  Example: The `GET /supervisoryOrganizations` REST API returns only the organizations that the user has
  authorization to access."* Scopes cap what a token may request; they never widen what the account may see.
- **Report/Task permissions are what REST needs. Integration permissions are SOAP-only.** Workday states this
  explicitly: *"Workday REST APIs require the Report/Task Permissions in the security policy, whether the user
  is an ISU or an individual user. The Integration Permissions only apply to Workday SOAP web services."*
  **View** for GET, **Modify** for POST/PUT/PATCH/DELETE. A runbook that tells an admin to grant Integration →
  Get for a REST integration is wrong and will look like a permissions bug forever.
- **Scopes are functional areas, published per endpoint.** Workday: *"The endpoint descriptions … include the
  domains that secure the endpoints. The endpoint descriptions also include the scope, which is the
  corresponding Workday functional area."* Both the REST API Explorer and the REST Services Directory are
  reachable signed-out.
- **Pagination is `limit`/`offset`,** default `limit` 20, maximum 100, *"Some REST APIs override this value with
  a maximum of 1000"*, and Workday sorts *"in ascending instance ID order"* by default.
- **`Non-Expiring Refresh Tokens` is a checkbox on the registration task,** and Workday's own integration guides
  tell administrators to tick it.
- **Refresh-token lifetime is absolute, not rolling** — for the Developer Site client at least: *"That refresh
  timeout begins at the first authorization step and doesn't get extended, even if you get a new refresh
  token."* **[unverified]** for the in-tenant client; assume the same and test.
- **Access-token lifetime is not a fixed number.** Workday: *"The token timeout is tied to the session timeout
  configured for the user by the Workday administrator."* Anything that hardcodes one hour is guessing (§9).
- **A tenant refresh copies production into the lower tenants.** Workday: *"A tenant refresh copies all assets
  (including the ISU configuration) from Production (PROD) to Implementation (IMPL) and Sandbox (SBOX)
  tenants."* Written about Extend apps, but the mechanism is the tenant, not the app (§10).
- **A Workday sandbox is not self-serve.** Non-production tenants belong to Workday customers and to partners
  under a partner agreement; Workday's partner and Marketplace programs are commercial relationships, not a
  signup form (§11).

If the customer's tenant does not look like this, stop and report what you actually see rather than clicking on.

## 1. The premise: per-customer credentials, not a central app

**Answer this out loud before you do anything else, because it reshapes the project plan.**

For a connector that reaches *other organizations'* Workday tenants, the working model is:

> Each customer's Workday administrator registers an API client in their own tenant and gives you that tenant's
> client ID, client secret, authorization endpoint, token endpoint and REST endpoint. You store one credential
> set **per connection**, not one per integration.

The consequences, all of which are project-planning consequences rather than technical ones:

- **Your connect flow must accept credentials from the customer.** A "Connect Workday" button that only kicks
  off a redirect cannot work, because you do not know the authorize host until the customer tells you.
- **Onboarding is a document.** Budget for a runbook, a support path for admins who get stuck at
  *Activate Pending Security Policy Changes*, and a way to collect a client secret securely.
- **Every scope change is a customer-side change.** You cannot add a functional area yourself. See §10.
- **There is nothing to "get approved".** No review queue, no install cap, no listing gate on the credential
  itself. The gate is the customer's own security team.

**The one thing that looks like a central app, and why it is not.** Workday's Developer Site Console does issue
an API client tied to *your* account, with a scope picker and a redirect URI, and it authenticates against
regional API gateways (`https://api.workday.com` / `https://api.us.wcp.workday.com` for the US, plus EU, UK and
Singapore gateways) rather than a customer host. But it is the **Workday Extend / Orchestrate** path — Workday
says plainly *"Don't use tenant base paths when calling Workday REST APIs with Extend and Orchestrate"* — and
reaching any customer's implementation or production tenant still requires **that customer's Company
Administrator** to add the tenant to the client's allowlist. It is per-customer consent wearing a central
client's clothes, and it comes with a different product (and a different commercial relationship) attached. If
someone proposes it as a shortcut to one-credential-for-all, that is a **Stop and ask**.

## 2. REST or SOAP — decide this first

Workday has two completely separate API surfaces, with two separate credential models. Picking the wrong one
costs weeks, because almost nothing transfers between them.

| | **REST (Workday REST APIs)** | **SOAP (Workday Web Services, WWS)** |
| --- | --- | --- |
| Credential | OAuth 2.0 — client ID + secret + refresh token | ISU username and password, carried as a WS-Security UsernameToken **inside the SOAP envelope** |
| Registration | `Register API Client` / `Register API Client for Integrations` in the tenant | No API client at all — just an ISU and a security group |
| Endpoint shape | one base path, service and version in the path | **one endpoint per service**, each versioned separately |
| Permissions | **Report/Task** View/Modify on domains | **Integration** Get/Put on domains |
| Workday's own positioning | *"better suited for smaller, user-initiated transactions … not for bulk data export or import"* | *"generally favored for large, scheduled, system-to-system data transfers"* |
| Coverage | Narrower; some objects have no REST equivalent | Much wider; the long tail of HR, payroll and financial objects lives here |
| Docs | REST API Explorer / REST Services Directory | Public Web Services API Directory (WSDLs and XSDs) |

**Choose SOAP when** the integration needs breadth (payslips, deductions, bank accounts, payment elections,
worker documents, financial objects) or bulk extraction. **Choose REST when** the integration needs a modern
JSON surface, low-latency reads, business-process events, or write operations that REST actually exposes.
Workday's own guidance is to use REST *"also for cases where no equivalent SOAP API exists"* — for example
retrieving business-process event attachments.

Two practical notes that decide the argument more often than the table does:

- **SOAP needs no OAuth registration at all.** If the customer's security team is slow, the ISU-and-password
  path gets you connected sooner — at the cost of holding a password.
- **Some data exists only on one side.** Worker document *files*, for instance, are a SOAP-side concern; the
  REST person photo endpoints are not a substitute. Check the object list against both directories before
  promising coverage.

**Product fact (2026-09-20).** This platform ships **two separate Workday connectors**, and the difference
between them is exactly this fork. The REST connector uses OAuth 2.0 authorization-code grant against the
service-based REST APIs (staffing, person, absence management, recruiting, learning, payroll, customer
accounts, accounts payable, procurement, business-process events) and covers HRIS, ATS, LMS and accounting
objects. The **legacy** connector is SOAP against Workday Web Services, authenticates with an **ISU username
and password over WS-Security UsernameToken with no HTTP Authorization header at all**, pins a single WWS
version across every service, and covers a noticeably wider object set — including payslips, deductions, bank
accounts, locations, taxonomies, payment terms, payments and a passthrough surface. **Choosing the wrong one is
the expensive mistake here.** Confirm the current split with the connector's owner before you write a runbook
for a customer.

## 3. Tenant, hosts and endpoints

Three values identify a customer, and they are not interchangeable.

| Value | What it is | Where the admin finds it |
| --- | --- | --- |
| **Tenant** | the customer's Workday tenant name, e.g. `tenant_name` | in the path of the REST endpoint, and in the sign-in URL |
| **Webservice host** | the host that serves API traffic, e.g. `wd3-services1.myworkday.com` | the host portion of the REST API endpoint |
| **Auth host** | the host that serves the authorize page, e.g. `wd3.myworkday.com` | the host portion of the authorization endpoint |

Workday's own worked example, verbatim: *"When the endpoint is
`https://wd3-services1.myworkday.com/api/v1/tenant_name`, then the Webservice Host is
`wd3-services1.myworkday.com` and the Tenant is `tenant_name`."* and *"When the authorization endpoint is
`https://wd3.myworkday.com/tenant_name/authorize`, the Auth Host is `wd3.myworkday.com`."*

So the endpoint shapes are:

```
authorize   https://<authHost>/<tenant>/authorize
token       https://<webserviceHost>/ccx/oauth2/<tenant>/token
REST        https://<webserviceHost>/api/<service>/<version>/<tenant>/...
```

Notes that each cost a cycle:

- **The authorize URL does not contain `/ccx/oauth2/`.** It is `https://<host>/<tenant>/authorize`. The token
  URL *does*. If you are working from a brief that puts both under `/ccx/oauth2/`, the authorize half is wrong.
- **The REST base path is published in two forms and both are in circulation.** Workday's REST fundamentals give
  `https://{tenantHostname}/api/{serviceName}/{version}/{tenant}` with the worked example
  `https://tenant1.myworkday.com/api/staffing/v7/gms/workers`; Workday's own product guides also print
  `https://example.myworkday.com/ccx/api/v1/<tenant>`. Treat **both `/api/…` and `/ccx/api/…`** as shapes a
  customer may paste, and normalize whichever arrives. A connector that only recognises one of them will build
  a doubled path and 404 (§13 has a live example of exactly this).
- **`wd2` / `wd3` / `wd5` / `wd103` style prefixes are the Workday pod the customer sits on**, and
  `-impl-`/`-services1` segments distinguish implementation from production service hosts. Workday's own
  examples show `wd3.myworkday.com`, `wd3-services1.myworkday.com` and `example.myworkday.com`; the fuller
  `wdN-impl-servicesN.workday.com` family is consistent across many integrators' guides but **[unverified]** on
  a publicly reachable Workday page. **Never hardcode a host — always collect it.**
- **Object ids** come in three documented forms: a 32-character Workday ID (WID), a reference id
  (`/workers/Employee_ID=21005`), and `me` for the current user.

**Tenant types are separate universes.** Workday customers run a **Production** tenant, a **Sandbox** (a copy of
production data, used for testing), a **Sandbox Preview** (a copy plus the next release's functionality), and
one or more **Implementation** tenants. Each has its own hostname, its own API client registration, its own ISU
and its own security activation. A credential from sandbox will not work against production, and testing in
sandbox proves nothing about production's domain grants. Plan for **two runbook runs per customer**, minimum.

## 4. Before any client exists: tenant setting, ISU, group, activation

This section is where Workday integrations actually die. Everything here is the customer admin's work.

**4.1 — Turn OAuth on for the tenant.** Task `Edit Tenant Setup – Security`, tick **OAuth 2.0 Clients Enabled**.
Workday's description: *"Enables OAuth 2.0 clients to access the Workday API for your tenant."* If this is off,
`Register API Client` is not usable and nothing downstream matters.

**4.2 — Create an Integration System User (ISU).** Task `Create Integration System User`. Workday's own guidance
for app integrations: create the account and *"Keep session timeout at zero to prevent expiration."* Common
practice is also to tick **Do Not Allow UI Sessions** so the account cannot be used interactively — sensible,
but note the interaction in §5.1.

Use a **dedicated ISU**, never a named person. A credential tied to a human dies when that human changes role or
leaves, and takes the customer's sync with it.

**4.3 — Create a security group and put the ISU in it.** Task `Create Security Group`, type **Integration System
Security Group (Unconstrained)** — or **Constrained**, if the customer wants to limit the integration to
specific supervisory organizations. Add the ISU as a member. A constrained group is a perfectly good answer to
a nervous security team, but say plainly that it will make the integration return a **subset** of the company,
and that the subset will look like missing data downstream.

**4.4 — Grant domain permissions.** Task `Maintain Permissions for Security Group`. For every endpoint the
integration calls, grant the group access on the domain that secures it:

- **REST: `Report/Task` permission — `View` for GET, `Modify` for POST/PUT/PATCH/DELETE.**
- **SOAP: `Integration` permission — `Get` / `Put`.**
- **Do not mix these up.** Workday: *"The Integration Permissions only apply to Workday SOAP web services."*

Two rules that catch people:

- **Sub-resources need the parent.** Workday: *"If your app calls a REST API sub-resource, ensure that the user
  has access to the parent resource."* A grant on the worker photo domain is useless if the worker domain is
  not granted.
- **Domain names drift between Workday releases.** Take the domain from the endpoint's own "Secured by" line in
  the current API documentation, not from a runbook written two releases ago.

**4.5 — Activate.** Task `Activate Pending Security Policy Changes`, with a comment. **Nothing in 4.3 or 4.4
takes effect until this runs.** Workday's own app-integration steps list it as a distinct step for exactly this
reason. Put it in the runbook in bold, and put it in your support macro too — it is the first thing to ask about
when a brand-new connection authenticates cleanly and returns nothing.

## 5. Register the API client — two tasks, two runbooks

Workday has **two** registration tasks and they produce different things. Pick deliberately.

### 5.1 `Register API Client` — authorization code grant (browser flow)

This is the one that uses a redirect URI and produces a user-bound token. Workday's own product guides give the
sequence:

| Field | Value |
| --- | --- |
| **Client Name** | anything identifiable, e.g. your product's name |
| **Client Grant Type** | **Authorization Code Grant** |
| **Access Token Type** | **Bearer** |
| **Redirection URI** | your callback (§6) |
| **Non-Expiring Refresh Tokens** | **tick it** (§9) |
| **Scope (Functional Areas)** | the areas the integration needs (§7) |

Then the admin, or whoever will own the connection, completes the browser authorization. **Whoever signs in at
the authorize screen is whose permissions the token carries.** Workday's own instruction for this is worth
copying into your runbook verbatim: sign in **in an incognito window** as the integration user, because
otherwise *"the integration doesn't connect with the wrong credentials for the Workday tenant, which would
cause syncing issues."* For SSO tenants Workday gives a bypass sign-in URL of the form
`https://<workdayDomain>/wday/authgwy/<tenant>/login.htmld?redirect=n`.

Note the tension with §4.2: an ISU set to **Do Not Allow UI Sessions** cannot sign in at an authorize screen.
For the browser flow the customer either relaxes that setting for the authorization, or authorizes as a
different service account. Raise it before they hit it. **[unverified]** on a public Workday page — confirm in
the tenant.

### 5.2 `Register API Client for Integrations` — ISU refresh-token grant (no browser)

A separate task. No redirect URI, no consent screen. The admin registers the client, ticks **Non-Expiring
Refresh Tokens**, selects the functional areas, saves the **Client ID** and **Client Secret**, and then — from
the client's related actions — runs **`Manage Refresh Tokens for Integrations`**, selects the **ISU** in the
**Workday Account** field, and copies the refresh token off the *Successfully Regenerated Refresh Token* page.

This path is structurally better for a machine integration: the token is bound to the ISU rather than to
whoever happened to click, there is no SSO or incognito dance, and there is no consent screen to re-run. Its
cost is that the customer hands you a long-lived refresh token in a form, so your credential intake has to be
secure. Workday documents this task as requiring the *Integration Security* domain in the Integration functional
area plus domains in the System functional area.

**If your connect flow supports both, offer 5.2 first.** Most Workday admins find it easier, and it removes the
"who authorized this?" ambiguity that causes partial-data incidents months later.

## 6. Redirect URIs

For the authorization-code path only (§5.1). Give the admin the exact strings; Workday matches them against what
your authorize request sends.

```
https://api.unified.to/oauth/code          # us (default)
https://api-eu.unified.to/oauth/code       # eu
https://api-au.unified.to/oauth/code       # au
https://api-dev.unified.to/oauth/code      # dev
```

- **Give each customer only the one host that serves them.** Because the client lives in *their* tenant, there is
  no benefit to registering four, and a wrong one is a support ticket.
- **Whether the task accepts more than one redirection URI per client is [unverified]** on a publicly reachable
  Workday page. Ask the admin what the form shows and record the answer here.
- **The redirect URI is constant; the authorize host is the customer's.** One callback serves every tenant. Carry
  the tenant identity in `state`.
- Workday's own authorize example marks `redirect_uri` as optional and defaulted from registration, and shows the
  same value sent again at the token exchange. Send it explicitly in both.

## 7. Scopes are functional areas — and they are only half the permission

**Layer 1 — Scope (Functional Areas), on the API client.** These are Workday product areas, not OAuth scope
strings in the usual sense. They are selected from a prompt at registration time, and they are **not sent in the
authorize request** — Workday's documented authorize parameters are `response_type`, `client_id`, `redirect_uri`
and `state`. A connector therefore cannot widen scope at connect time; the customer's registration decides it.

The functional-area names as they appear in Workday's published REST service descriptions (observed
2026-09-20 across the service specifications this platform works from — the exact list a given tenant offers
depends on which Workday products that customer licenses):

| Area | Typically covers |
| --- | --- |
| `Staffing` | workers, supervisory organizations, job changes — the backbone of any HRIS read |
| `Contact Information` | work/home addresses, emails, phones |
| `Personal Data` | gender, date of birth, marital status, personal photo |
| `Organizations and Roles` | supervisory org detail, org membership, manager resolution |
| `Time Off and Leave` | time-off details, balances, requests |
| `Core Payroll`, `USA Payroll`, `CAN Payroll` | payroll inputs and country payroll |
| `Core Compensation` | compensation |
| `Recruiting`, `Talent Pipeline`, `Worker Profile and Skills` | ATS objects, candidate skills/experience/education |
| `Learning Core` | learning content |
| `Customer Accounts`, `Supplier Accounts`, `Procurement`, `Project Billing` | AR, AP, purchase orders, billing |
| `Performance Enablement` | reviews and performance |
| `System`, `Tenant Non-Configurable` | tenant-level and business-process-event endpoints |

**Layer 2 — domain security policies, on the security group (§4.4).** This is the layer that actually decides
what comes back. Workday publishes, for each endpoint, both *"Secured by:"* (the domains) and *"Scope:"* (the
functional area) in the endpoint description in the REST API Explorer and the REST Services Directory. Read the
endpoint you actually call; do not extrapolate from a sibling.

**Why this matters more than it sounds.** A token can carry the `Staffing` scope and still return an empty list,
because the ISU's group was never granted `View` on the domain securing `/workers`. There is no error, no
warning, no `insufficient_scope` header — the collection is simply short or empty, exactly as Workday documents
for `GET /supervisoryOrganizations`. When a customer says "your sync is missing people", check the domain grants
and the group's constraint before you look at a single line of mapping code.

**Ask for the narrowest set that works, and grant `View` only** unless the integration writes. Write operations
need `Modify`, and business-process-backed writes additionally need permission on the business process itself.

## 8. Capture the credentials

From the registered client, the admin collects and sends you:

- **Client ID**
- **Client Secret** — Workday's own warning: *"it's not possible to recover it after closing this page."*
  If it is lost, the client must be re-registered or a new secret generated. Capture it at registration.
- **Authorization Endpoint** — `https://<authHost>/<tenant>/authorize`
- **Token Endpoint** — `https://<webserviceHost>/ccx/oauth2/<tenant>/token`
- **REST API Endpoint** — carries the webservice host and the tenant
- **Refresh Token** — only on the 5.2 path, from `Manage Refresh Tokens for Integrations`

**Token exchange.** Workday's published form for the authorization-code exchange is a POST to the token endpoint
with `grant_type=authorization_code`, `client_id`, `code` and `redirect_uri`, body encoded
`x-www-form-urlencoded`, with the client credentials in the Authorization header.

Be careful here: Workday's **Developer Site** client uses a non-standard header — base64 of
`clientId:clientSecret` prefixed with `ID ` and a space, not `Basic`. For the **in-tenant** client the
conventional `Authorization: Basic base64(client_id:client_secret)` is what integrators use and what this
platform sends, but **[unverified]** on a publicly reachable Workday page for the `/ccx/oauth2/<tenant>/token`
endpoint. If an exchange is rejected with no useful message, try the other header form and the credentials-in-body
form before concluding the secret is wrong.

**PKCE.** Workday documents PKCE (`code_challenge` / `code_challenge_method=S256` / `code_verifier`) for the
Developer Site client. It is aimed at public clients that cannot hold a secret; a server-side connector holding
a client secret does not need it. This platform does **not** send PKCE for Workday.

Rotating a client secret breaks every exchange and refresh for **that one customer** until the new value is
deployed. Never rotate without that customer's explicit go-ahead.

## 9. Token behaviour

| Token | Lifetime | Rule |
| --- | --- | --- |
| `access_token` | **not a fixed number** | Workday: *"The token timeout is tied to the session timeout configured for the user by the Workday administrator."* Bearer type. |
| `refresh_token` | absolute window set at registration, **or non-expiring** | *"That refresh timeout begins at the first authorization step and doesn't get extended, even if you get a new refresh token."* Ticking **Non-Expiring Refresh Tokens** removes the window. |
| `code` | short-lived, single use | Reuse gives a grant error |

What follows:

- **Do not hardcode an access-token lifetime.** Different customers configure different session timeouts, and an
  ISU is often configured with a session timeout of 0. A connector that assumes an hour will, for a customer
  whose timeout is shorter, keep presenting a dead token until the next scheduled refresh and produce
  intermittent `401 Invalid Access Token`. Prefer honouring the `expires_in` the token endpoint returns, and
  treat a 401 as a refresh trigger rather than a failure.
- **Insist on Non-Expiring Refresh Tokens at registration.** Because the refresh window is absolute rather than
  rolling, a connection that refreshes diligently every hour still dies on a fixed date unless the box was
  ticked. Retro-fitting it means the customer re-registers or re-mints — so get it right the first time.
- **Assume the refresh response may rotate the refresh token, and persist whatever comes back.** Workday's
  Developer Site path explicitly returns a new refresh token on every refresh. Whether the in-tenant
  `/ccx/oauth2/<tenant>/token` endpoint does the same is **[unverified]**. The safe implementation stores the
  returned refresh token when one is present and reuses the existing one when it is absent — which is what this
  platform does.
- **A "Refresh Token Rotation" setting** on the registration task could **not** be confirmed on any publicly
  reachable Workday page. If the admin reports seeing such a control, record what it says and add it here; do
  not assert it exists.

## 10. What silently kills a Workday connection

Rank these when a connection that worked stops working.

- **A tenant refresh.** Workday: *"A tenant refresh copies all assets (including the ISU configuration) from
  Production (PROD) to Implementation (IMPL) and Sandbox (SBOX) tenants."* When a customer refreshes their
  sandbox from production, the sandbox's registered clients, ISUs and security configuration are replaced
  wholesale by production's. Any credential you hold against that sandbox is now pointing at configuration that
  no longer exists as you knew it. **Treat every sandbox connection as disposable, and tell customers in writing
  that refreshing a tenant may require re-running the runbook for that tenant.** The precise effect on an
  already-issued refresh token is **[unverified]** — Workday does not publish it on a signed-out page — but the
  observed field behaviour is that lower-tenant connections stop working after a refresh and must be
  re-established. Do not promise otherwise.
- **Do not confuse tenant refreshes with Workday's weekly service updates.** Workday delivers *"Weekly service
  updates … within the weekend maintenance window"*, and separate feature releases that reach preview tenants
  about five weeks early. Those are code updates and do not re-copy tenant data or configuration. A tenant
  refresh is a customer-initiated copy of production into a lower tenant. Different events, different blast
  radius.
- **A functional area added after the token was minted.** Because scope lives on the client rather than in the
  authorize request, adding an area means the customer edits the client — and the existing token may not pick it
  up. **Re-mint the refresh token (5.2) or re-authorize (5.1) after any scope change.** Budget for this every
  time you add an object to the integration.
- **Domain grants changed but not activated.** Back to §4.5.
- **The ISU's password expired, or the account was disabled.** Especially on the SOAP path, where the password
  *is* the credential. Ask the customer to exempt the ISU from password expiry.
- **A named human authorized the connection and then left.** §4.2. This is why the ISU exists.
- **A constrained security group's scope changed.** Reorganizations move supervisory orgs; a constrained group
  quietly starts returning fewer workers.

## 11. Sandboxes, partners and Marketplace

- **You cannot self-serve a Workday tenant.** Workday tenants belong to Workday customers. A vendor gets one by
  being a customer, by being given access by a customer, or through a Workday partner arrangement — in every
  case a commercial conversation with a cost attached, not a signup form. Do not promise a testing environment
  you have not secured.
- **Test against a customer's Implementation or Sandbox tenant** where the customer will allow it. It is the
  normal arrangement, and it is also why §10's tenant-refresh warning matters so much.
- **Workday Marketplace and the partner programs are a separate, later question.** Workday describes Marketplace
  solutions as *"built on and certified for the Workday platform"*, with a **Design Approved** designation for
  integrations whose *"use case, architecture, and data mapping have been reviewed by Workday"*. Listing is
  about distribution and credibility; **none of it is required to connect a customer who wants you connected.**
  Eligibility, certification scope, agreements and fees are commercial decisions — surface them and hand back.

## 12. RaaS — the other data path, and what it does to scoping

**Reports-as-a-Service (RaaS)** exposes a Workday **custom report** as a web service. Workday's description:
RaaS *"enables advanced and search reports to function as web services, allowing access to report results
through URLs"*, with output as **CSV, GData, JSON, RSS, Simple XML or Workday XML**, and a stable namespace of
the form `urn:com.workday.report/<Report_Name>` that survives renaming the report.

It is, in practice, how a great many Workday integrations actually move bulk data, and it changes the shape of
this whole task:

- **The contract is the report, not the API.** A customer's Workday analyst builds the report, and its columns
  are whatever they chose. Two customers' "worker report" will not match. That is flexibility for them and
  per-customer mapping work for you.
- **Scoping moves.** What the report returns is bounded by the report's own data source and filters *and* by the
  running account's security — so §4 still applies, but the functional-area scope conversation is replaced by a
  conversation about which report and which data source.
- **Workday's own advice:** copy a standard report to a custom report and web-service-enable the copy, rather
  than depending on a delivered report Workday may change.
- **Report owner usernames must not contain a backslash** for GET requests to execute — an odd, real constraint
  worth knowing when a single customer's RaaS calls fail and nobody else's do.

If the integration's value is bulk HR extraction and the customer already has Workday analysts, ask whether RaaS
is the right path before writing a REST runbook.

## 13. What this platform's connector expects today

**Dated product facts, observed 2026-09-20.** Confirm with the connector's owner before you register anything —
connector configuration changes more often than a vendor's tenant does.

- **The REST connector uses per-customer OAuth credentials and says so.** It is explicitly *not* configured to
  use this platform's shared OAuth credentials. The connect experience asks the customer for **five** values —
  OAuth2 Client ID, OAuth2 Client Secret, Authorize Endpoint, Token Endpoint and REST API URL — with on-screen
  help pointing at Workday's *View API Clients* report for each. That is the per-customer model made concrete.
- **It also supports a token-style connection** taking Client ID, Client Secret, **Refresh Token**, Token URL,
  API URL and Tenant ID — i.e. the §5.2 ISU refresh-token path, where the customer mints the refresh token in
  Workday and pastes it in. On that path the connector immediately performs a refresh to obtain a first access
  token, so a bad value fails at connect time rather than silently later.
- **The authorize request carries `client_id`, `response_type`, `redirect_uri` and `state` — and no `scope`.**
  That is correct for Workday (scope lives on the API client, §7), but it means the connector has **no scope
  catalogue of its own**: what a connection can read is entirely decided by what the customer ticked. Your
  runbook is the only scope control you have.
- **No PKCE is sent.** The code exchange is a form POST with HTTP Basic client authentication, carrying
  `grant_type`, `code` and `redirect_uri`; refresh is a form POST with `grant_type` and `refresh_token`, also
  Basic-authenticated. On the token-style path the client id and secret are additionally repeated in the body.
- **Tenant and host are derived, not asked for, on the OAuth path.** The connector parses the tenant out of the
  pasted Token URL (`/ccx/oauth2/<tenant>/token`), falling back to the Authorize URL
  (`https://<host>/<tenant>/authorize`) and then to a `/ccx/api/v<n>/<tenant>` API URL, and substitutes it into
  every service path at request time. On the token-style path an explicitly-entered Tenant ID wins.
- **The pasted API URL is truncated to `/ccx/api`** so that service paths append cleanly instead of doubling the
  tenant segment. **This is a real risk** — see the bug note below.
- **List calls page with `limit`/`offset` and a maximum page size of 100**, reading results from the `data`
  array, with `format=json` on reads and `PATCH` for updates.
- **The connector's own setup notes tell the customer to tick Non-Expiring Refresh Tokens**, and to re-mint the
  refresh token if functional areas are added after the fact — consistent with §9 and §10.
- **The access-token lifetime is pinned to 3600 seconds**, including by overwriting whatever the token endpoint
  reports on refresh. See the risk note below.
- **The legacy SOAP connector** takes four values — Workday Host, Tenant Name, ISU Username, ISU Password —
  normalizes the host, appends `@<tenant>` to the username itself, sends no HTTP Authorization header, and
  parses SOAP faults into readable errors. Its own onboarding checklist walks the admin through ISU creation,
  an unconstrained integration system security group, per-service **Integration Get/Put** domain grants, and
  **Activate Pending Security Policy Changes**, then tells them to read host and tenant off a WSDL URL via the
  *Public Web Services* report.
- **No plaintext customer credential appears in either connector's source.** The only credential-shaped strings
  are obvious test placeholders in unit tests.

**Two things to raise with the connector's owner** (both verifiable against Workday's published endpoint shapes
in §3):

1. **The API-URL normalization only recognises `/ccx/api`.** Workday's REST fundamentals document the
   integration base path as `https://{tenantHostname}/api/{serviceName}/{version}/{tenant}`, and Workday's own
   product guide prints a customer-facing REST endpoint of `https://wd3-services1.myworkday.com/api/v1/tenant_name`
   — **without `/ccx`**. A customer who pastes that form gets no normalization, and service paths then append to
   `.../api/v1/<tenant>`, producing a doubled path that 404s. Worth handling both shapes.
2. **Pinning the access-token lifetime to 3600 seconds contradicts Workday's documented behaviour**, which ties
   the access-token timeout to the authorizing account's configured session timeout. For a customer whose ISU
   session timeout is shorter than an hour, the connector will treat a dead token as live and produce
   intermittent `401 Invalid Access Token` until the next refresh. Honouring the returned `expires_in`, and
   refreshing on 401, is the safer shape.

## 14. Verify end-to-end

Authorizing is the easy part. Test the part that fails.

1. Complete the connect flow against the **customer's real tenant**, with the account that will own the
   connection — not your own convenience account (§5.1).
2. Confirm you received a **refresh token**, and that **Non-Expiring Refresh Tokens** was ticked (§9).
3. Force a refresh, then force a **second** refresh using whatever the first one returned. That catches both a
   client ignoring rotation and a client over-trusting a pinned lifetime.
4. Make **one read per functional area** you asked for, and compare row counts against what that account sees in
   the Workday UI. A short list is a permission finding, not a mapping bug (§7).
5. Hit at least one **sub-resource** (a worker's addresses, photos or balances) — sub-resources fail
   independently of their parent when a domain grant is missing (§4.4).
6. If the customer has a sandbox, run the whole thing there too, and then ask when they next plan to refresh it
   (§10).

| Symptom | Cause |
| --- | --- |
| `401 Unauthorized` | Valid credentials were not supplied. Check the Authorization header form (§8). |
| `401 Invalid Access Token` | Token invalid or expired — generate a new one. If intermittent, suspect a pinned lifetime against a shorter session timeout (§9). |
| `401` everywhere, brand-new client | `OAuth 2.0 Clients Enabled` never ticked in tenant setup (§4.1). |
| `403 Not Authorized` / `S22 Permission Denied` | The account lacks the domain that secures the resource — **or** the parent resource of a sub-resource (§4.4). |
| `404 Not Found: {value}` / `S21` | Workday lists two causes: a bad path or id, **and** the user not having correct permissions. Check permissions before hunting for the record. |
| `S19 Not Authorized` (401) | Authentication missing or invalid — verify the bearer token. |
| List returns 200 with no rows, or far too few | The classic Workday failure. Domain grants missing, never activated (§4.5), or a constrained security group (§4.3). Not a mapping bug. |
| `Integration → Get` granted, REST still 403 | REST needs **Report/Task**; Integration permissions are SOAP-only (§4.4). |
| Everything worked, then a whole tenant stopped | A tenant refresh (§10), or a password/account change on the ISU. |
| One object stopped after you shipped a feature | A functional area was added to the client but the token was never re-minted (§10). |
| Authorize page 404s or shows the wrong company | Wrong auth host or wrong tenant in the authorize URL. Remember the auth host is **not** the webservice host (§3). |
| Paths 404 with the tenant appearing twice | The pasted REST URL was in a form the connector did not normalize (§13). |
| Redirect error at the callback | The redirection URI registered on the client does not match exactly (§6). |
| `405 Method not Allowed` / `S12` | Unsupported method for that resource. |
| `S3 Unsupported Version` | The service version in the path is not available in that tenant. |
| `S27` / `S38` (504) | Transaction timeout — reduce the scope of the request; for `S38` the write may have succeeded, so verify before retrying. |
| SOAP returns HTTP 500 with an XML body | A SOAP Fault, not a server outage — parse `faultstring` / `Message` for the real cause. |

## 15. What could not be verified without a Workday Community login

State these as open questions, not facts. Each was searched for and not found on a page a signed-out reader can
open as of 2026-09-20.

- The canonical admin-guide pages for **Register API Client**, **Register API Client for Integrations** and
  **Integration Security in Workday** (under `doc.workday.com/admin-guide/…/authentication-and-security/…`) are
  behind a Workday Community sign-in — some return `401`, one returns `200` with a sign-in screen. Everything in
  §5 was reconstructed from Workday product guides that *use* those tasks and are public.
- The full field list and option set of the registration tasks — including whether **multiple redirection URIs**
  are accepted, and whether a **Refresh Token Rotation** control exists.
- Whether the in-tenant `/ccx/oauth2/<tenant>/token` endpoint accepts standard **HTTP Basic** client
  authentication, and whether it rotates the refresh token on every refresh.
- The exact effect of a **tenant refresh** on already-issued refresh tokens and registered API clients in the
  refreshed tenant.
- The **sandbox refresh cadence** (commonly quoted as weekly) — Workday's public tenant-types material describes
  the tenant types but not a refresh schedule, and the tenant-management reference page returns **401**.
- The `wdN-impl-servicesN` **host naming family** beyond the `wd3.myworkday.com` / `wd3-services1.myworkday.com`
  and `example.myworkday.com` examples Workday publishes.
- Whether an ISU with **Do Not Allow UI Sessions** can complete an authorization-code consent screen.
- Whether there is an **error-message or rate-limit reference** for REST beyond the public error-code table —
  `community.workday.com/rest/*` returns **401**.

## 16. Hand off — never commit the secret

- **Do not** write a customer's client secret, refresh token or ISU password into source control, a test, a
  fixture, a committed `.env`, a ticket, a PR body or a chat channel. These are **customer** credentials, not
  yours, and a leak is that customer's incident.
- **Collect them through a secure intake**, not email. The admin is copying a value that is shown once.
- If a code change is needed (a new callback host, host normalization, a token-lifetime fix), keep it
  credential-free and say what the human must set out of band.
- Close with: which customer and which **tenant type**; **REST or SOAP**; which registration task was used
  (§5.1 or §5.2); the client ID; where the secret and refresh token were delivered; the three endpoint URLs with
  the `<authHost>` / `<webserviceHost>` / `<tenant>` values filled in; the exact **functional areas** ticked; the
  **domains** granted and confirmation that **Activate Pending Security Policy Changes** was run; which account
  the credential belongs to and whether it is a dedicated ISU; whether **Non-Expiring Refresh Tokens** was
  ticked; and anything left for the customer to do.

## Stop and ask

Hand back to a human rather than guessing when: someone proposes a single central Workday credential for all
customers, or proposes the Workday Extend / Developer Site client as a way to get one (§1); the choice between
REST and SOAP has not been made and the object list has not been checked against both directories (§2); a
customer's security team wants a **constrained** group and nobody has agreed what data loss that implies (§4.3);
a customer cannot or will not run `Activate Pending Security Policy Changes` (§4.5); the integration needs a
Workday tenant of your own, or a sandbox, or a Marketplace listing or certification — all commercial decisions
(§11); RaaS is on the table and nobody has decided who owns the report definition (§12); a token behaviour in §9
needs to be relied on rather than tested; or the customer's tenant does not match the **Platform state**
section above.

## References

Verified 2026-09-20 — every URL below returned HTTP 200 to an anonymous reader. Workday's developer
documentation also serves a Markdown form of each page at `https://developer.workday.com/doc/{docId}.md`, which
is the reliable way to read it (the HTML is a JavaScript shell).

**Workday REST and security**

- Concept: Workday REST API Security (user-bound access, Report/Task vs Integration permissions, domains and scopes) — https://developer.workday.com/doc/dan1370797986071.md
- Workday REST API Fundamentals (base paths, id formats, pagination) — https://developer.workday.com/doc/GUID-85810465-bcfb-4fdf-a26d-55eaff3968a8-enHYPHENus.md
- Reference: REST API Error Messages (the S-code table used in §14) — https://developer.workday.com/doc/GUID-6f06b090-c7b5-47cc-b929-d2a1f1116eef-enHYPHENus.md
- Concept: Workday REST API Pagination — https://developer.workday.com/doc/lvb1611857200890.md
- REST API Authentication (index) — https://developer.workday.com/doc/GUID-6c598444-ce67-40d5-bd95-267ecfe439b8-enHYPHENus.md
- REST APIs in Apps and Integrations (REST vs SOAP positioning, RaaS) — https://developer.workday.com/doc/GUID-6b063b57-9d85-474b-99b0-734d714652fd-enHYPHENus.md
- SOAP API Authentication and Security — https://developer.workday.com/doc/GUID-4c354bdb-06cd-461d-a632-ea8303beaedb-enHYPHENus.md
- REST API Explorer (per-endpoint "Secured by" and "Scope") — JavaScript app, open it in a browser — https://developer.workday.com/rest-api-explorer
- REST Services Directory — JavaScript app, open it in a browser — https://community.workday.com/sites/default/files/file-hosting/restapi/index.html
- Public Web Services (SOAP) API Directory — https://community.workday.com/sites/default/files/file-hosting/productionapi/index.html
- SOAP API Reference landing page — https://community-content.workday.com/en-us/public/products/platform-and-product-extensions/soap-api-reference.html
- Machine-readable index of Workday developer documentation — https://developer.workday.com/llms.txt

**The in-tenant registration runbook (public guides that use the tasks)**

- Connect to Workday Using OAuth — the fullest public walkthrough of `Register API Client`, the auth host vs webservice host split, `Non-Expiring Refresh Tokens`, the Scope (Functional Areas) prompt, the one-time client secret, and the incognito/SSO ISU sign-in — https://doc.workday.com/peakon/en-us/workday-peakon-employee-voice/integrations/workday-integration/nfa1667304944189.html
- Register API Client for Data Lake (Refresh Token Grant) — the `Register API Client for Integrations` + `Manage Refresh Tokens for Integrations` path — https://doc.workday.com/admin-guide/en-us/workday-data-cloud/data-out/workday-data-lake/register-api-client-for-data-lake--refresh-token-g.html
- Reference: Edit Tenant Setup – Security (the `OAuth 2.0 Clients Enabled` option) — https://doc.workday.com/admin-guide/en-us/manage-workday/tenant-configuration/tenant-setup/dan1370796470031.html
- Create Integration System Users for Apps (ISU, security group, Activate Pending Security Policy Changes) — https://developer.workday.com/doc/GUID-f8d46604-e156-492f-a324-62ed2f6496f7.md
- Install OfficeConnect with Tenants — a second public example of the authorize and REST endpoint shapes — https://doc.workday.com/adaptive-planning/en-us/workday-adaptive-planning-documentation/officeconnect-reports/using-officeconnect/install-and-set-up-officeconnect/configure-officeconnect-with-tenant-connections--i.html

**OAuth flows and the Developer Site client (Extend / Orchestrate)**

- Create Your API Client (Developer Site Console; Modify Authorized Tenants) — https://developer.workday.com/doc/zwx1518028675482.md
- Authenticate Using the Authorization Code Grant Type — https://developer.workday.com/doc/yai1528997518068.md
- Authenticate Using PKCE with the Authorization Code Grant Type — https://developer.workday.com/doc/hfk1553031369488.md
- Request a New Access Token with a Refresh Token (token timeout tied to session timeout; absolute refresh window) — https://developer.workday.com/doc/cqa1553122177664.md
- Reference: Workday Extend API Gateways and Authorization Base URLs (US/EU/UK/SG regions) — https://developer.workday.com/doc/dlh1653340161856.md
- Modify Authorized Non-Development Tenants for API Clients using ISU Authentication — https://developer.workday.com/doc/GUID-a600ff24-400c-4621-8f23-1ec27a56fc61-enHYPHENus.md
- Concept: ISU Authentication (Tenant Refresh from Production) — https://developer.workday.com/doc/GUID-cb3e0fcd-bb97-4005-8d10-3a02e88dab71-enHYPHENus.md

**Tenants, RaaS, partners**

- Workday Tenants and Tools (Production, Sandbox, Sandbox Preview, Implementation; weekly service updates) — https://doc.workday.com/workday-education/en-us/course-manuals/hcm-core-for-administrators/workday-tenants-and-tools.html
- Concept: Reports as a Service (RaaS) — https://doc.workday.com/admin-guide/en-us/reporting-and-analytics/custom-reports-and-analytics/reports-as-a-service-raas-/dan1370796320263.html
- Concept: Accessing RaaS Output — https://doc.workday.com/admin-guide/en-us/reporting-and-analytics/custom-reports-and-analytics/reports-as-a-service-raas-/dan1370797813643.html
- Concept: Reports-as-a-Service (developer site) — https://developer.workday.com/doc/hto1529968349582.md
- Workday Developers — https://developer.workday.com/
- Workday Partner Program Overview — https://www.workday.com/en-us/company/partners/partner-program-overview.html
- Become a Workday Partner — https://www.workday.com/en-us/company/partners/become-a-partner.html
- Workday Marketplace — https://marketplace.workday.com/

**Requires a Workday Community sign-in — listed so you know what you are missing, NOT as usable references**

Do not cite these as sources. They mostly answer an anonymous request with `401`, and at least one of them
intermittently answers `200` with an Okta sign-in screen instead — which is the same wall wearing a disguise, so
check that a page actually rendered content before you quote it. If you have a Workday Community account, these
are where the **[unverified]** items in §15 would most likely be settled.

- Administrator Guide: Register API Client for Integrations — https://doc.workday.com/admin-guide/en-us/authentication-and-security/authentication/oauth/dan1370797831458.html
- Administrator Guide: Integration Security in Workday (seen answering both `401` and `200`-with-sign-in) — https://doc.workday.com/admin-guide/en-us/authentication-and-security/security-for-integrations/dan1370797415412.html
- Community: API Authentication Methods — https://community-content.workday.com/content/workday-community/en-us/kits-and-tools/products/platform-and-product-extensions/integrations/api-authentication-methods.html
- Community: REST OAuth and REST error messages — https://community.workday.com/rest/oauth and https://community.workday.com/rest/error-messages
- Community: Tenant Management — https://community-content.workday.com/en-us/reference/get-help/support/tenant-management.html
