
Retrieve eligible absence types for a worker via absenceManagement/v4/workers/{id}/eligibleAbsenceTypes. Returns PTO, sick leave, and other time-off categories the worker can use. Use list_workers to find valid worker IDs.
Get absence/PTO balances via absenceManagement/v4/balances. Returns remaining balances across all absence plans (PTO, sick, etc). Pass workerid for one worker, or omit it to return balances across all workers the credential can see. Filter by category or effective date. Use listworkers to find valid worker IDs.
List time off entries for a worker via absenceManagement/v4/workers/{id}/timeOffDetails. Returns PTO requests with dates, status, and hours. Filter by date range, status, or time off type. Use list_workers to find valid worker IDs.
Get details of a specific time off entry via absenceManagement/v4/workers/{id}/timeOffDetails/{subId}. Returns full entry details including dates, type, quantity, and status. Use listtimeoff_entries to find valid entry IDs.
Submit a time off request via absenceManagement/v4/workers/{id}/requestTimeOff POST. Call geteligibleabsence_types first to get a time off type ID the worker is eligible for. Each day's date uses YYYY-MM-DD; start/end (partial-day entries) require a full ISO-8601 datetime, e.g. 2026-06-29T09:00:00.000Z.
List leaves of absence for a worker via absenceManagement/v4/workers/{id}/leavesOfAbsence. Returns formal leave records (FMLA, parental, medical, etc). Filter by date range, status, or leave type.
Get details of a specific leave of absence via absenceManagement/v4/workers/{id}/leavesOfAbsence/{subId}. Returns full leave details including type, dates, and status. Use listleavesof_absence to find valid leave IDs.
List custom object definitions via customObjectDefinition/v1/definitions. Returns definitions with their business object, alias and active flag. The API supports only limit/offset paging - no server-side filtering - so filter client-side. Use to discover definition IDs and business object WIDs.
Get detailed custom object definition by ID including all field definitions, data types, and validation rules via customObjectDefinition/v1. Use listcustomobject_definitions to find valid IDs.
List available custom field types via customObjectDefinition/v1/fieldTypes. Returns field type definitions that can be used when creating custom object definitions. Use to find valid field type IDs.
Get fields for a custom object definition via customObjectDefinition/v1/definitions/{id}/fields. Returns all field definitions with their types and constraints. Use listcustomobject_definitions to find valid definition IDs.
Create a custom object definition via customObjectDefinition/v1/definitions POST. Workday requires name, alias, multiinstance and at least one security domain; call listsecuritydomains first for domain WIDs. businessobjectid is the WID of the object to extend - read the businessObject of any listcustomobjectdefinitions entry. Keep the returned domains: GET never gives them back.
Replace a custom object definition via customObjectDefinition/v1/definitions/{id} PUT. Full replace, not a patch: re-send every value you want to keep, including businessobjectid and referenceid, which are cleared if omitted. Read current values with getcustomobjectdefinition; it does not return domains, so re-pick those with listsecuritydomains.
Delete a custom object definition permanently via customObjectDefinition/v1/definitions/{id} DELETE. Use listcustomobject_definitions to find valid IDs.
Get a worker's profile from compensation/v2/workers/{id}: title, location, supervisory org. Returns NO salary, bonus or pay-rate amounts - Workday REST exposes none. For base pay: listdatasources(alias='allWorkers'), getdatasourcefields(searchstring='base pay') for the field, then querywqldata. For paid amounts use listworkerpayslips. Use listworkers for IDs.
Request a one-time payment for a worker via compensation/v2/workers/{id}/requestOneTimePayment POST, the only compensation write in the Workday REST API. compensation/v2 has no prompt endpoints, so source every WID first: positionid from listworkerpositions, reasonid from listcompensationreasons, and each payments[].onetimepaymentplanid from listonetimepaymentplans.
List organizations of one type (cost centers, divisions, companies) via common/v1/organizations. typefilter is required: Workday returns zero rows without it. For supervisory orgs use listsupervisoryorganizations. A WQL data source for the org type (find with listdata_sources) avoids needing a type WID. No inactive or parent filter.
Get an organization by ID via common/v1/organizations/{id}. Workday returns only the reference (id, descriptor, href) - no hierarchy, manager, or members; for a supervisory org use getsupervisoryorganization (manager) and getorganizationworkers (members). If not found, use listorganizations or listsupervisory_organizations for valid IDs.
Retrieve a paginated list of supervisory organizations via common/v1/supervisoryOrganizations. Returns supervisory org hierarchy used for manager-worker relationships and approval chains. Use to find supervisory org IDs for getorganizationworkers.
Retrieve workers assigned to a supervisory organization via common/v1/supervisoryOrganizations/{id}/workers. Returns employees, contractors and contingent workers with position details. Page with limit/offset. The API offers no active-only or child-org filter. Use listsupervisoryorganizations to find valid IDs.
Get one supervisory organization via common/v1/supervisoryOrganizations/{id}, including its manager and name (and code, when the org has one). To find a worker's manager, pass the primarySupervisoryOrganization.id from getworker. If the returned manager.id is the worker's own ID, they lead that org and this result is not their manager. Use listsupervisory_organizations for IDs.
Retrieve a paginated list of pay groups via payroll/v2/payGroups. Returns pay group configurations including pay frequency, currency, and associated workers. Use to find pay group IDs for payroll operations.
Get details for a specific pay group via payroll/v2/payGroups/{id}. Returns pay group configuration, frequency, currency, and member information. Use listpaygroups to find valid IDs.
Get pay group detail by ID via payroll/v2/payGroupDetails/{id}. Returns processing period schedule, run category, and next period to process. The ID must be a payGroupDetail ID from the payGroupDetails array in listpaygroups or getpaygroup responses, not a pay group ID.
Retrieve a paginated list of pay slips for a specific worker via common/v1/workers/{id}/paySlips. Returns earnings, deductions, tax withholdings, gross pay, and net pay. Requires workerid from listworkers.
Get a specific pay slip for a worker via common/v1/workers/{wid}/paySlips/{id}. Returns complete earnings breakdown, deductions, tax withholdings, gross pay, and net pay. Use listworkerpay_slips to find valid slip IDs.
Submit one payroll input via payroll/v2/payrollInputs POST for supplemental earnings, deductions, bonuses or retro corrections. One input carries exactly one pay component, so submit separate calls per component. Get paycomponentid from listpaycomponents (or searchpaycomponents on 403) and inputdetails[].typeid from listpaycomponent_calculations.
Retrieve a paginated list of payroll inputs via payroll/v2/payrollInputs GET. Returns previously submitted payroll input data including earnings, deductions, and processing status.
Retrieve all available pay components (earnings codes) via payroll/v2/values/payrollInputsGroup/payComponents. Returns every earnings code name, category and taxability status in one call; not paginated. A 403 means the tenant has not granted this ISU the Payroll Public API (Payroll Input) domain - use searchpaycomponents instead, which reaches the same data over WQL.
Retrieve a paginated list of job postings via recruiting/v4/jobPostings. Returns posting titles, descriptions, locations and status. Filter server-side by category, jobposting, jobrequisition or jobsite, and page with limit/offset. The API offers no status filter, so filter on status client-side. Use to find posting IDs for getjob_posting.
Retrieve job posting details for a specific ID via recruiting/v4/jobPostings/{id}. Returns complete posting information including title, description, requirements, location, and application instructions. Use listjobpostings to find valid IDs.
Retrieve prospect profile information for a specific ID via recruiting/v4/prospects/{id}. Returns personal details, contact information, resume, and recruitment status. The Workday REST API has no endpoint that lists prospects; get IDs from createprospect responses or a WQL query (executewql_query, requires the System scope).
Create a recruiting prospect via recruiting/v4/prospects POST. Call listcountries first for countryid: it is always required, and it also decides which name parts that country's Recruiting Name Components config mandates, so send firstname and lastname unless you know they are optional there. Returns the new prospect ID for get_prospect.
Retrieve a paginated list of interviews via recruiting/v4/interviews. Returns interview schedules, candidates, interviewers, and status information. Use to find interview IDs for getinterview and submitinterview_feedback.
Retrieve interview details for a specific ID via recruiting/v4/interviews/{id}. Returns interview schedule, candidate, interviewers, evaluation status, and feedback. Use list_interviews to find valid IDs.
Submit feedback for an interview via recruiting/v4/interviews/{id}/feedback POST. The API has no hire/no-hire recommendation field. Use listinterviews for interviewid. overallratingid is spec-optional but required by tenants that set it so, and Workday publishes no way to list the rating scale - the only source is the overallRating of feedback already on an interview.
List the countries a prospect can be created against, via recruiting/v4/values/common/countries. Call before createprospect for its required countryid, which also decides which name parts that country mandates. The same country WIDs are what searchpaycomponents takes. Paginate with limit/offset; the list can run to a few hundred.
List WQL data sources via wql/v1/dataSources. Returns available data sources that can be queried with WQL. Use to find data source IDs for getdatasource and getdatasource_fields. Requires the System functional-area scope on the Workday API client and Workday Query Language domain access; without them every WQL call is denied.
Get a specific WQL data source by ID via wql/v1/dataSources/{id}. Returns data source metadata including name, description, and available fields. Use listdatasources to find valid IDs. Requires the System functional-area scope and Workday Query Language domain access.
Get fields for a WQL data source via wql/v1/dataSources/{id}/fields. Returns field names, types, and descriptions. Paginated: a data source can have thousands of fields and Workday returns 20 per page, so use limit/offset and check hasmore. Use listdata_sources to find valid data source IDs. Requires the System functional-area scope and Workday Query Language domain access.
Execute a WQL query via wql/v1/data POST (use for queries over 2,048 characters; otherwise querywqldata). Returns query results with column definitions. Use listdatasources and getdatasource_fields to understand available data. Page with limit/offset. Requires the System functional-area scope and Workday Query Language domain access.
Query WQL data via wql/v1/data GET with a query string parameter. Default choice for WQL queries up to 2,048 characters; use executewqlquery (POST) for longer ones. Returns paginated structured data. Use listdatasources and getdatasource_fields to build valid queries. Requires the System functional-area scope on the Workday API client and Workday Query Language domain access.
List Workday security domains via WQL (allDomains), for the domains field on createcustomobjectdefinition and updatecustomobjectdefinition. customObjectDefinition/v1 has no prompt for these and GET on a definition never returns its domains, so this is the only source. A tenant can hold many - pass search to filter by name server-side. Requires the System scope.
List business-process reasons (Event Classification Subcategories) via WQL, for reasonid on requestonetimepayment. compensation/v2 exposes no prompt for these, so this is the only source. Pass search 'One-Time Payment' to match across all reasons; use one whose descriptor starts with that process, as reasons from other processes are rejected (A5). Requires the System scope.
List one-time payment plans via WQL (allOneTimePaymentPlans), for payments[].onetimepaymentplanid on requestonetimepayment. compensation/v2 exposes no prompt for these, so this is the only source. Returns each plan's currency, which must match payments[].paymentcurrencyid. Active plans only unless include_inactive is true. Requires the System scope.
Find pay components for one payroll country via WQL, for paycomponentid on createpayrollinput. Use this when listpaycomponents returns 403: the REST prompt needs the Payroll Public API (Payroll Input) domain, and this WQL route does not. countryid is required (use listcountries). Pass search to match the payslip name server-side. Requires the System scope.
List pay component related calculations via WQL, the only reachable source for inputdetails[].typeid on createpayrollinput - payroll/v2 exposes no prompt for these. Rows read '<Pay Component> [Country] - <Calculation>', e.g. 'OASDI [USA] - Tax Wage Limit'. Pass search (e.g. the pay component name) to match across all calculations. Requires the System scope.
Retrieve time clock events via timeTracking/v4/timeClockEvents. Filter by worker and date range (fromdate/todate) and page with limit/offset. Returns punch records with timestamps. The API has no status filter. If entries are not found, verify workerid with listworkers.
Record one punch via timeTracking/v4/timeClockEvents POST; post an In and an Out to bracket a shift. On In send exactly one of timeentrycodeid (listtimeentrycodes), projectid (listtimetrackingprojects) or projectplantaskid (listprojectplantasks); on Out, Meal or Break send no timeentrycodeid. timezoneid: listworkertimezones. Omit position_id unless multi-position.
Update a time clock event via timeTracking/v4/timeClockEvents/{id}. The Workday endpoint is a full replace, so this tool first reads the event and overlays only the fields you send - send just what changes. Use listtimeclockevents for eventid. Workday rejects changing the worker or time zone and rejects turning a check-in into a check-out. Fails if the entry is locked or approved.
Delete a time clock event via timeTracking/v4/timeClockEvents/{id} DELETE. Use listtimeclockevents for eventid. A 403 here is a tenant permission gap, not a bad request: Workday secures this endpoint with its own Process: Delete Time Clock Event REST APIs domain, separate from the create and update domain. Otherwise the entry may be approved, locked or in a closed payroll period.
Retrieve time totals for a worker via timeTracking/v4/workers/{id}/timeTotals. Returns aggregated hours including regular, overtime and time-off summaries for the period containing perioddate, or the current period if omitted. If worker not found, use listworkers to find valid IDs.
Submit a worker's time for approval via timeTracking/v4/workers/{id}/timeReviewEvents POST. Pass any single date inside the target period - Workday expands it to the week or pay period per the worker's Time Entry Template, so there is no start/end. Use listworkers for workerid.
List the time entry codes a worker is eligible for on a date, via timeTracking/v4/values/timeTypes/timeEntryCodes. Call before createtimeclockevent for its timeentrycodeid. Both workerid (use listworkers) and date are required - Workday returns 0 rows without them. Not paginated. Hours-entry codes are listed too; a punch needs an in/out code, else 400 A707.
List the time zones a worker can punch in, via timeTracking/v4/values/timeValues/workerTimeZone. Call before createtimeclockevent for its required timezoneid. workerid is required (use list_workers) - Workday scopes this prompt to a worker and returns 0 rows without one. Not paginated.
List the projects a worker can bill a punch to on a date, via timeTracking/v4/values/timeTypes/projects. Call before createtimeclockevent for its projectid. Both workerid (use listworkers) and date are required - Workday scopes this prompt and returns 0 rows without them. Not paginated. An empty list means the worker bills no projects; use listtimeentry_codes instead.
List the project plan tasks a worker can bill a punch to on a date, via timeTracking/v4/values/timeTypes/projectPlanTasks. Call before createtimeclockevent for its projectplantaskid. workerid (use listworkers) and date are required - Workday scopes this prompt and returns 0 rows without them. Optionally narrow to one projectid from listtimetrackingprojects. Not paginated.
List a worker's positions, via timeTracking/v4/values/timeValues/positions. Source of positionid for createtimeclockevent and requestonetimepayment. workerid is required (use listworkers) - Workday scopes this prompt and returns 0 rows without one. When it returns one row, omit positionid on createtimeclock_event: Workday rejects it for single-position workers.
Validate Workday credentials. Verifies credentials during setup.
Retrieve a paginated list of workers via common/v1/workers. Use search for a case-insensitive prefix match on worker name (min 3 chars), and limit (max 100) plus offset for pagination. Returns worker id, name, business title, primary supervisory organization, and work email. Pass a returned id to get_worker.
Retrieve worker profile for a specific ID via common/v1/workers/{id}. Returns personal data, position details, and organizational relationships, but not the manager: pass primarySupervisoryOrganization.id to getsupervisoryorganization, and check the result is not the worker themselves. If worker not found, use list_workers to find valid worker IDs.
List a manager's direct reports via common/v1/workers/{id}/directReports. Returns each report's id, name, business title, primary supervisory organization, work email and whether they are a manager themselves. Page with limit/offset. Use listworkers for worker IDs; for a worker's own manager use getsupervisory_organization.

