---
name: workable-oauth-app
description: Establishes which Workable credential a connector actually needs and obtains it — customer-generated API access tokens (self-serve, Admin-only, expiring), partner tokens, or partner-gated OAuth 2.0 authorization-code credentials issued by Workable — with the subdomain/account model, the scope list, the member_id permission trap, rate limits and a safe credential handoff. Use when asked to get Workable OAuth credentials or an API token, register a Workable app, become a Workable integration partner, or fix a Workable auth error like a 401 on every call, an empty employee list, a token that expired, or `429` under light load. For any other vendor's developer portal, use that vendor's skill instead.
---

# Workable API Credentials

**Read this first: Workable has no self-serve developer portal where you register an OAuth app and walk away with a
client ID and secret.** There is no "create app" button. Workable's own OAuth documentation states it plainly:
"Official partners can access Workable API endpoints through OAuth 2.0 ... the Partner should be authorized beforehand
by Workable and get a `client_id` and `client_secret`." Credentials are issued by hand, by Workable, to accepted
partners. The partner program's Native Integration tier — the one described as leveraging the Partner API — states a
requirement of a **minimum of 15 mutual customers**. If the goal is "get Workable OAuth credentials this week," the
honest answer is that it is not available at any price without that partnership, and the run should stop at §5.

What *is* self-serve is different, and it is what almost every Workable integration actually runs on: **each customer
generates their own API access token inside their own Workable account**, from Settings → Integrations → Apps. It is
an Admin-only action, the token is account-level, it carries scopes the customer picks, it has a **mandatory
expiration** (30 days to a maximum of 2 years), and it is shown exactly once. A multi-tenant platform can run entirely
on this: ask each customer for a token and a subdomain, and store the pair. Decide which of the two models you are
building against before doing anything else (§1) — the expensive mistakes here all start with building the wrong one.

Three more things that cost real time: **every request is scoped to an account subdomain** and a connect flow that
does not collect one is broken before it starts (§2); an account-level token reading `/employees` silently returns
**only published employees with publicly available info** unless you pass a `member_id` (§7) — the classic
partial-data failure in this category; and tokens expire on a clock the customer chose and you never see (§5).

## Inputs to collect before you start

Ask in one batch. Never invent.

| Input | Notes |
| --- | --- |
| **Credential model** | Per-customer API access token, partner token, or partner OAuth? (§1) |
| **Partner status** | Do you have an accepted Workable partner application, or are you collecting per-customer tokens? (§4) |
| **Account subdomain(s)** | Required on every request; there is no way to guess it (§2) |
| **Scope set** | The exact `r_*` / `w_*` strings the connector calls (§7) |
| **Integration name**, logo, description | Shown to the customer on the OAuth grant screen and in the partner listing (§5) |
| **Redirect URI(s)** | Every callback host, final, up front — Workable provisions them for you (§6) |
| **Mutual customers** | The Native tier names a minimum of 15; be ready to say who (§4) |
| **Token expiry policy** | Which expiration you will tell customers to pick, and who watches the clock (§5) |
| **A Workable tenant to test against** | Sandbox access is a partner benefit, not a signup form (§4) |

## Quick Start

1. Establish which **credential model** this actually needs (§1). Most wrong turns start here.
2. Confirm you can collect the **account subdomain** in your connect flow — nothing works without it (§2).
3. Check whether existing credentials are worth reusing; re-issuing orphans every existing connection (§3).
4. If partner OAuth: apply to the partner program. This gates everything else (§4).
5. Obtain the credential — the customer's Integrations page, or Workable's partner provisioning (§5).
6. Give Workable **every** callback host in that same request; you cannot self-serve them later (§6).
7. Pin down scopes *and* the acting user's Workable permissions — both are required, neither substitutes (§7).
8. Check rate limits before you promise a sync cadence (§8).
9. Verify with a real authorize → callback → refresh → refresh-again round trip against a second account (§9).
10. Hand the secret to a human, never to source control (§10).

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

- **OAuth 2.0 is partner-gated, not self-serve.** Workable's OAuth page describes only an authorization-code flow for
  partners who have already been issued a `client_id` and `client_secret` by Workable. No public registration form,
  no console, no way to self-register a client.
