Stitchflow
Paychex logo

Paychex User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 11, 2026

Summary and recommendation

Paychex exposes a REST API (base URL: `https://api.paychex.com`) authenticated via OAuth 2.0 `client_credentials` grant. Access requires application registration and approval at developer.paychex.com - there is no self-serve sandbox. All worker endpoints are scoped under a `companyId` resolved from `GET /companies`; there is no global worker namespace.

Core scopes are `workers:read`, `workers:write`, and `companies:read`. Sub-resources - communications (email/phone), job, and compensation - are separate endpoints and are not embedded in the base worker response, meaning a full worker profile requires multiple sequential calls.

Pagination is offset/limit-based (default 25, max 100 per page); no cursor pagination is available, which can cause missed records if the dataset changes mid-page iteration. Rate limits are not publicly documented; build exponential backoff on HTTP 429 with `Retry-After` header inspection.

API quick reference

Has user APIYes
Auth methodOAuth 2.0 (client_credentials grant)
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredEnterprise

Authentication

Auth method: OAuth 2.0 (client_credentials grant)

Setup steps

  1. Register an application at https://developer.paychex.com to obtain a client_id and client_secret.
  2. POST to https://api.paychex.com/auth/oauth/v2/token with grant_type=client_credentials, client_id, and client_secret to receive an access_token.
  3. Include the access_token as a Bearer token in the Authorization header on all subsequent API requests.
  4. Tokens expire; implement token refresh logic by re-requesting via the token endpoint.

Required scopes

Scope Description Required for
workers:read Read worker (employee) records including personal, contact, and employment data. GET /companies/{companyId}/workers
workers:write Create and update worker records. POST/PATCH /companies/{companyId}/workers
companies:read Read company-level data required to resolve companyId for worker operations. GET /companies

User object / data model

Field Type Description On create On update Notes
workerId string (UUID) Unique identifier for the worker within Paychex. system-generated immutable Used as path parameter for worker-specific operations.
employeeId string Client-assigned employee ID (e.g., badge number). optional updatable May differ from workerId.
name.firstName string Worker's legal first name. required updatable
name.lastName string Worker's legal last name. required updatable
name.middleName string Worker's middle name. optional updatable
birthDate string (ISO 8601 date) Worker's date of birth. optional updatable Format: YYYY-MM-DD
sex string (enum) Worker's gender. Values: MALE, FEMALE, NOT_SPECIFIED. optional updatable
ethnicityCode string (enum) EEO ethnicity classification code. optional updatable
communications[].type string (enum) Contact type: BUSINESS_EMAIL, PERSONAL_EMAIL, BUSINESS_PHONE, etc. optional updatable Array of communication objects.
communications[].value string The actual email address or phone number. optional updatable
currentStatus.statusType string (enum) Employment status: ACTIVE, TERMINATED, LEAVE_OF_ABSENCE, etc. required updatable Changing to TERMINATED triggers offboarding logic.
currentStatus.effectiveDate string (ISO 8601 date) Date the current status takes effect. required required
location.locationId string (UUID) Reference to the worker's assigned work location. optional updatable Must be a valid locationId from the company's location list.
job.title string Worker's job title. optional updatable
job.positionId string (UUID) Reference to the position definition. optional updatable
hireDate string (ISO 8601 date) Worker's original hire date. required updatable
workerType string (enum) Classification: EMPLOYEE, CONTRACTOR. required immutable after creation
exemptionType string (enum) FLSA exemption: EXEMPT, NON_EXEMPT. optional updatable

Core endpoints

List Companies (resolve companyId)

  • Method: GET
  • URL: https://api.paychex.com/companies
  • Watch out for: companyId is required for all worker endpoints; retrieve it here first.

Request example

GET /companies
Authorization: Bearer {access_token}

Response example

{
  "content": [
    {"companyId": "abc123", "displayId": "ACME", "legalName": "Acme Corp"}
  ]
}

List Workers

  • Method: GET
  • URL: https://api.paychex.com/companies/{companyId}/workers
  • Watch out for: Returns all statuses by default; filter by statusType query param to limit to ACTIVE workers.

Request example

GET /companies/abc123/workers?limit=25&offset=0
Authorization: Bearer {access_token}

Response example

{
  "content": [
    {"workerId": "w1", "name": {"firstName": "Jane", "lastName": "Doe"},
     "currentStatus": {"statusType": "ACTIVE"}}
  ],
  "offset": 0, "limit": 25
}

Get Worker by ID

  • Method: GET
  • URL: https://api.paychex.com/companies/{companyId}/workers/{workerId}
  • Watch out for: Response depth varies; some sub-resources (communications, job) may require separate sub-resource calls.

Request example

GET /companies/abc123/workers/w1
Authorization: Bearer {access_token}

Response example

