# How to Use the Plane API: A Complete Guide to REST Integration

> Incorporate the Plane API into your projects with this comprehensive guide. Learn how to generate API keys, authenticate requests, and integrate REST endpoints for seamless workflow automation.

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: api-reference
- Published: 2026-06-20

---

**To use the Plane API, generate an API token from your instance and include it in the `X-Api-Key` header for all requests to `/api/v1/` endpoints.**

Plane (from the open-source repository `makeplane/plane`) exposes a Django REST Framework-powered external API that enables programmatic access to projects, issues, cycles, and modules. This guide covers the authentication flow, core endpoints, and practical implementation patterns derived directly from the source code.

## Understanding Plane's API Architecture

Plane maintains two distinct REST API layers to separate internal web application traffic from third-party integrations.

### External API vs Web-App API

The **External API** (`/api/v1/`) is the stable, public-facing interface designed for automation scripts and external tools. It requires **API-Key authentication** via the `X-Api-Key` header and is implemented in [`plane/app/middleware/api_authentication.py`](https://github.com/makeplane/plane/blob/main/plane/app/middleware/api_authentication.py)【/cache/repos/github.com/makeplane/plane/preview/apps/api/plane/app/middleware/api_authentication.py】.

The **Web-App API** (`/api/`) serves the Plane web UI exclusively and relies on session cookies or tokens handled by the frontend. This internal layer is subject to breaking changes and should not be used for external integrations.

Both layers are documented via an OpenAPI schema available at `/api/schema/` when the `ENABLE_DRF_SPECTACULAR` setting is enabled.

## Authentication and API Keys

The `APIKeyAuthentication` class validates tokens against the `APIToken` model, checking expiry dates, active status, and user associations before allowing access to protected resources.

### Creating and Using API Tokens

1. **Generate a token** via the Plane UI (User → API Tokens) or programmatically via `POST /api/users/api-tokens/`. The `ApiTokenEndpoint` in [`plane/app/views/api.py`](https://github.com/makeplane/plane/blob/main/plane/app/views/api.py) handles this request【/cache/repos/github.com/makeplane/plane/preview/apps/api/plane/app/views/api.py】.
2. **Store the token securely** – the plaintext value is returned only once upon creation.
3. **Include the header** in every request: `X-Api-Key: <your-token>`.

If authentication fails, the API returns a **401 Unauthorized** error.

```bash
curl -X POST https://your-plane-instance.com/api/users/api-tokens/ \
     -H "Content-Type: application/json" \
     -d '{"label":"ci-cd-token","description":"Automation token"}'

```

The response contains the token value required for subsequent requests:

```json
{
  "token": "a1b2c3d4e5f6...",
  "label": "ci-cd-token",
  "created_at": "2024-06-20T12:34:56Z"
}

```

## Core Endpoints and Resources

The external API routes are defined in [`plane/api/urls.py`](https://github.com/makeplane/plane/blob/main/plane/api/urls.py) and aggregated in [`plane/app/urls/__init__.py`](https://github.com/makeplane/plane/blob/main/plane/app/urls/__init__.py)【/cache/repos/github.com/makeplane/plane/preview/apps/api/plane/app/urls/__init__.py】. All external endpoints share the `/api/v1/` prefix and require workspace slugs in the URL path.

### Issues and Project Management

The issue endpoints are routed through [`plane/app/urls/issue.py`](https://github.com/makeplane/plane/blob/main/plane/app/urls/issue.py)【/cache/repos/github.com/makeplane/plane/preview/apps/api/plane/app/urls/issue.py】 and implemented in [`plane/app/views/issue/base.py`](https://github.com/makeplane/plane/blob/main/plane/app/views/issue/base.py):

- **List issues**: `GET /api/v1/workspaces/{slug}/projects/{project_id}/issues/list/`
- **Create issue**: `POST /api/v1/workspaces/{slug}/projects/{project_id}/issues/`
- **Retrieve issue**: `GET /api/v1/workspaces/{slug}/projects/{project_id}/issues/{issue_id}/`
- **Update issue**: `PATCH /api/v1/workspaces/{slug}/projects/{project_id}/issues/{issue_id}/`
- **Delete issue**: `DELETE /api/v1/workspaces/{slug}/projects/{project_id}/issues/{issue_id}/`

### Comments, Attachments, and Reactions

- **Comments**: `GET|POST /api/v1/workspaces/{slug}/projects/{project_id}/issues/{issue_id}/comments/`
- **Attachments**: `GET|POST .../issues/{issue_id}/attachments/`
- **Reactions**: `POST .../issues/{issue_id}/reactions/` (accepts emoji codes)

### Workspaces and Higher-Level Resources

- **Projects**: `GET /api/v1/workspaces/{slug}/projects/`
- **Cycles**: `GET /api/v1/workspaces/{slug}/cycles/`
- **Modules**: `GET /api/v1/workspaces/{slug}/modules/`
- **Pages**: `GET /api/v1/workspaces/{slug}/pages/`

### Pagination and Filtering

List endpoints support **cursor-based pagination** using `cursor` and `page_size` query parameters. **Filtering** is handled via query parameters that map to Django filtersets defined in [`plane/utils/filters/filterset.py`](https://github.com/makeplane/plane/blob/main/plane/utils/filters/filterset.py)【/cache/repos/github.com/makeplane/plane/preview/apps/api/plane/utils/filters/filterset.py】, allowing you to filter issues by state, assignee, label, and other attributes.

## Practical Code Examples

### Listing Issues with Python

Use the `requests` library to fetch issues with pagination:

```python
import requests

API_URL = "https://your-plane-instance.com/api/v1"
API_KEY = "a1b2c3d4e5f6..."

headers = {"X-Api-Key": API_KEY}
workspace = "engineering"
project_id = "e3b0c442-98fc-1c14-9af4-123456789abc"

response = requests.get(
    f"{API_URL}/workspaces/{workspace}/projects/{project_id}/issues/list/",
    headers=headers,
    params={"page_size": 20, "cursor": "eyJpZCI6MTB9"}
)

data = response.json()
for issue in data["results"]:
    print(f"{issue['id']}: {issue['name']}")

```

### Creating an Issue

```bash
curl -X POST https://your-plane-instance.com/api/v1/workspaces/engineering/projects/e3b0c442-98fc-1c14-9af4-123456789abc/issues/ \
     -H "X-Api-Key: a1b2c3d4e5f6..." \
     -H "Content-Type: application/json" \
     -d '{
           "name": "Fix authentication middleware",
           "description": "Update token validation logic",
           "state_id": "d8f9e2c3-...",
           "assignee_ids": ["user-uuid-1"]
         }'

```

### Adding a Comment

```python
import requests

issue_id = "f2a1b3c4-d5e6-7f89-0a1b-2c3d4e5f6g7h"
url = f"{API_URL}/workspaces/{workspace}/projects/{project_id}/issues/{issue_id}/comments/"

payload = {"comment": "Investigating the API authentication flow."}
response = requests.post(url, json=payload, headers=headers)
print(response.status_code)  # 201 Created

```

### Retrieving the OpenAPI Schema

When `ENABLE_DRF_SPECTACULAR` is enabled, fetch the complete schema for auto-generating clients:

```bash
curl -H "X-Api-Key: a1b2c3d4e5f6..." \
     https://your-plane-instance.com/api/schema/

```

## Key Source Files and Implementation Details

The Plane API follows a standard Django REST Framework pipeline: **router → view → serializer → model**.

| File | Purpose |
|------|---------|
| [`plane/app/middleware/api_authentication.py`](https://github.com/makeplane/plane/blob/main/plane/app/middleware/api_authentication.py) | Implements `APIKeyAuthentication` for validating `X-Api-Key` headers【/cache/repos/github.com/makeplane/plane/preview/apps/api/plane/app/middleware/api_authentication.py】 |
| [`plane/app/views/api.py`](https://github.com/makeplane/plane/blob/main/plane/app/views/api.py) | Contains `ApiTokenEndpoint` for token CRUD operations【/cache/repos/github.com/makeplane/plane/preview/apps/api/plane/app/views/api.py】 |
| [`plane/api/urls.py`](https://github.com/makeplane/plane/blob/main/plane/api/urls.py) | Root router for external API routes (`/api/v1/`) |
| [`plane/app/urls/__init__.py`](https://github.com/makeplane/plane/blob/main/plane/app/urls/__init__.py) | Aggregates all application URL patterns【/cache/repos/github.com/makeplane/plane/preview/apps/api/plane/app/urls/__init__.py】 |
| [`plane/app/urls/issue.py`](https://github.com/makeplane/plane/blob/main/plane/app/urls/issue.py) | URL patterns for issue-related endpoints (CRUD, comments, attachments)【/cache/repos/github.com/makeplane/plane/preview/apps/api/plane/app/urls/issue.py】 |
| [`plane/app/views/issue/base.py`](https://github.com/makeplane/plane/blob/main/plane/app/views/issue/base.py) | Viewset implementations including `IssueViewSet` |
| [`plane/utils/filters/filterset.py`](https://github.com/makeplane/plane/blob/main/plane/utils/filters/filterset.py) | Filter definitions for query parameter parsing【/cache/repos/github.com/makeplane/plane/preview/apps/api/plane/utils/filters/filterset.py】 |

## Summary

- **Plane exposes two API layers**: use the External API (`/api/v1/`) for integrations, not the internal Web-App API.
- **Authentication requires an API key** passed via the `X-Api-Key` header, validated by middleware in [`plane/app/middleware/api_authentication.py`](https://github.com/makeplane/plane/blob/main/plane/app/middleware/api_authentication.py).
- **Core resources** include issues, projects, cycles, and modules, accessible through RESTful endpoints under `/api/v1/workspaces/{slug}/`.
- **Pagination uses cursor-based** `cursor` and `page_size` parameters, while filtering leverages Django filtersets.
- **Source code references**: Token management lives in [`plane/app/views/api.py`](https://github.com/makeplane/plane/blob/main/plane/app/views/api.py), and issue routing is defined in [`plane/app/urls/issue.py`](https://github.com/makeplane/plane/blob/main/plane/app/urls/issue.py).

## Frequently Asked Questions

### How do I authenticate with the Plane API?

Include a valid API token in the `X-Api-Key` header for every request to `/api/v1/` endpoints. Generate tokens via the Plane UI under User → API Tokens or programmatically via `POST /api/users/api-tokens/`. The `APIKeyAuthentication` class in [`plane/app/middleware/api_authentication.py`](https://github.com/makeplane/plane/blob/main/plane/app/middleware/api_authentication.py) validates these tokens against the database.

### What is the difference between `/api/` and `/api/v1/`?

The `/api/v1/` prefix denotes the stable External API designed for third-party integrations and requires API-Key authentication. The `/api/` prefix serves the internal web application and uses session-based authentication. External integrations should only use `/api/v1/` endpoints as defined in [`plane/api/urls.py`](https://github.com/makeplane/plane/blob/main/plane/api/urls.py).

### Does Plane support OpenAPI or Swagger documentation?

Yes, when the `ENABLE_DRF_SPECTACULAR` environment variable is set to true, Plane serves an OpenAPI schema at `/api/schema/`. This schema describes all available endpoints, request bodies, and response formats, allowing you to import the specification into tools like Postman or Swagger UI.

### How does pagination work in the Plane API?

List endpoints use cursor-based pagination rather than offset-based. Pass `cursor` and `page_size` as query parameters to navigate through large result sets. The `next` and `previous` fields in the response provide cursors for subsequent requests, implemented via Django filtersets in [`plane/utils/filters/filterset.py`](https://github.com/makeplane/plane/blob/main/plane/utils/filters/filterset.py).