- **The OAuth endpoints are on the `www` host**, not the per-account subdomain: authorize at
  `https://www.workable.com/oauth/authorize`, token and refresh at `https://www.workable.com/oauth/token`, revoke at
  `https://www.workable.com/oauth/revoke`. The authorize call carries `resource=user`. An `api.workable.com` alias
  answers on the same paths today, but the documented host is `www`.
- **The developer hub moved.** `developers.workable.com` now redirects to `https://www.workable.com/developers`, and
  the partner-apply URL still printed at the bottom of Workable's own OAuth page
  (`developers.workable.com/partner-program/apply`) **404s**. The live applications are at
  `https://www.workable.com/partnership-program/apply` and, for the third-party tier,
  `https://www.workable.com/partnership-program/third-party-partners/apply`. Any runbook still pointing at the old URL
  is stale.
- **Customer API tokens now expire.** The generation form requires an expiration from a fixed dropdown: 30 days,
  90 days, 6 months, 1 year, 2 years. There is no "never." Expired tokens can be **renewed in place** — the token
  string does not change — which is the one piece of good news in this section.
- **Only Admin users can generate or revoke tokens**, and Workable states there is "no dedicated access only for this
  use": you cannot delegate token management without making someone an Admin.
- **Three token types with different reach.** Workable's endpoint reference labels each endpoint "accessible with all
  token types", "with account tokens and user tokens", or "only with user tokens". Offers (`/offers/:id` and its
  approve/reject actions) are **user-token only**; `/members`, `/recruiters` and `/events` exclude partner tokens.
- **A Workable MCP server exists** at `https://mcp.workable.com/mcp` (documented page last updated 2026-07-16) and it
  *does* support OAuth Dynamic Client Registration (RFC 7591/8414) at `https://mcp.workable.com/oauth/register`,
  unauthenticated. Its metadata points at the same `workable.com/oauth/authorize` and `/oauth/token` endpoints and
  advertises PKCE `S256`. **This is not a back door to REST API credentials** — the registration is scoped to the MCP
  resource. Do not present it as a self-serve OAuth route for a connector; if someone wants to try, that is a question
  for Workable, not an assumption to build on.
- **The authorization server advertises more scopes than the customer-facing token form offers**, including `w_jobs`,
  `r_offers`, `w_offers`, `r_time_tracking`, `w_time_tracking`, `r_reviews`, `w_reviews` and `r_reports`. See §7
  before requesting any of them.

If the Integrations page or the developer hub does not look like this, stop and report what you actually see.

## 1. Which credential, and who issues it

A reader arriving here is usually holding the wrong one. Workable runs one API surface — Workable's own docs and help
centre call it the **SPI**, after the `/spi/v3` path prefix every authenticated call uses — but three credential types
reach it with different privileges:

| Credential | Who issues it | How it is sent | Reach |
| --- | --- | --- | --- |
| **API access token** (account token) | The customer, self-serve, Admin only, in their own account | `Authorization: Bearer <token>` against `https://<subdomain>.workable.com/spi/v3/` | Account-level. Excluded from user-token-only endpoints (offers). Reduced employee visibility without `member_id` (§7) |
| **OAuth 2.0 access token** (user token) | Workable, to accepted partners, as `client_id` + `client_secret`; the customer then grants access | `Authorization: Bearer <token>`; authorize/token on `https://www.workable.com/oauth/` | Acts as the authorizing **user**. The only type that reaches offers. Spans every account that user can see (§2) |
| **Partner token** | The customer generates it against an *already-listed* official integration on their Integrations page | `Authorization: Bearer <token>` **plus** `X-WORKABLE-CLIENT-ID: <UID>` | Limited to the scopes agreed when the integration was built. Excluded from `/members`, `/recruiters`, `/events` |

Two traps follow directly from the table:

- **"Partner token" and "partner OAuth" are different things.** A partner token is a per-customer bearer token for a
  listed integration, with a client-ID header — no redirect, no consent screen, no refresh. Partner OAuth is the
  redirect flow. Both require Workable to have accepted you as a partner; only one involves a `client_secret`.
- **The `X-WORKABLE-CLIENT-ID` header is also the legacy rate-limit lever.** Workable documents that third parties
  "still borrowing account tokens" can get partner-tier limits by setting that header. Sending it does not upgrade
  what the token may *read* — only what it may read *quickly* (§8).

