Skip to content

Plane API v2

API v2 is the current, recommended REST interface to Plane. It is resource-oriented, JSON in and JSON out, and described by a machine-readable OpenAPI document you can generate clients from.

Everything here is stable and actively developed. API v1 remains available and supported for existing integrations, but it is in maintenance — build new integrations on v2.

Already on v1?

The two versions run side by side, so you can migrate one resource at a time. See Migrating from v1 for the field-by-field differences.

Base URL

DeploymentBase URL
Plane Cloudhttps://api.plane.so
Self-managedYour instance URL, for example https://plane.example.com

Every v2 path lives under /api/v2/, and every URL ends with a trailing slash. Dropping it is the single most common first-request mistake — /api/v2/users/me is not the same URL as /api/v2/users/me/.

The OpenAPI document advertises one servers entry that matches your deployment, so a generated client points at the right host without further configuration.

Quickstart

GET /api/v2/users/me/ needs no workspace or project id, which makes it the fastest way to confirm your credentials work.

Verify your credentials
bash
curl "https://api.plane.so/api/v2/users/me/" \
  -H "X-Api-Key: $PLANE_API_KEY"
Response200
json
{
  "id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
  "email": "priya@my-team.io",
  "display_name": "priya",
  "principal_kind": "api_key",
  "scopes": []
}

If that returns 200, your key is valid. If it returns 401, the header name or the key itself is wrong — see Authentication.

Requests

  • JSON only. Send Content-Type: application/json on requests with a body; responses are JSON.
  • POST creates, PATCH updates. There is no PUT — sending one returns 405 method_not_allowed.
  • PATCH is partial. Only the fields you send are changed. Omitting a field leaves it untouched, which is not the same as sending null to clear it.
  • DELETE returns 204 with an empty body. Deletes are soft.
  • Writes take ids, not nested objects. Set a relation with its *_id field (state_id, parent_id, lead_id) or its *_ids array (assignee_ids, label_ids).
  • Audit fields are read-only. created_at, created_by_id, and their siblings are set server-side; sending them has no effect.
bash
# Create
curl -X POST \
  "https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/states/" \
  -H "X-Api-Key: $PLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "In Review", "color": "#3f76ff", "group": "started"}'

# Partial update — color changes, nothing else does
curl -X PATCH \
  "https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/states/f960d3c2-8524-4a41-b8eb-055ce4be2a7f/" \
  -H "X-Api-Key: $PLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"color": "#ffffff"}'

Design principles

These five rules hold across every v2 endpoint. Learning them once means you rarely have to re-read a reference page.

Reads are sparse

A read returns foreign keys as *_id fields and many-to-many relations as *_ids arrays. No related objects are embedded by default, so response size stays predictable no matter how many relations a resource has.

json
{
  "id": "8f4c2b1e-0d3a-4f7b-9c21-6e5a8b7d4f13",
  "name": "Fix login redirect",
  "state_id": "f960d3c2-8524-4a41-b8eb-055ce4be2a7f",
  "assignee_ids": ["16c61a3a-512a-48ac-b0be-b6b46fe6f430"]
}

Expansion is separate-key

?expand= adds an object beside the id — it never replaces it. ?expand=state gives you state_id and a state object, so code that reads state_id keeps working whether or not the caller asked for the expansion.

Only work items and members support ?expand=. See Expanding relations for the allowed values.

Errors are RFC 9457

Every failure is application/problem+json with a stable machine-readable code. Branch on code, never on the HTTP status or the human-readable detail. See Errors.

Cross-tenant ids return 404

A workspace, project, or record id that belongs to someone else returns 404 resource_not_found, never 403. The API does not distinguish "does not exist" from "exists but is not yours", so it never leaks the existence of a resource you cannot see.

A 404 is not always a typo

If a 404 surprises you on an id you are confident is real, check which workspace or project the id belongs to before checking your spelling.

v2 evolves additively

Inside v2 the contract only grows: new optional fields, new enum values, new endpoints. Removals and renames are reserved for a future major version. Write clients that ignore unknown response fields and tolerate unrecognized enum values, and upgrades stay uneventful.

What's in v2

88 operations across these resources.

ResourceBase pathReference
Work items/api/v2/workspaces/{slug}/projects/{project_id}/work-items/Overview
Work item comments…/work-items/{work_item_id}/comments/Overview
States…/projects/{project_id}/states/Overview
Labels…/projects/{project_id}/labels/Overview
Cycles…/projects/{project_id}/cycles/Overview
Modules…/projects/{project_id}/modules/Overview
Work item types & properties, project…/projects/{project_id}/work-item-types/, …/work-item-properties/Overview
Work item types & properties, workspace/api/v2/workspaces/{slug}/work-item-types/, …/work-item-properties/Overview
Work item property contexts/api/v2/workspaces/{slug}/work-item-properties/{property_id}/contexts/Overview
Members/api/v2/workspaces/{slug}/members/, …/projects/{project_id}/members/Overview
Workspace features/api/v2/workspaces/{slug}/features/Overview
Audit logs/api/v2/workspaces/{slug}/audit-logs/Overview
Users/api/v2/users/me/Overview

Two surfaces for work item types

A workspace manages work item types either at the project level or at the workspace level — never both at once. Writing to the wrong surface returns a 409. See Work item type modes.

Machine-readable schema

The full OpenAPI document is served live at:

GET /api/v2/schema/

Point a generator at it to produce a typed client, or diff it between releases to see exactly what changed. It is the authority for every field, enum, and scope in this reference.

Where to go next