{
  "workerId": "w1",
  "name": {"firstName": "Jane", "lastName": "Doe"},
  "hireDate": "2022-01-10",
  "currentStatus": {"statusType": "ACTIVE", "effectiveDate": "2022-01-10"}
}

Create Worker

  • Method: POST
  • URL: https://api.paychex.com/companies/{companyId}/workers
  • Watch out for: workerType is immutable after creation. Missing required fields return HTTP 400 with field-level validation errors.

Request example

POST /companies/abc123/workers
Content-Type: application/json
{
  "name": {"firstName": "John", "lastName": "Smith"},
  "hireDate": "2024-06-01",
  "workerType": "EMPLOYEE",
  "currentStatus": {"statusType": "ACTIVE", "effectiveDate": "2024-06-01"}
}

Response example

{
  "workerId": "w99",
  "name": {"firstName": "John", "lastName": "Smith"},
  "hireDate": "2024-06-01",
  "currentStatus": {"statusType": "ACTIVE"}
}

Update Worker

  • Method: PATCH
  • URL: https://api.paychex.com/companies/{companyId}/workers/{workerId}
  • Watch out for: PATCH is partial update. Sending null for a field may clear it; omit fields you do not intend to change.

Request example

PATCH /companies/abc123/workers/w99
Content-Type: application/json
{
  "name": {"firstName": "Jonathan"}
}

Response example

{
  "workerId": "w99",
  "name": {"firstName": "Jonathan", "lastName": "Smith"}
}

Terminate Worker

  • Method: POST
  • URL: https://api.paychex.com/companies/{companyId}/workers/{workerId}/status
  • Watch out for: effectiveDate cannot be in the past beyond a configurable company window. terminationReasonCode must match company-configured codes.

Request example

POST /companies/abc123/workers/w99/status
Content-Type: application/json
{
  "statusType": "TERMINATED",
  "effectiveDate": "2024-12-31",
  "terminationReasonCode": "VOLUNTARY"
}

Response example

{
  "workerId": "w99",
  "currentStatus": {"statusType": "TERMINATED", "effectiveDate": "2024-12-31"}
}

Get Worker Communications (email/phone)

  • Method: GET
  • URL: https://api.paychex.com/companies/{companyId}/workers/{workerId}/communications
  • Watch out for: Communications are a sub-resource; not returned inline on the worker object by default.

Request example

GET /companies/abc123/workers/w1/communications
Authorization: Bearer {access_token}

Response example

{
  "content": [
    {"communicationId": "c1", "type": "BUSINESS_EMAIL", "value": "jane@acme.com"}
  ]
}

List Positions

  • Method: GET
  • URL: https://api.paychex.com/companies/{companyId}/positions
  • Watch out for: positionId values are needed when assigning a worker to a job; positions must be pre-configured in Paychex Flex.

Request example

GET /companies/abc123/positions
Authorization: Bearer {access_token}

Response example

{
  "content": [
    {"positionId": "p1", "title": "Software Engineer", "departmentId": "d1"}
  ]
}

Rate limits, pagination, and events

  • Rate limits: Paychex does not publicly document specific rate limit tiers. General API throttling applies; exceeding limits returns HTTP 429.
  • Rate-limit headers: Yes
  • Retry-After header: Yes
  • Rate-limit notes: On HTTP 429, inspect Retry-After header for backoff interval. Specific request-per-minute limits are not published in official docs as of research date.
  • Pagination method: offset
  • Default page size: 25
  • Max page size: 100
  • Pagination pointer: offset / limit
Plan Limit Concurrent
Standard API Access Not publicly documented 0
  • Webhooks available: No
  • Webhook notes: Paychex does not publicly document a native webhook/event subscription system in the developer portal as of research date. Real-time event delivery is not confirmed in official docs.
  • Alternative event strategy: Poll the /workers endpoint on a schedule using the updatedSince query parameter (if supported) or compare snapshots. Third-party middleware (e.g., RoboMQ Hire2Retire) can bridge Paychex to identity providers for near-real-time sync.

SCIM API status

  • SCIM available: No
  • SCIM version: Not documented
  • Plan required: Enterprise
  • Endpoint: Not documented

Limitations:

  • No native SCIM 2.0 endpoint is documented by Paychex.
  • Okta's Paychex integration uses Okta's proprietary provisioning connector (not SCIM) to sync users.
  • Third-party tools such as RoboMQ Hire2Retire provide AD/Entra-to-Paychex sync via the REST API, not SCIM.
  • SSO is supported via SAML 2.0 but does not include SCIM provisioning.

Common scenarios

Three primary automation scenarios are supported by the API, each with non-trivial sequencing requirements:

Onboard a new employee: Resolve companyId → resolve positionId from GET /companies/{companyId}/positionsPOST /companies/{companyId}/workers with workerType, hireDate, and job.positionId → separately POST /companies/{companyId}/workers/{workerId}/communications to attach BUSINESS_EMAIL. Worker creation and communication creation are independent calls; a failed communication POST leaves the worker record without contact data. Note: workerType (EMPLOYEE vs. CONTRACTOR) is immutable after creation - incorrect classification requires creating a new worker record.