> **Product fact — dated.** As of **2026-09-20**, Unified.to's Workable connector supports **two** authentication
> modes and collects the account subdomain in both. In **API-token** mode it asks the customer for two separate
> inputs — the token itself, and the subdomain, framed as `https://{subdomain}.workable.com` — and points them at
> Workable's account Integrations settings page to generate the token; the token is sent as a bearer credential and
> the subdomain is substituted into the API base host. In **OAuth 2.0** mode it runs an authorization-code flow that
> *also* requires the customer to supply the subdomain, sends `response_type`, `resource`, `client_id`, a
> space-delimited `scope`, `redirect_uri` and `state` on the authorize call, exchanges and refreshes with a
> form-encoded POST carrying client ID, client secret and (for refresh) the refresh token — and points its authorize
> and token calls at Workable's `api.` host rather than the `www.` host the documentation shows. Its OAuth scope set
> is per unified object and per direction, drawn from `r_candidates`, `w_candidates`, `r_jobs`, `w_jobs`,
> `r_requisitions`, `r_employees` and `r_account`. When a client ID is present the connector sets the
> `X-WORKABLE-CLIENT-ID` header on every call, including in API-token mode. It records Workable's published limits as
> 10 requests/10 seconds for API tokens and 50/10 seconds for OAuth, and applies a fixed backoff because Workable does
> not reliably return `Retry-After`. It records sandbox access as partner-only, and the URL it stores for obtaining
> OAuth credentials is the **`developers.workable.com` partner-apply page that now 404s** (see Platform state). The
> connector's recorded last-tested date is well over two years old. **Confirm all of this with the connector's owner
> before acting on it** — connector configuration changes independently of this skill.

## 2. The subdomain and account model — collect it or nothing works

Every authenticated Workable call is scoped to an account, and the account is named in the **host**:

```
https://<account subdomain>.workable.com/spi/v3/jobs
```

There is no tenant discovery from a bare API token, and no default. A connect flow that collects only a credential is
incomplete: **ask for the subdomain as a first-class input**, alongside the token, and validate it before saving.
Workable tells customers to find it in their company profile settings; it is also the host of their Workable URL.

Whether one credential spans accounts depends entirely on the type:

- **Account tokens are single-account.** The token was generated inside one Workable account and reaches that account.
  A customer with two Workable accounts must generate two tokens, and your connect flow must let them make two
  connections.
- **OAuth user tokens can span accounts.** The flow authorizes a *user*, and `GET /accounts` (scope `r_jobs`) returns
  "a collection of all the accounts you have access to," each with its own subdomain. Workable's own MCP server is
  built on this shape: call `get_accounts` first, then pass the chosen `subdomain` on every subsequent call. A
  connector on OAuth should therefore **enumerate accounts after authorization and let the customer choose**, rather
  than assuming one. Silently picking the first account is a data-leak-shaped bug when a recruiter has access to two
  client tenants.
- Note the asymmetry with the OAuth endpoints: the authorize/token calls go to the **`www`** host, while the data
  calls go to the **per-account subdomain** host. Workable's own OAuth example calls
  `https://www.workable.com/spi/v3/accounts` with a user token to discover the subdomains first. A client that
  hardcodes one host for everything will work right up until it does not.

## 3. Reuse the existing credentials, or request new ones

Re-issued partner credentials mean a **new client ID, and every existing customer authorization is bound to the old
one** — every customer re-authorizes. Reuse what exists for: adding a redirect URI, changing scopes, rotating a
compromised secret, or diagnosing a failure.

Request a **new** client only when the user explicitly wants one: a separate product, a replacement for a compromised
client, or a deliberate move from per-customer tokens to partner OAuth (which is a migration project, not a config
change — every customer re-connects).

For **per-customer API tokens** the calculus is much gentler: each customer's token is independent, so revoking or
regenerating one affects exactly one tenant. Say which path you are taking before you start.

## 4. Accounts: the partner program, and a tenant to test against

**The partner program** (`https://www.workable.com/developers`, applications at
`https://www.workable.com/partnership-program/apply`) is the gate for anything Workable has to issue. What the run
needs to know:

- Workable's technology-partner page describes two tiers. **Native Integration Partners** — the tier described as
  leveraging the Partner API — state a requirement of a **minimum of 15 mutual customers**. **3rd Party Integration
  Partners** are described as having **no mutual-customer requirement** but correspondingly fewer benefits (directory
  listing and co-marketing, rather than the dedicated technical support and API access of the Native tier). A
  pre-revenue product with no Workable customers has no route to the Native tier.
