/teams) who are
moving to the new Teams API (/external/v1/teams).
New to the Teams API?
API Overview
What the Teams API returns and how it’s structured.
API Scopes
Scopes required to access each Teams API endpoint.
OAuth 2.0 Guide
End-to-end OAuth 2.0 workflow guide for Pleo integrations.
OAuth 2.0 with Postman
How to configure Postman to authenticate using OAuth 2.0 and call the Pleo API.
Standalone API Key Guide
End-to-end guide for authenticating with standalone API keys.
Standalone API Keys with Postman
How to make API calls using standalone API keys in Postman.
Key Differences
- Authentication moves from a logged-in user session (JWT) to OAuth 2.0 client credentials.
- Authorisation moves from a company
OWNERrole check on the caller to a token-scoped permission check (team:read/team:write/team:delete) against the team’s company or organisation. - List responses use cursor-based pagination instead of offset-based pagination.
- Single-resource responses are wrapped in a
dataenvelope. - The
reviewersfield has been removed from the team response. - On create,
companyIdis derived from the access token rather than the request body, giving the same caller experience as before, just with a different token type. A team can also be created directly under anorganizationId, which takes precedence overcompanyIdif both are provided. See Creating a Team. - Team name uniqueness is now validated on create. See Behaviour Change: Team Names Must Be Unique.
- Updating a team moves from
PUT(all fields required) toPATCH(all fields optional). See Breaking Change: PUT → PATCH, All Fields Now Optional. - The sentinel for deleting a team’s code changes from
nullto an empty string"". See Breaking Change: Deleting the Code. - Adding and removing team members moves to dedicated
/membersendpoints and no longer accepts multiple employee IDs viaPUT. See Managing Team Members.
Endpoint Mapping
The following sections cover each change in detail.
Authentication and Permission Scopes
Before: the legacy endpoints authenticated the caller as a logged-in Pleo user and required theOWNER role on the target company.
After: the new API authenticates callers as OAuth 2.0 clients. Access is granted
per-scope, and the token must carry access to the company or organisation the team belongs to:
There is no longer a concept of a per-user role (e.g.
OWNER) on the request. Access is entirely
determined by what the client’s token is scoped to.
Creating a Team
POST /external/v1/teams creates a new team for a company or an organisation the OAuth 2.0 token
has access to.
companyId and organizationId Are Optional Body Fields
Before: the legacy endpoint extracted companyId from the caller’s JWT server-side (via
preload: jwtCompanyId), so it was never part of the request body:
organizationId and companyId fields in the
body:
- If
organizationIdis supplied, it takes precedence: the team is created directly under that organisation, and the token must be scoped to it. - Otherwise,
companyIdis used if supplied (the token must have access to that company), and defaults to the company the token is scoped to if omitted.
companyId can still be omitted:
companyId explicitly, or create an organisation-level team by supplying
organizationId instead:
Behaviour Change: Team Names Must Be Unique
Unlike the legacy endpoint, which did not validate team name uniqueness, the new endpoint enforces the same validation as the rest of Teams API: a team name must be unique (case-insensitively) within the company and within its organisation. Creating a team with a name that already exists in the company or organisation now returns a400 Bad Request, where it would previously have succeeded.
If your integration relies on being able to create teams with duplicate names, you’ll need to
adjust for this before migrating.
Updating a Team
PATCH /external/v1/teams/{teamId} partially updates a team’s name and/or code. It requires a
company- or organisation-scoped access token authorised for the team, and the team:write
scope, the same scope used by Create Team.
Breaking Change: PUT → PATCH, All Fields Now Optional
Before: the legacy endpoint is aPUT and, per standard PUT semantics, expects the full
resource on every call: both name and code are required fields:
PATCH. Both fields are optional, and any field omitted from
the request body is left unchanged:
Breaking Change: Deleting the Code (null vs Empty String)
Before: send code: null to delete a team’s code:
code: "" (empty string) to delete it. Sending null, or omitting code entirely,
leaves the existing code unchanged:
Managing Team Members
Adding and removing team members moves from a singlePUT/DELETE pair on /teams/:teamId/employees/:employeeId
to two dedicated endpoints under /members, documented in OpenAPI schema as
Add Team Members and Remove Team Member.
Adding Team Members
POST /external/v1/teams/{teamId}/members adds one or more Employees to a Team. It requires the
same team:write scope as Create Team and Update Team.
200 OK with a data array containing one result per requested Employee, rather
than failing the whole call if one Employee can’t be added:
Removing a Team Member
DELETE /external/v1/teams/{teamId}/members/{employeeId} removes a single Employee from a Team. It
requires the team:write scope.
If the Employee is not currently a member of the Team, this is a no-op rather than an error: the
call still returns 200 OK, with false as the response body (true if an Employee was actually
removed).
Response Structure Changes
Envelope
GET /external/v1/teams/{teamId} and PATCH /external/v1/teams/{teamId} now wrap the team in a
data object, rather than returning the team as the top-level response body:
GET /external/v1/teams returns a cursor-paginated response, replacing the previous offset-based
pagination (limit/offset query params) used on GET /teams.
reviewers Field Removed
The reviewers field (the list of employee IDs assigned as a team’s reviewers) has been removed
from the team response entirely. It is no longer present on any new Teams API response:
Errors
No Impact on Existing Integrations
This change doesn’t affect any webhooks or events your integration relies on. Team creation behaves the same whether you use the legacy or new endpoint: no new event types, and no changes to existing event payloads.Team Update Events
Updating a team now publishes two separate update events instead of one, compared to the legacy endpoint.- Legacy: a single update event fires whenever the team’s
nameorcodechanges, carrying both fields. - New: two independent events fire instead: one when
namechanges, one whencodechanges. Updating both fields in a single request triggers both events. This isn’t new behaviour introduced by this migration: the new endpoint has always emitted these two separate events.