# How the Django Backend API is Organized in Plane

> Explore the Django backend API organization in Plane. Discover how Plane uses a modular app-per-domain architecture for distinct namespaces and efficient CRUD operations.

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: architecture
- Published: 2026-08-22

---

**Plane uses a modular, app-per-domain architecture where independent Django apps are stitched together via a root URL dispatcher located at [`apps/api/plane/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/urls.py), separating core CRUD operations, public read-only endpoints, licensing, authentication, and versioned experimental features into distinct namespaces.**

The backend of the open-source project management tool Plane (`makeplane/plane`) is built as a classic Django project that eschews monolithic design in favor of clear functional separation. Each major domain—workspaces and issues, public sharing, licensing, authentication—lives in its own Django app with dedicated URL configurations, viewsets, and serializers. This organization provides a clean, versioned REST API for the React frontend while maintaining strict boundaries between internal business logic and external-facing resources.

## High-Level Domain Architecture

Plane splits the backend into six distinct Django apps, each registered under a specific URL prefix in the root dispatcher. This separation ensures that public integrations, internal tools, and administrative functions operate in isolated contexts.

### Core Business Logic (plane.app)

The **primary API** lives under the `api/` prefix and handles authenticated CRUD operations for workspaces, projects, issues, cycles, and modules. Implemented in [`apps/api/plane/app/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/urls.py), this app registers viewsets using Django REST Framework routers. All endpoints here require authentication and enforce workspace-level permissions through custom classes imported from `utils/permissions/`.

### Public Read-Only API (plane.space)

Exposed under `api/public/`, the **Space** app ([`apps/api/plane/space/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/space/urls.py)) provides unauthenticated, read-only access to workspace and project data for embedding and external integrations. Viewsets here filter results based on public visibility flags, ensuring that only explicitly shared resources are accessible without a JWT token.

### Instance Licensing (plane.license)

The **license** app ([`apps/api/plane/license/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/license/urls.py)) manages multi-tenant instance provisioning under `api/instances/`. It handles creation, renewal, and validation of Plane licenses. Endpoints here are typically consumed by deployment automation or admin dashboards to check instance validity against the licensing server.

### Versioned Experimental API (plane.api)

Future-proofing and experimental features reside in [`apps/api/plane/api/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/api/urls.py), mounted at `api/v1/`. This namespace hosts new endpoints—such as analytics or reporting features—that may evolve independently of the stable `api/` surface, allowing the Plane team to iterate without breaking existing clients.

### Authentication (plane.authentication)

All identity flows—JWT issuance, OAuth callbacks, magic-link login, and password reset—are encapsulated in [`apps/api/plane/authentication/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/authentication/urls.py) under the `auth/` prefix. This isolation centralizes security logic, including token refresh and session management, away from business data endpoints.

### Web UI (plane.web)

Server-side rendered pages, including the Django admin panel and error templates, are served by [`apps/api/plane/web/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/web/urls.py) from the root path `/`. While not part of the JSON API, this app completes the backend by handling HTML responses and admin interface routing.

## Root URL Dispatcher

The entry point for all routing is [`apps/api/plane/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/urls.py). This file imports Django's `include` function and maps each domain to its dedicated sub-router:

```python
urlpatterns = [
    path("api/", include("plane.app.urls")),
    path("api/public/", include("plane.space.urls")),
    path("api/instances/", include("plane.license.urls")),
    path("api/v1/", include("plane.api.urls")),
    path("auth/", include("plane.authentication.urls")),
    path("", include("plane.web.urls")),
]

```

When the `ENABLE_DRF_SPECTACULAR` setting is true, the dispatcher appends additional routes exposing the OpenAPI schema at `api/schema/` and interactive documentation at `api/schema/swagger-ui/` and `api/schema/redoc/`.

## Shared Infrastructure and Utilities

All domain apps import common utilities from `apps/api/plane/utils/`, ensuring consistent behavior across the API surface:

