santifer/career-ops · error · Error
workday: cannot derive CXS endpoint for ${entry.name}
Error message
workday: cannot derive CXS endpoint for ${entry.name} What it means
Workday auto-detects a tenant by matching entry.api then entry.careers_url against ^https://([\w-]+)\.(wd[\w-]*)\.myworkdayjobs\.com/(?:[a-z]{2}-[A-Z]{2}/)?([^/?#]+). resolveEndpoint returns null when neither matches, and fetch() throws because it cannot build the CXS POST URL (/wday/cxs/<tenant>/<site>/jobs).
Source
Thrown at providers/workday.mjs:176
/**
* Fetch all job postings for a Workday-backed entry, paginating through
* the tenant's CXS API.
*
* Some tenants front their CXS API with Cloudflare bot management (seen
* live: geico) that 500s requests missing ordinary browser headers — the
* default UA/accept-language-less request trips it even over plain HTTPS
* with no other red flags. A real Chrome UA + accept-language + matching
* origin/referer clears it without needing per-tenant config (same fix
* as providers/glints.mjs's firewall).
*
* @param {{ name?: string, api?: string, careers_url?: string, max_pages?: number }} entry
* @param {{ fetchJson: (url: string, opts?: object) => Promise<any>, sinceMs?: number, maxPages?: number, syntheticEntries?: boolean }} ctx
* @returns {Promise<Array<{title: string, url: string, company: string, location: string, postedAt?: number}>>}
*/
async fetch(entry, ctx) {
const ep = resolveEndpoint(entry);
if (!ep) throw new Error(`workday: cannot derive CXS endpoint for ${entry.name}`);
const postOpts = {
method: 'POST',
redirect: 'error',
headers: {
'content-type': 'application/json',
accept: 'application/json',
'user-agent': BROWSER_LIKE_USER_AGENT,
'accept-language': 'en-US,en;q=0.9',
origin: ep.origin,
referer: `${ep.jobBase}/`,
},
};
const makeBody = (offset) => JSON.stringify({ limit: PAGE_SIZE, offset, searchText: '', appliedFacets: {} });
const sinceMs = typeof ctx?.sinceMs === 'number' ? ctx.sinceMs : null;
const first = await fetchJsonWithRetry(ctx, ep.api, { ...postOpts, body: makeBody(0) }, RETRY_POLICY);
const jobs = parseWorkdayResponse(first, entry);View on GitHub (pinned to 9b17a8ac97)
Solutions
- Set api: https://<tenant>.<instance>.myworkdayjobs.com/<site> on the entry (e.g. https://23andme.wd5.myworkdayjobs.com/23).
- Or set careers_url to that same tenant URL.
- Verify the instance segment matches the wd… pattern (wd1, wd5, …) and the site segment is present.
- For branded pages, keep careers_url as the branded page AND add api: with the raw tenant URL — the resolver tries api: first.
Example fix
# before - name: PTC provider: workday careers_url: https://www.ptc.com/en/careers # after - name: PTC provider: workday careers_url: https://www.ptc.com/en/careers api: https://ptc.wd1.myworkdayjobs.com/PTC
Defensive patterns
Strategy: validation
Validate before calling
const WD_RE = /^https:\/\/([\w-]+)\.(wd[\w-]*)\.myworkdayjobs\.com\/(?:[a-z]{2}-[A-Z]{2}\/)?([^\/?#]+)/;
function workdayEndpointResolvable(entry) {
for (const u of [entry.api, entry.careers_url]) {
if (typeof u === "string" && WD_RE.test(u)) return true;
}
return false;
}
// before scan
if (entry.provider === "workday" && !workdayEndpointResolvable(entry)) {
console.warn(`skip ${entry.name}: set api: to the myworkdayjobs.com tenant URL`);
continue;
} Type guard
const isWorkdayTenantUrl = (u) => typeof u === "string" &&
/^https:\/\/([\w-]+)\.(wd[\w-]*)\.myworkdayjobs\.com\/(?:[a-z]{2}-[A-Z]{2}\/)?([^\/?#]+)/.test(u); Prevention
- For branded careers pages, always pair careers_url with an explicit api: tenant URL.
- Confirm the instance segment is the wd… subdomain Workday assigned the tenant.
- Lint portals.yml provider: workday entries for a resolvable tenant regex.
When it happens
Trigger: An entry marked provider: workday whose api and careers_url are both missing, both branded domains (e.g. https://www.ptc.com/en/careers), or whose tenant URL has an unusual locale/path segment the regex rejects (e.g. lowercase-only locale, missing site segment, non-wd instance subdomain).
Common situations: Company has a branded careers page backed by Workday but the entry does not also set api: to the raw myworkdayjobs.com tenant URL; the instance segment is not a wd… subdomain; the locale path is malformed.
Related errors
- workable: cannot derive feed URL for ${entry.name}
- smartrecruiters: cannot derive API URL for ${entry.name}
- softgarden: cannot resolve widget URL for ${entry.name}
- successfactors: cannot resolve origin for ${entry.name}
- teamtailor: cannot derive jobs.rss URL for ${fallbackCompany
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/25f313deadfad365.
Report an issue: GitHub.