Terminate an employee: Confirm workerId via GET /companies/{companyId}/workers?statusType=ACTIVEPOST /companies/{companyId}/workers/{workerId}/status with statusType=TERMINATED, effectiveDate, and terminationReasonCode. The terminationReasonCode must match codes configured in the specific company account; generic values return HTTP 400. Effective date restrictions are enforced against the company's open payroll calendar - past-dated or mid-period terminations may be blocked server-side.

Sync active employee list to an external system (identity graph use case): GET /companies/{companyId}/workers?statusType=ACTIVE&limit=100&offset=0 → paginate by incrementing offset until the content array is empty → for each worker, optionally fetch GET /companies/{companyId}/workers/{workerId}/communications to retrieve email addresses for identity graph mapping. No delta or changelog endpoint is confirmed; full-list polling is required on every sync cycle. For large orgs, this means significant API quota consumption per run.

Onboard a new employee

  1. POST /auth/oauth/v2/token to obtain access_token.
  2. GET /companies to retrieve companyId.
  3. GET /companies/{companyId}/positions to find the correct positionId.
  4. POST /companies/{companyId}/workers with name, hireDate, workerType=EMPLOYEE, currentStatus=ACTIVE, and job.positionId.
  5. POST /companies/{companyId}/workers/{workerId}/communications to add BUSINESS_EMAIL and phone.

Watch out for: Worker creation and communication creation are separate calls; a failed communication POST leaves a worker without contact info-handle errors and retry independently.

Terminate an employee

  1. GET /companies/{companyId}/workers?statusType=ACTIVE to confirm worker is active and retrieve workerId.
  2. Confirm termination date is within the company's allowed window (not mid-open-payroll-period).
  3. POST /companies/{companyId}/workers/{workerId}/status with statusType=TERMINATED, effectiveDate, and terminationReasonCode.
  4. Verify response shows currentStatus.statusType=TERMINATED.

Watch out for: terminationReasonCode must match codes configured in the specific Paychex Flex company account; generic codes may be rejected with HTTP 400.

Sync active employee list to an external system

  1. GET /companies/{companyId}/workers?statusType=ACTIVE&limit=100&offset=0.
  2. Iterate pages by incrementing offset by limit until content array is empty.
  3. For each worker, optionally GET /companies/{companyId}/workers/{workerId}/communications to retrieve email addresses.
  4. Map workerId, name, hireDate, job.title, and BUSINESS_EMAIL to the target system's user schema.
  5. Store a local snapshot and re-run on schedule to detect changes (no webhook available).

Watch out for: No delta/changelog endpoint is confirmed; full list polling is required. Large companies with many employees will consume significant API quota per sync cycle.

Why building this yourself is a trap

The Paychex API is functional for structured HR data reads and writes, but several design constraints create integration risk at scale. There are no native webhooks or event subscriptions documented in the developer portal - real-time or near-real-time sync is not achievable without polling or a third-party middleware layer (e.g., RoboMQ Hire2Retire).

This makes Paychex a poor fit as a low-latency identity event source without additional infrastructure. The offset-based pagination model introduces a race condition risk: if employees are added or terminated between page fetches, records can be skipped or duplicated in a single sync pass.

OAuth tokens have a finite TTL with no documented refresh token flow under client_credentials; proactive token re-acquisition logic is required to avoid mid-operation 401 failures. Finally, the API is gated behind an approval process at developer.paychex.com - teams should account for provisioning lead time before committing to an API-based integration in a project timeline.

For teams building an identity graph across HR and IT systems, Paychex's lack of SCIM and webhooks means the platform functions as a polled data source rather than an authoritative real-time event emitter, and the integration architecture must be designed accordingly.

Automate Paychex workflows without one-off scripts

Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 11, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

Abnormal Security logo

Abnormal Security

API Only
AutomationAPI only
Last updatedMar 2026

Abnormal Security is an enterprise email security platform focused on detecting and investigating threats such as phishing, account takeover (ATO), and vendor email compromise. It does not support SCIM provisioning, which means every app in your stack

ActiveCampaign logo

ActiveCampaign

API Only
AutomationAPI only
Last updatedFeb 2026

ActiveCampaign uses a group-based permission model: every user belongs to exactly one group, and all feature-area access (Contacts, Campaigns, Automations, Deals, Reports, Templates) is configured at the group level, not per individual. The default Adm

ADP logo

ADP

API Only
AutomationAPI only
Last updatedFeb 2026

ADP Workforce Now is a mid-market to enterprise HCM platform that serves as the HR source of record for employee data — payroll, benefits, time, and talent. User access is governed by a hybrid permission model: predefined security roles (Security Maste