- **Permissions** ([`utils/permissions/base.py`](https://github.com/makeplane/plane/blob/main/utils/permissions/base.py)): Custom DRF permission classes that evaluate workspace, project, and page-level access controls before viewset execution.
- **Pagination** ([`utils/paginator.py`](https://github.com/makeplane/plane/blob/main/utils/paginator.py), [`utils/global_paginator.py`](https://github.com/makeplane/plane/blob/main/utils/global_paginator.py)): Unified pagination logic applied to all list endpoints, returning standardized `count`, `next`, and `previous` metadata.
- **OpenAPI Helpers** (`utils/openapi/`): Decorators and response schemas consumed by **drf-spectacular** to generate accurate OpenAPI 3.0 specifications.
- **Error Handling** ([`utils/error_codes.py`](https://github.com/makeplane/plane/blob/main/utils/error_codes.py), [`utils/exception_logger.py`](https://github.com/makeplane/plane/blob/main/utils/exception_logger.py)): Standardized error code constants and centralized exception logging for debugging production issues.

## Request Lifecycle

An incoming HTTP request flows through the following stages, as implemented in the Plane Django backend:

1. **URL Resolution**: Django's resolver matches the request path against the prefixes defined in [`apps/api/plane/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/urls.py) and delegates to the included sub-router.
2. **Router Dispatch**: The DRF router (defined in the specific app's [`urls.py`](https://github.com/makeplane/plane/blob/main/urls.py)) forwards the request to the appropriate viewset method (e.g., `list`, `create`, `retrieve`).
3. **Permission Enforcement**: Custom permission classes from `utils/permissions/` validate the user's JWT token and workspace membership before executing business logic.
4. **Serialization**: The viewset's serializer validates incoming data against model constraints and converts outgoing ORM instances to JSON.
5. **Business Logic Execution**: The viewset interacts with Django models (e.g., `Issue`, `Project`) and utility functions to perform the requested operation.
6. **Response Rendering**: DRF renders the response as JSON, optionally wrapping list results with pagination metadata from the global paginator.

## Configuration and API Documentation

Global settings reside in [`apps/api/plane/settings/common.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/settings/common.py). The boolean flag `ENABLE_DRF_SPECTACULAR` controls whether the auto-generated OpenAPI schema endpoints are exposed. When enabled, the **drf-spectacular** library introspects all registered viewsets to produce interactive documentation at `/api/schema/swagger-ui/`.

## Practical API Usage Examples

The following examples demonstrate how clients interact with the Plane Django backend API.

### List Workspaces (Authenticated)

Request workspace data through the core app:

```http
GET /api/workspaces/
Authorization: Bearer <JWT_TOKEN>

```

Response includes pagination metadata:

```json
{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {"id":"ws_01","name":"Acme Corp"},
    {"id":"ws_02","name":"Beta Ltd"}
  ]
}

```

This endpoint is handled by the `WorkspaceViewSet` registered in [`apps/api/plane/app/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/urls.py).

### Create Issue via Public API

Insert issues into a public project without authentication:

```http
POST /api/public/projects/{project_id}/issues/
Content-Type: application/json

{
  "title": "Bug in login flow",
  "description": "Users cannot log in after password reset."
}

```

The `plane.space` app processes this request, filtering by public permissions defined in [`apps/api/plane/space/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/space/urls.py).

### Authenticate via Magic Link

Initiate passwordless login through the authentication app:

```http
POST /auth/magic-signin/
Content-Type: application/json

{
  "email": "user@example.com"
}

```

The backend generates a one-time link and emails the user; confirmation hits `POST /auth/magic-signin/confirm/` to issue a JWT, as implemented in [`apps/api/plane/authentication/views.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/authentication/views.py).

## Summary

- **Modular Design**: Plane separates core CRUD, public access, licensing, authentication, and web UI into distinct Django apps (`plane.app`, `plane.space`, `plane.license`, `plane.api`, `plane.authentication`, `plane.web`).
- **Central Routing**: The root dispatcher at [`apps/api/plane/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/urls.py) maps URL prefixes to sub-routers, maintaining clean namespace separation (e.g., `api/`, `api/public/`, `auth/`).
- **Shared Utilities**: Common permissions, pagination, and error handling live in `apps/api/plane/utils/` and are reused across all viewsets.
- **Lifecycle Consistency**: Every request passes through standardized permission checks, serialization, and pagination layers managed by Django REST Framework.
- **Optional Documentation**: The `ENABLE_DRF_SPECTACULAR` setting toggles auto-generated OpenAPI schemas and Swagger UI endpoints.

## Frequently Asked Questions

### Where is the main entry point for Plane's API routing?

The root URL configuration is defined in [`apps/api/plane/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/urls.py). This file includes all sub-applications—such as `plane.app` for core endpoints and `plane.space` for public APIs—mapping them to distinct prefixes like `api/` and `api/public/`.

### How does Plane separate authenticated internal APIs from public integrations?

Plane isolates public endpoints in the `plane.space` app mounted at `api/public/`, which uses read-only viewsets and public-only permissions. Internal CRUD operations reside in `plane.app` under `api/` and enforce authentication via custom permission classes in [`apps/api/plane/utils/permissions/base.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/utils/permissions/base.py).

### What controls the auto-generated API documentation in Plane?

The `ENABLE_DRF_SPECTACULAR` boolean in [`apps/api/plane/settings/common.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/settings/common.py) determines whether **drf-spectacular** exposes the OpenAPI schema at `api/schema/` and interactive UIs at `api/schema/swagger-ui/` and `api/schema/redoc/`. This feature is optional and typically enabled in development environments.

### Which Django app handles instance licensing and provisioning?

The `plane.license` app, configured in [`apps/api/plane/license/urls.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/license/urls.py), manages multi-tenant instance lifecycle under the `api/instances/` prefix. It handles license validation, creation, and renewal separate from project management data.