- Workable publishes **no review timeline, no cost and no SLA** for partner applications. Do not invent one. Plan in
  months and say so.
- The partner categories Workable advertises — sourcing, assessments, video interviews, background checks,
  HRIS/onboarding — are **partner categories, not separate API surfaces**. (Do not confuse the `/spi/` path prefix
  with "sourcing partner integration": Workable's own help centre uses "SPI" as the name of the whole API.) If a
  category needs a dedicated API, that is something Workable tells you during onboarding, not something to assume.
- The agreement is a **business and legal decision**. Do not fill in revenue, volume, security or compliance claims on
  the user's behalf — collect the questions and hand them back.

**A tenant to test against.** Workable's sandbox route runs through the partner program
(`https://www.workable.com/partnership-program/technology-partners`); there is no self-serve developer tenant. Without
partner status, the only test tenant is a real Workable account someone already pays for — which means testing
**writes** against production data. Say that out loud before anyone runs a create.

Anything requiring a human — signing the agreement, email verification, being made an Admin, requesting sandbox
access — hand back rather than looping.

**If this session has no browser automation** (the usual case for a CLI or cloud run), do not pretend to click. Give
the user an exact, ordered click path with the literal values to paste — the redirect URIs from §6 and the scope
strings from §7 — or a ready-to-send application/email body for §5, then continue once they report back with the
client ID or the subdomain.

## 5. Obtaining the credentials

### Path A — Customer-generated API access token (self-serve, available today)

This is what a customer does in their own account, and the only path that needs nothing from Workable:

1. The customer must be an **Admin**. Workable states there is "no dedicated access only for this use" — no
   API-management permission exists to delegate.
2. Profile icon → **Settings → Integrations → Apps**, then the **API Access Tokens** section near the top. (Workable
   also links this directly as `https://www.workable.com/backend/account/integrations`.)
3. **+ Generate API token** → name it → choose an **expiration** (30 days / 90 days / 6 months / 1 year / 2 years) →
   select **at least one** scope (§7) → **Generate token**.
4. The token appears in a modal **once**. "Your access token will not be visible after closing the modal. If you need
   it again, you should generate a new one."
5. Separately, get the **account subdomain** (§2). Ask for it by name; do not derive it from an email domain.

Two operational facts worth telling the customer at the same time:

- **Renewing an expired token keeps the same string.** Workable's expired-token **Renew** button means every script
  using it keeps working with no redeploy. **Revoking** does the opposite — it breaks everything bound to that token
  immediately and forces a new one. Tell customers to renew, never to regenerate, unless the token leaked.
- **Multiple tokens are allowed**, with different scopes and expirations. A customer can issue one narrow token per
  integration rather than one god token shared by four vendors. That is the right ask.

### Path B — Partner token (requires being a listed official partner)

For an integration Workable has already accepted and listed. The customer opens their Integrations page, finds your
integration, and clicks **Generate Token**; Workable shows the permissions attached and the token can be revoked at
any time. Calls must carry **both** `Authorization: Bearer <Partner Token>` and `X-WORKABLE-CLIENT-ID: <UID>`, and
Workable's docs say to email `partners@workable.com` to obtain the UID (`integrations@workable.com` appears in the
rate-limit note for the same header). Scopes "will be determined during the development of your integration" — they
are negotiated, not chosen at runtime.

### Path C — Partner OAuth 2.0 (authorization code), for a multi-tenant connector

Only after Workable has issued a `client_id` and `client_secret`. The documented flow:

- **Authorize** — open a browser window to
  `https://www.workable.com/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&resource=user&response_type=code&scope=r_jobs+r_candidates+w_candidates`.
  Scopes are space-delimited (`+`-encoded in the query). Note `resource=user` — it is in every documented example.
  Workable's docs list `state` as no more than good practice; treat it as mandatory, because it is your only CSRF
  defence here.
- **Callback** — Workable redirects to your `redirect_uri` with `?code=...` in the query string.
- **Exchange** — `POST https://www.workable.com/oauth/token` with `grant_type=authorization_code`, `client_id`,
  `client_secret`, `code` and `redirect_uri`. Workable's own example sends these as **multipart form fields**
  (`curl -F`); a client that only knows how to post a JSON body should be checked before blaming the credentials.
