Summary and recommendation
Pleo exposes a REST API at `https://external.pleo.io/v0` using OAuth 2.0, supporting both client credentials (partner/service accounts) and authorization code (user-delegated) flows. Core employee lifecycle operations - list, get, create, update, and deactivate - are available via the `/employees` resource. The API is company-scoped per credential set; multi-company management requires separate credentials per company.
Tokens are short-lived, so token refresh logic is mandatory to avoid mid-operation 401 errors. Rate limit thresholds are not publicly documented; HTTP 429 is returned on breach, and exponential backoff should be implemented as a baseline. Pagination is cursor-based using `pageToken` - offset arithmetic will not work reliably.
No webhook system is available for employee lifecycle events; polling `GET /employees` with a stored cursor is the only event-detection mechanism.
For teams building an identity graph across SaaS applications, Pleo's employee object provides the fields needed to anchor spend identity: `id` (UUID), `email`, `role` (enum: OWNER, BOOKKEEPER, MEMBER), `status` (enum: ACTIVE, INVITED, DEACTIVATED), `departmentId`, and `companyId`.
These fields are sufficient to join Pleo identity records against a canonical employee directory and detect provisioning drift - active Pleo accounts with no corresponding active directory record, or DEACTIVATED records still appearing as active in upstream systems.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 (client credentials flow for partner/API access; authorization code flow for user-delegated access) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise (Beyond plan or higher; SAML SSO must be enabled first) |
Authentication
Auth method: OAuth 2.0 (client credentials flow for partner/API access; authorization code flow for user-delegated access)
Setup steps
- Register an application in the Pleo Developer Portal (developers.pleo.io) to obtain a client_id and client_secret.
- Request an access token via POST to https://external.pleo.io/v0/auth/headless/token using client_credentials grant type.
- Include the returned Bearer token in the Authorization header for all subsequent API requests.
- Ensure the token is refreshed before expiry; Pleo tokens are short-lived.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| read:employees | Read employee (user) records including profile and status. | GET /employees, GET /employees/{id} |
| write:employees | Create and update employee records. | POST /employees, PATCH /employees/{id} |
| read:company | Read company-level metadata. | GET /companies/{id} |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | string (UUID) | Unique identifier for the employee. | system-generated | immutable | Used as path parameter in employee-specific endpoints. |
| firstName | string | Employee's first name. | required | optional | |
| lastName | string | Employee's last name. | required | optional | |
| string | Employee's work email address; used as login identifier. | required | optional | Must be unique within the company. | |
| role | string (enum) | Employee role within Pleo: OWNER, BOOKKEEPER, MEMBER. | optional (defaults to MEMBER) | optional | Controls permissions within the Pleo platform. |
| status | string (enum) | Account status: ACTIVE, INVITED, DEACTIVATED. | system-set to INVITED on creation | can be set to DEACTIVATED to offboard | Deactivating does not delete the employee record. |
| departmentId | string (UUID) | ID of the department the employee belongs to. | optional | optional | Department must exist before assignment. |
| spendingLimit | object | Monthly spending limit configuration for the employee's card. | optional | optional | Contains amount (integer, minor currency units) and currency fields. |
| locale | string | BCP 47 locale tag for the employee (e.g., en-GB). | optional | optional | |
| companyId | string (UUID) | ID of the company the employee belongs to. | system-set | immutable | Derived from the authenticated API client's company context. |
Core endpoints
List Employees
- Method: GET
- URL:
https://external.pleo.io/v0/employees - Watch out for: Pagination uses cursor-based pageToken; do not rely on offset arithmetic.
Request example
GET /v0/employees?pageSize=50 HTTP/1.1
Authorization: Bearer {access_token}
Response example
{
"data": [{"id":"uuid","firstName":"Jane","lastName":"Doe","email":"jane@co.com","status":"ACTIVE"}],
"nextPageToken": "abc123"
}
Get Employee by ID
- Method: GET
- URL:
https://external.pleo.io/v0/employees/{employeeId} - Watch out for: Returns 404 if the employee ID does not belong to the authenticated client's company.
Request example
GET /v0/employees/uuid-1234 HTTP/1.1
Authorization: Bearer {access_token}
Response example
{
"data": {"id":"uuid-1234","firstName":"Jane","email":"jane@co.com","status":"ACTIVE","role":"MEMBER"}
}
Create Employee (Invite)
- Method: POST
- URL:
https://external.pleo.io/v0/employees - Watch out for: Creating an employee triggers an invitation email to the provided address immediately.
Request example
POST /v0/employees HTTP/1.1
Authorization: Bearer {access_token}
Content-Type: application/json
{"firstName":"John","lastName":"Smith","email":"john@co.com"}
Response example
{
"data": {"id":"uuid-5678","status":"INVITED","email":"john@co.com"}
}
Update Employee
- Method: PATCH
- URL:
https://external.pleo.io/v0/employees/{employeeId} - Watch out for: Only fields included in the PATCH body are updated; omitted fields retain their current values.
Request example
PATCH /v0/employees/uuid-5678 HTTP/1.1
Authorization: Bearer {access_token}
Content-Type: application/json
{"departmentId":"dept-uuid","role":"BOOKKEEPER"}
Response example
{
"data": {"id":"uuid-5678","role":"BOOKKEEPER","departmentId":"dept-uuid"}
}
Deactivate Employee
- Method: DELETE
- URL:
https://external.pleo.io/v0/employees/{employeeId} - Watch out for: This deactivates the employee and revokes card access; it does not permanently delete the record or historical expense data.
Request example
DELETE /v0/employees/uuid-5678 HTTP/1.1
Authorization: Bearer {access_token}
Response example
HTTP 204 No Content
List Departments
- Method: GET
- URL:
https://external.pleo.io/v0/departments - Watch out for: Department IDs are required when assigning employees to departments; fetch this list first.
Request example
GET /v0/departments HTTP/1.1
Authorization: Bearer {access_token}
Response example
{
"data": [{"id":"dept-uuid","name":"Engineering"}]
}
Get Company
- Method: GET
- URL:
https://external.pleo.io/v0/companies/{companyId} - Watch out for: The companyId is scoped to the authenticated client; cross-company access is not supported.
Request example
GET /v0/companies/co-uuid HTTP/1.1
Authorization: Bearer {access_token}
Response example
{
"data": {"id":"co-uuid","name":"Acme Corp","currency":"EUR"}
}
Rate limits, pagination, and events
- Rate limits: Pleo does not publicly document specific rate limit thresholds in its developer docs as of the knowledge cutoff. Limits are enforced but exact values are not published.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: HTTP 429 is returned when rate limits are exceeded. Retry-After header presence is not confirmed in official docs. Implement exponential backoff.
- Pagination method: cursor
- Default page size: 50
- Max page size: 100
- Pagination pointer: pageToken
| Plan | Limit | Concurrent |
|---|---|---|
| All plans | 0 |
- Webhooks available: No
- Webhook notes: Pleo's public developer API documentation does not describe a webhook system for user/employee lifecycle events as of the knowledge cutoff.
- Alternative event strategy: Poll GET /employees with a stored cursor/pageToken to detect new or changed employee records.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise (Beyond plan or higher; SAML SSO must be enabled first)
Endpoint: https://external.pleo.io/scim/v2
Supported operations: GET /Users, GET /Users/{id}, POST /Users, PATCH /Users/{id}, PUT /Users/{id}, DELETE /Users/{id}
Limitations:
- SCIM provisioning is currently documented only for Okta and Microsoft Entra ID (Azure AD) integrations.
- SAML SSO must be configured and active before SCIM can be enabled.
- Group/department sync via SCIM is not confirmed in official documentation.
- Available on Enterprise/Beyond plan only; not available on Starter, Essential, or Advanced plans.
- Google Workspace and OneLogin SCIM integrations are not officially supported.
Common scenarios
Three primary automation scenarios are supported by the current API surface.
Onboard: POST /v0/employees with firstName, lastName, and email immediately creates the record and fires an invitation email - there is no draft state. Capture the returned id, then PATCH /v0/employees/{id} to set departmentId, role, and spendingLimit. Note that spendingLimit values use minor currency units (cents), not decimal amounts.
Offboard: Locate the employee UUID via GET /v0/employees filtered by email if not already stored in your identity graph. DELETE /v0/employees/{id} deactivates the account and revokes card access immediately. The record and all historical expense data are retained; a subsequent GET /v0/employees/{id} should return status: DEACTIVATED as confirmation.
SCIM sync via Okta or Entra ID: Requires the Beyond/Enterprise plan with SAML SSO active. The SCIM base URL is https://external.pleo.io/scim/v2. SCIM is documented only for Okta and Microsoft Entra ID; Google Workspace and OneLogin are not officially supported. Group/department sync via SCIM is unconfirmed in official documentation. The SCIM Bearer token is generated once in Pleo admin - if lost, a new token must be generated and updated in the IdP connector.
Onboard a new employee
- POST /v0/employees with firstName, lastName, and email to create and invite the employee.
- Capture the returned employee id from the response.
- PATCH /v0/employees/{id} to assign departmentId, role, and spendingLimit as needed.
- Employee receives invitation email and completes account setup independently.
Watch out for: The invitation email fires on POST creation; ensure the email address is correct before calling the endpoint.
Offboard a departing employee
- GET /v0/employees to locate the employee's UUID by email if not already stored.
- DELETE /v0/employees/{id} to deactivate the account and revoke card access.
- Verify the employee's status returns DEACTIVATED on a subsequent GET /v0/employees/{id}.
Watch out for: DELETE deactivates but does not delete; expense history and records are retained for audit purposes.
Sync employees via SCIM with Okta
- Ensure the company is on the Beyond/Enterprise plan.
- Configure SAML SSO in Pleo admin settings and verify it is active.
- In Pleo admin, navigate to Integrations > Okta and retrieve the SCIM base URL and Bearer token.
- In Okta, add the Pleo app, set the SCIM connector base URL to https://external.pleo.io/scim/v2, and paste the Bearer token.
- Enable SCIM provisioning features (Create Users, Update User Attributes, Deactivate Users) in Okta.
- Assign users or groups in Okta to push provisioning events to Pleo.
Watch out for: SCIM token is generated once in Pleo admin; if lost, a new token must be generated and updated in Okta.
Why building this yourself is a trap
The most common integration failure points with Pleo's API are predictable but non-obvious from the docs alone. Creating an employee immediately sends an invitation email; a test run against production will notify real users.
The DELETE endpoint deactivates, not deletes - callers that treat a 200 response as full removal will leave ghost records in their identity graph. SCIM cannot be enabled without SAML SSO already active and verified; attempting to configure SCIM first will block provisioning entirely.
The API is single-company per credential set, so any multi-tenant or multi-entity architecture requires explicit credential management per company. Finally, SCIM provisioning is gated to Enterprise/Beyond - teams that build SCIM-dependent workflows on lower-tier plans will hit a hard wall when attempting to enable the integration.
Automate Pleo 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.