In an mcp.json file, add the configuration below, and restart Cursor.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "url": "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
5 "headers": {
6 "Authorization": "Bearer yMt*****"
7 }
8 }
9 }
10}
11Open your Claude Desktop configuration file and add the server configuration below. You'll also need to restart the application for the changes to take effect.
Make sure Claude is using the Node v20+.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "command": "npx",
5 "args": [
6 "-y",
7 "mcp-remote@latest",
8 "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
9 "--header",
10 "Authorization: Bearer ${AUTH_TOKEN}"
11 ],
12 "env": {
13 "AUTH_TOKEN": "yMt*****"
14 }
15 }
16 }
17}Open your Windsurf MCP configuration file and add the server configuration below.
Click on the refresh button in the top right of the Manage MCP server page or in the top right of the chat box in the box icon.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "command": "npx",
5 "args": [
6 "-y",
7 "mcp-remote@latest",
8 "https://ah-api.merge.dev/api/v1/tool-packs/<tool-pack-id>/registered-users/<registered-user-id>/mcp",
9 "--header",
10 "Authorization: Bearer ${AUTH_TOKEN}"
11 ],
12 "env": {
13 "AUTH_TOKEN": "<ah-production-access-key>"
14 }
15 }
16 }
17 }In Command Palette (Cmd+Shift+P on macOS, Ctrl+Shift+P on Windows), run "MCP: Open User Configuration".
You can then add the configuration below and press "start" right under servers. Enter the auth token when prompted.
Learn more in the official documentation ↗
1{
2 "inputs": [
3 {
4 "type": "promptString",
5 "id": "agent-handler-auth",
6 "description": "Agent Handler AUTH_TOKEN", // "yMt*****" when prompt
7 "password": true
8 }
9 ],
10 "servers": {
11 "agent-handler": {
12 "type": "stdio",
13 "command": "npx",
14 "args": [
15 "-y",
16 "mcp-remote@latest",
17 "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
18 "--header",
19 "Authorization: Bearer ${input:agent-handler-auth}"
20 ]
21 }
22 }
23}It’s an MCP server that lets you access data and functionality from Workday via tools. Your agents can call these tools to update employee records, retrieve compensation details on a group of employees, and more.
Workday doesn’t currently support an “official” MCP server. But you can access Workday MCP servers with dozens of tools from 3rd-party platforms, like Merge Agent Handler.
Here are some popular use cases:
Here are just a few reasons:
Yes, Merge Agent Handler includes a security gateway with data loss protection (DLP) that scans tool inputs and responses for sensitive information and enforces security policies. These rules can automatically block, redact, or mask sensitive data fields during tool execution.
Here are a few custom rules you can set:
You can follow these steps:
1. Create an Agent Handler account and sign in.
2. Create (or select) a Registered User for the identity you want to act as. This is the identity boundary Agent Handler uses to scope credentials and audit logs.
3. Add the Workday connector and configure its tool access. Do this in Agent Handler so the tools you need are available to your agent via MCP (and scoped appropriately).
4. Create a Tool Pack that includes Workday. Tool Packs define the set of tools your MCP client will see when it connects.
5. Have the Registered User authenticate to Workday via Merge Link. Agent Handler will prompt for auth if the user isn't connected yet, and store the Workday credentials under that Registered User.
6. Generate your MCP connection config. The MCP endpoint comes in the following form: https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp
7. Add that MCP server to your MCP client (Claude Desktop, Windsurf, VS Code, Cursor). Use the provided JSON config pattern for your client, and make sure you are on Node v20 if you are using the <code class="blog_inline-code">npx mcp-remote</code> flow.
8. Authenticate the MCP connection with your Agent Handler API key.Your MCP client sends it as <code class="blog_inline-code">Authorization: Bearer ${AUTH_TOKEN}</code> and you can rotate it as needed.
9. Test a few Workday tool calls, then verify in your logs. Run simple prompts first, and confirm the tool calls and underlying requests look correct in Agent Handler’s logs.
10. Add rules before going to production. Set DLP and access-control rules per connector/tool, and optionally by region, to block or redact sensitive HR data.
Yes, Agent Handler for Employees lets your employees connect Claude, ChatGPT, Microsoft Copilot, Cursor, and other MCP-compatible AI tools to Workday without bypassing IT governance.
Instead of setting up direct connections with personal credentials that IT can't monitor or revoke, each employee authenticates through Agent Handler and gets individual credentials tied to their identity.
IT also provisions access by role or group via SCIM. An HR manager, for example, gets Workday access to manage employee records and org changes, BambooHR to track PTO and HR data, and Google Sheets to report headcount; while a finance analyst gets Workday access to pull compensation data, NetSuite to close books, and Expensify to reconcile expenses.
Every tool call an employee's AI makes to Workday is also inspected against your DLP rules and logged to a searchable audit trail, giving security teams full visibility into what data was accessed and by whom.
Whether you're an engineer experimenting with agents or a product manager looking to add tools, you can get started for free now