- **Response** — `{ access_token, token_type: "bearer", expires_in: 7200, refresh_token, scope, created_at }`.
  **`expires_in` is 7200 — two hours.**
- **Refresh** — same endpoint, `grant_type=refresh_token` with `client_id`, `client_secret`, `refresh_token`. The
  response "contains a new pair of tokens": **the refresh token rotates**. Store the new one from every refresh, or
  the second refresh fails.
- **Revoke** — `POST https://www.workable.com/oauth/revoke` with `client_id`, `client_secret` and `token` (the refresh
  token). If the user revokes from either side, the authorization-code flow must be repeated; Workable says this
  "could manifest as a `401` response code on a refresh token request."
- **PKCE** is not part of the documented partner flow, though the authorization server advertises `S256` in its
  metadata. Do not assume either way without asking Workable.

Workable does not publish a refresh-token lifetime. Two hours on the access token plus rotation on every refresh means
**a connection that is never exercised is a connection you cannot vouch for** — refresh on a schedule, not lazily.

## 6. Redirect URLs

For **partner OAuth only** — neither the API token nor the partner token has a redirect.

Workable's docs say the `redirect_uri` "should also be the same provided by the partner in the provisioning process":
you do not edit it in a console, you give Workable the list when they provision your client. Register **every**
callback host up front. For Unified.to these are one per data center; confirm the current list with the platform owner
rather than assuming:

```
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
```

The `redirect_uri` you send on both the authorize call **and** the token exchange must match what Workable
provisioned. Workable does not document its matching rules, so assume exact string matching and no trailing-slash
forgiveness. Discovering a missing region later is an email and a wait, not a config change.

## 7. Scopes — and the permission rules that outrank them

Scopes are `r_<resource>` / `w_<resource>`, space-delimited on the authorize URL. The set a **customer** can choose
from when generating an account token, as Workable documents it:

| Scope | Grants |
| --- | --- |
| `r_account` | `/departments`, `/legal_entities`, `/work_schedules`, `/employee_fields` |
| `r_jobs` | Jobs, application forms, questions, stages, custom attributes, job members and recruiters, events — **and** `/accounts`, `/accounts/:subdomain`, `/members`, `/recruiters`, `/stages` |
| `r_candidates` | Candidates, candidate and job activities, `/candidates/:id/offer` |
| `r_employees` | **Sensitive.** `/employees`, `/employees/:id`, employee documents |
| `r_requisitions` | `/requisitions`, `/requisitions/:code` (Hiring Plan accounts only) |
| `r_timeoff` | Time-off categories, requests, balances |
| `w_candidates` | Create/update candidates, comments, tags, disqualify, revert, copy, relocate, move, ratings |
| `w_comments` | Candidate comments only (a subset of `w_candidates`) |
| `w_departments` | Create, update, merge, delete departments |
| `w_employees` | Employee records and documents |
| `w_members` | Update, invite, deactivate, restore members |
| `w_requisitions` | Create, update, approve, reject requisitions |
| `w_timeoff` | Create time-off requests |

**The authorization server advertises more than that list**, including `w_jobs`, `r_offers`, `w_offers`,
`r_time_tracking`, `w_time_tracking`, `r_reviews`, `w_reviews` and `r_reports`. Those appear in endpoint requirements
too (time tracking, performance-review templates). But they are **not in the customer-facing token form**, so a
connector that requests them on an account token will not get them, and a customer told to "tick `w_jobs`" will report
that the option does not exist. Request the extras only against partner OAuth, and confirm with Workable that your
client is permitted them rather than assuming the metadata is a menu.

Three rules that outrank scopes entirely — this is where silent partial data comes from:

1. **Token type gates endpoints, independently of scope.** Workable's reference labels each endpoint. `/offers/:id`
   and offer approve/reject are "accessible **only with user tokens**" — an account token with a perfect scope set
   cannot read an offer. `/members`, `/recruiters` and `/events` are "accessible with account tokens and user tokens",
   which excludes partner tokens. If a capability is missing, check the label before changing the scope.
2. **`/employees` quietly returns less to an account token.** Workable: "If you are using an account level token you
   will get only published employees with publicly available info. To get all types of employees there is a need to
   add `member_id` of an hr admin." `/employees/:id` and employee documents are the same — user tokens work directly,
   account tokens "need `member_id` to work." **This produces a 200 with a short, thin list, not an error.** An HRIS
   sync that looks like it is working and is missing most of the workforce is this. Whose `member_id` you pass decides
   what you see, so it is that member's Workable permissions — not your scopes — that cap the data.
3. **The generating user's role decides whether the token exists at all.** Only Admins can create or revoke tokens.
   A customer contact who is not an Admin will report that the Integrations page has no such section, and they will be
   right.

Ask for the narrow set. Changing scopes on an account token means the customer generates a **new** token (the old one
keeps its original scopes), and changing them on partner OAuth means every customer re-authorizes.

## 8. Rate limits, the MCP server, listing

- **Rate limits are per client, in fixed 10-second windows**, and the credential type sets the ceiling:
  **account tokens 10 requests / 10 seconds**, **OAuth 2.0 tokens 50 / 10 seconds**, **partner tokens 50 / 10
  seconds**. Ten requests per ten seconds is *low* — it is the binding constraint on any full ATS sync, and it is the
  single strongest technical argument for partner status. Exceeding it returns **HTTP 429**.
- Read `X-Rate-Limit-Limit`, `X-Rate-Limit-Remaining` and `X-Rate-Limit-Reset` (a timestamp of the next interval) from
  every response. **Workable does not document a `Retry-After` header** — do not rely on one; back off to the reset
  timestamp.
- Legacy partners still using borrowed account tokens can reach the 50/10s tier by setting `X-WORKABLE-CLIENT-ID`.
  That changes throughput only, never what the token may read.
- **The MCP server** (`https://mcp.workable.com/mcp`) is a separate product surface with its own OAuth metadata and
  Dynamic Client Registration. It is worth knowing about — it confirms the authorize/token endpoints and the full
  scope vocabulary — but it is not a credential route for a REST connector (§ Platform state).
- **Listing** in the partner directory comes with the partner program. Workable's docs also carry pages promoting
  third-party unified-API vendors as an alternative to building direct; treat directory presence as no evidence of
  what any given integration is built on.

## 9. Verify end-to-end

Do not stop at "the token came back." Test what a customer's tenant actually does:

1. Call `GET /accounts` first. It proves the credential works, and on OAuth it tells you **which subdomains** this
   user can reach (§2). Confirm your connect flow makes the customer choose when there is more than one.
2. Make one real read against `https://<subdomain>.workable.com/spi/v3/jobs` — not against the `www` host.
3. **Read `/employees` and count.** Compare against what the customer says their headcount is. A short list is the
   `member_id` trap (§7), not an empty account.
4. Read an **offer** if your product maps offers. An account token failing here is by design, not a bug.
5. On OAuth: **refresh, then refresh again using the token returned by the first refresh.** That is what catches a
   client that ignores rotation, and it is a two-minute test for a failure that otherwise appears hours later.
6. Watch `X-Rate-Limit-Remaining` during a real sync, not a single call. At 10/10s you will find the ceiling fast.
7. Re-run against a **second** account, not the one that generated the credential.

| Symptom | Cause |
| --- | --- |
| 401 on every call, credential looks correct | Missing space between `Bearer` and the token — Workable calls this out by name (§5) |
| 401 on a refresh-token request | The user revoked the grant from either side; the full authorization-code flow must be repeated (§5) |
| 404 on every call, credential is valid | Wrong or missing account subdomain in the host, or calling the `www` host for account-scoped data (§2) |
| Auth works, `/offers/:id` 403s or 404s | Offers are **user-token only**; an account or partner token cannot reach them (§7) |
| `/employees` returns a short list of thin records, HTTP 200 | Account-level token without `member_id` of an HR admin — published employees with public info only (§7) |
| `/members`, `/recruiters` or `/events` unavailable | Partner tokens are excluded from these endpoints (§7) |
| Worked for months, 401 today, nothing changed | The customer's token hit its chosen expiration (30d–2y). Tell them to **Renew**, not regenerate (§5) |
| Everything broke at once for one customer | An Admin revoked the token — revocation is immediate and total (§5) |
| Customer says the Integrations page has no token section | They are not an Admin; there is no delegated permission (§5) |
| `429` under light load | 10 requests/10 seconds on account tokens. Throttle from the `X-Rate-Limit-*` headers; there is no `Retry-After` (§8) |
| Second refresh fails, first succeeded | The refresh token rotates; store the new one from every response (§5) |
| Customer cannot find the scope you asked for | It is an authorization-server scope, not one offered on the account-token form (§7) |
| Requisition endpoints 403/404 for one customer | Requisitions are gated on the Hiring Plan subscription (§7) |
| Browser-side call blocked | Workable does not support CORS; this API is server-to-server only |

## 10. Hand off — never commit the token

- **Do not** write an API access token, partner token, client secret or refresh token into source control, a test, a
  fixture, a committed `.env`, a ticket, a PR body or a chat channel. Values go to the human running this, for their
  secret store or console. The account **subdomain** is not a secret and may be recorded; the token is, and they
  usually arrive in the same message — separate them before you paste anything.
- If a code change is needed (a callback host, a scope string, a token host), keep it secret-free and say plainly what
  the human must set out of band.
- Report a secret once so it can be pasted into the secret store, say clearly that it is now in the transcript and can
  be revoked, then move on.
- Close with: which credential model you ended up on; the account subdomain(s); the client ID if partner OAuth; where
  the secret was delivered; the authorize, token and revoke endpoints; the exact scope strings; **the expiration the
  customer chose and who is watching that clock**; the `member_id` requirement to pass on if employees are in scope;
  the rate-limit tier the connector will actually get; and whatever is still waiting on Workable.

## Stop and ask

Hand back to a human rather than guessing when: **there is no accepted Workable partnership** and the ask is OAuth
credentials (say so in the first reply — do not start a registration that cannot complete); the partner application
needs mutual-customer, revenue, security or compliance claims; someone proposes moving live per-customer connections
to partner OAuth (every customer re-connects); the only available test tenant is production and the task involves
writes; a customer cannot generate a token because nobody wants to be made an Admin; the `member_id` requirement means
asking a customer to nominate an HR admin whose visibility defines your data set; the 10-requests/10-seconds ceiling
makes a promised sync cadence impossible; someone wants to treat the MCP server's Dynamic Client Registration as a
REST-API credential route; or the Integrations page and developer hub do not match the **Platform state** section.

## References

Verified to resolve on 2026-09-20.

- Getting started / generate an API access token — https://workable.readme.io/reference/generate-an-access-token
- OAuth 2.0 (partner authorization-code flow, refresh, revoke) — https://workable.readme.io/page/oauth
- Partner Token — https://workable.readme.io/reference/partner-token
- Rate limiting — https://workable.readme.io/reference/rate-limits
- `/accounts` — https://workable.readme.io/reference/accounts
- `/accounts/:subdomain` — https://workable.readme.io/reference/accountssubdomain
- `/employees` (account-token visibility limits) — https://workable.readme.io/reference/employees
- `/employees/:id` (`member_id` requirement) — https://workable.readme.io/reference/employeesid
- `/offers/:id` (user tokens only) — https://workable.readme.io/reference/requisitionscode-copy-1
- Webhook subscriptions — https://workable.readme.io/reference/webhook-subscriptions
- Workable MCP server — https://workable.readme.io/reference/workable-mcp-server
- MCP OAuth authorization-server metadata — https://mcp.workable.com/.well-known/oauth-authorization-server
- Full documentation index (every endpoint with its scope and token types) — https://workable.readme.io/llms.txt
- Generating/revoking access tokens (Admin rule, expirations, scope table) — https://help.workable.com/hc/en-us/articles/115015785428-Generating-revoking-access-tokens-for-Workable-s-API
- Troubleshooting API issues — https://help.workable.com/hc/en-us/articles/4903195036183-Troubleshooting-API-issues
- Workable API Documentation (help centre) — https://help.workable.com/hc/en-us/articles/115013356548-Workable-API-Documentation
- Workable API help section — https://help.workable.com/hc/en-us/sections/4903169507735-Workable-API
- Integrating with Workable via Unified.to API — https://help.workable.com/hc/en-us/articles/21453337746327-Integrating-with-Workable-via-Unified-to-API
- Developer hub and partner program — https://www.workable.com/developers
- Technology partner program and tiers — https://www.workable.com/partnership-program/technology-partners
- Partner application — https://www.workable.com/partnership-program/apply
- Third-party partner application — https://www.workable.com/partnership-program/third-party-partners/apply
- Account Integrations settings (sign-in required) — https://www.workable.com/backend/account/integrations
