diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc18afd3..228f98b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,39 +2,51 @@ name: CI on: pull_request: - types: [opened, synchronize, reopened] + push: + branches: [master] # seeds the .turbo cache that PR runs restore from + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: - check-format: - name: Check Formatting + format: + name: Format (oxfmt) runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 with: node-version: 24 cache: pnpm - - run: pnpm install --frozen-lockfile - - run: pnpm check:format build: - name: Build + name: Types + build (turbo) runs-on: ubuntu-latest + timeout-minutes: 20 steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 with: node-version: 24 cache: pnpm - + - uses: actions/cache@v4 + with: + path: .turbo + key: turbo-${{ runner.os }}-${{ github.sha }} + restore-keys: turbo-${{ runner.os }}- - run: pnpm install --frozen-lockfile - - - run: pnpm build + # Guards against a second copy of vue / vitepress / the base theme sneaking in + # (the shared theme package must resolve the same copies as the apps). + - run: pnpm dedupe --check + # Turborepo Remote Cache is intentionally not wired up here (needs TURBO_TOKEN / + # TURBO_TEAM); the local .turbo cache above is enough for two apps. + - run: pnpm turbo run check:types build diff --git a/.gitignore b/.gitignore index b6b7f563..eb8e6a42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,21 @@ # Dependencies -/node_modules +node_modules/ +.pnpm-store/ -# Build output -/build -.docusaurus +# Build output and caches (any app) +**/.vitepress/dist/ +**/.vitepress/cache/ +**/.vitepress/.temp/ +*.timestamp-*.mjs +.turbo/ +.vercel/ -# VitePress -.vitepress/dist -.vitepress/cache -docs/.vitepress/dist -docs/.vitepress/cache - -# Misc -.DS_Store +# Local env files (.env.example templates stay tracked) .env .env.local -.env.development.local -.env.test.local -.env.production.local +.env.*.local -npm-debug.log* -yarn-debug.log* -yarn-error.log* +# OS / editor / local tooling +.DS_Store +*.log +.claude/settings.local.json diff --git a/.oxfmtrc.json b/.oxfmtrc.json index 55c15df3..43078c0f 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -1,4 +1,9 @@ { "$schema": "./node_modules/oxfmt/configuration_schema.json", - "ignorePatterns": [] + "ignorePatterns": [ + "pnpm-lock.yaml", + "**/.vitepress/cache/**", + "**/.vitepress/dist/**", + "**/.turbo/**" + ] } diff --git a/AGENTS.md b/AGENTS.md index 7d0a65bd..e89a3116 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,81 +1,83 @@ -# AGENTS.md — Plane Documentation +# AGENTS.md — Plane documentation monorepo -## Project overview +Guidance for AI coding agents (Claude Code, Codex, Cursor, …) working in this repository. `CLAUDE.md` is a +symlink to this file. Each app has its own `AGENTS.md` with content conventions — **read the app's file before +editing content there** (`apps/docs/AGENTS.md`, `apps/developer-docs/AGENTS.md`). -This is the [Plane](https://plane.so) product documentation site, built with [VitePress v1.6.3](https://vitepress.dev/) and hosted at [docs.plane.so](https://docs.plane.so). All content lives in the `docs/` directory as Markdown files. +## What this repo is + +pnpm workspace + Turborepo holding both Plane documentation sites and their shared theme: + +| Package | Path | What | +| ------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `docs` | `apps/docs` | [docs.plane.so](https://docs.plane.so) — product documentation | +| `developer-docs` | `apps/developer-docs` | [developers.plane.so](https://developers.plane.so) — API reference, self-hosting, dev tools | +| `@plane/docs-theme` | `packages/theme` | Shared VitePress theme (tokens, fonts, header, layout, Card/CardGroup/Tags, Copy page menu, cookie consent) — consumed as source, no build step | ## Stack -| Tool | Version/Notes | -| --------------- | --------------- | -| Framework | VitePress 1.6.3 | -| Package manager | pnpm 11.8.0 | -| Node | >=24.0.0 | -| Formatting | oxfmt | -| Styling | Tailwind CSS v4 | +| Tool | Version / notes | +| --------------- | ---------------------------------------------------------------------------- | +| Framework | VitePress 2.0.0-alpha.16 (pinned in the `catalog:` of `pnpm-workspace.yaml`) | +| Base theme | `@voidzero-dev/vitepress-theme` 4.8.x (brings Tailwind CSS v4) | +| Package manager | pnpm 11.8.0 (`packageManager` in root `package.json`) | +| Node | >=24.0.0 | +| Task runner | Turborepo 2.x (`turbo.json`) | +| Formatting | oxfmt (root `.oxfmtrc.json`, defaults: printWidth 100, trailing commas) | +| Type-checking | `tsc` per package, all extending root `tsconfig.base.json` | -## Common commands +## Common commands (run from the repo root) ```bash -pnpm dev # Start local dev server (http://localhost:5173) -pnpm build # Build static output into docs/.vitepress/dist -pnpm preview # Preview the production build locally -pnpm fix:format # Auto-format all files with oxfmt -pnpm check:format # Check formatting without writing +pnpm install # one lockfile for the whole workspace +pnpm dev:docs # docs.plane.so dev server → http://localhost:5173 +pnpm dev:developer-docs # developers.plane.so dev server → http://localhost:5174 +pnpm dev # both, interleaved log output +pnpm build # turbo run build (apps//docs/.vitepress/dist) +pnpm preview # turbo run preview (:4173 / :4174) +pnpm check:types # turbo run check:types (theme + both apps) +pnpm check:format # oxfmt --check . +pnpm fix:format # oxfmt --write . +pnpm check # check:format + check:types +pnpm --filter docs + + + + diff --git a/apps/developer-docs/docs/.vitepress/theme/components/CodePanel.vue b/apps/developer-docs/docs/.vitepress/theme/components/CodePanel.vue new file mode 100644 index 00000000..5cc08a8d --- /dev/null +++ b/apps/developer-docs/docs/.vitepress/theme/components/CodePanel.vue @@ -0,0 +1,207 @@ + + + + + diff --git a/apps/developer-docs/docs/.vitepress/theme/components/ResponsePanel.vue b/apps/developer-docs/docs/.vitepress/theme/components/ResponsePanel.vue new file mode 100644 index 00000000..7b623280 --- /dev/null +++ b/apps/developer-docs/docs/.vitepress/theme/components/ResponsePanel.vue @@ -0,0 +1,163 @@ + + + + + diff --git a/apps/developer-docs/docs/.vitepress/theme/index.ts b/apps/developer-docs/docs/.vitepress/theme/index.ts new file mode 100644 index 00000000..41c43395 --- /dev/null +++ b/apps/developer-docs/docs/.vitepress/theme/index.ts @@ -0,0 +1,42 @@ +/** + * developers.plane.so theme = shared Plane docs theme (@plane/docs-theme, packages/theme) + * + developer-docs specifics (API reference components/layout). + */ +import { onMounted, watch, nextTick } from "vue"; +import { useRoute } from "vitepress"; +import { createPlaneTheme } from "@plane/docs-theme"; +import ApiParam from "./components/ApiParam.vue"; +import CodePanel from "./components/CodePanel.vue"; +import ResponsePanel from "./components/ResponsePanel.vue"; +import "./site.css"; + +/** Toggle `.api-page` on `.VPDoc` for API reference pages (two-column layout, no aside). */ +function updateApiPageClass() { + if (typeof document === "undefined") return; + const path = window.location.pathname; + const isApiPage = + path.includes("/api-reference/") && + !path.endsWith("/introduction") && + !path.endsWith("/introduction.html"); + document.querySelector(".VPDoc")?.classList.toggle("api-page", isApiPage); +} + +export default createPlaneTheme({ + brand: { + logoOnLight: "/logo/dev-logo-watermark-light.png", + logoOnDark: "/logo/dev-logo-watermark-dark.png", + logoAlt: "Plane", + menuTitle: "Plane Developers", + footerBg: "https://media.docs.plane.so/logo/og-docs.webp", + monoIcon: "/logo/favicon-32x32.png", + }, + components: { ApiParam, CodePanel, ResponsePanel }, + setup() { + const route = useRoute(); + onMounted(() => nextTick(updateApiPageClass)); + watch( + () => route.path, + () => nextTick(updateApiPageClass), + ); + }, +}); diff --git a/apps/developer-docs/docs/.vitepress/theme/site.css b/apps/developer-docs/docs/.vitepress/theme/site.css new file mode 100644 index 00000000..caba9b9f --- /dev/null +++ b/apps/developer-docs/docs/.vitepress/theme/site.css @@ -0,0 +1,30 @@ +/* developers.plane.so — site-specific styles (everything shared lives in @plane/docs-theme, packages/theme) */ + +/* --- Home page hero (docs/index.md) --- */ +.vp-doc .home-hero { + margin-bottom: 2rem; +} + +.vp-doc .home-hero h1 { + font-size: 2.5rem; + font-weight: 600; + letter-spacing: -0.02em; + margin: 0 0 0.5rem; + line-height: 1.15; +} + +.vp-doc .home-hero .home-hero-text { + font-size: 1.75rem; + font-weight: 600; + letter-spacing: -0.02em; + margin: 0 0 1rem; + color: var(--vp-c-text-1); +} + +.vp-doc .home-hero .home-hero-tagline { + font-size: 1.125rem; + color: var(--vp-c-text-2); + max-width: 42rem; + margin: 0 0 1.5rem; + line-height: 1.6; +} diff --git a/apps/developer-docs/docs/api-reference/assets/create-user-asset-upload.md b/apps/developer-docs/docs/api-reference/assets/create-user-asset-upload.md new file mode 100644 index 00000000..735620c0 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/assets/create-user-asset-upload.md @@ -0,0 +1,157 @@ +--- +title: Create user asset upload +description: Create user asset upload via Plane API. HTTP request format, parameters, scopes, and example responses for create user asset upload. +keywords: plane, plane api, rest api, api integration, assets, create user asset upload +--- + +# Create user asset upload + +
+ POST + /api/v1/assets/user-assets/ +
+ +
+
+ +Generate presigned URL for user asset upload + +
+ +### Body Parameters + +
+ + + +Original filename of the asset + + + + + +MIME type of the file + +- `image/jpeg` - JPEG +- `image/png` - PNG +- `image/webp` - WebP +- `image/jpg` - JPG +- `image/gif` - GIF + + + + + +File size in bytes + + + + + +Type of user asset + +- `USER_AVATAR` - User Avatar +- `USER_COVER` - User Cover + + + +
+
+ +
+ +### Scopes + +`assets:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "asset_id": "550e8400-e29b-41d4-a716-446655440000", + "asset_url": "/api/assets/v2/static/550e8400-e29b-41d4-a716-446655440000/", + "upload_data": { + "url": "https://uploads.example.com/plane-bucket", + "fields": { + "Content-Type": "image/png", + "key": "user-assets/550e8400-e29b-41d4-a716-446655440000/profile-image.png", + "x-amz-algorithm": "AWS4-HMAC-SHA256", + "x-amz-credential": "example/20240101/us-east-1/s3/aws4_request", + "x-amz-date": "20240101T000000Z", + "policy": "example-policy", + "x-amz-signature": "example-signature" + } + } +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/assets/create-workspace-asset-upload.md b/apps/developer-docs/docs/api-reference/assets/create-workspace-asset-upload.md new file mode 100644 index 00000000..f5e18f13 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/assets/create-workspace-asset-upload.md @@ -0,0 +1,181 @@ +--- +title: Create workspace asset upload +description: Create workspace asset upload via Plane API. HTTP request format, parameters, scopes, and example responses for create workspace asset upload. +keywords: plane, plane api, rest api, api integration, assets, create workspace asset upload +--- + +# Create workspace asset upload + +
+ POST + /api/v1/workspaces/{workspace_slug}/assets/ +
+ +
+
+ +Generate presigned URL for generic asset upload + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Original filename of the asset + + + + + +MIME type of the file + + + + + +File size in bytes + + + + + +UUID of the project to associate with the asset + + + + + +External identifier for the asset (for integration tracking) + + + + + +External source system (for integration tracking) + + + +
+
+ +
+ +### Scopes + +`assets:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "asset_id": "550e8400-e29b-41d4-a716-446655440000", + "asset_url": "/api/assets/v2/workspaces/my-workspace/projects/None/issues/None/attachments/550e8400-e29b-41d4-a716-446655440000/", + "upload_data": { + "url": "https://uploads.example.com/plane-bucket", + "fields": { + "Content-Type": "image/png", + "key": "workspace-assets/550e8400-e29b-41d4-a716-446655440000/workspace-image.png", + "x-amz-algorithm": "AWS4-HMAC-SHA256", + "x-amz-credential": "example/20240101/us-east-1/s3/aws4_request", + "x-amz-date": "20240101T000000Z", + "policy": "example-policy", + "x-amz-signature": "example-signature" + } + } +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/assets/delete-user-asset.md b/apps/developer-docs/docs/api-reference/assets/delete-user-asset.md new file mode 100644 index 00000000..4ef203b1 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/assets/delete-user-asset.md @@ -0,0 +1,96 @@ +--- +title: Delete user asset +description: Delete user asset via Plane API. HTTP request format, parameters, scopes, and example responses for delete user asset. +keywords: plane, plane api, rest api, api integration, assets, delete user asset +--- + +# Delete user asset + +
+ DELETE + /api/v1/assets/user-assets/{asset_id}/ +
+ +
+
+ +Delete user asset. + +Delete a user profile asset (avatar or cover image) and remove its reference from the user profile. +This performs a soft delete by marking the asset as deleted and updating the user's profile. + +
+ +### Path Parameters + +
+ + + +Asset ID + + + +
+
+ +
+ +### Scopes + +`assets:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/assets/get-workspace-asset.md b/apps/developer-docs/docs/api-reference/assets/get-workspace-asset.md new file mode 100644 index 00000000..275ebbde --- /dev/null +++ b/apps/developer-docs/docs/api-reference/assets/get-workspace-asset.md @@ -0,0 +1,112 @@ +--- +title: Get workspace asset +description: Get workspace asset via Plane API. HTTP request format, parameters, scopes, and example responses for get workspace asset. +keywords: plane, plane api, rest api, api integration, assets, get workspace asset +--- + +# Get workspace asset + +
+ GET + /api/v1/workspaces/{workspace_slug}/assets/{asset_id}/ +
+ +
+
+ +Get presigned URL for asset download + +
+ +### Path Parameters + +
+ + + +The unique identifier of the asset. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`assets:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "design-spec.pdf", + "type": "application/pdf", + "asset_url": "https://cdn.example.com/workspace-assets/design-spec.pdf", + "attributes": { + "entity_type": "WORKSPACE" + } +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/assets/overview.md b/apps/developer-docs/docs/api-reference/assets/overview.md new file mode 100644 index 00000000..d9311057 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/assets/overview.md @@ -0,0 +1,40 @@ +--- +title: Overview +description: Plane Assets API overview. Learn how user and workspace asset uploads work through the Plane API. +keywords: plane, plane api, rest api, api integration, assets, uploads, files +--- + +# Overview + +Assets let you upload and manage files used across user profiles and workspaces, including images and other binary resources. + +[Learn more about using the Plane API](https://developers.plane.so/api-reference/introduction) + +
+
+ +## The Asset Object + +### Attributes + +
+
+ + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "design-spec.pdf", + "type": "application/pdf", + "asset_url": "https://cdn.example.com/workspace-assets/design-spec.pdf", + "attributes": { + "entity_type": "WORKSPACE" + } +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/assets/update-user-asset.md b/apps/developer-docs/docs/api-reference/assets/update-user-asset.md new file mode 100644 index 00000000..d90901f2 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/assets/update-user-asset.md @@ -0,0 +1,134 @@ +--- +title: Update user asset +description: Update user asset via Plane API. HTTP request format, parameters, scopes, and example responses for update user asset. +keywords: plane, plane api, rest api, api integration, assets, update user asset +--- + +# Update user asset + +
+ PATCH + /api/v1/assets/user-assets/{asset_id}/ +
+ +
+
+ +Mark user asset as uploaded + +
+ +### Path Parameters + +
+ + + +Asset ID + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Additional attributes to update for the asset + + + +
+
+ +
+ +### Scopes + +`assets:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/assets/update-workspace-asset.md b/apps/developer-docs/docs/api-reference/assets/update-workspace-asset.md new file mode 100644 index 00000000..b58f9233 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/assets/update-workspace-asset.md @@ -0,0 +1,128 @@ +--- +title: Update workspace asset +description: Update workspace asset via Plane API. HTTP request format, parameters, scopes, and example responses for update workspace asset. +keywords: plane, plane api, rest api, api integration, assets, update workspace asset +--- + +# Update workspace asset + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/assets/{asset_id}/ +
+ +
+
+ +Update generic asset after upload completion + +
+ +### Path Parameters + +
+ + + +The unique identifier of the asset. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Whether the asset has been successfully uploaded + + + +
+
+ +
+ +### Scopes + +`assets:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/add-customer-property.md b/apps/developer-docs/docs/api-reference/customer/add-customer-property.md new file mode 100644 index 00000000..acff5c32 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/add-customer-property.md @@ -0,0 +1,285 @@ +--- +title: Create a customer property +description: Create a customer property via Plane API. HTTP request format, parameters, scopes, and example responses for create a customer property. +keywords: plane, plane api, rest api, api integration, customer, create a customer property +--- + +# Create a customer property + +
+ POST + /api/v1/workspaces/{workspace_slug}/customer-properties/ +
+ +
+
+ +Create a new customer property in the specified workspace. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Display name. + + + + + +Description. + + + + + +Logo props. + + + + + +Sort order. + + + + + +- `TEXT` - Text +- `DATETIME` - Datetime +- `DECIMAL` - Decimal +- `BOOLEAN` - Boolean +- `OPTION` - Option +- `RELATION` - Relation +- `URL` - URL +- `EMAIL` - Email +- `FILE` - File + + + + + +- `ISSUE` - Issue +- `USER` - User + + + + + +Is required. + + + + + +Default value. + + + + + +Settings. + + + + + +Is active. + + + + + +Is multi. + + + + + +Validation rules. + + + + + +External source. + + + + + +External id. + + + + + +Created by. + + + + + +Updated by. + + + +
+
+ +
+ +### Scopes + +`customers.properties:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "display_name": "Example Name", + "description": "Example description", + "property_type": "TEXT", + "deleted_at": "2024-01-01T00:00:00Z", + "logo_props": "example-value", + "sort_order": 1, + "relation_type": "ISSUE", + "is_required": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/add-customer-request.md b/apps/developer-docs/docs/api-reference/customer/add-customer-request.md new file mode 100644 index 00000000..4333e34d --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/add-customer-request.md @@ -0,0 +1,187 @@ +--- +title: Create a customer request +description: Create a customer request via Plane API. HTTP request format, parameters, scopes, and example responses for create a customer request. +keywords: plane, plane api, rest api, api integration, customer, create a customer request +--- + +# Create a customer request + +
+ POST + /api/v1/workspaces/{workspace_slug}/customers/{customer_id}/requests/ +
+ +
+
+ +Create a new request for the specified customer. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the customer. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Description html. + + + + + +Link. + + + + + +Work item ids. + + + +
+
+ +
+ +### Scopes + +`customers.requests:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "name": "Example Name", + "description": "example-value", + "description_html": "

Example content

", + "description_stripped": "Example description", + "email": "Example Name", + "website_url": "https://example.com/resource", + "logo_props": "example-value", + "domain": "Example Name", + "employees": 1, + "stage": "Example Name", + "contract_status": "Example Name", + "revenue": "Example Name", + "archived_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000", + "logo_asset": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/add-customer.md b/apps/developer-docs/docs/api-reference/customer/add-customer.md new file mode 100644 index 00000000..e03b09c3 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/add-customer.md @@ -0,0 +1,269 @@ +--- +title: Create a customer +description: Create a customer via Plane API. HTTP request format, parameters, scopes, and example responses for create a customer. +keywords: plane, plane api, rest api, api integration, customer, create a customer +--- + +# Create a customer + +
+ POST + /api/v1/workspaces/{workspace_slug}/customers/ +
+ +
+
+ +Create a new customer in the specified workspace. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Description html. + + + + + +Description stripped. + + + + + +Email. + + + + + +Website url. + + + + + +Logo props. + + + + + +Domain. + + + + + +Employees. + + + + + +Stage. + + + + + +Contract status. + + + + + +Revenue. + + + + + +Archived at. + + + + + +Created by. + + + + + +Updated by. + + + + + +Logo asset. + + + +
+
+ +
+ +### Scopes + +`customers:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "description": "example-value", + "deleted_at": "2024-01-01T00:00:00Z", + "customer_request_count": 1, + "logo_url": "Example Name", + "description_html": "

Example content

", + "description_stripped": "Example description", + "description_binary": "Example description", + "email": "Example Name" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/delete-customer-property.md b/apps/developer-docs/docs/api-reference/customer/delete-customer-property.md new file mode 100644 index 00000000..ff1f66fd --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/delete-customer-property.md @@ -0,0 +1,102 @@ +--- +title: Delete a customer property +description: Delete a customer property via Plane API. HTTP request format, parameters, scopes, and example responses for delete a customer property. +keywords: plane, plane api, rest api, api integration, customer, delete a customer property +--- + +# Delete a customer property + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/customer-properties/{property_id}/ +
+ +
+
+ +Permanently delete a customer property from the workspace. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers.properties:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/delete-customer-request.md b/apps/developer-docs/docs/api-reference/customer/delete-customer-request.md new file mode 100644 index 00000000..f21ae730 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/delete-customer-request.md @@ -0,0 +1,108 @@ +--- +title: Delete a customer request +description: Delete a customer request via Plane API. HTTP request format, parameters, scopes, and example responses for delete a customer request. +keywords: plane, plane api, rest api, api integration, customer, delete a customer request +--- + +# Delete a customer request + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/customers/{customer_id}/requests/{resource_id}/ +
+ +
+
+ +Permanently delete a customer request and unlink any linked issue + +
+ +### Path Parameters + +
+ + + +The unique identifier of the customer. + + + + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers.requests:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/delete-customer.md b/apps/developer-docs/docs/api-reference/customer/delete-customer.md new file mode 100644 index 00000000..ff0ce230 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/delete-customer.md @@ -0,0 +1,102 @@ +--- +title: Delete a customer +description: Delete a customer via Plane API. HTTP request format, parameters, scopes, and example responses for delete a customer. +keywords: plane, plane api, rest api, api integration, customer, delete a customer +--- + +# Delete a customer + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/customers/{resource_id}/ +
+ +
+
+ +Permanently delete a customer from the workspace. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/get-customer-detail.md b/apps/developer-docs/docs/api-reference/customer/get-customer-detail.md new file mode 100644 index 00000000..c359a66d --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/get-customer-detail.md @@ -0,0 +1,117 @@ +--- +title: Retrieve a customer +description: Retrieve a customer via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a customer. +keywords: plane, plane api, rest api, api integration, customer, retrieve a customer +--- + +# Retrieve a customer + +
+ GET + /api/v1/workspaces/{workspace_slug}/customers/{resource_id}/ +
+ +
+
+ +Get a specific customer by ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "description": "example-value", + "deleted_at": "2024-01-01T00:00:00Z", + "customer_request_count": 1, + "logo_url": "Example Name", + "description_html": "

Example content

", + "description_stripped": "Example description", + "description_binary": "Example description", + "email": "Example Name" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/get-customer-property-detail.md b/apps/developer-docs/docs/api-reference/customer/get-customer-property-detail.md new file mode 100644 index 00000000..38c99ec8 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/get-customer-property-detail.md @@ -0,0 +1,117 @@ +--- +title: Retrieve a customer property +description: Retrieve a customer property via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a customer property. +keywords: plane, plane api, rest api, api integration, customer, retrieve a customer property +--- + +# Retrieve a customer property + +
+ GET + /api/v1/workspaces/{workspace_slug}/customer-properties/{property_id}/ +
+ +
+
+ +Retrieve a specific customer property by ID. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers.properties:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "display_name": "Example Name", + "description": "Example description", + "property_type": "TEXT", + "deleted_at": "2024-01-01T00:00:00Z", + "logo_props": "example-value", + "sort_order": 1, + "relation_type": "ISSUE", + "is_required": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/get-customer-property-value.md b/apps/developer-docs/docs/api-reference/customer/get-customer-property-value.md new file mode 100644 index 00000000..881ae4f8 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/get-customer-property-value.md @@ -0,0 +1,112 @@ +--- +title: Retrieve a customer property value +description: Retrieve a customer property value via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a customer property value. +keywords: plane, plane api, rest api, api integration, customer, retrieve a customer property value +--- + +# Retrieve a customer property value + +
+ GET + /api/v1/workspaces/{workspace_slug}/customers/{customer_id}/property-values/{property_id}/ +
+ +
+
+ +Retrieve values for a specific property of a customer. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the customer. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers.property_values:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "detail": "Property values retrieved successfully" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/get-customer-request-detail.md b/apps/developer-docs/docs/api-reference/customer/get-customer-request-detail.md new file mode 100644 index 00000000..c9663521 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/get-customer-request-detail.md @@ -0,0 +1,127 @@ +--- +title: Retrieve a customer request +description: Retrieve a customer request via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a customer request. +keywords: plane, plane api, rest api, api integration, customer, retrieve a customer request +--- + +# Retrieve a customer request + +
+ GET + /api/v1/workspaces/{workspace_slug}/customers/{customer_id}/requests/{resource_id}/ +
+ +
+
+ +Get a specific customer request by ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the customer. + + + + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers.requests:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "name": "Example Name", + "description": "example-value", + "description_html": "

Example content

", + "description_stripped": "Example description", + "email": "Example Name", + "website_url": "https://example.com/resource", + "logo_props": "example-value", + "domain": "Example Name", + "employees": 1, + "stage": "Example Name", + "contract_status": "Example Name", + "revenue": "Example Name", + "archived_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000", + "logo_asset": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/link-work-items-to-customer.md b/apps/developer-docs/docs/api-reference/customer/link-work-items-to-customer.md new file mode 100644 index 00000000..c6b1b519 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/link-work-items-to-customer.md @@ -0,0 +1,136 @@ +--- +title: Link work items to customer +description: Link work items to customer via Plane API. HTTP request format, parameters, scopes, and example responses for link work items to customer. +keywords: plane, plane api, rest api, api integration, customer, link work items to customer +--- + +# Link work items to customer + +
+ POST + /api/v1/workspaces/{workspace_slug}/customers/{customer_id}/issues/ +
+ +
+
+ +Link one or more issues to a customer, optionally within a specific customer request. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the customer. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Array of issue IDs to link + + + +
+
+ +
+ +### Scopes + +`customers.work_items:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "detail": "Issues linked successfully" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/list-customer-properties.md b/apps/developer-docs/docs/api-reference/customer/list-customer-properties.md new file mode 100644 index 00000000..09921e03 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/list-customer-properties.md @@ -0,0 +1,117 @@ +--- +title: List all customer properties +description: List all customer properties via Plane API. HTTP request format, parameters, scopes, and example responses for list all customer properties. +keywords: plane, plane api, rest api, api integration, customer, list all customer properties +--- + +# List all customer properties + +
+ GET + /api/v1/workspaces/{workspace_slug}/customer-properties/ +
+ +
+
+ +List all customer properties in a workspace. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers.properties:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/list-customer-property-values.md b/apps/developer-docs/docs/api-reference/customer/list-customer-property-values.md new file mode 100644 index 00000000..ce108517 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/list-customer-property-values.md @@ -0,0 +1,106 @@ +--- +title: List all customer property values +description: List all customer property values via Plane API. HTTP request format, parameters, scopes, and example responses for list all customer property values. +keywords: plane, plane api, rest api, api integration, customer, list all customer property values +--- + +# List all customer property values + +
+ GET + /api/v1/workspaces/{workspace_slug}/customers/{customer_id}/property-values/ +
+ +
+
+ +Retrieve all property values for a specific customer. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the customer. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers.property_values:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "detail": "Customer property values retrieved successfully" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/list-customer-requests.md b/apps/developer-docs/docs/api-reference/customer/list-customer-requests.md new file mode 100644 index 00000000..24816f5c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/list-customer-requests.md @@ -0,0 +1,121 @@ +--- +title: List all customer requests +description: List all customer requests via Plane API. HTTP request format, parameters, scopes, and example responses for list all customer requests. +keywords: plane, plane api, rest api, api integration, customer, list all customer requests +--- + +# List all customer requests + +
+ GET + /api/v1/workspaces/{workspace_slug}/customers/{customer_id}/requests/ +
+ +
+
+ +List all requests for a customer with optional search filtering + +
+ +### Path Parameters + +
+ + + +The unique identifier of the customer. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers.requests:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "name": "Example Name", + "description": "example-value", + "description_html": "

Example content

", + "description_stripped": "Example description", + "email": "Example Name", + "website_url": "https://example.com/resource", + "logo_props": "example-value", + "domain": "Example Name", + "employees": 1, + "stage": "Example Name", + "contract_status": "Example Name", + "revenue": "Example Name", + "archived_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000", + "logo_asset": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/list-customer-work-items.md b/apps/developer-docs/docs/api-reference/customer/list-customer-work-items.md new file mode 100644 index 00000000..3f2aa2fd --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/list-customer-work-items.md @@ -0,0 +1,106 @@ +--- +title: List all customer work items +description: List all customer work items via Plane API. HTTP request format, parameters, scopes, and example responses for list all customer work items. +keywords: plane, plane api, rest api, api integration, customer, list all customer work items +--- + +# List all customer work items + +
+ GET + /api/v1/workspaces/{workspace_slug}/customers/{customer_id}/issues/ +
+ +
+
+ +List all issues linked to a customer, with filtering by request + +
+ +### Path Parameters + +
+ + + +The unique identifier of the customer. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers.work_items:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "detail": "Customer issues retrieved successfully" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/list-customers.md b/apps/developer-docs/docs/api-reference/customer/list-customers.md new file mode 100644 index 00000000..34f4e34c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/list-customers.md @@ -0,0 +1,114 @@ +--- +title: List all customers +description: List all customers via Plane API. HTTP request format, parameters, scopes, and example responses for list all customers. +keywords: plane, plane api, rest api, api integration, customer, list all customers +--- + +# List all customers + +
+ GET + /api/v1/workspaces/{workspace_slug}/customers/ +
+ +
+
+ +List all customers in a workspace with optional search filtering + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/overview.md b/apps/developer-docs/docs/api-reference/customer/overview.md new file mode 100644 index 00000000..5305c111 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/overview.md @@ -0,0 +1,71 @@ +--- +title: Overview +description: Plane Customer API overview. Learn about endpoints, request/response format, and how to work with customer via REST API. +keywords: plane, plane api, rest api, api integration, customers, crm, customer management +--- + +# Overview + +Customers allow you to manage customer relationships and track customer-related work items, requests, and custom properties within a workspace. + +[Learn more about Customers](https://docs.plane.so/customers) + +
+
+ +## The Customer Object + +### Attributes + +- `id` _uuid_ + + Unique identifier for the customer + +- `name` _string_ **(required)** + + Name of the customer + +- `email` _string_ + + Email address of the customer + +- `workspace` _uuid_ + + Workspace UUID which is automatically saved + +- `created_at` _timestamp_ + + The timestamp when the customer was created + +- `updated_at` _timestamp_ + + The timestamp when the customer was last updated + +- `created_by` _uuid_ + + ID of the user who created the customer + +- `updated_by` _uuid_ + + ID of the user who last updated the customer + +
+
+ + + +```json +{ + "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "created_at": "2023-11-19T11:56:55.176802Z", + "updated_at": "2023-11-19T11:56:55.176809Z", + "name": "Acme Corporation", + "email": "contact@acme.com", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/customer/unlink-work-item-from-customer.md b/apps/developer-docs/docs/api-reference/customer/unlink-work-item-from-customer.md new file mode 100644 index 00000000..da1b16cb --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/unlink-work-item-from-customer.md @@ -0,0 +1,108 @@ +--- +title: Unlink work item from customer +description: Unlink work item from customer via Plane API. HTTP request format, parameters, scopes, and example responses for unlink work item from customer. +keywords: plane, plane api, rest api, api integration, customer, unlink work item from customer +--- + +# Unlink work item from customer + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/customers/{customer_id}/issues/{work_item_id}/ +
+ +
+
+ +Remove the link between an issue and a customer/customer request. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the customer. + + + + + +The unique identifier of the work item. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`customers.work_items:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/update-customer-detail.md b/apps/developer-docs/docs/api-reference/customer/update-customer-detail.md new file mode 100644 index 00000000..fb110b86 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/update-customer-detail.md @@ -0,0 +1,278 @@ +--- +title: Update a customer +description: Update a customer via Plane API. HTTP request format, parameters, scopes, and example responses for update a customer. +keywords: plane, plane api, rest api, api integration, customer, update a customer +--- + +# Update a customer + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/customers/{resource_id}/ +
+ +
+
+ +Update an existing customer with the provided fields. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Description html. + + + + + +Description stripped. + + + + + +Email. + + + + + +Website url. + + + + + +Logo props. + + + + + +Domain. + + + + + +Employees. + + + + + +Stage. + + + + + +Contract status. + + + + + +Revenue. + + + + + +Archived at. + + + + + +Created by. + + + + + +Updated by. + + + + + +Logo asset. + + + +
+
+ +
+ +### Scopes + +`customers:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "description": "example-value", + "deleted_at": "2024-01-01T00:00:00Z", + "customer_request_count": 1, + "logo_url": "Example Name", + "description_html": "

Example content

", + "description_stripped": "Example description", + "description_binary": "Example description", + "email": "Example Name" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/update-customer-property-detail.md b/apps/developer-docs/docs/api-reference/customer/update-customer-property-detail.md new file mode 100644 index 00000000..3f03c58b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/update-customer-property-detail.md @@ -0,0 +1,291 @@ +--- +title: Update a customer property +description: Update a customer property via Plane API. HTTP request format, parameters, scopes, and example responses for update a customer property. +keywords: plane, plane api, rest api, api integration, customer, update a customer property +--- + +# Update a customer property + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/customer-properties/{property_id}/ +
+ +
+
+ +Update an existing customer property with the provided fields. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Display name. + + + + + +Description. + + + + + +Logo props. + + + + + +Sort order. + + + + + +- `TEXT` - Text +- `DATETIME` - Datetime +- `DECIMAL` - Decimal +- `BOOLEAN` - Boolean +- `OPTION` - Option +- `RELATION` - Relation +- `URL` - URL +- `EMAIL` - Email +- `FILE` - File + + + + + +- `ISSUE` - Issue +- `USER` - User + + + + + +Is required. + + + + + +Default value. + + + + + +Settings. + + + + + +Is active. + + + + + +Is multi. + + + + + +Validation rules. + + + + + +External source. + + + + + +External id. + + + + + +Created by. + + + + + +Updated by. + + + +
+
+ +
+ +### Scopes + +`customers.properties:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "display_name": "Example Name", + "description": "Example description", + "property_type": "TEXT", + "deleted_at": "2024-01-01T00:00:00Z", + "logo_props": "example-value", + "sort_order": 1, + "relation_type": "ISSUE", + "is_required": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/update-customer-property-value.md b/apps/developer-docs/docs/api-reference/customer/update-customer-property-value.md new file mode 100644 index 00000000..9f13bf20 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/update-customer-property-value.md @@ -0,0 +1,138 @@ +--- +title: Update a customer property value +description: Update a customer property value via Plane API. HTTP request format, parameters, scopes, and example responses for update a customer property value. +keywords: plane, plane api, rest api, api integration, customer, update a customer property value +--- + +# Update a customer property value + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/customers/{customer_id}/property-values/{property_id}/ +
+ +
+
+ +Update values for a specific property of a customer. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the customer. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Array of values for the property + + + +
+
+ +
+ +### Scopes + +`customers.property_values:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/customer/update-customer-request-detail.md b/apps/developer-docs/docs/api-reference/customer/update-customer-request-detail.md new file mode 100644 index 00000000..97ccb60a --- /dev/null +++ b/apps/developer-docs/docs/api-reference/customer/update-customer-request-detail.md @@ -0,0 +1,193 @@ +--- +title: Update a customer request +description: Update a customer request via Plane API. HTTP request format, parameters, scopes, and example responses for update a customer request. +keywords: plane, plane api, rest api, api integration, customer, update a customer request +--- + +# Update a customer request + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/customers/{customer_id}/requests/{resource_id}/ +
+ +
+
+ +Update an existing customer request with the provided fields. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the customer. + + + + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Description html. + + + + + +Link. + + + + + +Work item ids. + + + +
+
+ +
+ +### Scopes + +`customers.requests:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "name": "Example Name", + "description": "example-value", + "description_html": "

Example content

", + "description_stripped": "Example description", + "email": "Example Name", + "website_url": "https://example.com/resource", + "logo_props": "example-value", + "domain": "Example Name", + "employees": 1, + "stage": "Example Name", + "contract_status": "Example Name", + "revenue": "Example Name", + "archived_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000", + "logo_asset": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/cycle/add-cycle-work-items.md b/apps/developer-docs/docs/api-reference/cycle/add-cycle-work-items.md new file mode 100644 index 00000000..33822017 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/add-cycle-work-items.md @@ -0,0 +1,151 @@ +--- +title: Add work items to cycle +description: Add work items to cycle via Plane API. HTTP request format, parameters, scopes, and example responses for add work items to cycle. +keywords: plane, plane api, rest api, api integration, cycle, add work items to cycle +--- + +# Add work items to cycle + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/cycles/{cycle_id}/cycle-issues/ +
+ +
+
+ +Assign multiple work items to a cycle. Automatically handles bulk creation and updates with activity tracking. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the cycle. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +List of issue IDs to add to the cycle + + + +
+
+ +
+ +### Scopes + +`projects.cycles:write` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "cycle": "550e8400-e29b-41d4-a716-446655440000", + "issue": "550e8400-e29b-41d4-a716-446655440000", + "sub_issues_count": 3, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/cycle/add-cycle.md b/apps/developer-docs/docs/api-reference/cycle/add-cycle.md new file mode 100644 index 00000000..f570af3b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/add-cycle.md @@ -0,0 +1,634 @@ +--- +title: Create a cycle +description: Create a cycle via Plane API. HTTP request format, parameters, scopes, and example responses for create a cycle. +keywords: plane, plane api, rest api, api integration, cycle, create a cycle +--- + +# Create a cycle + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/cycles/ +
+ +
+
+ +Create a new development cycle with specified name, description, and date range. Supports external ID tracking for integration purposes. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Start date. + + + + + +End date. + + + + + +User who owns the cycle. If not provided, defaults to the current user. + + + + + +External source. + + + + + +External id. + + + + + +- `Africa/Abidjan` - Africa/Abidjan +- `Africa/Accra` - Africa/Accra +- `Africa/Addis_Ababa` - Africa/Addis_Ababa +- `Africa/Algiers` - Africa/Algiers +- `Africa/Asmara` - Africa/Asmara +- `Africa/Bamako` - Africa/Bamako +- `Africa/Bangui` - Africa/Bangui +- `Africa/Banjul` - Africa/Banjul +- `Africa/Bissau` - Africa/Bissau +- `Africa/Blantyre` - Africa/Blantyre +- `Africa/Brazzaville` - Africa/Brazzaville +- `Africa/Bujumbura` - Africa/Bujumbura +- `Africa/Cairo` - Africa/Cairo +- `Africa/Casablanca` - Africa/Casablanca +- `Africa/Ceuta` - Africa/Ceuta +- `Africa/Conakry` - Africa/Conakry +- `Africa/Dakar` - Africa/Dakar +- `Africa/Dar_es_Salaam` - Africa/Dar_es_Salaam +- `Africa/Djibouti` - Africa/Djibouti +- `Africa/Douala` - Africa/Douala +- `Africa/El_Aaiun` - Africa/El_Aaiun +- `Africa/Freetown` - Africa/Freetown +- `Africa/Gaborone` - Africa/Gaborone +- `Africa/Harare` - Africa/Harare +- `Africa/Johannesburg` - Africa/Johannesburg +- `Africa/Juba` - Africa/Juba +- `Africa/Kampala` - Africa/Kampala +- `Africa/Khartoum` - Africa/Khartoum +- `Africa/Kigali` - Africa/Kigali +- `Africa/Kinshasa` - Africa/Kinshasa +- `Africa/Lagos` - Africa/Lagos +- `Africa/Libreville` - Africa/Libreville +- `Africa/Lome` - Africa/Lome +- `Africa/Luanda` - Africa/Luanda +- `Africa/Lubumbashi` - Africa/Lubumbashi +- `Africa/Lusaka` - Africa/Lusaka +- `Africa/Malabo` - Africa/Malabo +- `Africa/Maputo` - Africa/Maputo +- `Africa/Maseru` - Africa/Maseru +- `Africa/Mbabane` - Africa/Mbabane +- `Africa/Mogadishu` - Africa/Mogadishu +- `Africa/Monrovia` - Africa/Monrovia +- `Africa/Nairobi` - Africa/Nairobi +- `Africa/Ndjamena` - Africa/Ndjamena +- `Africa/Niamey` - Africa/Niamey +- `Africa/Nouakchott` - Africa/Nouakchott +- `Africa/Ouagadougou` - Africa/Ouagadougou +- `Africa/Porto-Novo` - Africa/Porto-Novo +- `Africa/Sao_Tome` - Africa/Sao_Tome +- `Africa/Tripoli` - Africa/Tripoli +- `Africa/Tunis` - Africa/Tunis +- `Africa/Windhoek` - Africa/Windhoek +- `America/Adak` - America/Adak +- `America/Anchorage` - America/Anchorage +- `America/Anguilla` - America/Anguilla +- `America/Antigua` - America/Antigua +- `America/Araguaina` - America/Araguaina +- `America/Argentina/Buenos_Aires` - America/Argentina/Buenos_Aires +- `America/Argentina/Catamarca` - America/Argentina/Catamarca +- `America/Argentina/Cordoba` - America/Argentina/Cordoba +- `America/Argentina/Jujuy` - America/Argentina/Jujuy +- `America/Argentina/La_Rioja` - America/Argentina/La_Rioja +- `America/Argentina/Mendoza` - America/Argentina/Mendoza +- `America/Argentina/Rio_Gallegos` - America/Argentina/Rio_Gallegos +- `America/Argentina/Salta` - America/Argentina/Salta +- `America/Argentina/San_Juan` - America/Argentina/San_Juan +- `America/Argentina/San_Luis` - America/Argentina/San_Luis +- `America/Argentina/Tucuman` - America/Argentina/Tucuman +- `America/Argentina/Ushuaia` - America/Argentina/Ushuaia +- `America/Aruba` - America/Aruba +- `America/Asuncion` - America/Asuncion +- `America/Atikokan` - America/Atikokan +- `America/Bahia` - America/Bahia +- `America/Bahia_Banderas` - America/Bahia_Banderas +- `America/Barbados` - America/Barbados +- `America/Belem` - America/Belem +- `America/Belize` - America/Belize +- `America/Blanc-Sablon` - America/Blanc-Sablon +- `America/Boa_Vista` - America/Boa_Vista +- `America/Bogota` - America/Bogota +- `America/Boise` - America/Boise +- `America/Cambridge_Bay` - America/Cambridge_Bay +- `America/Campo_Grande` - America/Campo_Grande +- `America/Cancun` - America/Cancun +- `America/Caracas` - America/Caracas +- `America/Cayenne` - America/Cayenne +- `America/Cayman` - America/Cayman +- `America/Chicago` - America/Chicago +- `America/Chihuahua` - America/Chihuahua +- `America/Ciudad_Juarez` - America/Ciudad_Juarez +- `America/Costa_Rica` - America/Costa_Rica +- `America/Creston` - America/Creston +- `America/Cuiaba` - America/Cuiaba +- `America/Curacao` - America/Curacao +- `America/Danmarkshavn` - America/Danmarkshavn +- `America/Dawson` - America/Dawson +- `America/Dawson_Creek` - America/Dawson_Creek +- `America/Denver` - America/Denver +- `America/Detroit` - America/Detroit +- `America/Dominica` - America/Dominica +- `America/Edmonton` - America/Edmonton +- `America/Eirunepe` - America/Eirunepe +- `America/El_Salvador` - America/El_Salvador +- `America/Fort_Nelson` - America/Fort_Nelson +- `America/Fortaleza` - America/Fortaleza +- `America/Glace_Bay` - America/Glace_Bay +- `America/Goose_Bay` - America/Goose_Bay +- `America/Grand_Turk` - America/Grand_Turk +- `America/Grenada` - America/Grenada +- `America/Guadeloupe` - America/Guadeloupe +- `America/Guatemala` - America/Guatemala +- `America/Guayaquil` - America/Guayaquil +- `America/Guyana` - America/Guyana +- `America/Halifax` - America/Halifax +- `America/Havana` - America/Havana +- `America/Hermosillo` - America/Hermosillo +- `America/Indiana/Indianapolis` - America/Indiana/Indianapolis +- `America/Indiana/Knox` - America/Indiana/Knox +- `America/Indiana/Marengo` - America/Indiana/Marengo +- `America/Indiana/Petersburg` - America/Indiana/Petersburg +- `America/Indiana/Tell_City` - America/Indiana/Tell_City +- `America/Indiana/Vevay` - America/Indiana/Vevay +- `America/Indiana/Vincennes` - America/Indiana/Vincennes +- `America/Indiana/Winamac` - America/Indiana/Winamac +- `America/Inuvik` - America/Inuvik +- `America/Iqaluit` - America/Iqaluit +- `America/Jamaica` - America/Jamaica +- `America/Juneau` - America/Juneau +- `America/Kentucky/Louisville` - America/Kentucky/Louisville +- `America/Kentucky/Monticello` - America/Kentucky/Monticello +- `America/Kralendijk` - America/Kralendijk +- `America/La_Paz` - America/La_Paz +- `America/Lima` - America/Lima +- `America/Los_Angeles` - America/Los_Angeles +- `America/Lower_Princes` - America/Lower_Princes +- `America/Maceio` - America/Maceio +- `America/Managua` - America/Managua +- `America/Manaus` - America/Manaus +- `America/Marigot` - America/Marigot +- `America/Martinique` - America/Martinique +- `America/Matamoros` - America/Matamoros +- `America/Mazatlan` - America/Mazatlan +- `America/Menominee` - America/Menominee +- `America/Merida` - America/Merida +- `America/Metlakatla` - America/Metlakatla +- `America/Mexico_City` - America/Mexico_City +- `America/Miquelon` - America/Miquelon +- `America/Moncton` - America/Moncton +- `America/Monterrey` - America/Monterrey +- `America/Montevideo` - America/Montevideo +- `America/Montserrat` - America/Montserrat +- `America/Nassau` - America/Nassau +- `America/New_York` - America/New_York +- `America/Nome` - America/Nome +- `America/Noronha` - America/Noronha +- `America/North_Dakota/Beulah` - America/North_Dakota/Beulah +- `America/North_Dakota/Center` - America/North_Dakota/Center +- `America/North_Dakota/New_Salem` - America/North_Dakota/New_Salem +- `America/Nuuk` - America/Nuuk +- `America/Ojinaga` - America/Ojinaga +- `America/Panama` - America/Panama +- `America/Paramaribo` - America/Paramaribo +- `America/Phoenix` - America/Phoenix +- `America/Port-au-Prince` - America/Port-au-Prince +- `America/Port_of_Spain` - America/Port_of_Spain +- `America/Porto_Velho` - America/Porto_Velho +- `America/Puerto_Rico` - America/Puerto_Rico +- `America/Punta_Arenas` - America/Punta_Arenas +- `America/Rankin_Inlet` - America/Rankin_Inlet +- `America/Recife` - America/Recife +- `America/Regina` - America/Regina +- `America/Resolute` - America/Resolute +- `America/Rio_Branco` - America/Rio_Branco +- `America/Santarem` - America/Santarem +- `America/Santiago` - America/Santiago +- `America/Santo_Domingo` - America/Santo_Domingo +- `America/Sao_Paulo` - America/Sao_Paulo +- `America/Scoresbysund` - America/Scoresbysund +- `America/Sitka` - America/Sitka +- `America/St_Barthelemy` - America/St_Barthelemy +- `America/St_Johns` - America/St_Johns +- `America/St_Kitts` - America/St_Kitts +- `America/St_Lucia` - America/St_Lucia +- `America/St_Thomas` - America/St_Thomas +- `America/St_Vincent` - America/St_Vincent +- `America/Swift_Current` - America/Swift_Current +- `America/Tegucigalpa` - America/Tegucigalpa +- `America/Thule` - America/Thule +- `America/Tijuana` - America/Tijuana +- `America/Toronto` - America/Toronto +- `America/Tortola` - America/Tortola +- `America/Vancouver` - America/Vancouver +- `America/Whitehorse` - America/Whitehorse +- `America/Winnipeg` - America/Winnipeg +- `America/Yakutat` - America/Yakutat +- `Antarctica/Casey` - Antarctica/Casey +- `Antarctica/Davis` - Antarctica/Davis +- `Antarctica/DumontDUrville` - Antarctica/DumontDUrville +- `Antarctica/Macquarie` - Antarctica/Macquarie +- `Antarctica/Mawson` - Antarctica/Mawson +- `Antarctica/McMurdo` - Antarctica/McMurdo +- `Antarctica/Palmer` - Antarctica/Palmer +- `Antarctica/Rothera` - Antarctica/Rothera +- `Antarctica/Syowa` - Antarctica/Syowa +- `Antarctica/Troll` - Antarctica/Troll +- `Antarctica/Vostok` - Antarctica/Vostok +- `Arctic/Longyearbyen` - Arctic/Longyearbyen +- `Asia/Aden` - Asia/Aden +- `Asia/Almaty` - Asia/Almaty +- `Asia/Amman` - Asia/Amman +- `Asia/Anadyr` - Asia/Anadyr +- `Asia/Aqtau` - Asia/Aqtau +- `Asia/Aqtobe` - Asia/Aqtobe +- `Asia/Ashgabat` - Asia/Ashgabat +- `Asia/Atyrau` - Asia/Atyrau +- `Asia/Baghdad` - Asia/Baghdad +- `Asia/Bahrain` - Asia/Bahrain +- `Asia/Baku` - Asia/Baku +- `Asia/Bangkok` - Asia/Bangkok +- `Asia/Barnaul` - Asia/Barnaul +- `Asia/Beirut` - Asia/Beirut +- `Asia/Bishkek` - Asia/Bishkek +- `Asia/Brunei` - Asia/Brunei +- `Asia/Chita` - Asia/Chita +- `Asia/Choibalsan` - Asia/Choibalsan +- `Asia/Colombo` - Asia/Colombo +- `Asia/Damascus` - Asia/Damascus +- `Asia/Dhaka` - Asia/Dhaka +- `Asia/Dili` - Asia/Dili +- `Asia/Dubai` - Asia/Dubai +- `Asia/Dushanbe` - Asia/Dushanbe +- `Asia/Famagusta` - Asia/Famagusta +- `Asia/Gaza` - Asia/Gaza +- `Asia/Hebron` - Asia/Hebron +- `Asia/Ho_Chi_Minh` - Asia/Ho_Chi_Minh +- `Asia/Hong_Kong` - Asia/Hong_Kong +- `Asia/Hovd` - Asia/Hovd +- `Asia/Irkutsk` - Asia/Irkutsk +- `Asia/Jakarta` - Asia/Jakarta +- `Asia/Jayapura` - Asia/Jayapura +- `Asia/Jerusalem` - Asia/Jerusalem +- `Asia/Kabul` - Asia/Kabul +- `Asia/Kamchatka` - Asia/Kamchatka +- `Asia/Karachi` - Asia/Karachi +- `Asia/Kathmandu` - Asia/Kathmandu +- `Asia/Khandyga` - Asia/Khandyga +- `Asia/Kolkata` - Asia/Kolkata +- `Asia/Krasnoyarsk` - Asia/Krasnoyarsk +- `Asia/Kuala_Lumpur` - Asia/Kuala_Lumpur +- `Asia/Kuching` - Asia/Kuching +- `Asia/Kuwait` - Asia/Kuwait +- `Asia/Macau` - Asia/Macau +- `Asia/Magadan` - Asia/Magadan +- `Asia/Makassar` - Asia/Makassar +- `Asia/Manila` - Asia/Manila +- `Asia/Muscat` - Asia/Muscat +- `Asia/Nicosia` - Asia/Nicosia +- `Asia/Novokuznetsk` - Asia/Novokuznetsk +- `Asia/Novosibirsk` - Asia/Novosibirsk +- `Asia/Omsk` - Asia/Omsk +- `Asia/Oral` - Asia/Oral +- `Asia/Phnom_Penh` - Asia/Phnom_Penh +- `Asia/Pontianak` - Asia/Pontianak +- `Asia/Pyongyang` - Asia/Pyongyang +- `Asia/Qatar` - Asia/Qatar +- `Asia/Qostanay` - Asia/Qostanay +- `Asia/Qyzylorda` - Asia/Qyzylorda +- `Asia/Riyadh` - Asia/Riyadh +- `Asia/Sakhalin` - Asia/Sakhalin +- `Asia/Samarkand` - Asia/Samarkand +- `Asia/Seoul` - Asia/Seoul +- `Asia/Shanghai` - Asia/Shanghai +- `Asia/Singapore` - Asia/Singapore +- `Asia/Srednekolymsk` - Asia/Srednekolymsk +- `Asia/Taipei` - Asia/Taipei +- `Asia/Tashkent` - Asia/Tashkent +- `Asia/Tbilisi` - Asia/Tbilisi +- `Asia/Tehran` - Asia/Tehran +- `Asia/Thimphu` - Asia/Thimphu +- `Asia/Tokyo` - Asia/Tokyo +- `Asia/Tomsk` - Asia/Tomsk +- `Asia/Ulaanbaatar` - Asia/Ulaanbaatar +- `Asia/Urumqi` - Asia/Urumqi +- `Asia/Ust-Nera` - Asia/Ust-Nera +- `Asia/Vientiane` - Asia/Vientiane +- `Asia/Vladivostok` - Asia/Vladivostok +- `Asia/Yakutsk` - Asia/Yakutsk +- `Asia/Yangon` - Asia/Yangon +- `Asia/Yekaterinburg` - Asia/Yekaterinburg +- `Asia/Yerevan` - Asia/Yerevan +- `Atlantic/Azores` - Atlantic/Azores +- `Atlantic/Bermuda` - Atlantic/Bermuda +- `Atlantic/Canary` - Atlantic/Canary +- `Atlantic/Cape_Verde` - Atlantic/Cape_Verde +- `Atlantic/Faroe` - Atlantic/Faroe +- `Atlantic/Madeira` - Atlantic/Madeira +- `Atlantic/Reykjavik` - Atlantic/Reykjavik +- `Atlantic/South_Georgia` - Atlantic/South_Georgia +- `Atlantic/St_Helena` - Atlantic/St_Helena +- `Atlantic/Stanley` - Atlantic/Stanley +- `Australia/Adelaide` - Australia/Adelaide +- `Australia/Brisbane` - Australia/Brisbane +- `Australia/Broken_Hill` - Australia/Broken_Hill +- `Australia/Darwin` - Australia/Darwin +- `Australia/Eucla` - Australia/Eucla +- `Australia/Hobart` - Australia/Hobart +- `Australia/Lindeman` - Australia/Lindeman +- `Australia/Lord_Howe` - Australia/Lord_Howe +- `Australia/Melbourne` - Australia/Melbourne +- `Australia/Perth` - Australia/Perth +- `Australia/Sydney` - Australia/Sydney +- `Canada/Atlantic` - Canada/Atlantic +- `Canada/Central` - Canada/Central +- `Canada/Eastern` - Canada/Eastern +- `Canada/Mountain` - Canada/Mountain +- `Canada/Newfoundland` - Canada/Newfoundland +- `Canada/Pacific` - Canada/Pacific +- `Europe/Amsterdam` - Europe/Amsterdam +- `Europe/Andorra` - Europe/Andorra +- `Europe/Astrakhan` - Europe/Astrakhan +- `Europe/Athens` - Europe/Athens +- `Europe/Belgrade` - Europe/Belgrade +- `Europe/Berlin` - Europe/Berlin +- `Europe/Bratislava` - Europe/Bratislava +- `Europe/Brussels` - Europe/Brussels +- `Europe/Bucharest` - Europe/Bucharest +- `Europe/Budapest` - Europe/Budapest +- `Europe/Busingen` - Europe/Busingen +- `Europe/Chisinau` - Europe/Chisinau +- `Europe/Copenhagen` - Europe/Copenhagen +- `Europe/Dublin` - Europe/Dublin +- `Europe/Gibraltar` - Europe/Gibraltar +- `Europe/Guernsey` - Europe/Guernsey +- `Europe/Helsinki` - Europe/Helsinki +- `Europe/Isle_of_Man` - Europe/Isle_of_Man +- `Europe/Istanbul` - Europe/Istanbul +- `Europe/Jersey` - Europe/Jersey +- `Europe/Kaliningrad` - Europe/Kaliningrad +- `Europe/Kirov` - Europe/Kirov +- `Europe/Kyiv` - Europe/Kyiv +- `Europe/Lisbon` - Europe/Lisbon +- `Europe/Ljubljana` - Europe/Ljubljana +- `Europe/London` - Europe/London +- `Europe/Luxembourg` - Europe/Luxembourg +- `Europe/Madrid` - Europe/Madrid +- `Europe/Malta` - Europe/Malta +- `Europe/Mariehamn` - Europe/Mariehamn +- `Europe/Minsk` - Europe/Minsk +- `Europe/Monaco` - Europe/Monaco +- `Europe/Moscow` - Europe/Moscow +- `Europe/Oslo` - Europe/Oslo +- `Europe/Paris` - Europe/Paris +- `Europe/Podgorica` - Europe/Podgorica +- `Europe/Prague` - Europe/Prague +- `Europe/Riga` - Europe/Riga +- `Europe/Rome` - Europe/Rome +- `Europe/Samara` - Europe/Samara +- `Europe/San_Marino` - Europe/San_Marino +- `Europe/Sarajevo` - Europe/Sarajevo +- `Europe/Saratov` - Europe/Saratov +- `Europe/Simferopol` - Europe/Simferopol +- `Europe/Skopje` - Europe/Skopje +- `Europe/Sofia` - Europe/Sofia +- `Europe/Stockholm` - Europe/Stockholm +- `Europe/Tallinn` - Europe/Tallinn +- `Europe/Tirane` - Europe/Tirane +- `Europe/Ulyanovsk` - Europe/Ulyanovsk +- `Europe/Vaduz` - Europe/Vaduz +- `Europe/Vatican` - Europe/Vatican +- `Europe/Vienna` - Europe/Vienna +- `Europe/Vilnius` - Europe/Vilnius +- `Europe/Volgograd` - Europe/Volgograd +- `Europe/Warsaw` - Europe/Warsaw +- `Europe/Zagreb` - Europe/Zagreb +- `Europe/Zurich` - Europe/Zurich +- `GMT` - GMT +- `Indian/Antananarivo` - Indian/Antananarivo +- `Indian/Chagos` - Indian/Chagos +- `Indian/Christmas` - Indian/Christmas +- `Indian/Cocos` - Indian/Cocos +- `Indian/Comoro` - Indian/Comoro +- `Indian/Kerguelen` - Indian/Kerguelen +- `Indian/Mahe` - Indian/Mahe +- `Indian/Maldives` - Indian/Maldives +- `Indian/Mauritius` - Indian/Mauritius +- `Indian/Mayotte` - Indian/Mayotte +- `Indian/Reunion` - Indian/Reunion +- `Pacific/Apia` - Pacific/Apia +- `Pacific/Auckland` - Pacific/Auckland +- `Pacific/Bougainville` - Pacific/Bougainville +- `Pacific/Chatham` - Pacific/Chatham +- `Pacific/Chuuk` - Pacific/Chuuk +- `Pacific/Easter` - Pacific/Easter +- `Pacific/Efate` - Pacific/Efate +- `Pacific/Fakaofo` - Pacific/Fakaofo +- `Pacific/Fiji` - Pacific/Fiji +- `Pacific/Funafuti` - Pacific/Funafuti +- `Pacific/Galapagos` - Pacific/Galapagos +- `Pacific/Gambier` - Pacific/Gambier +- `Pacific/Guadalcanal` - Pacific/Guadalcanal +- `Pacific/Guam` - Pacific/Guam +- `Pacific/Honolulu` - Pacific/Honolulu +- `Pacific/Kanton` - Pacific/Kanton +- `Pacific/Kiritimati` - Pacific/Kiritimati +- `Pacific/Kosrae` - Pacific/Kosrae +- `Pacific/Kwajalein` - Pacific/Kwajalein +- `Pacific/Majuro` - Pacific/Majuro +- `Pacific/Marquesas` - Pacific/Marquesas +- `Pacific/Midway` - Pacific/Midway +- `Pacific/Nauru` - Pacific/Nauru +- `Pacific/Niue` - Pacific/Niue +- `Pacific/Norfolk` - Pacific/Norfolk +- `Pacific/Noumea` - Pacific/Noumea +- `Pacific/Pago_Pago` - Pacific/Pago_Pago +- `Pacific/Palau` - Pacific/Palau +- `Pacific/Pitcairn` - Pacific/Pitcairn +- `Pacific/Pohnpei` - Pacific/Pohnpei +- `Pacific/Port_Moresby` - Pacific/Port_Moresby +- `Pacific/Rarotonga` - Pacific/Rarotonga +- `Pacific/Saipan` - Pacific/Saipan +- `Pacific/Tahiti` - Pacific/Tahiti +- `Pacific/Tarawa` - Pacific/Tarawa +- `Pacific/Tongatapu` - Pacific/Tongatapu +- `Pacific/Wake` - Pacific/Wake +- `Pacific/Wallis` - Pacific/Wallis +- `US/Alaska` - US/Alaska +- `US/Arizona` - US/Arizona +- `US/Central` - US/Central +- `US/Eastern` - US/Eastern +- `US/Hawaii` - US/Hawaii +- `US/Mountain` - US/Mountain +- `US/Pacific` - US/Pacific +- `UTC` - UTC + + + +
+
+ +
+ +### Scopes + +`projects.cycles:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "start_date": "2024-01-01T00:00:00Z", + "end_date": "2024-01-01T00:00:00Z", + "status": "current", + "total_issues": 15, + "completed_issues": 8, + "cancelled_issues": 1, + "started_issues": 4, + "unstarted_issues": 2, + "backlog_issues": 0, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/cycle/archive-cycle.md b/apps/developer-docs/docs/api-reference/cycle/archive-cycle.md new file mode 100644 index 00000000..f3eca679 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/archive-cycle.md @@ -0,0 +1,108 @@ +--- +title: Archive a cycle +description: Archive a cycle via Plane API. HTTP request format, parameters, scopes, and example responses for archive a cycle. +keywords: plane, plane api, rest api, api integration, cycle, archive a cycle +--- + +# Archive a cycle + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/cycles/{cycle_id}/archive/ +
+ +
+
+ +Move a completed cycle to archived status for historical tracking. Only cycles that have ended can be archived. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the cycle. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.cycles:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/cycle/delete-cycle.md b/apps/developer-docs/docs/api-reference/cycle/delete-cycle.md new file mode 100644 index 00000000..f1da7e83 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/delete-cycle.md @@ -0,0 +1,108 @@ +--- +title: Delete a cycle +description: Delete a cycle via Plane API. HTTP request format, parameters, scopes, and example responses for delete a cycle. +keywords: plane, plane api, rest api, api integration, cycle, delete a cycle +--- + +# Delete a cycle + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/cycles/{resource_id}/ +
+ +
+
+ +Permanently remove a cycle and all its associated issue relationships + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.cycles:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/cycle/get-cycle-detail.md b/apps/developer-docs/docs/api-reference/cycle/get-cycle-detail.md new file mode 100644 index 00000000..778f6837 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/get-cycle-detail.md @@ -0,0 +1,125 @@ +--- +title: Retrieve a cycle +description: Retrieve a cycle via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a cycle. +keywords: plane, plane api, rest api, api integration, cycle, retrieve a cycle +--- + +# Retrieve a cycle + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/cycles/{resource_id}/ +
+ +
+
+ +Retrieve details of a specific cycle by its ID. Supports cycle status filtering. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.cycles:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "start_date": "2024-01-01T00:00:00Z", + "end_date": "2024-01-01T00:00:00Z", + "status": "current", + "total_issues": 15, + "completed_issues": 8, + "cancelled_issues": 1, + "started_issues": 4, + "unstarted_issues": 2, + "backlog_issues": 0, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/cycle/list-archived-cycles.md b/apps/developer-docs/docs/api-reference/cycle/list-archived-cycles.md new file mode 100644 index 00000000..b9382bb3 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/list-archived-cycles.md @@ -0,0 +1,144 @@ +--- +title: List all archived cycles +description: List all archived cycles via Plane API. HTTP request format, parameters, scopes, and example responses for list all archived cycles. +keywords: plane, plane api, rest api, api integration, cycle, list all archived cycles +--- + +# List all archived cycles + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/archived-cycles/ +
+ +
+
+ +Retrieve all cycles that have been archived in the project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.cycles:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/cycle/list-cycle-work-items.md b/apps/developer-docs/docs/api-reference/cycle/list-cycle-work-items.md new file mode 100644 index 00000000..e8f54577 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/list-cycle-work-items.md @@ -0,0 +1,152 @@ +--- +title: List all work items in a cycle +description: List all work items in a cycle via Plane API. HTTP request format, parameters, scopes, and example responses for list all work items in a cycle. +keywords: plane, plane api, rest api, api integration, cycle, list all work items in a cycle +--- + +# List all work items in a cycle + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/cycles/{cycle_id}/cycle-issues/ +
+ +
+
+ +Retrieve all work items assigned to a cycle. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the cycle. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.cycles:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "cycle": "550e8400-e29b-41d4-a716-446655440000", + "issue": "550e8400-e29b-41d4-a716-446655440000", + "sub_issues_count": 3, + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/cycle/list-cycles.md b/apps/developer-docs/docs/api-reference/cycle/list-cycles.md new file mode 100644 index 00000000..68286155 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/list-cycles.md @@ -0,0 +1,171 @@ +--- +title: List all cycles +description: List all cycles via Plane API. HTTP request format, parameters, scopes, and example responses for list all cycles. +keywords: plane, plane api, rest api, api integration, cycle, list all cycles +--- + +# List all cycles + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/cycles/ +
+ +
+
+ +Retrieve all cycles in a project. Supports filtering by cycle status like current, upcoming, completed, or draft. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Filter cycles by status + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.cycles:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "start_date": "2024-01-01T00:00:00Z", + "end_date": "2024-01-01T00:00:00Z", + "status": "current" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/cycle/overview.md b/apps/developer-docs/docs/api-reference/cycle/overview.md new file mode 100644 index 00000000..8ac2327d --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/overview.md @@ -0,0 +1,107 @@ +--- +title: Overview +description: Plane Cycle API overview. Learn about endpoints, request/response format, and how to work with cycle via REST API. +keywords: plane, plane api, rest api, api integration, cycles, sprints, iterations +--- + +# Overview + +Cycles are custom time periods in which a team works to complete items from their backlog. At the end of a cycle, the team typically has a new version of their project or product ready. + +[Learn more about Cycles](https://docs.plane.so/core-concepts/cycles) + +
+
+ +## The Cycles Object + +### Attributes + +- `name` string (required) + + Name of the cycle + +- `description` string + + Description of the cycle + +- `start_date` date + + Start date of the cycle + +- `end_date` date + + End date of the cycle + +- `created_at` _timestamp_ + + The timestamp of the time when the project was created + +- `updated_at` _timestamp_ + + The timestamp of the time when the project was last updated + +- `view_props` + + It store the filters and the display properties selected by the user to visualize the issues in the module + +- `sort_order` + + It gives the position of the module at which it should be displayed + +- `created_by` , `updated_by` _uuid_ + + These values are auto saved and represent the id of the user that created or the updated the + +- `Project` uuid + + It contains projects uuid which is automatically saved. + +- `Workspace` uuid + + It contains workspace uuid which is automatically saved + +- `owned_by` uuid + + The user ID of the cycle owner + +- `archived_at` _timestamp_ + + The timestamp when the cycle was archived (if archived) + +- `timezone` string + + The timezone for the cycle + +- `version` number + + Version number of the cycle + +
+
+ + + +```json +{ + "id": "50ebc791-65e4-4b4d-a164-3b4e529e55a5", + "created_at": "2023-11-19T12:18:14.900078Z", + "updated_at": "2023-11-19T12:18:14.900088Z", + "name": "cycle testing", + "description": "", + "start_date": null, + "end_date": null, + "view_props": {}, + "sort_order": 35535.0, + "created_by": "0649cb9d-05c8-4ef4-8e8b-d108ccddd42c", + "updated_by": "0649cb9d-05c8-4ef4-8e8b-d108ccddd42c", + "project": "6436c4ae-fba7-45dc-ad4a-5440e17cb1b2", + "workspace": "c467e125-59e3-44ec-b5ee-f9c1e138c611", + "owned_by": "0649cb9d-05c8-4ef4-8e8b-d108ccddd42c" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/cycle/remove-cycle-work-item.md b/apps/developer-docs/docs/api-reference/cycle/remove-cycle-work-item.md new file mode 100644 index 00000000..169adb81 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/remove-cycle-work-item.md @@ -0,0 +1,114 @@ +--- +title: Remove work item from cycle +description: Remove work item from cycle via Plane API. HTTP request format, parameters, scopes, and example responses for remove work item from cycle. +keywords: plane, plane api, rest api, api integration, cycle, remove work item from cycle +--- + +# Remove work item from cycle + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/cycles/{cycle_id}/cycle-issues/{work_item_id}/ +
+ +
+
+ +Remove a work item from a cycle while keeping the work item in the project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the cycle. + + + + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.cycles:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/cycle/transfer-cycle-work-items.md b/apps/developer-docs/docs/api-reference/cycle/transfer-cycle-work-items.md new file mode 100644 index 00000000..13aa703e --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/transfer-cycle-work-items.md @@ -0,0 +1,138 @@ +--- +title: Transfer cycle work items +description: Transfer cycle work items via Plane API. HTTP request format, parameters, scopes, and example responses for transfer cycle work items. +keywords: plane, plane api, rest api, api integration, cycle, transfer cycle work items +--- + +# Transfer cycle work items + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/cycles/{cycle_id}/transfer-issues/ +
+ +
+
+ +Move incomplete work items from the current cycle to a new target cycle. Captures progress snapshot and transfers only unfinished work items. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the cycle. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +ID of the target cycle to transfer issues to + + + +
+
+ +
+ +### Scopes + +`projects.cycles:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "message": "Success" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/cycle/unarchive-cycle.md b/apps/developer-docs/docs/api-reference/cycle/unarchive-cycle.md new file mode 100644 index 00000000..8d39af89 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/unarchive-cycle.md @@ -0,0 +1,108 @@ +--- +title: Restore a cycle +description: Restore a cycle via Plane API. HTTP request format, parameters, scopes, and example responses for restore a cycle. +keywords: plane, plane api, rest api, api integration, cycle, restore a cycle +--- + +# Restore a cycle + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/archived-cycles/{cycle_id}/unarchive/ +
+ +
+
+ +Restore an archived cycle to active status, making it available for regular use. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the cycle. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.cycles:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/cycle/update-cycle-detail.md b/apps/developer-docs/docs/api-reference/cycle/update-cycle-detail.md new file mode 100644 index 00000000..3f8b50f6 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/cycle/update-cycle-detail.md @@ -0,0 +1,640 @@ +--- +title: Update a cycle +description: Update a cycle via Plane API. HTTP request format, parameters, scopes, and example responses for update a cycle. +keywords: plane, plane api, rest api, api integration, cycle, update a cycle +--- + +# Update a cycle + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/cycles/{resource_id}/ +
+ +
+
+ +Modify an existing cycle's properties like name, description, or date range. Completed cycles can only have their sort order changed. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Start date. + + + + + +End date. + + + + + +User who owns the cycle. If not provided, defaults to the current user. + + + + + +External source. + + + + + +External id. + + + + + +- `Africa/Abidjan` - Africa/Abidjan +- `Africa/Accra` - Africa/Accra +- `Africa/Addis_Ababa` - Africa/Addis_Ababa +- `Africa/Algiers` - Africa/Algiers +- `Africa/Asmara` - Africa/Asmara +- `Africa/Bamako` - Africa/Bamako +- `Africa/Bangui` - Africa/Bangui +- `Africa/Banjul` - Africa/Banjul +- `Africa/Bissau` - Africa/Bissau +- `Africa/Blantyre` - Africa/Blantyre +- `Africa/Brazzaville` - Africa/Brazzaville +- `Africa/Bujumbura` - Africa/Bujumbura +- `Africa/Cairo` - Africa/Cairo +- `Africa/Casablanca` - Africa/Casablanca +- `Africa/Ceuta` - Africa/Ceuta +- `Africa/Conakry` - Africa/Conakry +- `Africa/Dakar` - Africa/Dakar +- `Africa/Dar_es_Salaam` - Africa/Dar_es_Salaam +- `Africa/Djibouti` - Africa/Djibouti +- `Africa/Douala` - Africa/Douala +- `Africa/El_Aaiun` - Africa/El_Aaiun +- `Africa/Freetown` - Africa/Freetown +- `Africa/Gaborone` - Africa/Gaborone +- `Africa/Harare` - Africa/Harare +- `Africa/Johannesburg` - Africa/Johannesburg +- `Africa/Juba` - Africa/Juba +- `Africa/Kampala` - Africa/Kampala +- `Africa/Khartoum` - Africa/Khartoum +- `Africa/Kigali` - Africa/Kigali +- `Africa/Kinshasa` - Africa/Kinshasa +- `Africa/Lagos` - Africa/Lagos +- `Africa/Libreville` - Africa/Libreville +- `Africa/Lome` - Africa/Lome +- `Africa/Luanda` - Africa/Luanda +- `Africa/Lubumbashi` - Africa/Lubumbashi +- `Africa/Lusaka` - Africa/Lusaka +- `Africa/Malabo` - Africa/Malabo +- `Africa/Maputo` - Africa/Maputo +- `Africa/Maseru` - Africa/Maseru +- `Africa/Mbabane` - Africa/Mbabane +- `Africa/Mogadishu` - Africa/Mogadishu +- `Africa/Monrovia` - Africa/Monrovia +- `Africa/Nairobi` - Africa/Nairobi +- `Africa/Ndjamena` - Africa/Ndjamena +- `Africa/Niamey` - Africa/Niamey +- `Africa/Nouakchott` - Africa/Nouakchott +- `Africa/Ouagadougou` - Africa/Ouagadougou +- `Africa/Porto-Novo` - Africa/Porto-Novo +- `Africa/Sao_Tome` - Africa/Sao_Tome +- `Africa/Tripoli` - Africa/Tripoli +- `Africa/Tunis` - Africa/Tunis +- `Africa/Windhoek` - Africa/Windhoek +- `America/Adak` - America/Adak +- `America/Anchorage` - America/Anchorage +- `America/Anguilla` - America/Anguilla +- `America/Antigua` - America/Antigua +- `America/Araguaina` - America/Araguaina +- `America/Argentina/Buenos_Aires` - America/Argentina/Buenos_Aires +- `America/Argentina/Catamarca` - America/Argentina/Catamarca +- `America/Argentina/Cordoba` - America/Argentina/Cordoba +- `America/Argentina/Jujuy` - America/Argentina/Jujuy +- `America/Argentina/La_Rioja` - America/Argentina/La_Rioja +- `America/Argentina/Mendoza` - America/Argentina/Mendoza +- `America/Argentina/Rio_Gallegos` - America/Argentina/Rio_Gallegos +- `America/Argentina/Salta` - America/Argentina/Salta +- `America/Argentina/San_Juan` - America/Argentina/San_Juan +- `America/Argentina/San_Luis` - America/Argentina/San_Luis +- `America/Argentina/Tucuman` - America/Argentina/Tucuman +- `America/Argentina/Ushuaia` - America/Argentina/Ushuaia +- `America/Aruba` - America/Aruba +- `America/Asuncion` - America/Asuncion +- `America/Atikokan` - America/Atikokan +- `America/Bahia` - America/Bahia +- `America/Bahia_Banderas` - America/Bahia_Banderas +- `America/Barbados` - America/Barbados +- `America/Belem` - America/Belem +- `America/Belize` - America/Belize +- `America/Blanc-Sablon` - America/Blanc-Sablon +- `America/Boa_Vista` - America/Boa_Vista +- `America/Bogota` - America/Bogota +- `America/Boise` - America/Boise +- `America/Cambridge_Bay` - America/Cambridge_Bay +- `America/Campo_Grande` - America/Campo_Grande +- `America/Cancun` - America/Cancun +- `America/Caracas` - America/Caracas +- `America/Cayenne` - America/Cayenne +- `America/Cayman` - America/Cayman +- `America/Chicago` - America/Chicago +- `America/Chihuahua` - America/Chihuahua +- `America/Ciudad_Juarez` - America/Ciudad_Juarez +- `America/Costa_Rica` - America/Costa_Rica +- `America/Creston` - America/Creston +- `America/Cuiaba` - America/Cuiaba +- `America/Curacao` - America/Curacao +- `America/Danmarkshavn` - America/Danmarkshavn +- `America/Dawson` - America/Dawson +- `America/Dawson_Creek` - America/Dawson_Creek +- `America/Denver` - America/Denver +- `America/Detroit` - America/Detroit +- `America/Dominica` - America/Dominica +- `America/Edmonton` - America/Edmonton +- `America/Eirunepe` - America/Eirunepe +- `America/El_Salvador` - America/El_Salvador +- `America/Fort_Nelson` - America/Fort_Nelson +- `America/Fortaleza` - America/Fortaleza +- `America/Glace_Bay` - America/Glace_Bay +- `America/Goose_Bay` - America/Goose_Bay +- `America/Grand_Turk` - America/Grand_Turk +- `America/Grenada` - America/Grenada +- `America/Guadeloupe` - America/Guadeloupe +- `America/Guatemala` - America/Guatemala +- `America/Guayaquil` - America/Guayaquil +- `America/Guyana` - America/Guyana +- `America/Halifax` - America/Halifax +- `America/Havana` - America/Havana +- `America/Hermosillo` - America/Hermosillo +- `America/Indiana/Indianapolis` - America/Indiana/Indianapolis +- `America/Indiana/Knox` - America/Indiana/Knox +- `America/Indiana/Marengo` - America/Indiana/Marengo +- `America/Indiana/Petersburg` - America/Indiana/Petersburg +- `America/Indiana/Tell_City` - America/Indiana/Tell_City +- `America/Indiana/Vevay` - America/Indiana/Vevay +- `America/Indiana/Vincennes` - America/Indiana/Vincennes +- `America/Indiana/Winamac` - America/Indiana/Winamac +- `America/Inuvik` - America/Inuvik +- `America/Iqaluit` - America/Iqaluit +- `America/Jamaica` - America/Jamaica +- `America/Juneau` - America/Juneau +- `America/Kentucky/Louisville` - America/Kentucky/Louisville +- `America/Kentucky/Monticello` - America/Kentucky/Monticello +- `America/Kralendijk` - America/Kralendijk +- `America/La_Paz` - America/La_Paz +- `America/Lima` - America/Lima +- `America/Los_Angeles` - America/Los_Angeles +- `America/Lower_Princes` - America/Lower_Princes +- `America/Maceio` - America/Maceio +- `America/Managua` - America/Managua +- `America/Manaus` - America/Manaus +- `America/Marigot` - America/Marigot +- `America/Martinique` - America/Martinique +- `America/Matamoros` - America/Matamoros +- `America/Mazatlan` - America/Mazatlan +- `America/Menominee` - America/Menominee +- `America/Merida` - America/Merida +- `America/Metlakatla` - America/Metlakatla +- `America/Mexico_City` - America/Mexico_City +- `America/Miquelon` - America/Miquelon +- `America/Moncton` - America/Moncton +- `America/Monterrey` - America/Monterrey +- `America/Montevideo` - America/Montevideo +- `America/Montserrat` - America/Montserrat +- `America/Nassau` - America/Nassau +- `America/New_York` - America/New_York +- `America/Nome` - America/Nome +- `America/Noronha` - America/Noronha +- `America/North_Dakota/Beulah` - America/North_Dakota/Beulah +- `America/North_Dakota/Center` - America/North_Dakota/Center +- `America/North_Dakota/New_Salem` - America/North_Dakota/New_Salem +- `America/Nuuk` - America/Nuuk +- `America/Ojinaga` - America/Ojinaga +- `America/Panama` - America/Panama +- `America/Paramaribo` - America/Paramaribo +- `America/Phoenix` - America/Phoenix +- `America/Port-au-Prince` - America/Port-au-Prince +- `America/Port_of_Spain` - America/Port_of_Spain +- `America/Porto_Velho` - America/Porto_Velho +- `America/Puerto_Rico` - America/Puerto_Rico +- `America/Punta_Arenas` - America/Punta_Arenas +- `America/Rankin_Inlet` - America/Rankin_Inlet +- `America/Recife` - America/Recife +- `America/Regina` - America/Regina +- `America/Resolute` - America/Resolute +- `America/Rio_Branco` - America/Rio_Branco +- `America/Santarem` - America/Santarem +- `America/Santiago` - America/Santiago +- `America/Santo_Domingo` - America/Santo_Domingo +- `America/Sao_Paulo` - America/Sao_Paulo +- `America/Scoresbysund` - America/Scoresbysund +- `America/Sitka` - America/Sitka +- `America/St_Barthelemy` - America/St_Barthelemy +- `America/St_Johns` - America/St_Johns +- `America/St_Kitts` - America/St_Kitts +- `America/St_Lucia` - America/St_Lucia +- `America/St_Thomas` - America/St_Thomas +- `America/St_Vincent` - America/St_Vincent +- `America/Swift_Current` - America/Swift_Current +- `America/Tegucigalpa` - America/Tegucigalpa +- `America/Thule` - America/Thule +- `America/Tijuana` - America/Tijuana +- `America/Toronto` - America/Toronto +- `America/Tortola` - America/Tortola +- `America/Vancouver` - America/Vancouver +- `America/Whitehorse` - America/Whitehorse +- `America/Winnipeg` - America/Winnipeg +- `America/Yakutat` - America/Yakutat +- `Antarctica/Casey` - Antarctica/Casey +- `Antarctica/Davis` - Antarctica/Davis +- `Antarctica/DumontDUrville` - Antarctica/DumontDUrville +- `Antarctica/Macquarie` - Antarctica/Macquarie +- `Antarctica/Mawson` - Antarctica/Mawson +- `Antarctica/McMurdo` - Antarctica/McMurdo +- `Antarctica/Palmer` - Antarctica/Palmer +- `Antarctica/Rothera` - Antarctica/Rothera +- `Antarctica/Syowa` - Antarctica/Syowa +- `Antarctica/Troll` - Antarctica/Troll +- `Antarctica/Vostok` - Antarctica/Vostok +- `Arctic/Longyearbyen` - Arctic/Longyearbyen +- `Asia/Aden` - Asia/Aden +- `Asia/Almaty` - Asia/Almaty +- `Asia/Amman` - Asia/Amman +- `Asia/Anadyr` - Asia/Anadyr +- `Asia/Aqtau` - Asia/Aqtau +- `Asia/Aqtobe` - Asia/Aqtobe +- `Asia/Ashgabat` - Asia/Ashgabat +- `Asia/Atyrau` - Asia/Atyrau +- `Asia/Baghdad` - Asia/Baghdad +- `Asia/Bahrain` - Asia/Bahrain +- `Asia/Baku` - Asia/Baku +- `Asia/Bangkok` - Asia/Bangkok +- `Asia/Barnaul` - Asia/Barnaul +- `Asia/Beirut` - Asia/Beirut +- `Asia/Bishkek` - Asia/Bishkek +- `Asia/Brunei` - Asia/Brunei +- `Asia/Chita` - Asia/Chita +- `Asia/Choibalsan` - Asia/Choibalsan +- `Asia/Colombo` - Asia/Colombo +- `Asia/Damascus` - Asia/Damascus +- `Asia/Dhaka` - Asia/Dhaka +- `Asia/Dili` - Asia/Dili +- `Asia/Dubai` - Asia/Dubai +- `Asia/Dushanbe` - Asia/Dushanbe +- `Asia/Famagusta` - Asia/Famagusta +- `Asia/Gaza` - Asia/Gaza +- `Asia/Hebron` - Asia/Hebron +- `Asia/Ho_Chi_Minh` - Asia/Ho_Chi_Minh +- `Asia/Hong_Kong` - Asia/Hong_Kong +- `Asia/Hovd` - Asia/Hovd +- `Asia/Irkutsk` - Asia/Irkutsk +- `Asia/Jakarta` - Asia/Jakarta +- `Asia/Jayapura` - Asia/Jayapura +- `Asia/Jerusalem` - Asia/Jerusalem +- `Asia/Kabul` - Asia/Kabul +- `Asia/Kamchatka` - Asia/Kamchatka +- `Asia/Karachi` - Asia/Karachi +- `Asia/Kathmandu` - Asia/Kathmandu +- `Asia/Khandyga` - Asia/Khandyga +- `Asia/Kolkata` - Asia/Kolkata +- `Asia/Krasnoyarsk` - Asia/Krasnoyarsk +- `Asia/Kuala_Lumpur` - Asia/Kuala_Lumpur +- `Asia/Kuching` - Asia/Kuching +- `Asia/Kuwait` - Asia/Kuwait +- `Asia/Macau` - Asia/Macau +- `Asia/Magadan` - Asia/Magadan +- `Asia/Makassar` - Asia/Makassar +- `Asia/Manila` - Asia/Manila +- `Asia/Muscat` - Asia/Muscat +- `Asia/Nicosia` - Asia/Nicosia +- `Asia/Novokuznetsk` - Asia/Novokuznetsk +- `Asia/Novosibirsk` - Asia/Novosibirsk +- `Asia/Omsk` - Asia/Omsk +- `Asia/Oral` - Asia/Oral +- `Asia/Phnom_Penh` - Asia/Phnom_Penh +- `Asia/Pontianak` - Asia/Pontianak +- `Asia/Pyongyang` - Asia/Pyongyang +- `Asia/Qatar` - Asia/Qatar +- `Asia/Qostanay` - Asia/Qostanay +- `Asia/Qyzylorda` - Asia/Qyzylorda +- `Asia/Riyadh` - Asia/Riyadh +- `Asia/Sakhalin` - Asia/Sakhalin +- `Asia/Samarkand` - Asia/Samarkand +- `Asia/Seoul` - Asia/Seoul +- `Asia/Shanghai` - Asia/Shanghai +- `Asia/Singapore` - Asia/Singapore +- `Asia/Srednekolymsk` - Asia/Srednekolymsk +- `Asia/Taipei` - Asia/Taipei +- `Asia/Tashkent` - Asia/Tashkent +- `Asia/Tbilisi` - Asia/Tbilisi +- `Asia/Tehran` - Asia/Tehran +- `Asia/Thimphu` - Asia/Thimphu +- `Asia/Tokyo` - Asia/Tokyo +- `Asia/Tomsk` - Asia/Tomsk +- `Asia/Ulaanbaatar` - Asia/Ulaanbaatar +- `Asia/Urumqi` - Asia/Urumqi +- `Asia/Ust-Nera` - Asia/Ust-Nera +- `Asia/Vientiane` - Asia/Vientiane +- `Asia/Vladivostok` - Asia/Vladivostok +- `Asia/Yakutsk` - Asia/Yakutsk +- `Asia/Yangon` - Asia/Yangon +- `Asia/Yekaterinburg` - Asia/Yekaterinburg +- `Asia/Yerevan` - Asia/Yerevan +- `Atlantic/Azores` - Atlantic/Azores +- `Atlantic/Bermuda` - Atlantic/Bermuda +- `Atlantic/Canary` - Atlantic/Canary +- `Atlantic/Cape_Verde` - Atlantic/Cape_Verde +- `Atlantic/Faroe` - Atlantic/Faroe +- `Atlantic/Madeira` - Atlantic/Madeira +- `Atlantic/Reykjavik` - Atlantic/Reykjavik +- `Atlantic/South_Georgia` - Atlantic/South_Georgia +- `Atlantic/St_Helena` - Atlantic/St_Helena +- `Atlantic/Stanley` - Atlantic/Stanley +- `Australia/Adelaide` - Australia/Adelaide +- `Australia/Brisbane` - Australia/Brisbane +- `Australia/Broken_Hill` - Australia/Broken_Hill +- `Australia/Darwin` - Australia/Darwin +- `Australia/Eucla` - Australia/Eucla +- `Australia/Hobart` - Australia/Hobart +- `Australia/Lindeman` - Australia/Lindeman +- `Australia/Lord_Howe` - Australia/Lord_Howe +- `Australia/Melbourne` - Australia/Melbourne +- `Australia/Perth` - Australia/Perth +- `Australia/Sydney` - Australia/Sydney +- `Canada/Atlantic` - Canada/Atlantic +- `Canada/Central` - Canada/Central +- `Canada/Eastern` - Canada/Eastern +- `Canada/Mountain` - Canada/Mountain +- `Canada/Newfoundland` - Canada/Newfoundland +- `Canada/Pacific` - Canada/Pacific +- `Europe/Amsterdam` - Europe/Amsterdam +- `Europe/Andorra` - Europe/Andorra +- `Europe/Astrakhan` - Europe/Astrakhan +- `Europe/Athens` - Europe/Athens +- `Europe/Belgrade` - Europe/Belgrade +- `Europe/Berlin` - Europe/Berlin +- `Europe/Bratislava` - Europe/Bratislava +- `Europe/Brussels` - Europe/Brussels +- `Europe/Bucharest` - Europe/Bucharest +- `Europe/Budapest` - Europe/Budapest +- `Europe/Busingen` - Europe/Busingen +- `Europe/Chisinau` - Europe/Chisinau +- `Europe/Copenhagen` - Europe/Copenhagen +- `Europe/Dublin` - Europe/Dublin +- `Europe/Gibraltar` - Europe/Gibraltar +- `Europe/Guernsey` - Europe/Guernsey +- `Europe/Helsinki` - Europe/Helsinki +- `Europe/Isle_of_Man` - Europe/Isle_of_Man +- `Europe/Istanbul` - Europe/Istanbul +- `Europe/Jersey` - Europe/Jersey +- `Europe/Kaliningrad` - Europe/Kaliningrad +- `Europe/Kirov` - Europe/Kirov +- `Europe/Kyiv` - Europe/Kyiv +- `Europe/Lisbon` - Europe/Lisbon +- `Europe/Ljubljana` - Europe/Ljubljana +- `Europe/London` - Europe/London +- `Europe/Luxembourg` - Europe/Luxembourg +- `Europe/Madrid` - Europe/Madrid +- `Europe/Malta` - Europe/Malta +- `Europe/Mariehamn` - Europe/Mariehamn +- `Europe/Minsk` - Europe/Minsk +- `Europe/Monaco` - Europe/Monaco +- `Europe/Moscow` - Europe/Moscow +- `Europe/Oslo` - Europe/Oslo +- `Europe/Paris` - Europe/Paris +- `Europe/Podgorica` - Europe/Podgorica +- `Europe/Prague` - Europe/Prague +- `Europe/Riga` - Europe/Riga +- `Europe/Rome` - Europe/Rome +- `Europe/Samara` - Europe/Samara +- `Europe/San_Marino` - Europe/San_Marino +- `Europe/Sarajevo` - Europe/Sarajevo +- `Europe/Saratov` - Europe/Saratov +- `Europe/Simferopol` - Europe/Simferopol +- `Europe/Skopje` - Europe/Skopje +- `Europe/Sofia` - Europe/Sofia +- `Europe/Stockholm` - Europe/Stockholm +- `Europe/Tallinn` - Europe/Tallinn +- `Europe/Tirane` - Europe/Tirane +- `Europe/Ulyanovsk` - Europe/Ulyanovsk +- `Europe/Vaduz` - Europe/Vaduz +- `Europe/Vatican` - Europe/Vatican +- `Europe/Vienna` - Europe/Vienna +- `Europe/Vilnius` - Europe/Vilnius +- `Europe/Volgograd` - Europe/Volgograd +- `Europe/Warsaw` - Europe/Warsaw +- `Europe/Zagreb` - Europe/Zagreb +- `Europe/Zurich` - Europe/Zurich +- `GMT` - GMT +- `Indian/Antananarivo` - Indian/Antananarivo +- `Indian/Chagos` - Indian/Chagos +- `Indian/Christmas` - Indian/Christmas +- `Indian/Cocos` - Indian/Cocos +- `Indian/Comoro` - Indian/Comoro +- `Indian/Kerguelen` - Indian/Kerguelen +- `Indian/Mahe` - Indian/Mahe +- `Indian/Maldives` - Indian/Maldives +- `Indian/Mauritius` - Indian/Mauritius +- `Indian/Mayotte` - Indian/Mayotte +- `Indian/Reunion` - Indian/Reunion +- `Pacific/Apia` - Pacific/Apia +- `Pacific/Auckland` - Pacific/Auckland +- `Pacific/Bougainville` - Pacific/Bougainville +- `Pacific/Chatham` - Pacific/Chatham +- `Pacific/Chuuk` - Pacific/Chuuk +- `Pacific/Easter` - Pacific/Easter +- `Pacific/Efate` - Pacific/Efate +- `Pacific/Fakaofo` - Pacific/Fakaofo +- `Pacific/Fiji` - Pacific/Fiji +- `Pacific/Funafuti` - Pacific/Funafuti +- `Pacific/Galapagos` - Pacific/Galapagos +- `Pacific/Gambier` - Pacific/Gambier +- `Pacific/Guadalcanal` - Pacific/Guadalcanal +- `Pacific/Guam` - Pacific/Guam +- `Pacific/Honolulu` - Pacific/Honolulu +- `Pacific/Kanton` - Pacific/Kanton +- `Pacific/Kiritimati` - Pacific/Kiritimati +- `Pacific/Kosrae` - Pacific/Kosrae +- `Pacific/Kwajalein` - Pacific/Kwajalein +- `Pacific/Majuro` - Pacific/Majuro +- `Pacific/Marquesas` - Pacific/Marquesas +- `Pacific/Midway` - Pacific/Midway +- `Pacific/Nauru` - Pacific/Nauru +- `Pacific/Niue` - Pacific/Niue +- `Pacific/Norfolk` - Pacific/Norfolk +- `Pacific/Noumea` - Pacific/Noumea +- `Pacific/Pago_Pago` - Pacific/Pago_Pago +- `Pacific/Palau` - Pacific/Palau +- `Pacific/Pitcairn` - Pacific/Pitcairn +- `Pacific/Pohnpei` - Pacific/Pohnpei +- `Pacific/Port_Moresby` - Pacific/Port_Moresby +- `Pacific/Rarotonga` - Pacific/Rarotonga +- `Pacific/Saipan` - Pacific/Saipan +- `Pacific/Tahiti` - Pacific/Tahiti +- `Pacific/Tarawa` - Pacific/Tarawa +- `Pacific/Tongatapu` - Pacific/Tongatapu +- `Pacific/Wake` - Pacific/Wake +- `Pacific/Wallis` - Pacific/Wallis +- `US/Alaska` - US/Alaska +- `US/Arizona` - US/Arizona +- `US/Central` - US/Central +- `US/Eastern` - US/Eastern +- `US/Hawaii` - US/Hawaii +- `US/Mountain` - US/Mountain +- `US/Pacific` - US/Pacific +- `UTC` - UTC + + + +
+
+ +
+ +### Scopes + +`projects.cycles:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "start_date": "2024-01-01T00:00:00Z", + "end_date": "2024-01-01T00:00:00Z", + "status": "current", + "total_issues": 15, + "completed_issues": 8, + "cancelled_issues": 1, + "started_issues": 4, + "unstarted_issues": 2, + "backlog_issues": 0, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/epics/add-epic-work-items.md b/apps/developer-docs/docs/api-reference/epics/add-epic-work-items.md new file mode 100644 index 00000000..d7a3dc32 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/epics/add-epic-work-items.md @@ -0,0 +1,179 @@ +--- +title: Add work items to epic +description: Add work items to epic via Plane API. HTTP request format, parameters, scopes, and example responses for add work items to epic. +keywords: plane, plane api, rest api, api integration, epics, add work items to epic +--- + +# Add work items to epic + +
+ POST + /api/v1/workspaces/{slug}/projects/{project_id}/epics/{epic_id}/issues/ +
+ +
+
+ +Add multiple work items as sub-issues under an epic. Validates type hierarchy before assignment. + +
+ +### Path Parameters + +
+ + + +Epic ID + + + + + +Project ID + + + + + +Workspace slug + + + +
+
+ +
+ +### Body Parameters + +
+ + + +List of work item IDs to add to the epic + + + +
+
+ +
+ +### Scopes + +`projects.epics:write` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440010", + "name": "Implement login screen", + "description_html": "

Build the login screen UI

", + "description_stripped": "Build the login screen UI", + "description_binary": null, + "state": "550e8400-e29b-41d4-a716-446655440002", + "priority": "high", + "assignees": [], + "labels": [], + "type": null, + "type_id": null, + "estimate_point": null, + "point": null, + "start_date": null, + "target_date": null, + "parent": "550e8400-e29b-41d4-a716-446655440001", + "sequence_id": 12, + "sort_order": 65535.0, + "is_draft": false, + "completed_at": null, + "archived_at": null, + "last_activity_at": "2025-03-15T10:00:00Z", + "project": "550e8400-e29b-41d4-a716-446655440000", + "workspace": "550e8400-e29b-41d4-a716-446655440003", + "external_id": null, + "external_source": null, + "deleted_at": null, + "created_at": "2025-03-10T09:00:00Z", + "updated_at": "2025-03-15T10:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440005", + "updated_by": null + } +] +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/epics/create-epic.md b/apps/developer-docs/docs/api-reference/epics/create-epic.md new file mode 100644 index 00000000..214e06d8 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/epics/create-epic.md @@ -0,0 +1,239 @@ +--- +title: Create an epic +description: Create an epic via Plane API. HTTP request format, parameters, scopes, and example responses for create an epic. +keywords: plane, plane api, rest api, api integration, epics, create an epic +--- + +# Create an epic + +
+ POST + /api/v1/workspaces/{slug}/projects/{project_id}/epics/ +
+ +
+
+ +Create a new epic in the specified project with the provided details. + +
+ +### Path Parameters + +
+ + + +Project ID + + + + + +Workspace slug + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name of the epic. + + + + + +HTML-formatted description of the epic. + + + + + +ID of the state (status) to assign to the epic. + + + + + +ID of the parent work item. + + + + + +Priority level. Possible values: `none`, `urgent`, `high`, `medium`, `low`. + + + + + +Start date of the epic in YYYY-MM-DD format. + + + + + +Target completion date in YYYY-MM-DD format. + + + + + +List of user IDs to assign to the epic. + + + + + +List of label IDs to apply to the epic. + + + + + +ID of the estimate point. + + + + + +Name of the source system if importing from another tool. + + + + + +External identifier from the source system. + + + +
+
+ +
+ +### Scopes + +`projects.epics:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Develop Mobile Application Framework", + "description": {}, + "description_html": "

Create a cross-platform mobile application framework

", + "description_stripped": "Create a cross-platform mobile application framework", + "description_binary": null, + "state": "550e8400-e29b-41d4-a716-446655440001", + "priority": "high", + "assignees": [], + "labels": [], + "type": "550e8400-e29b-41d4-a716-446655440002", + "estimate_point": null, + "point": null, + "start_date": "2025-03-01", + "target_date": "2025-06-30", + "parent": null, + "sequence_id": 57, + "sort_order": 605535.0, + "is_draft": false, + "completed_at": null, + "archived_at": null, + "project": "550e8400-e29b-41d4-a716-446655440000", + "workspace": "550e8400-e29b-41d4-a716-446655440003", + "external_id": null, + "external_source": null, + "deleted_at": null, + "created_at": "2025-03-01T21:23:54.645263Z", + "updated_at": "2025-03-01T21:23:54.645263Z", + "created_by": "550e8400-e29b-41d4-a716-446655440004", + "updated_by": null +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/epics/delete-epic.md b/apps/developer-docs/docs/api-reference/epics/delete-epic.md new file mode 100644 index 00000000..62005afb --- /dev/null +++ b/apps/developer-docs/docs/api-reference/epics/delete-epic.md @@ -0,0 +1,108 @@ +--- +title: Delete an epic +description: Delete an epic via Plane API. HTTP request format, parameters, scopes, and example responses for delete an epic. +keywords: plane, plane api, rest api, api integration, epics, delete an epic +--- + +# Delete an epic + +
+ DELETE + /api/v1/workspaces/{slug}/projects/{project_id}/epics/{epic_id}/ +
+ +
+
+ +Permanently delete an existing epic from the project. Child work items will have their parent unset. + +
+ +### Path Parameters + +
+ + + +Epic ID + + + + + +Project ID + + + + + +Workspace slug + + + +
+
+ +
+ +### Scopes + +`projects.epics:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/epics/get-epic-detail.md b/apps/developer-docs/docs/api-reference/epics/get-epic-detail.md new file mode 100644 index 00000000..be05bebb --- /dev/null +++ b/apps/developer-docs/docs/api-reference/epics/get-epic-detail.md @@ -0,0 +1,130 @@ +--- +title: Retrieve an epic +description: Retrieve an epic via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve an epic. +keywords: plane, plane api, rest api, api integration, epics, retrieve an epic +--- + +# Retrieve an epic + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/epics/{resource_id}/ +
+ +
+
+ +Retrieve an epic by id + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Comma-separated list of fields to include in response + + + +
+
+ +
+ +### Scopes + +`projects.epics:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/epics/list-epic-work-items.md b/apps/developer-docs/docs/api-reference/epics/list-epic-work-items.md new file mode 100644 index 00000000..db55225c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/epics/list-epic-work-items.md @@ -0,0 +1,178 @@ +--- +title: List epic work items +description: List epic work items via Plane API. HTTP request format, parameters, scopes, and example responses for list epic work items. +keywords: plane, plane api, rest api, api integration, epics, list epic work items +--- + +# List epic work items + +
+ GET + /api/v1/workspaces/{slug}/projects/{project_id}/epics/{epic_id}/issues/ +
+ +
+
+ +Retrieve all work items under an epic. + +
+ +### Path Parameters + +
+ + + +Epic ID + + + + + +Project ID + + + + + +Workspace slug + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.epics:read` `projects.work-items:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": null, + "sub_grouped_by": null, + "total_count": 5, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": false, + "prev_page_results": false, + "count": 5, + "total_pages": 1, + "total_results": 5, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440010", + "name": "Implement login screen", + "description_html": "

Build the login screen UI

", + "description_stripped": "Build the login screen UI", + "description_binary": null, + "state": "550e8400-e29b-41d4-a716-446655440002", + "priority": "high", + "assignees": ["550e8400-e29b-41d4-a716-446655440005"], + "labels": [], + "type": null, + "type_id": null, + "estimate_point": null, + "point": null, + "start_date": "2025-03-10", + "target_date": "2025-03-20", + "parent": "550e8400-e29b-41d4-a716-446655440001", + "sequence_id": 12, + "sort_order": 65535.0, + "is_draft": false, + "completed_at": null, + "archived_at": null, + "last_activity_at": "2025-03-15T10:00:00Z", + "project": "550e8400-e29b-41d4-a716-446655440000", + "workspace": "550e8400-e29b-41d4-a716-446655440003", + "external_id": null, + "external_source": null, + "deleted_at": null, + "created_at": "2025-03-10T09:00:00Z", + "updated_at": "2025-03-15T10:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440005", + "updated_by": null + } + ] +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/epics/list-epics.md b/apps/developer-docs/docs/api-reference/epics/list-epics.md new file mode 100644 index 00000000..6edbe43b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/epics/list-epics.md @@ -0,0 +1,145 @@ +--- +title: List all epics +description: List all epics via Plane API. HTTP request format, parameters, scopes, and example responses for list all epics. +keywords: plane, plane api, rest api, api integration, epics, list all epics +--- + +# List all epics + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/epics/ +
+ +
+
+ +List epics + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.epics:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/epics/overview.md b/apps/developer-docs/docs/api-reference/epics/overview.md new file mode 100644 index 00000000..e23287af --- /dev/null +++ b/apps/developer-docs/docs/api-reference/epics/overview.md @@ -0,0 +1,182 @@ +--- +title: Overview +description: Plane Epics API overview. Learn about endpoints, request/response format, and how to work with epics via REST API. +keywords: plane, plane api, rest api, api integration, epics, features, stories +--- + +# Overview + +Epics help you group related tasks into a larger work item, providing a hierarchical structure for managing complex projects. Use epics to break down major objectives into smaller, manageable pieces while keeping everything organized. +[Learn more about Epics](https://docs.plane.so/core-concepts/issues/epics). + +
+
+ +## The Epics Object + +### Attributes + +- `id` string + + Unique identifier for the epic. + +- `name` string + + Name of the epic. + +- `description` object + + JSON representation of the epic description. + +- `description_html` string + + HTML-formatted description of the epic. + +- `description_stripped` string + + Plain text version of the description. + +- `description_binary` string + + Binary representation of the description. + +- `state` string + + ID of the state (status) of the epic. + +- `priority` string + + Priority level. Possible values: `none`, `urgent`, `high`, `medium`, `low`. + +- `assignees` array + + Array of user IDs assigned to the epic. + +- `labels` array + + Array of label IDs applied to the epic. + +- `type` string + + ID of the work item type for the epic. + +- `estimate_point` string + + ID of the estimate point, or null if not estimated. + +- `point` integer + + Point value for the epic, or null. + +- `start_date` string + + Start date of the epic in YYYY-MM-DD format. + +- `target_date` string + + Target completion date in YYYY-MM-DD format. + +- `parent` string + + ID of the parent work item, or null if no parent. + +- `sequence_id` integer + + Auto-generated sequential identifier for the epic within the project. + +- `sort_order` number + + Auto-generated sort order for display purposes. + +- `is_draft` boolean + + Whether the epic is a draft. + +- `completed_at` timestamp + + Time at which the epic was completed, or null if not completed. + +- `archived_at` timestamp + + Time at which the epic was archived, or null if not archived. + +- `project` string + + ID of the project containing this epic. + +- `workspace` string + + ID of the workspace containing this epic. + +- `external_id` string + + External identifier if imported from another system, or null. + +- `external_source` string + + Name of the source system if imported, or null. + +- `deleted_at` timestamp + + Time at which the epic was deleted, or null if not deleted. + +- `created_at` timestamp + + Time at which the epic was created. + +- `updated_at` timestamp + + Time at which the epic was last updated. + +- `created_by` string + + ID of the user who created the epic. + +- `updated_by` string + + ID of the user who last updated the epic. + +
+
+ + + +```json +{ + "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "name": "Develop Mobile Application Framework", + "description": {}, + "description_html": "

Create a cross-platform mobile application framework that supports all core system functionalities with native-like performance and user experience

", + "description_stripped": "Create a cross-platform mobile application framework that supports all core system functionalities with native-like performance and user experience", + "description_binary": null, + "state": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "priority": "medium", + "assignees": [], + "labels": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], + "type": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "estimate_point": null, + "point": null, + "start_date": "2025-02-28", + "target_date": "2025-06-20", + "parent": null, + "sequence_id": 57, + "sort_order": 605535.0, + "is_draft": false, + "completed_at": null, + "archived_at": null, + "project": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "workspace": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "external_id": null, + "external_source": null, + "deleted_at": null, + "created_at": "2025-03-01T21:23:54.645263+05:30", + "updated_at": "2025-03-03T10:38:44.667276+05:30", + "created_by": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "updated_by": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +} +``` + +
+ +
+
diff --git a/apps/developer-docs/docs/api-reference/epics/update-epic.md b/apps/developer-docs/docs/api-reference/epics/update-epic.md new file mode 100644 index 00000000..9450ecd4 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/epics/update-epic.md @@ -0,0 +1,239 @@ +--- +title: Update an epic +description: Update an epic via Plane API. HTTP request format, parameters, scopes, and example responses for update an epic. +keywords: plane, plane api, rest api, api integration, epics, update an epic +--- + +# Update an epic + +
+ PATCH + /api/v1/workspaces/{slug}/projects/{project_id}/epics/{epic_id}/ +
+ +
+
+ +Partially update an existing epic with the provided fields. Supports external ID validation to prevent conflicts. + +
+ +### Path Parameters + +
+ + + +Epic ID + + + + + +Project ID + + + + + +Workspace slug + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name of the epic. + + + + + +HTML-formatted description of the epic. + + + + + +ID of the state (status) to assign to the epic. + + + + + +ID of the parent work item. + + + + + +Priority level. Possible values: `none`, `urgent`, `high`, `medium`, `low`. + + + + + +Start date of the epic in YYYY-MM-DD format. + + + + + +Target completion date in YYYY-MM-DD format. + + + + + +List of user IDs to assign to the epic. + + + + + +List of label IDs to apply to the epic. + + + + + +ID of the estimate point. + + + + + +Name of the source system if importing from another tool. + + + + + +External identifier from the source system. + + + +
+
+ +
+ +### Scopes + +`projects.epics:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440001", + "name": "Updated Epic Name", + "description": {}, + "description_html": "

Create a cross-platform mobile application framework

", + "description_stripped": "Create a cross-platform mobile application framework", + "description_binary": null, + "state": "550e8400-e29b-41d4-a716-446655440002", + "priority": "medium", + "assignees": [], + "labels": [], + "type": "550e8400-e29b-41d4-a716-446655440003", + "estimate_point": null, + "point": null, + "start_date": "2025-03-01", + "target_date": "2025-09-30", + "parent": null, + "sequence_id": 57, + "sort_order": 605535.0, + "is_draft": false, + "completed_at": null, + "archived_at": null, + "project": "550e8400-e29b-41d4-a716-446655440000", + "workspace": "550e8400-e29b-41d4-a716-446655440004", + "external_id": null, + "external_source": null, + "deleted_at": null, + "created_at": "2025-03-01T21:23:54.645263Z", + "updated_at": "2025-03-05T14:12:00.123456Z", + "created_by": "550e8400-e29b-41d4-a716-446655440005", + "updated_by": "550e8400-e29b-41d4-a716-446655440005" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/estimate/add-estimate-points.md b/apps/developer-docs/docs/api-reference/estimate/add-estimate-points.md new file mode 100644 index 00000000..5039840d --- /dev/null +++ b/apps/developer-docs/docs/api-reference/estimate/add-estimate-points.md @@ -0,0 +1,179 @@ +--- +title: Create estimate points +description: Create estimate points via Plane API. HTTP request format, parameters, scopes, and example responses for create estimate points. +keywords: plane, plane api, rest api, api integration, estimate points, create estimate points +--- + +# Create estimate points + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/estimates/{estimate_id}/estimate-points/ +
+ +
+
+ +Create estimate points for a project estimate. You can send a JSON array directly or wrap it inside `estimate_points`. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the estimate. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Array of estimate point objects. Each object can include: `value` (string, required, max 20 chars), `key` (integer), +`description` (string), `external_id` (string), and `external_source` (string). + + + +
+
+ +
+ +### Scopes + +`projects.estimates:write` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440010", + "created_at": "2024-01-15T10:40:00Z", + "updated_at": "2024-01-20T12:15:00Z", + "estimate": "550e8400-e29b-41d4-a716-446655440000", + "key": 1, + "value": "1", + "description": "Tiny", + "external_id": null, + "external_source": null, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" + }, + { + "id": "550e8400-e29b-41d4-a716-446655440011", + "created_at": "2024-01-15T10:41:00Z", + "updated_at": "2024-01-20T12:16:00Z", + "estimate": "550e8400-e29b-41d4-a716-446655440000", + "key": 2, + "value": "2", + "description": "Small", + "external_id": null, + "external_source": null, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/estimate/add-estimate.md b/apps/developer-docs/docs/api-reference/estimate/add-estimate.md new file mode 100644 index 00000000..a133f296 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/estimate/add-estimate.md @@ -0,0 +1,180 @@ +--- +title: Create an estimate +description: Create an estimate via Plane API. HTTP request format, parameters, scopes, and example responses for create an estimate. +keywords: plane, plane api, rest api, api integration, estimates, create an estimate +--- + +# Create an estimate + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/estimates/ +
+ +
+
+ +Create a new estimate for the project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name of the estimate. + + + + + +Description of the estimate. + + + + + +Type of estimate. Possible values: `categories`, `points`, `time`. + + + + + +Whether this estimate is the most recently used estimate for the project. + + + + + +External ID from an external system. + + + + + +External source identifier. + + + +
+
+ +
+ +### Scopes + +`projects.estimates:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-15T10:30:00Z", + "updated_at": "2024-01-20T12:10:00Z", + "name": "Story Points", + "description": "Standard story point scale", + "type": "points", + "last_used": true, + "external_id": null, + "external_source": null, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/estimate/delete-estimate-point.md b/apps/developer-docs/docs/api-reference/estimate/delete-estimate-point.md new file mode 100644 index 00000000..be56e03a --- /dev/null +++ b/apps/developer-docs/docs/api-reference/estimate/delete-estimate-point.md @@ -0,0 +1,114 @@ +--- +title: Delete an estimate point +description: Delete an estimate point via Plane API. HTTP request format, parameters, scopes, and example responses for delete an estimate point. +keywords: plane, plane api, rest api, api integration, estimate points, delete an estimate point +--- + +# Delete an estimate point + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/estimates/{estimate_id}/estimate-points/{estimate_point_id}/ +
+ +
+
+ +Delete a single estimate point from a project estimate. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the estimate point. + + + + + +The unique identifier of the estimate. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.estimates:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/estimate/delete-estimate.md b/apps/developer-docs/docs/api-reference/estimate/delete-estimate.md new file mode 100644 index 00000000..982bec90 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/estimate/delete-estimate.md @@ -0,0 +1,102 @@ +--- +title: Delete an estimate +description: Delete an estimate via Plane API. HTTP request format, parameters, scopes, and example responses for delete an estimate. +keywords: plane, plane api, rest api, api integration, estimates, delete an estimate +--- + +# Delete an estimate + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/estimates/ +
+ +
+
+ +Delete the estimate configured for a project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.estimates:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/estimate/get-estimate.md b/apps/developer-docs/docs/api-reference/estimate/get-estimate.md new file mode 100644 index 00000000..a5108bb8 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/estimate/get-estimate.md @@ -0,0 +1,118 @@ +--- +title: Get an estimate +description: Get an estimate via Plane API. HTTP request format, parameters, scopes, and example responses for get an estimate. +keywords: plane, plane api, rest api, api integration, estimates, get an estimate +--- + +# Get an estimate + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/estimates/ +
+ +
+
+ +Retrieve the estimate configured for a project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.estimates:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-15T10:30:00Z", + "updated_at": "2024-01-20T12:10:00Z", + "name": "Story Points", + "description": "Standard story point scale", + "type": "points", + "last_used": true, + "external_id": null, + "external_source": null, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/estimate/list-estimate-points.md b/apps/developer-docs/docs/api-reference/estimate/list-estimate-points.md new file mode 100644 index 00000000..8e62db7c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/estimate/list-estimate-points.md @@ -0,0 +1,141 @@ +--- +title: List estimate points +description: List estimate points via Plane API. HTTP request format, parameters, scopes, and example responses for list estimate points. +keywords: plane, plane api, rest api, api integration, estimate points, list estimate points +--- + +# List estimate points + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/estimates/{estimate_id}/estimate-points/ +
+ +
+
+ +Retrieve all estimate points for a project estimate. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the estimate. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.estimates:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440010", + "created_at": "2024-01-15T10:40:00Z", + "updated_at": "2024-01-20T12:15:00Z", + "estimate": "550e8400-e29b-41d4-a716-446655440000", + "key": 1, + "value": "1", + "description": "Tiny", + "external_id": null, + "external_source": null, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" + }, + { + "id": "550e8400-e29b-41d4-a716-446655440011", + "created_at": "2024-01-15T10:41:00Z", + "updated_at": "2024-01-20T12:16:00Z", + "estimate": "550e8400-e29b-41d4-a716-446655440000", + "key": 2, + "value": "2", + "description": "Small", + "external_id": null, + "external_source": null, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/estimate/overview.md b/apps/developer-docs/docs/api-reference/estimate/overview.md new file mode 100644 index 00000000..b64787bb --- /dev/null +++ b/apps/developer-docs/docs/api-reference/estimate/overview.md @@ -0,0 +1,160 @@ +--- +title: Overview +description: Plane Estimate API overview. Learn about estimate objects, estimate points, and how to work with estimates via REST API. +keywords: plane, plane api, rest api, api integration, estimates, estimate points +--- + +# Overview + +Estimates define how work is sized in a project. An estimate represents the scale (categories, points, or time), and +estimate points represent the allowed values on work items. + +
+
+ +## The Estimate Object + +### Attributes + +- `name` _string_ **(required)** + + Name of the estimate + +- `description` _string_ + + Description of the estimate + +- `type` _string_ + + Type of estimate. Possible values: `categories`, `points`, `time` + +- `last_used` _boolean_ + + Whether this estimate is the most recently used estimate for the project + +- `external_id` _string_ or _null_ + + External ID from an external system + +- `external_source` _string_ or _null_ + + External source identifier + +- `created_at`, `updated_at` _timestamp_ + + Timestamps when the estimate was created and last updated + +- `created_by`, `updated_by` _uuid_ + + IDs of the users who created and last updated the estimate + +- `project` _uuid_ + + Project ID associated with the estimate + +- `workspace` _uuid_ + + Workspace ID associated with the estimate + +
+
+ + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-15T10:30:00Z", + "updated_at": "2024-01-20T12:10:00Z", + "name": "Story Points", + "description": "Standard story point scale", + "type": "points", + "last_used": true, + "external_id": "sp-001", + "external_source": "jira", + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" +} +``` + + + +
+
+ +
+
+ +## The Estimate Point Object + +### Attributes + +- `estimate` _uuid_ **(required)** + + Estimate ID this point belongs to + +- `key` _integer_ + + Numeric key used for ordering and display + +- `value` _string_ **(required)** + + Display value for the estimate point (max 20 characters) + +- `description` _string_ + + Description of the estimate point + +- `external_id` _string_ or _null_ + + External ID from an external system + +- `external_source` _string_ or _null_ + + External source identifier + +- `created_at`, `updated_at` _timestamp_ + + Timestamps when the estimate point was created and last updated + +- `created_by`, `updated_by` _uuid_ + + IDs of the users who created and last updated the estimate point + +- `project` _uuid_ + + Project ID associated with the estimate point + +- `workspace` _uuid_ + + Workspace ID associated with the estimate point + +
+
+ + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440010", + "created_at": "2024-01-15T10:40:00Z", + "updated_at": "2024-01-20T12:15:00Z", + "estimate": "550e8400-e29b-41d4-a716-446655440000", + "key": 3, + "value": "3", + "description": "Small", + "external_id": "sp-3", + "external_source": "jira", + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/estimate/update-estimate-point.md b/apps/developer-docs/docs/api-reference/estimate/update-estimate-point.md new file mode 100644 index 00000000..fe1a2e7c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/estimate/update-estimate-point.md @@ -0,0 +1,180 @@ +--- +title: Update an estimate point +description: Update an estimate point via Plane API. HTTP request format, parameters, scopes, and example responses for update an estimate point. +keywords: plane, plane api, rest api, api integration, estimate points, update an estimate point +--- + +# Update an estimate point + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/estimates/{estimate_id}/estimate-points/{estimate_point_id}/ +
+ +
+
+ +Update a single estimate point for a project estimate. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the estimate point. + + + + + +The unique identifier of the estimate. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Numeric key used for ordering and display. + + + + + +Display value for the estimate point (max 20 characters). + + + + + +Description of the estimate point. + + + + + +External ID from an external system. + + + + + +External source identifier. + + + +
+
+ +
+ +### Scopes + +`projects.estimates:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440010", + "created_at": "2024-01-15T10:40:00Z", + "updated_at": "2024-01-21T09:45:00Z", + "estimate": "550e8400-e29b-41d4-a716-446655440000", + "key": 3, + "value": "3", + "description": "Small", + "external_id": null, + "external_source": null, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/estimate/update-estimate.md b/apps/developer-docs/docs/api-reference/estimate/update-estimate.md new file mode 100644 index 00000000..9a49f576 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/estimate/update-estimate.md @@ -0,0 +1,160 @@ +--- +title: Update an estimate +description: Update an estimate via Plane API. HTTP request format, parameters, scopes, and example responses for update an estimate. +keywords: plane, plane api, rest api, api integration, estimates, update an estimate +--- + +# Update an estimate + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/estimates/ +
+ +
+
+ +Update the estimate for a project. Only fields provided in the request will be updated. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name of the estimate. + + + + + +Description of the estimate. + + + + + +External ID from an external system. + + + + + +External source identifier. + + + +
+
+ +
+ +### Scopes + +`projects.estimates:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-15T10:30:00Z", + "updated_at": "2024-01-21T09:30:00Z", + "name": "Story Points", + "description": "Updated story point scale", + "type": "points", + "last_used": true, + "external_id": null, + "external_source": null, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/create-project-mapping.md b/apps/developer-docs/docs/api-reference/idp-group-sync/create-project-mapping.md new file mode 100644 index 00000000..be0d50dc --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/create-project-mapping.md @@ -0,0 +1,156 @@ +--- +title: Create project group mapping +description: Create a project group mapping via Plane API. HTTP request format, parameters, scopes, and example responses for create project group mapping. +keywords: plane, plane api, rest api, api integration, idp group sync, create project group mapping +--- + +# Create project group mapping + +
+ POST + /api/v1/workspaces/{workspace_slug}/group-sync/project-mappings/ +
+ +
+
+ +Create a new IdP group → project mapping. Use `project` to map to a specific project, or `all_projects: true` to map to all projects in the workspace. These two fields are mutually exclusive. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +The name of the IdP group to map. + + + + + +Project role slug to assign to members of the IdP group (e.g. `member`, `admin`, `guest`). + + + + + +Project identifier to map the group to (e.g. `ENG`). Mutually exclusive with `all_projects`. + + + + + +When `true`, maps the group to all projects in the workspace. Mutually exclusive with `project`. + + + +
+
+ +
+ +### Scopes + +`workspaces.group_sync:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "661f9511-f30c-52e5-b827-557766551111", + "idp_group_name": "engineering", + "project": "ENG", + "all_projects": false, + "role": "member", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/create-workspace-mapping.md b/apps/developer-docs/docs/api-reference/idp-group-sync/create-workspace-mapping.md new file mode 100644 index 00000000..fd2c83ed --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/create-workspace-mapping.md @@ -0,0 +1,139 @@ +--- +title: Create workspace group mapping +description: Create a workspace group mapping via Plane API. HTTP request format, parameters, scopes, and example responses for create workspace group mapping. +keywords: plane, plane api, rest api, api integration, idp group sync, create workspace group mapping +--- + +# Create workspace group mapping + +
+ POST + /api/v1/workspaces/{workspace_slug}/group-sync/workspace-mappings/ +
+ +
+
+ +Create a new IdP group → workspace role mapping. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +The name of the IdP group to map. + + + + + +Workspace role slug to assign to members of the IdP group (e.g. `member`, `admin`). + + + +
+
+ +
+ +### Scopes + +`workspaces.group_sync:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "772g0622-g41d-63f6-c938-668877662222", + "idp_group_name": "leadership", + "role": "admin", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/delete-project-mapping.md b/apps/developer-docs/docs/api-reference/idp-group-sync/delete-project-mapping.md new file mode 100644 index 00000000..7d917028 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/delete-project-mapping.md @@ -0,0 +1,102 @@ +--- +title: Delete project group mapping +description: Delete a project group mapping via Plane API. HTTP request format, parameters, scopes, and example responses for delete project group mapping. +keywords: plane, plane api, rest api, api integration, idp group sync, delete project group mapping +--- + +# Delete project group mapping + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/group-sync/project-mappings/{mapping_id}/ +
+ +
+
+ +Delete an IdP group → project mapping. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the project group mapping. + + + +
+
+ +
+ +### Scopes + +`workspaces.group_sync:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/delete-workspace-mapping.md b/apps/developer-docs/docs/api-reference/idp-group-sync/delete-workspace-mapping.md new file mode 100644 index 00000000..d0442c98 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/delete-workspace-mapping.md @@ -0,0 +1,102 @@ +--- +title: Delete workspace group mapping +description: Delete a workspace group mapping via Plane API. HTTP request format, parameters, scopes, and example responses for delete workspace group mapping. +keywords: plane, plane api, rest api, api integration, idp group sync, delete workspace group mapping +--- + +# Delete workspace group mapping + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/group-sync/workspace-mappings/{mapping_id}/ +
+ +
+
+ +Delete an IdP group → workspace role mapping. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the workspace group mapping. + + + +
+
+ +
+ +### Scopes + +`workspaces.group_sync:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/get-group-sync-config.md b/apps/developer-docs/docs/api-reference/idp-group-sync/get-group-sync-config.md new file mode 100644 index 00000000..d50a0c39 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/get-group-sync-config.md @@ -0,0 +1,170 @@ +--- +title: Get group sync config +description: Get IdP group sync configuration via Plane API. HTTP request format, parameters, scopes, and example responses for get group sync config. +keywords: plane, plane api, rest api, api integration, idp group sync, get group sync config +--- + +# Get group sync config + +
+ GET + /api/v1/workspaces/{workspace_slug}/group-sync/config/ +
+ +
+
+ +Retrieve the IdP group sync configuration for the workspace. Auto-creates the config with defaults on first access. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Response Attributes + +
+ + + +Unique identifier of the config. + + + + + +Whether IdP group sync is enabled for the workspace. + + + + + +Sync group memberships automatically on user login. + + + + + +Automatically remove users from projects or workspace when removed from their IdP group. + + + + + +Allow sync to run outside of login events. + + + + + +The IdP claim key that contains group membership data (e.g. `groups`). + + + + + +Role slug assigned to users when added to the workspace via group sync. + + + + + +The timestamp of when the config was created. + + + + + +The timestamp of when the config was last updated. + + + +
+
+ +
+ +### Scopes + +`workspaces.group_sync:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "is_enabled": true, + "sync_on_login": true, + "auto_remove": false, + "sync_offline": false, + "group_attribute_key": "groups", + "default_workspace_role": "member", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/get-project-mapping.md b/apps/developer-docs/docs/api-reference/idp-group-sync/get-project-mapping.md new file mode 100644 index 00000000..1a2c2d7e --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/get-project-mapping.md @@ -0,0 +1,162 @@ +--- +title: Get project group mapping +description: Get a project group mapping via Plane API. HTTP request format, parameters, scopes, and example responses for get project group mapping. +keywords: plane, plane api, rest api, api integration, idp group sync, get project group mapping +--- + +# Get project group mapping + +
+ GET + /api/v1/workspaces/{workspace_slug}/group-sync/project-mappings/{mapping_id}/ +
+ +
+
+ +Retrieve a single IdP group → project mapping by its ID. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the project group mapping. + + + +
+
+ +
+ +### Response Attributes + +
+ + + +Unique identifier of the mapping. + + + + + +The name of the IdP group. + + + + + +Project identifier the group is mapped to. `null` when `all_projects` is `true`. + + + + + +Whether the group is mapped to all projects in the workspace. + + + + + +Role slug assigned to group members within the project. + + + + + +The timestamp of when the mapping was created. + + + + + +The timestamp of when the mapping was last updated. + + + +
+
+ +
+ +### Scopes + +`workspaces.group_sync:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "661f9511-f30c-52e5-b827-557766551111", + "idp_group_name": "engineering", + "project": "ENG", + "all_projects": false, + "role": "member", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/get-workspace-mapping.md b/apps/developer-docs/docs/api-reference/idp-group-sync/get-workspace-mapping.md new file mode 100644 index 00000000..6deff28f --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/get-workspace-mapping.md @@ -0,0 +1,148 @@ +--- +title: Get workspace group mapping +description: Get a workspace group mapping via Plane API. HTTP request format, parameters, scopes, and example responses for get workspace group mapping. +keywords: plane, plane api, rest api, api integration, idp group sync, get workspace group mapping +--- + +# Get workspace group mapping + +
+ GET + /api/v1/workspaces/{workspace_slug}/group-sync/workspace-mappings/{mapping_id}/ +
+ +
+
+ +Retrieve a single IdP group → workspace role mapping by its ID. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the workspace group mapping. + + + +
+
+ +
+ +### Response Attributes + +
+ + + +Unique identifier of the mapping. + + + + + +The name of the IdP group. + + + + + +Role slug assigned to group members within the workspace. + + + + + +The timestamp of when the mapping was created. + + + + + +The timestamp of when the mapping was last updated. + + + +
+
+ +
+ +### Scopes + +`workspaces.group_sync:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "772g0622-g41d-63f6-c938-668877662222", + "idp_group_name": "leadership", + "role": "admin", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/list-project-mappings.md b/apps/developer-docs/docs/api-reference/idp-group-sync/list-project-mappings.md new file mode 100644 index 00000000..ff497451 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/list-project-mappings.md @@ -0,0 +1,167 @@ +--- +title: List project group mappings +description: List project group mappings via Plane API. HTTP request format, parameters, scopes, and example responses for list project group mappings. +keywords: plane, plane api, rest api, api integration, idp group sync, list project group mappings +--- + +# List project group mappings + +
+ GET + /api/v1/workspaces/{workspace_slug}/group-sync/project-mappings/ +
+ +
+
+ +Retrieve all IdP group → project mappings for the workspace. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Response Attributes + +
+ + + +Unique identifier of the mapping. + + + + + +The name of the IdP group. + + + + + +Project identifier the group is mapped to. `null` when `all_projects` is `true`. + + + + + +Whether the group is mapped to all projects in the workspace. + + + + + +Role slug assigned to group members within the project. + + + + + +The timestamp of when the mapping was created. + + + + + +The timestamp of when the mapping was last updated. + + + +
+
+ +
+ +### Scopes + +`workspaces.group_sync:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "661f9511-f30c-52e5-b827-557766551111", + "idp_group_name": "engineering", + "project": "ENG", + "all_projects": false, + "role": "member", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + }, + { + "id": "772g0622-g41d-63f6-c938-668877662222", + "idp_group_name": "all-staff", + "project": null, + "all_projects": true, + "role": "guest", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/list-workspace-mappings.md b/apps/developer-docs/docs/api-reference/idp-group-sync/list-workspace-mappings.md new file mode 100644 index 00000000..57dabc18 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/list-workspace-mappings.md @@ -0,0 +1,151 @@ +--- +title: List workspace group mappings +description: List workspace group mappings via Plane API. HTTP request format, parameters, scopes, and example responses for list workspace group mappings. +keywords: plane, plane api, rest api, api integration, idp group sync, list workspace group mappings +--- + +# List workspace group mappings + +
+ GET + /api/v1/workspaces/{workspace_slug}/group-sync/workspace-mappings/ +
+ +
+
+ +Retrieve all IdP group → workspace role mappings for the workspace. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Response Attributes + +
+ + + +Unique identifier of the mapping. + + + + + +The name of the IdP group. + + + + + +Role slug assigned to group members within the workspace. + + + + + +The timestamp of when the mapping was created. + + + + + +The timestamp of when the mapping was last updated. + + + +
+
+ +
+ +### Scopes + +`workspaces.group_sync:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "772g0622-g41d-63f6-c938-668877662222", + "idp_group_name": "leadership", + "role": "admin", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + }, + { + "id": "883h1733-h52e-74g7-d049-779988773333", + "idp_group_name": "engineering", + "role": "member", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/overview.md b/apps/developer-docs/docs/api-reference/idp-group-sync/overview.md new file mode 100644 index 00000000..8ef5a25c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/overview.md @@ -0,0 +1,162 @@ +--- +title: Overview +description: Plane IDP Group Sync API overview. Learn about endpoints, request/response format, and how to manage IdP group sync configuration and mappings via REST API. +keywords: plane, plane api, rest api, api integration, idp group sync, group sync config, project mappings, workspace mappings +--- + +# Overview + +IDP Group Sync lets workspace admins programmatically manage how IdP groups map to Plane projects and workspaces. All endpoints are workspace-scoped and require workspace admin permissions. + +
+
+ +### The Group Sync Config Object + +### Attributes + +- `id` _uuid_ + + Unique identifier of the config. + +- `is_enabled` _boolean_ + + Whether IdP group sync is enabled for the workspace. + +- `sync_on_login` _boolean_ + + Sync group memberships automatically on user login. + +- `auto_remove` _boolean_ + + Automatically remove users from projects/workspace when they are removed from the IdP group. + +- `sync_offline` _boolean_ + + Allow sync to run outside of login events. + +- `group_attribute_key` _string_ + + The IdP claim key that contains group membership data (e.g. `groups`). + +- `default_workspace_role` _string_ + + Role slug assigned to users when added to the workspace via group sync. + +- `created_at` _timestamp_ + + The timestamp of when the config was created. + +- `updated_at` _timestamp_ + + The timestamp of when the config was last updated. + +### The Project Group Mapping Object + +### Attributes + +- `id` _uuid_ + + Unique identifier of the mapping. + +- `idp_group_name` _string_ + + The name of the IdP group to map. + +- `project` _string_ + + Project identifier the group is mapped to. Mutually exclusive with `all_projects`. + +- `all_projects` _boolean_ + + When `true`, maps the group to all projects in the workspace. Mutually exclusive with `project`. + +- `role` _string_ + + Role slug assigned to group members within the project. + +- `created_at` _timestamp_ + + The timestamp of when the mapping was created. + +- `updated_at` _timestamp_ + + The timestamp of when the mapping was last updated. + +### The Workspace Group Mapping Object + +### Attributes + +- `id` _uuid_ + + Unique identifier of the mapping. + +- `idp_group_name` _string_ + + The name of the IdP group to map. + +- `role` _string_ + + Role slug assigned to group members within the workspace. + +- `created_at` _timestamp_ + + The timestamp of when the mapping was created. + +- `updated_at` _timestamp_ + + The timestamp of when the mapping was last updated. + +
+
+ + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "is_enabled": true, + "sync_on_login": true, + "auto_remove": false, + "sync_offline": false, + "group_attribute_key": "groups", + "default_workspace_role": "member", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + + + +```json +{ + "id": "661f9511-f30c-52e5-b827-557766551111", + "idp_group_name": "engineering", + "project": "ENG", + "all_projects": false, + "role": "member", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + + + +```json +{ + "id": "772g0622-g41d-63f6-c938-668877662222", + "idp_group_name": "leadership", + "role": "admin", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/update-group-sync-config.md b/apps/developer-docs/docs/api-reference/idp-group-sync/update-group-sync-config.md new file mode 100644 index 00000000..e7a2547b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/update-group-sync-config.md @@ -0,0 +1,179 @@ +--- +title: Update group sync config +description: Update IdP group sync configuration via Plane API. HTTP request format, parameters, scopes, and example responses for update group sync config. +keywords: plane, plane api, rest api, api integration, idp group sync, update group sync config +--- + +# Update group sync config + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/group-sync/config/ +
+ +
+
+ +Update the IdP group sync configuration for the workspace. Supports partial updates — only include fields you want to change. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Enable or disable IdP group sync for the workspace. + + + + + +Sync group memberships automatically on user login. + + + + + +Automatically remove users from projects or workspace when removed from their IdP group. + + + + + +Allow sync to run outside of login events. + + + + + +The IdP claim key that contains group membership data (e.g. `groups`). + + + + + +Role slug assigned to users when added to the workspace via group sync. + + + +
+
+ +
+ +### Scopes + +`workspaces.group_sync:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "is_enabled": true, + "sync_on_login": true, + "auto_remove": false, + "sync_offline": false, + "group_attribute_key": "groups", + "default_workspace_role": "member", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/update-project-mapping.md b/apps/developer-docs/docs/api-reference/idp-group-sync/update-project-mapping.md new file mode 100644 index 00000000..b2b81ea5 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/update-project-mapping.md @@ -0,0 +1,152 @@ +--- +title: Update project group mapping +description: Update a project group mapping via Plane API. HTTP request format, parameters, scopes, and example responses for update project group mapping. +keywords: plane, plane api, rest api, api integration, idp group sync, update project group mapping +--- + +# Update project group mapping + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/group-sync/project-mappings/{mapping_id}/ +
+ +
+
+ +Update an existing IdP group → project mapping. Supports partial updates. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the project group mapping. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +The name of the IdP group to map. + + + + + +Project role slug to assign to members of the IdP group (e.g. `member`, `admin`, `guest`). + + + + + +Project identifier to map the group to (e.g. `ENG`). Mutually exclusive with `all_projects`. + + + + + +When `true`, maps the group to all projects in the workspace. Mutually exclusive with `project`. + + + +
+
+ +
+ +### Scopes + +`workspaces.group_sync:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "661f9511-f30c-52e5-b827-557766551111", + "idp_group_name": "engineering", + "project": "ENG", + "all_projects": false, + "role": "admin", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/idp-group-sync/update-workspace-mapping.md b/apps/developer-docs/docs/api-reference/idp-group-sync/update-workspace-mapping.md new file mode 100644 index 00000000..f8dc6c90 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/idp-group-sync/update-workspace-mapping.md @@ -0,0 +1,138 @@ +--- +title: Update workspace group mapping +description: Update a workspace group mapping via Plane API. HTTP request format, parameters, scopes, and example responses for update workspace group mapping. +keywords: plane, plane api, rest api, api integration, idp group sync, update workspace group mapping +--- + +# Update workspace group mapping + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/group-sync/workspace-mappings/{mapping_id}/ +
+ +
+
+ +Update an existing IdP group → workspace role mapping. Supports partial updates. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the workspace group mapping. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +The name of the IdP group to map. + + + + + +Workspace role slug to assign to members of the IdP group (e.g. `member`, `admin`). + + + +
+
+ +
+ +### Scopes + +`workspaces.group_sync:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "772g0622-g41d-63f6-c938-668877662222", + "idp_group_name": "leadership", + "role": "member", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/inbox-issue/add-inbox-issue.md b/apps/developer-docs/docs/api-reference/inbox-issue/add-inbox-issue.md new file mode 100644 index 00000000..550d199b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/inbox-issue/add-inbox-issue.md @@ -0,0 +1,130 @@ +--- +title: Add intake issue +description: Create intake issue via Plane API. HTTP POST request format, required fields, and example responses. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks +--- + +# Add intake issue + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/inbox-issues/ +
+ +
+
+ +Adds an intake issue in a project + +
+ +### Path Parameters + +
+ + + + + + + + + +
+
+ +
+ +### Body Parameters + +
+ + + +An object containing the issue details, including a `name` field (required). + + + +
+
+ +
+ +### Scopes + +`projects.intakes:write` + +
+ +
+
+ + + + + + + + + +```json +{ + "id": "project-uuid", + "name": "Project Name", + "identifier": "PROJ", + "description": "Project description", + "created_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/inbox-issue/delete-inbox-issue.md b/apps/developer-docs/docs/api-reference/inbox-issue/delete-inbox-issue.md new file mode 100644 index 00000000..5133129c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/inbox-issue/delete-inbox-issue.md @@ -0,0 +1,106 @@ +--- +title: Delete intake issue +description: Delete an intake issue from the inbox via Plane API. Permanently removes the triaged submission. Returns 204 on success. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks +--- + +# Delete intake issue + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/inbox-issues/{issue_id} +
+ +
+
+ +Deletes an intake issue + +
+ +### Path Parameters + +
+ + + + + + + + + + + + + + + + + +
+
+ +
+ +### Scopes + +`projects.intakes:write` + +
+ +
+
+ + + + + + + + + +```json +// 204 No Content +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/inbox-issue/get-inbox-issue-detail.md b/apps/developer-docs/docs/api-reference/inbox-issue/get-inbox-issue-detail.md new file mode 100644 index 00000000..96eacc42 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/inbox-issue/get-inbox-issue-detail.md @@ -0,0 +1,108 @@ +--- +title: Get intake issue detail +description: Get intake issue detail details via Plane API. Retrieve complete information for a specific resource. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks +--- + +# Get intake issue detail + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/inbox-issues/{issue_id} +
+ +
+
+ +Gets the details of an intake issue + +
+ +### Path Parameters + +
+ + + + + + + + + + + + + +
+
+ +
+ +### Scopes + +`projects.intakes:read` + +
+ +
+
+ + + + + + + + + +```json +{ + "id": "project-uuid", + "name": "Project Name", + "identifier": "PROJ", + "description": "Project description", + "created_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/inbox-issue/list-inbox-issues.md b/apps/developer-docs/docs/api-reference/inbox-issue/list-inbox-issues.md new file mode 100644 index 00000000..f9984fff --- /dev/null +++ b/apps/developer-docs/docs/api-reference/inbox-issue/list-inbox-issues.md @@ -0,0 +1,104 @@ +--- +title: List intake issues +description: List intake issues via Plane API. HTTP GET request with pagination, filtering, and query parameters. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks +--- + +# List intake issues + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/inbox-issues/ +
+ +
+
+ +Gets all the intake issue of a project + +
+ +### Path Parameters + +
+ + + + + + + + + +
+
+ +
+ +### Scopes + +`projects.intakes:read` + +
+ +
+
+ + + + + + + + + +```json +{ + "id": "project-uuid", + "name": "Project Name", + "identifier": "PROJ", + "description": "Project description", + "created_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/inbox-issue/overview.md b/apps/developer-docs/docs/api-reference/inbox-issue/overview.md new file mode 100644 index 00000000..f621d3e3 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/inbox-issue/overview.md @@ -0,0 +1,129 @@ +--- +title: Overview +description: Plane Inbox-Issue API overview. Learn about endpoints, request/response format, and how to work with inbox-issue via REST API. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks +--- + +# Overview + +::: warning +**Deprecation notice** + +We are deprecating all `/api/v1/.../inbox-issues/` endpoints in favor of `/api/v1/.../intake-issues/`. + +**End of support** +31st March 2025 + +**What you need to do** +To ensure uninterrupted service, replace all `/inbox-issues/` references with `/intake-issues/` in your codebase before the support end date. +::: + +To enable the Intake feature, the user can hit a PATCH request on the project api with the body as + +```http +GET /api/v1/workspaces/{workspace_slug}/projects/{project_id}/inbox-issues/ +POST /api/v1/workspaces/{workspace_slug}/projects/{project_id}/inbox-issues/ +GET /api/v1/workspaces/{workspace_slug}/projects/{project_id}/inbox-issues/{work_item_id}/ +PATCH /api/v1/workspaces/{workspace_slug}/projects/{project_id}/inbox-issues/{work_item_id}/ +DELETE /api/v1/workspaces/{workspace_slug}/projects/{project_id}/inbox-issues/{work_item_id}/ +``` + +``` +{ + inbox_view:true, +} +``` + +To create an Intake issue, the payload should be sent in the below format + +```json +{ + "issue": { + "name": "Snoozed Issue 2", + "priority": "high" + } +} +``` + +
+
+ +### Intake issue object + +**Attribute** + +- `created_at` _timestamp_ + + The timestamp of the time when the project was created + +- `updated_at` _timestamp_ + + The timestamp of the time when the project was last updated + +- `status` + + the status of the issue can be in above mentioned status + - \-2 - Pending + - \-1 - Rejected + - 0 - Snoozed + - 1 - Accepted + - 2 - Duplicate + +- `snoozed_till` + + The time untill the issue is snoozed. + +- `source` + + The source describes the type intake issue from + +- `created_by` , `updated_by` _uuid_ + + These values are auto saved and represent the id of the user that created or updated the module + +- `Project` uuid + + It contains projects uuid which is automatically saved. + +- `Workspace` uuid + + It contains workspace uuid which is automatically saved. + +- `inbox` + + intake id of the issue + +- `issue` + + issue id of the issue + +- `duplicate_to` + + Id of the issue of which the current issue is duplicate of. + +
+
+ + + +```json +{ + "id": "0de4d6d1-fdc7-4849-8080-dc379ab210e3", + "pending_issue_count": 0, + "created_at": "2023-11-21T07:32:26.072634Z", + "updated_at": "2023-11-21T07:32:26.072648Z", + "name": "a dummy project with Intake", + "description": "", + "is_default": true, + "view_props": {}, + "created_by": "0649cb9d-05c8-4ef4-8e8b-d108ccddd42c", + "updated_by": "0649cb9d-05c8-4ef4-8e8b-d108ccddd42c", + "project": "6436c4ae-fba7-45dc-ad4a-5440e17cb1b2", + "workspace": "c467e125-59e3-44ec-b5ee-f9c1e138c611" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/inbox-issue/update-inbox-issue-detail.md b/apps/developer-docs/docs/api-reference/inbox-issue/update-inbox-issue-detail.md new file mode 100644 index 00000000..e69cfeed --- /dev/null +++ b/apps/developer-docs/docs/api-reference/inbox-issue/update-inbox-issue-detail.md @@ -0,0 +1,134 @@ +--- +title: Update intake issue detail +description: Update intake issue detail via Plane API. HTTP PATCH request format, editable fields, and example responses. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks +--- + +# Update intake issue detail + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/inbox-issues/{issue_id} +
+ +
+
+ +Updates the details of an intake issue + +
+ +### Path Parameters + +
+ + + + + + + + + + + + + +
+
+ +
+ +### Body Parameters + +
+ + + +An object containing the issue details to update, including an optional `name` field. + + + +
+
+ +
+ +### Scopes + +`projects.intakes:write` + +
+ +
+
+ + + + + + + + + +```json +{ + "id": "project-uuid", + "name": "Project Name", + "identifier": "PROJ", + "description": "Project description", + "created_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/initiative/add-epics-to-initiative.md b/apps/developer-docs/docs/api-reference/initiative/add-epics-to-initiative.md new file mode 100644 index 00000000..8148ceee --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/add-epics-to-initiative.md @@ -0,0 +1,138 @@ +--- +title: Add epics to initiative +description: Add epics to initiative via Plane API. HTTP request format, parameters, scopes, and example responses for add epics to initiative. +keywords: plane, plane api, rest api, api integration, initiative, add epics to initiative +--- + +# Add epics to initiative + +
+ POST + /api/v1/workspaces/{workspace_slug}/initiatives/{initiative_id}/epics/ +
+ +
+
+ +Add epics to an initiative by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Epic ids. + + + +
+
+ +
+ +### Scopes + +`initiatives.epics:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/add-initiative-label.md b/apps/developer-docs/docs/api-reference/initiative/add-initiative-label.md new file mode 100644 index 00000000..64d133dd --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/add-initiative-label.md @@ -0,0 +1,156 @@ +--- +title: Create an initiative label +description: Create an initiative label via Plane API. HTTP request format, parameters, scopes, and example responses for create an initiative label. +keywords: plane, plane api, rest api, api integration, initiative, create an initiative label +--- + +# Create an initiative label + +
+ POST + /api/v1/workspaces/{workspace_slug}/initiatives/labels/ +
+ +
+
+ +Create a new initiative label in the workspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Color. + + + + + +Sort order. + + + +
+
+ +
+ +### Scopes + +`initiatives.labels:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/add-initiative.md b/apps/developer-docs/docs/api-reference/initiative/add-initiative.md new file mode 100644 index 00000000..57c8a6ae --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/add-initiative.md @@ -0,0 +1,228 @@ +--- +title: Create an initiative +description: Create an initiative via Plane API. HTTP request format, parameters, scopes, and example responses for create an initiative. +keywords: plane, plane api, rest api, api integration, initiative, create an initiative +--- + +# Create an initiative + +
+ POST + /api/v1/workspaces/{workspace_slug}/initiatives/ +
+ +
+
+ +Create a new initiative in the workspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Description html. + + + + + +Description stripped. + + + + + +Start date. + + + + + +End date. + + + + + +Logo props. + + + + + +- `DRAFT` - Draft +- `PLANNED` - Planned +- `ACTIVE` - Active +- `COMPLETED` - Completed +- `CLOSED` - Closed + + + + + +Archived at. + + + + + +Created by. + + + + + +Updated by. + + + + + +Lead. + + + +
+
+ +
+ +### Scopes + +`initiatives:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/add-labels-to-initiative.md b/apps/developer-docs/docs/api-reference/initiative/add-labels-to-initiative.md new file mode 100644 index 00000000..158066cd --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/add-labels-to-initiative.md @@ -0,0 +1,139 @@ +--- +title: Add labels to initiative +description: Add labels to initiative via Plane API. HTTP request format, parameters, scopes, and example responses for add labels to initiative. +keywords: plane, plane api, rest api, api integration, initiative, add labels to initiative +--- + +# Add labels to initiative + +
+ POST + /api/v1/workspaces/{workspace_slug}/initiatives/{initiative_id}/labels/ +
+ +
+
+ +Add labels to an initiative by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Label ids. + + + +
+
+ +
+ +### Scopes + +`initiatives.labels:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/add-projects-to-initiative.md b/apps/developer-docs/docs/api-reference/initiative/add-projects-to-initiative.md new file mode 100644 index 00000000..e39af227 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/add-projects-to-initiative.md @@ -0,0 +1,143 @@ +--- +title: Add projects to initiative +description: Add projects to initiative via Plane API. HTTP request format, parameters, scopes, and example responses for add projects to initiative. +keywords: plane, plane api, rest api, api integration, initiative, add projects to initiative +--- + +# Add projects to initiative + +
+ POST + /api/v1/workspaces/{workspace_slug}/initiatives/{initiative_id}/projects/ +
+ +
+
+ +Add projects to an initiative by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Project ids. + + + +
+
+ +
+ +### Scopes + +`initiatives.projects:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "identifier": "PROJ-123", + "network": 2, + "project_lead": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/delete-initiative-label.md b/apps/developer-docs/docs/api-reference/initiative/delete-initiative-label.md new file mode 100644 index 00000000..06e05abf --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/delete-initiative-label.md @@ -0,0 +1,108 @@ +--- +title: Delete an initiative label +description: Delete an initiative label via Plane API. HTTP request format, parameters, scopes, and example responses for delete an initiative label. +keywords: plane, plane api, rest api, api integration, initiative, delete an initiative label +--- + +# Delete an initiative label + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/initiatives/labels/{label_id}/ +
+ +
+
+ +Delete an initiative label by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative label. + + + + + +The unique identifier of the label. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`initiatives.labels:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/delete-initiative.md b/apps/developer-docs/docs/api-reference/initiative/delete-initiative.md new file mode 100644 index 00000000..09059883 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/delete-initiative.md @@ -0,0 +1,108 @@ +--- +title: Delete an initiative +description: Delete an initiative via Plane API. HTTP request format, parameters, scopes, and example responses for delete an initiative. +keywords: plane, plane api, rest api, api integration, initiative, delete an initiative +--- + +# Delete an initiative + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/initiatives/{initiative_id}/ +
+ +
+
+ +Delete an initiative by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative. + + + + + +The unique identifier of the initiative. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`initiatives:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/get-initiative-detail.md b/apps/developer-docs/docs/api-reference/initiative/get-initiative-detail.md new file mode 100644 index 00000000..7d4d4ef9 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/get-initiative-detail.md @@ -0,0 +1,114 @@ +--- +title: Retrieve an initiative +description: Retrieve an initiative via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve an initiative. +keywords: plane, plane api, rest api, api integration, initiative, retrieve an initiative +--- + +# Retrieve an initiative + +
+ GET + /api/v1/workspaces/{workspace_slug}/initiatives/{initiative_id}/ +
+ +
+
+ +Retrieve an initiative by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative. + + + + + +The unique identifier of the initiative. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`initiatives:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/get-initiative-label-detail.md b/apps/developer-docs/docs/api-reference/initiative/get-initiative-label-detail.md new file mode 100644 index 00000000..d4b94921 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/get-initiative-label-detail.md @@ -0,0 +1,115 @@ +--- +title: Retrieve an initiative label +description: Retrieve an initiative label via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve an initiative label. +keywords: plane, plane api, rest api, api integration, initiative, retrieve an initiative label +--- + +# Retrieve an initiative label + +
+ GET + /api/v1/workspaces/{workspace_slug}/initiatives/labels/{label_id}/ +
+ +
+
+ +Retrieve an initiative label by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative label. + + + + + +The unique identifier of the label. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`initiatives.labels:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/list-initiative-epics.md b/apps/developer-docs/docs/api-reference/initiative/list-initiative-epics.md new file mode 100644 index 00000000..6d528453 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/list-initiative-epics.md @@ -0,0 +1,154 @@ +--- +title: List all epics for an initiative +description: List all epics for an initiative via Plane API. HTTP request format, parameters, scopes, and example responses for list all epics for an initiative. +keywords: plane, plane api, rest api, api integration, initiative, list all epics for an initiative +--- + +# List all epics for an initiative + +
+ GET + /api/v1/workspaces/{workspace_slug}/initiatives/{initiative_id}/epics/ +
+ +
+
+ +List all epics associated with an initiative + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`initiatives.epics:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "priority": "high", + "sequence_id": 123, + "state": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "group": "started" + }, + "assignees": [], + "labels": [], + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/list-initiative-labels-for-initiative.md b/apps/developer-docs/docs/api-reference/initiative/list-initiative-labels-for-initiative.md new file mode 100644 index 00000000..ebc55ad2 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/list-initiative-labels-for-initiative.md @@ -0,0 +1,145 @@ +--- +title: List all labels for an initiative +description: List all labels for an initiative via Plane API. HTTP request format, parameters, scopes, and example responses for list all labels for an initiative. +keywords: plane, plane api, rest api, api integration, initiative, list all labels for an initiative +--- + +# List all labels for an initiative + +
+ GET + /api/v1/workspaces/{workspace_slug}/initiatives/{initiative_id}/labels/ +
+ +
+
+ +List all labels associated with an initiative + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`initiatives.labels:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/list-initiative-labels.md b/apps/developer-docs/docs/api-reference/initiative/list-initiative-labels.md new file mode 100644 index 00000000..c955f484 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/list-initiative-labels.md @@ -0,0 +1,141 @@ +--- +title: List all initiative labels +description: List all initiative labels via Plane API. HTTP request format, parameters, scopes, and example responses for list all initiative labels. +keywords: plane, plane api, rest api, api integration, initiative, list all initiative labels +--- + +# List all initiative labels + +
+ GET + /api/v1/workspaces/{workspace_slug}/initiatives/labels/ +
+ +
+
+ +List all initiative labels in the workspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`initiatives.labels:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z" + } + ] + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/list-initiative-projects.md b/apps/developer-docs/docs/api-reference/initiative/list-initiative-projects.md new file mode 100644 index 00000000..ea2ff0ec --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/list-initiative-projects.md @@ -0,0 +1,146 @@ +--- +title: List all projects in an initiative +description: List all projects in an initiative via Plane API. HTTP request format, parameters, scopes, and example responses for list all projects in an initiative. +keywords: plane, plane api, rest api, api integration, initiative, list all projects in an initiative +--- + +# List all projects in an initiative + +
+ GET + /api/v1/workspaces/{workspace_slug}/initiatives/{initiative_id}/projects/ +
+ +
+
+ +List all projects associated with an initiative + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`initiatives.projects:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "identifier": "PROJ-123", + "network": 2 + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/list-initiatives.md b/apps/developer-docs/docs/api-reference/initiative/list-initiatives.md new file mode 100644 index 00000000..cdeafd51 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/list-initiatives.md @@ -0,0 +1,141 @@ +--- +title: List all initiatives +description: List all initiatives via Plane API. HTTP request format, parameters, scopes, and example responses for list all initiatives. +keywords: plane, plane api, rest api, api integration, initiative, list all initiatives +--- + +# List all initiatives + +
+ GET + /api/v1/workspaces/{workspace_slug}/initiatives/ +
+ +
+
+ +List all initiatives in the workspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`initiatives:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z" + } + ] + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/overview.md b/apps/developer-docs/docs/api-reference/initiative/overview.md new file mode 100644 index 00000000..ea352771 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/overview.md @@ -0,0 +1,108 @@ +--- +title: Overview +description: Plane Initiative API overview. Learn about endpoints, request/response format, and how to work with initiative via REST API. +keywords: plane, plane api, rest api, api integration, initiatives, roadmap, planning +--- + +# Overview + +Initiatives are high-level strategic goals that help organize and track work across multiple projects, epics, and work items, providing a way to group related work and measure progress toward larger objectives. + +[Learn more about Initiatives](https://docs.plane.so/core-concepts/projects/initiatives) + +
+
+ +## The Initiatives Object + +### Attributes + +- `id` _uuid_ + + Unique identifier for the initiative + +- `name` _string_ **(required)** + + Name of the initiative + +- `description` _string_ + + Plain text description of the initiative + +- `description_html` _string_ + + HTML description of the initiative + +- `description_stripped` _string_ + + Stripped version of the HTML description + +- `description_binary` _string_ + + Binary description of the initiative + +- `lead` _uuid_ + + User ID of the initiative lead + +- `start_date` _date_ + + Start date of the initiative in YYYY-MM-DD format + +- `end_date` _date_ + + End date of the initiative in YYYY-MM-DD format + +- `logo_props` _object_ + + Logo properties for the initiative + +- `state` _string_ + + State of the initiative. Can be: DRAFT, PLANNED, ACTIVE, COMPLETED, CLOSED + +- `workspace` _uuid_ + + Workspace UUID which is automatically saved + +- `created_at` _timestamp_ + + The timestamp when the initiative was created + +- `updated_at` _timestamp_ + + The timestamp when the initiative was last updated + +- `created_by` _uuid_ + + ID of the user who created the initiative + +- `updated_by` _uuid_ + + ID of the user who last updated the initiative + +
+
+ + + +```json +{ + "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "created_at": "2023-11-19T11:56:55.176802Z", + "updated_at": "2023-11-19T11:56:55.176809Z", + "name": "Q1 Product Launch", + "description": "Launch new product features in Q1", + "description_html": "

Launch new product features in Q1

", + "lead": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "start_date": "2024-01-01", + "end_date": "2024-03-31", + "state": "ACTIVE", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" +} +``` + +
+ +
+
diff --git a/apps/developer-docs/docs/api-reference/initiative/remove-epics-from-initiative.md b/apps/developer-docs/docs/api-reference/initiative/remove-epics-from-initiative.md new file mode 100644 index 00000000..36f3931a --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/remove-epics-from-initiative.md @@ -0,0 +1,102 @@ +--- +title: Remove epics from initiative +description: Remove epics from initiative via Plane API. HTTP request format, parameters, scopes, and example responses for remove epics from initiative. +keywords: plane, plane api, rest api, api integration, initiative, remove epics from initiative +--- + +# Remove epics from initiative + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/initiatives/{initiative_id}/epics/ +
+ +
+
+ +Remove epics from an initiative by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`initiatives.epics:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/remove-labels-from-initiative.md b/apps/developer-docs/docs/api-reference/initiative/remove-labels-from-initiative.md new file mode 100644 index 00000000..1d114b13 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/remove-labels-from-initiative.md @@ -0,0 +1,102 @@ +--- +title: Remove labels from initiative +description: Remove labels from initiative via Plane API. HTTP request format, parameters, scopes, and example responses for remove labels from initiative. +keywords: plane, plane api, rest api, api integration, initiative, remove labels from initiative +--- + +# Remove labels from initiative + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/initiatives/{initiative_id}/labels/ +
+ +
+
+ +Remove labels from an initiative by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`initiatives.labels:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/remove-projects-from-initiative.md b/apps/developer-docs/docs/api-reference/initiative/remove-projects-from-initiative.md new file mode 100644 index 00000000..0b6d2652 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/remove-projects-from-initiative.md @@ -0,0 +1,102 @@ +--- +title: Remove projects from initiative +description: Remove projects from initiative via Plane API. HTTP request format, parameters, scopes, and example responses for remove projects from initiative. +keywords: plane, plane api, rest api, api integration, initiative, remove projects from initiative +--- + +# Remove projects from initiative + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/initiatives/{initiative_id}/projects/ +
+ +
+
+ +Remove projects from an initiative by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`initiatives.projects:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/update-initiative-detail.md b/apps/developer-docs/docs/api-reference/initiative/update-initiative-detail.md new file mode 100644 index 00000000..bf77386d --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/update-initiative-detail.md @@ -0,0 +1,243 @@ +--- +title: Update an initiative +description: Update an initiative via Plane API. HTTP request format, parameters, scopes, and example responses for update an initiative. +keywords: plane, plane api, rest api, api integration, initiative, update an initiative +--- + +# Update an initiative + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/initiatives/{initiative_id}/ +
+ +
+
+ +Update an initiative by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative. + + + + + +The unique identifier of the initiative. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Description html. + + + + + +Description stripped. + + + + + +Start date. + + + + + +End date. + + + + + +Logo props. + + + + + +- `DRAFT` - Draft +- `PLANNED` - Planned +- `ACTIVE` - Active +- `COMPLETED` - Completed +- `CLOSED` - Closed + + + + + +Archived at. + + + + + +Created by. + + + + + +Updated by. + + + + + +Lead. + + + +
+
+ +
+ +### Scopes + +`initiatives:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/initiative/update-initiative-label-detail.md b/apps/developer-docs/docs/api-reference/initiative/update-initiative-label-detail.md new file mode 100644 index 00000000..bd00aa16 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/initiative/update-initiative-label-detail.md @@ -0,0 +1,168 @@ +--- +title: Update an initiative label +description: Update an initiative label via Plane API. HTTP request format, parameters, scopes, and example responses for update an initiative label. +keywords: plane, plane api, rest api, api integration, initiative, update an initiative label +--- + +# Update an initiative label + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/initiatives/labels/{label_id}/ +
+ +
+
+ +Update an initiative label by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the initiative label. + + + + + +The unique identifier of the label. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Color. + + + + + +Sort order. + + + +
+
+ +
+ +### Scopes + +`initiatives.labels:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/intake-issue/add-intake-issue.md b/apps/developer-docs/docs/api-reference/intake-issue/add-intake-issue.md new file mode 100644 index 00000000..6da9e384 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/intake-issue/add-intake-issue.md @@ -0,0 +1,155 @@ +--- +title: Create an intake work item +description: Create an intake work item via Plane API. HTTP request format, parameters, scopes, and example responses for create an intake work item. +keywords: plane, plane api, rest api, api integration, intake issue, create an intake work item +--- + +# Create an intake work item + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/intake-issues/ +
+ +
+
+ +Submit a new work item to the project's intake queue for review and triage. Automatically creates the work item with default triage state and tracks activity. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Issue data for the intake issue + + + +
+
+ +
+ +### Scopes + +`projects.intakes:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": 0, + "source": "in_app", + "issue": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "priority": "medium", + "sequence_id": 124 + }, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/intake-issue/delete-intake-issue.md b/apps/developer-docs/docs/api-reference/intake-issue/delete-intake-issue.md new file mode 100644 index 00000000..35825de1 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/intake-issue/delete-intake-issue.md @@ -0,0 +1,108 @@ +--- +title: Delete an intake work item +description: Delete an intake work item via Plane API. HTTP request format, parameters, scopes, and example responses for delete an intake work item. +keywords: plane, plane api, rest api, api integration, intake issue, delete an intake work item +--- + +# Delete an intake work item + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/intake-issues/{work_item_id}/ +
+ +
+
+ +Permanently remove an intake work item from the triage queue. Also deletes the underlying work item if it hasn't been accepted yet. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.intakes:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/intake-issue/get-intake-issue-detail.md b/apps/developer-docs/docs/api-reference/intake-issue/get-intake-issue-detail.md new file mode 100644 index 00000000..32686783 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/intake-issue/get-intake-issue-detail.md @@ -0,0 +1,123 @@ +--- +title: Retrieve an intake work item +description: Retrieve an intake work item via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve an intake work item. +keywords: plane, plane api, rest api, api integration, intake issue, retrieve an intake work item +--- + +# Retrieve an intake work item + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/intake-issues/{work_item_id}/ +
+ +
+
+ +Retrieve details of a specific intake work item. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.intakes:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": 0, + "source": "in_app", + "issue": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "priority": "medium", + "sequence_id": 124 + }, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/intake-issue/list-intake-issues.md b/apps/developer-docs/docs/api-reference/intake-issue/list-intake-issues.md new file mode 100644 index 00000000..a718d547 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/intake-issue/list-intake-issues.md @@ -0,0 +1,156 @@ +--- +title: List all intake work items +description: List all intake work items via Plane API. HTTP request format, parameters, scopes, and example responses for list all intake work items. +keywords: plane, plane api, rest api, api integration, intake issue, list all intake work items +--- + +# List all intake work items + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/intake-issues/ +
+ +
+
+ +Retrieve all work items in the project's intake queue. Returns paginated results when listing all intake work items. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.intakes:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/intake-issue/overview.md b/apps/developer-docs/docs/api-reference/intake-issue/overview.md new file mode 100644 index 00000000..1146b8dc --- /dev/null +++ b/apps/developer-docs/docs/api-reference/intake-issue/overview.md @@ -0,0 +1,115 @@ +--- +title: Overview +description: Plane Intake-Issue API overview. Learn about endpoints, request/response format, and how to work with intake-issue via REST API. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks, intake, triage, submissions +--- + +# Overview + +Intake allows guests to create work items that admins and members can review and move into a project. + +[Learn more about Intake](https://docs.plane.so/core-concepts/intake) + +## Enable Intake + +To enable the Intake feature, the user can hit a PATCH request on the project api with the body as + +``` +{ + intake_view:true, +} +``` + +To create an Intake work item, the payload should be sent in the below format + +```json +{ + "issue": { + "name": "Snoozed task 2", + "priority": "high" + } +} +``` + +
+
+ +### The Intake Object + +**Attribute** + +- `created_at` _timestamp_ + + The timestamp of the time when the project was created + +- `updated_at` _timestamp_ + + The timestamp of the time when the project was last updated + +- `status` + + the status of the work item can be in above mentioned status + - \-2 - Pending + - \-1 - Rejected + - 0 - Snoozed + - 1 - Accepted + - 2 - Duplicate + +- `snoozed_till` + + The time untill the work item is snoozed. + +- `source` + + The source describes the type of intake from + +- `created_by` , `updated_by` _uuid_ + + These values are auto saved and represent the id of the user that created or updated the module + +- `Project` uuid + + It contains projects uuid which is automatically saved. + +- `Workspace` uuid + + It contains workspace uuid which is automatically saved. + +- `inbox` + + intake id of the work item + +- `issue` + + work item id of the work item + +- `duplicate_to` + + Id of the work item of which the current work item is duplicate of. + +
+
+ + + +```json +{ + "id": "0de4d6d1-fdc7-4849-8080-dc379ab210e3", + "pending_issue_count": 0, + "created_at": "2023-11-21T07:32:26.072634Z", + "updated_at": "2023-11-21T07:32:26.072648Z", + "name": "a dummy project with Intake", + "description": "", + "is_default": true, + "view_props": {}, + "created_by": "0649cb9d-05c8-4ef4-8e8b-d108ccddd42c", + "updated_by": "0649cb9d-05c8-4ef4-8e8b-d108ccddd42c", + "project": "6436c4ae-fba7-45dc-ad4a-5440e17cb1b2", + "workspace": "c467e125-59e3-44ec-b5ee-f9c1e138c611" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/intake-issue/update-intake-issue-detail.md b/apps/developer-docs/docs/api-reference/intake-issue/update-intake-issue-detail.md new file mode 100644 index 00000000..c933e7f7 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/intake-issue/update-intake-issue-detail.md @@ -0,0 +1,198 @@ +--- +title: Update an intake work item +description: Update an intake work item via Plane API. HTTP request format, parameters, scopes, and example responses for update an intake work item. +keywords: plane, plane api, rest api, api integration, intake issue, update an intake work item +--- + +# Update an intake work item + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/intake-issues/{work_item_id}/ +
+ +
+
+ +Modify an existing intake work item's properties or status for triage processing. Supports status changes like accept, reject, or mark as duplicate. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +- `-2` - Pending +- `-1` - Rejected +- `0` - Snoozed +- `1` - Accepted +- `2` - Duplicate + + + + + +Snoozed till. + + + + + +Duplicate to. + + + + + +Source. + + + + + +Source email. + + + + + +Issue data to update in the intake issue + + + +
+
+ +
+ +### Scopes + +`projects.intakes:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": 0, + "source": "in_app", + "issue": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "priority": "medium", + "sequence_id": 124 + }, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/introduction.md b/apps/developer-docs/docs/api-reference/introduction.md new file mode 100644 index 00000000..b9bf82ac --- /dev/null +++ b/apps/developer-docs/docs/api-reference/introduction.md @@ -0,0 +1,287 @@ +--- +title: Plane API Documentation +description: Complete REST API reference for Plane. Learn authentication, HTTP methods, pagination, rate limiting, and how to integrate Plane with your applications programmatically. +keywords: plane api, rest api reference, plane api authentication, api integration, plane api key, plane endpoints, work items api, projects api +--- + +# Plane API Documentation + +The Plane API is organized around REST. Our API has predictable resource-oriented URLs, accepts application/json request bodies, returns JSON responses, and uses standard HTTP response codes, authentication, and verbs. + +## Base URL + +All requests to the Plane Cloud API must be made to the following base URL: + +``` +https://api.plane.so/ +``` + +This URL should be prefixed to all endpoint paths. + +For example, to retrieve all projects in a workspace: + +``` +GET https://api.plane.so/api/v1/workspaces/{workspace_slug}/projects/ +``` + +::: tip +If you're using a self-hosted instance of Plane, your API base URL will differ based on your custom domain and setup. +::: + +## Authentication + +Our APIs use a key for authentication. The API key should be included in the header of each request to verify the client's identity and permissions. The key should be passed as the value of the `X-API-Key` header. + +::: info +You must have a Plane account or be registered to your instance to generate a key. +::: + +### Generating an API Key + +1. Log into your Plane account and go to **Profile Settings**. +2. Go to **Personal Access Tokens** in the list of tabs available. +3. Click `Add personal access token`. +4. Choose a title and description so you know why you are creating this token and where you will use it. +5. Choose an expiry if you want this to stop working after a point. + +### Using the API Key + +To authenticate an API request, include your API key in the request header: + +``` +X-API-Key: +``` + +It is important to keep your API key confidential to prevent unauthorized access to your account. + +### Using an OAuth Token + +If your application uses [OAuth](/dev-tools/build-plane-app/overview) to obtain user authorization (for example, a Plane app you've built), you can authenticate API requests with the OAuth access token. Include the token in the `Authorization` header as a Bearer token: + +``` +Authorization: Bearer +``` + +The access token is scoped to the permissions (scopes) the user granted when authorizing your app. See [OAuth scopes](/dev-tools/build-plane-app/oauth-scopes) for the full list of available scopes. + +### Example of an Authenticated API Request + +**Using an API key:** + +``` +GET /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/ +Headers: + X-API-Key: plane_api_ +``` + +**Using an OAuth token:** + +``` +GET /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/ +Headers: + Authorization: Bearer +``` + +**Response:** + +```json +{ + [ ... ] +} +``` + +### Error Handling + +- **Missing API Key**: If the `X-API-Key` header is not included, the API will return an error indicating that authentication is required. +- **Invalid API Key**: If the provided API key is invalid or expired, the API will return an error message indicating an authentication failure. + +### Security Recommendations + +- **Keep the API Key Secret**: Treat your API key like a password. Do not share it or expose it in client-side code. +- **Regenerate Key If Compromised**: If you suspect that your API key has been compromised, generate a new one immediately and update your applications. + +--- + +## HTTP Methods + +HTTP defines a set of request methods, also known as HTTP verbs, to indicate the desired action for a given resource. + +| Verb | Description | Example | +| ------ | --------------------------------------------------- | ------------------------------- | +| GET | Requests a representation of the specified resource | Fetch all issues from a project | +| POST | Submits an entity to the specified resource | Create a project | +| DELETE | Deletes the specified resource | Delete a module-issue | +| PATCH | Applies partial modifications to a resource | Edit a module | + +## Status Codes + +### Success Responses + +| Status Code | Description | +| -------------- | --------------------------------------------------------------------------------------------------- | +| 200 OK | The request succeeded, and a new resource was created, generally sent in GET or PATCH requests. | +| 201 Created | The request is succeeded, and a new resource was created, generally sent in POST or PATCH requests. | +| 204 No Content | The request is succeeded, and no body is sent, generally comes from the DELETE request. | + +### Error Responses + +| Status Code | Description | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 400 Bad Request | The server cannot or will not process the request due to something that is perceived to be a client error. | +| 401 Unauthorized | Although the HTTP standard specifies "unauthorized", semantically, this response means "unauthenticated". That is, the client must authenticate itself to get the requested response. | +| 404 Not Found | The server cannot find the requested resource. This means the URL is not recognized. | +| 429 Throttling Error | The server is processing too many requests at once and is unable to process your request. Retry the request after some time. | +| 500 Internal Server Error | The server has encountered a situation it does not know how to handle. | +| 502 Bad Gateway | This error response means that the server got an invalid response while working as a gateway to get a response needed to handle the request. | +| 503 Service Unavailable | The server is not ready to handle the request. Common causes are a server that is down for maintenance or is overloaded. | +| 504 Gateway Timeout | This error response is given when the server acts as a gateway and cannot get a timely response. | + +--- + +## Pagination + +### Overview + +This API implements a cursor-based pagination system, allowing clients to efficiently navigate through large datasets. The system uses a cursor parameter to manage the position and direction of pagination. + +### Cursor Format + +The cursor is a string formatted as `value:offset:is_prev`, where: + +- `value` represents the page size (number of items per page). +- `offset` is the current page number (starting from 0). +- `is_prev` indicates whether the cursor is moving to the previous page (`1`) or to the next page (`0`). + +### Request Parameters + +- **`per_page` (optional)**: Number of items to display per page. Defaults to 100. The maximum allowed value specified by the server is 100. +- **`cursor` (optional)**: Cursor string to navigate to a specific page. If not provided, pagination starts from the first page. + +### Response Fields + +The paginated response includes the following fields: + +| Field | Description | +| ------------------- | -------------------------------------------------------------------- | +| `next_cursor` | Cursor string for the next page. | +| `prev_cursor` | Cursor string for the previous page. | +| `next_page_results` | Boolean indicating if there are more results after the current page. | +| `prev_page_results` | Boolean indicating if there are results before the current page. | +| `count` | Total number of items on the current page. | +| `total_pages` | Estimated total number of pages. | +| `total_results` | Total number of items across all pages. | +| `extra_stats` | Additional statistics, if any. | +| `results` | Array of items for the current page. | + +### Example: Fetching the First Page + +**Request:** + +``` +GET /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/?per_page=20 +``` + +**Response:** + +```json +{ + "next_cursor": "20:1:0", + "prev_cursor": "", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 50, + "total_results": 1000, + "extra_stats": {}, + "results": [ ... ] +} +``` + +### Example: Fetching the Next Page + +**Request:** + +``` +GET /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items?per_page=20&cursor=20:1:0 +``` + +**Response:** + +```json +{ + "next_cursor": "20:2:0", + "prev_cursor": "20:0:1", + "next_page_results": true, + "prev_page_results": true, + "count": 20, + "total_pages": 50, + "total_results": 1000, + "extra_stats": {}, + "results": [ ... ] +} +``` + +--- + +## Rate Limiting + +### Overview + +To ensure fair usage and maintain the quality of service for all users, our API implements rate limiting. Rate limiting restricts the number of requests a client can make within a certain time frame. + +### Rate Limit Details + +- **Limit**: Each client is limited to 60 requests per minute. +- **Reset Interval**: The rate limit counter resets every minute. +- **Scope of Limitation**: The rate limit applies to all requests made with a given API key. + +### Identifying Your Rate Limit Status + +Rate limit status is communicated in the response headers of each API request: + +- **`X-RateLimit-Remaining`**: The number of requests remaining in the current rate limit window. +- **`X-RateLimit-Reset`**: The time at which the current rate limit window resets (in UTC epoch seconds). + +``` +X-RateLimit-Remaining: 45 +X-RateLimit-Reset: 1700327957 +``` + +--- + +## Fields and Expand Query Parameters + +Our API provides flexible data retrieval capabilities through two powerful query parameters: `fields` and `expand`. These parameters allow clients to tailor the response data to their specific needs, optimizing both the payload size and the clarity of the response. + +### Fields Parameter + +The `fields` parameter enables clients to selectively retrieve only a subset of fields for a given resource. This is particularly useful for minimizing response size and bandwidth consumption, especially when the client requires only specific pieces of data. + +**Usage:** + +The `fields` parameter accepts a comma-separated list of field names that the client wants to be included in the response. + +``` +GET /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/?fields=id,name,description +``` + +In this example, the API will return only the `id`, `name`, and `description` fields of the resource. + +### Expand Parameter + +The `expand` parameter allows clients to request additional related information to be included in the response. This is useful for retrieving detailed information about nested resources without making separate API calls. + +**Usage:** + +The `expand` parameter can be used to include details of related resources or nested objects in the response. + +``` +GET /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/?expand=assignees,state +``` + +This request will return the resource data along with expanded information about the `assignees` and `state`. + +### Error Handling + +- Invalid or unrecognized field names passed in the `fields` parameter will result in an error response, indicating which fields are invalid. +- Similarly, if `expand` is used on fields that cannot be expanded, an appropriate error message will be returned. diff --git a/apps/developer-docs/docs/api-reference/issue-activity/get-issue-activity-detail.md b/apps/developer-docs/docs/api-reference/issue-activity/get-issue-activity-detail.md new file mode 100644 index 00000000..80af29cd --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-activity/get-issue-activity-detail.md @@ -0,0 +1,174 @@ +--- +title: Retrieve a work item activity +description: Retrieve a work item activity via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a work item activity. +keywords: plane, plane api, rest api, api integration, issue activity, retrieve a work item activity +--- + +# Retrieve a work item activity + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/activities/{resource_id}/ +
+ +
+
+ +Retrieve details of a specific activity. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.work_items.activities:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-activity/list-issue-activities.md b/apps/developer-docs/docs/api-reference/issue-activity/list-issue-activities.md new file mode 100644 index 00000000..b4dc47cd --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-activity/list-issue-activities.md @@ -0,0 +1,168 @@ +--- +title: List all work item activity +description: List all work item activity via Plane API. HTTP request format, parameters, scopes, and example responses for list all work item activity. +keywords: plane, plane api, rest api, api integration, issue activity, list all work item activity +--- + +# List all work item activity + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/activities/ +
+ +
+
+ +Retrieve all activities for a work item. Supports filtering by activity type and date range. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.work_items.activities:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-activity/overview.md b/apps/developer-docs/docs/api-reference/issue-activity/overview.md new file mode 100644 index 00000000..c13e012c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-activity/overview.md @@ -0,0 +1,120 @@ +--- +title: Overview +description: Plane Issue-Activity API overview. Learn about endpoints, request/response format, and how to work with issue-activity via REST API. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks +--- + +# Overview + +Work item activity represents the history of all changes made to a work item, including property changes, comments, and other modifications. + +
+
+ +## The Activity Object + +### Attributes + +- `id` _uuid_ + + Unique identifier for the activity + +- `created_at` _timestamp_ + + The timestamp of the time when the activity was created + +- `updated_at` _timestamp_ + + The timestamp of the time when the activity was last updated + +- `deleted_at` _timestamp_ or _null_ + + The timestamp when the activity was deleted (if deleted) + +- `verb` _string_ + + created or updated + +- `field` _string_ or _null_ + + The field that got changed null when created + +- `old_value` _string_ + + Old value of the field + +- `new_value` _string_ + + New value of the field + +- `comment` _string_ + + Comment auto generated + +- `attachments` - _\[url,\]_ + + Url of all the attachments that are in the activity + +- `old_identifier` _uuid_ + + Old identifier of the field + +- `new_identifier` _uuid_ + + New identifier of the field + +- `epoch` _float_ + + Epoch float field when the activity was created. + +- `project` uuid + + It contains projects uuid which is automatically saved. + +- `workspace` uuid + + It contains workspace uuid which is automatically saved + +- `issue` _uuid_ + + The work item the activity is attached to + +- `issue_comment` _uuid or null_ + + The comment uuid if the activity was created due to a comment + +- `actor` uuid + + The actor who triggered this activity + +
+
+ + + +```json +{ + "id": "35612f5b-3eff-4130-b91c-c976ff887a20", + "created_at": "2023-11-19T11:56:55.452555Z", + "updated_at": "2023-11-19T11:56:55.452561Z", + "verb": "created", + "field": null, + "old_value": null, + "new_value": null, + "comment": "created the work item", + "attachments": [], + "old_identifier": null, + "new_identifier": null, + "epoch": 1700395015.0, + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4", + "issue": "e1c25c66-5bb8-465e-a818-92a483423443", + "issue_comment": null, + "actor": "16c61a3a-512a-48ac-b0be-b6b46fe6f430" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/issue-attachments/complete-upload.md b/apps/developer-docs/docs/api-reference/issue-attachments/complete-upload.md new file mode 100644 index 00000000..ca08ccbc --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-attachments/complete-upload.md @@ -0,0 +1,140 @@ +--- +title: Complete upload +description: Complete upload via Plane API. HTTP request format, parameters, scopes, and example responses for complete upload. +keywords: plane, plane api, rest api, api integration, issue attachments, complete upload +--- + +# Complete upload + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/attachments/{resource_id}/ +
+ +
+
+ +Mark an attachment as uploaded after successful file transfer to storage. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Mark attachment as uploaded + + + +
+
+ +
+ +### Scopes + +`projects.work_items.attachments:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-attachments/delete-attachment.md b/apps/developer-docs/docs/api-reference/issue-attachments/delete-attachment.md new file mode 100644 index 00000000..dddf4ee8 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-attachments/delete-attachment.md @@ -0,0 +1,114 @@ +--- +title: Delete an attachment +description: Delete an attachment via Plane API. HTTP request format, parameters, scopes, and example responses for delete an attachment. +keywords: plane, plane api, rest api, api integration, issue attachments, delete an attachment +--- + +# Delete an attachment + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/attachments/{resource_id}/ +
+ +
+
+ +Permanently remove an attachment from a work item. Records deletion activity for audit purposes. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.attachments:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-attachments/get-attachment-detail.md b/apps/developer-docs/docs/api-reference/issue-attachments/get-attachment-detail.md new file mode 100644 index 00000000..7d5505f1 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-attachments/get-attachment-detail.md @@ -0,0 +1,118 @@ +--- +title: Retrieve an attachment +description: Retrieve an attachment via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve an attachment. +keywords: plane, plane api, rest api, api integration, issue attachments, retrieve an attachment +--- + +# Retrieve an attachment + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/attachments/{resource_id}/ +
+ +
+
+ +Download attachment file. Returns a redirect to the presigned download URL. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.attachments:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "detail": "Authentication credentials were not provided or are invalid." +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-attachments/get-attachments.md b/apps/developer-docs/docs/api-reference/issue-attachments/get-attachments.md new file mode 100644 index 00000000..deee2740 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-attachments/get-attachments.md @@ -0,0 +1,122 @@ +--- +title: List all attachments +description: List all attachments via Plane API. HTTP request format, parameters, scopes, and example responses for list all attachments. +keywords: plane, plane api, rest api, api integration, issue attachments, list all attachments +--- + +# List all attachments + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/attachments/ +
+ +
+
+ +Retrieve all attachments for a work item. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.attachments:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "size": 1024000, + "asset_url": "https://example.com/resource", + "attributes": { + "name": "Example Name", + "type": "image/png", + "size": 1024000 + }, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-attachments/get-upload-credentials.md b/apps/developer-docs/docs/api-reference/issue-attachments/get-upload-credentials.md new file mode 100644 index 00000000..62e4d316 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-attachments/get-upload-credentials.md @@ -0,0 +1,174 @@ +--- +title: Get upload credentials +description: Get upload credentials via Plane API. HTTP request format, parameters, scopes, and example responses for get upload credentials. +keywords: plane, plane api, rest api, api integration, issue attachments, get upload credentials +--- + +# Get upload credentials + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/attachments/ +
+ +
+
+ +Generate presigned URL for uploading file attachments to a work item. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Original filename of the asset + + + + + +MIME type of the file + + + + + +File size in bytes + + + + + +External identifier for the asset (for integration tracking) + + + + + +External source system (for integration tracking) + + + +
+
+ +
+ +### Scopes + +`projects.work_items.attachments:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "detail": "Presigned download URL generated successfully" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-attachments/overview.md b/apps/developer-docs/docs/api-reference/issue-attachments/overview.md new file mode 100644 index 00000000..f32a0196 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-attachments/overview.md @@ -0,0 +1,184 @@ +--- +title: Overview +description: Plane Issue-Attachments API overview. Learn about endpoints, request/response format, and how to work with issue-attachments via REST API. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks, attachments, files, uploads +--- + +# Overview + +Allows you to manage file attachments associated with work items and Intake work items. You can upload new attachments and retrieve existing attachments for a specific work item. + +[Learn more about Attachments](https://docs.plane.so/core-concepts/issues/overview#add-links-and-attachments) + +## Upload process + +1. Get the [upload credentials](/api-reference/issue-attachments/get-upload-credentials). +2. [Upload the file](/api-reference/issue-attachments/upload-file) to storage. +3. [Complete attachment upload](/api-reference/issue-attachments/complete-upload) to notify server. + +
+
+ +## The Attachment Object + +### Attributes + +- `id` _string_ + + Unique identifier for the attachment + +- `created_at` , `updated_at`, `deleted_at` _timestamp_ + + Timestamp when the attachment was created, when it was last modified or deleted + +- `attributes` _object_ + + Contains file metadata: + - `name` _string_ + + Original filename of the attachment + - `size` _integer_ + + File size in bytes + - `type` _string_ + + MIME type of the file + +- `asset` _string_ + + Storage path/identifier for the attachment file + +- `entity_type` _string_ + + Always `ISSUE_ATTACHMENT` for work item attachments + +- `entity_identifier` _string_ + + Entity identifier for the attachment + +- `is_deleted` _boolean_ + + Whether the attachment has been deleted + +- `is_archived` _boolean_ + + Whether the attachment has been archived + +- `external_id` _string_ or _null_ + + External identifier if the issue and its attachments are imported to Plane + +- `external_source` _string_ or _null_ + + Name of the source if the issue and its attachments are imported to Plane + +- `size` _integer_ + + File size in bytes + +- `is_uploaded` _boolean_ + + Whether the file has been successfully uploaded + +- `storage_metadata` _object_ + + Cloud storage metadata: + - `ETag` _string_ + + Storage provider's entity tag + - `Metadata` _object_ + + Additional storage metadata + - `ContentType` _object_ + + MIME type of stored file + - `LastModified` _timestamp_ + + Last modification time in storage + - `ContentLength` _integer_ + + File size in bytes + +- `created_by` _string_ + + ID of user who created the attachment + +- `updated_by` _string_ + + ID of user who last modified the attachment + +- `deleted_by` _string_ + + ID of user who deleted the attachment + +- `workspace` _string_ + + ID of workspace containing the attachment + +- `project` _string_ + + ID of project containing the work item + +- `issue` _string_ + + ID of work item containing the attachment + +- `user` _string_ + + ID of user associated with the attachment + +- `draft_issue` _string_ + + ID of draft work item if applicable + +- `comment` _string_ + + ID of comment if attachment is associated with a comment + +- `page` _string_ + + ID of page if attachment is associated with a page + +
+
+ + + +```json +{ + "id": "8caf3ed5-4f57-9674-76c4fce146b2", + "created_at": "2024-10-30T09:32:32.815273Z", + "updated_at": "2024-10-30T09:32:35.533136Z", + "deleted_at": null, + "attributes": { + "name": "plane-logo.png", + "size": 135686, + "type": "image/png" + }, + "asset": "9b8aab8a-9052-fc735350abe8/6893d862ecb740d4b7f9f6542cda539c-plane.png", + "entity_type": "ISSUE_ATTACHMENT", + "is_deleted": false, + "is_archived": false, + "external_id": null, + "external_source": null, + "size": 135686.0, + "is_uploaded": true, + "storage_metadata": { + "ETag": "\"72d0d4be99999fe60c2fbc08c8b\"", + "Metadata": {}, + "ContentType": "image/png", + "LastModified": "2024-10-30T09:32:34+00:00", + "ContentLength": 135686 + }, + "created_by": "575de6bf-e120-43bb-9f6a-eae276210575", + "updated_by": "575de6bf-e120-43bb-9f6a-eae276210575", + "workspace": "9b8aab8a-9s6a-99ac-fc735350abe8", + "project": "1790bd-5262-42fb-ac55-568c19a5", + "issue": "7ba090-7702-4e26-a61e-aa6b866f7" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/issue-attachments/update-attachment.md b/apps/developer-docs/docs/api-reference/issue-attachments/update-attachment.md new file mode 100644 index 00000000..89c696d2 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-attachments/update-attachment.md @@ -0,0 +1,140 @@ +--- +title: Update an attachment +description: Update an attachment via Plane API. HTTP request format, parameters, scopes, and example responses for update an attachment. +keywords: plane, plane api, rest api, api integration, issue attachments, update an attachment +--- + +# Update an attachment + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/attachments/{resource_id}/ +
+ +
+
+ +Mark an attachment as uploaded after successful file transfer to storage. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Mark attachment as uploaded + + + +
+
+ +
+ +### Scopes + +`projects.work_items.attachments:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-attachments/upload-file.md b/apps/developer-docs/docs/api-reference/issue-attachments/upload-file.md new file mode 100644 index 00000000..bfebf6a2 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-attachments/upload-file.md @@ -0,0 +1,121 @@ +--- +title: Upload file +description: Upload a file to the presigned storage URL returned by Plane. Includes the required multipart form fields for attachment uploads. +keywords: plane, plane api, rest api, api integration, attachments, uploads, s3 +--- + +# Upload file + +
+ POST + https://planefs-uploads.s3.amazonaws.com/ +
+ +
+
+ +Use the presigned form fields returned by the attachment upload-credentials endpoint to upload the binary file directly to object storage. + +
+ +### Body Parameters + +
+ + + +MIME type of the file being uploaded. + + + + + +Storage key returned by Plane for this upload. + + + + + +Base64-encoded upload policy returned by Plane. + + + + + +AWS signature returned by Plane. + + + + + +Binary file contents to upload. + + + +
+
+ +
+
+ + + + + + + + + +No response body. + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/issue-comment/add-issue-comment.md b/apps/developer-docs/docs/api-reference/issue-comment/add-issue-comment.md new file mode 100644 index 00000000..07d7c49c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-comment/add-issue-comment.md @@ -0,0 +1,199 @@ +--- +title: Create a work item comment +description: Create a work item comment via Plane API. HTTP request format, parameters, scopes, and example responses for create a work item comment. +keywords: plane, plane api, rest api, api integration, issue comment, create a work item comment +--- + +# Create a work item comment + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/comments/ +
+ +
+
+ +Add a new comment to a work item with HTML content. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Comment json. + + + + + +Comment html. + + + + + +- `INTERNAL` - INTERNAL +- `EXTERNAL` - EXTERNAL + + + + + +External source. + + + + + +External id. + + + + + +Parent. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.comments:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "comment_html": "

Example content

", + "comment_json": { + "type": "doc", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This issue has been resolved by implementing OAuth 2.0 flow." + } + ] + } + ] + }, + "actor": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "first_name": "John", + "last_name": "Doe", + "display_name": "Example Name", + "avatar": "https://example.com/assets/example-image.png" + }, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-comment/delete-issue-comment.md b/apps/developer-docs/docs/api-reference/issue-comment/delete-issue-comment.md new file mode 100644 index 00000000..f5e9481f --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-comment/delete-issue-comment.md @@ -0,0 +1,114 @@ +--- +title: Delete a work item comment +description: Delete a work item comment via Plane API. HTTP request format, parameters, scopes, and example responses for delete a work item comment. +keywords: plane, plane api, rest api, api integration, issue comment, delete a work item comment +--- + +# Delete a work item comment + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/comments/{resource_id}/ +
+ +
+
+ +Permanently remove a comment from a work item. Records deletion activity for audit purposes. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.comments:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-comment/get-issue-comment-detail.md b/apps/developer-docs/docs/api-reference/issue-comment/get-issue-comment-detail.md new file mode 100644 index 00000000..103a5478 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-comment/get-issue-comment-detail.md @@ -0,0 +1,142 @@ +--- +title: Retrieve a work item comment +description: Retrieve a work item comment via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a work item comment. +keywords: plane, plane api, rest api, api integration, issue comment, retrieve a work item comment +--- + +# Retrieve a work item comment + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/comments/{resource_id}/ +
+ +
+
+ +Retrieve details of a specific comment. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.comments:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "comment_html": "

Example content

", + "comment_json": { + "type": "doc", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This issue has been resolved by implementing OAuth 2.0 flow." + } + ] + } + ] + }, + "actor": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "first_name": "John", + "last_name": "Doe", + "display_name": "Example Name", + "avatar": "https://example.com/assets/example-image.png" + }, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-comment/list-issue-comments.md b/apps/developer-docs/docs/api-reference/issue-comment/list-issue-comments.md new file mode 100644 index 00000000..a95cef87 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-comment/list-issue-comments.md @@ -0,0 +1,168 @@ +--- +title: List all work item comments +description: List all work item comments via Plane API. HTTP request format, parameters, scopes, and example responses for list all work item comments. +keywords: plane, plane api, rest api, api integration, issue comment, list all work item comments +--- + +# List all work item comments + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/comments/ +
+ +
+
+ +Retrieve all comments for a work item. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.work_items.comments:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-comment/overview.md b/apps/developer-docs/docs/api-reference/issue-comment/overview.md new file mode 100644 index 00000000..fb07c1ae --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-comment/overview.md @@ -0,0 +1,121 @@ +--- +title: Overview +description: Plane Issue-Comment API overview. Learn about endpoints, request/response format, and how to work with issue-comment via REST API. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks, comments, discussion, collaboration +--- + +# Overview + +Comments allow team members to discuss and collaborate on work items by adding text, mentions, and attachments. + +[Learn more about Work Item Comments](https://docs.plane.so/core-concepts/issues/overview#comment-on-work-items) + +
+
+ +## The Comments Object + +### Attributes + +- `id` _uuid_ + + Unique identifier for the comment + +- `created_at` _timestamp_ + + The timestamp when the comment was created + +- `updated_at` _timestamp_ + + The timestamp when the comment was last updated + +- `deleted_at` _timestamp_ or _null_ + + The timestamp when the comment was deleted (if deleted) + +- `edited_at` _timestamp_ or _null_ + + The timestamp when the comment was last edited + +- `comment_html` _string_ + + HTML string version of the comment + +- `comment_stripped` _string_ + + Stripped string version of the comment + +- `comment_json` _object_ + + JSON object version of the comment + +- `attachments` _string[]_ + + Array of attachment URLs + +- `access` _string_ + + If the comment should be visible externally also if the project is published or not. Takes in two values + - INTERNAL + - EXTERNAL + +- `external_source` _string_ + + External source identifier + +- `external_id` _string_ + + External ID from the external source + +- `is_member` _boolean_ + + Whether the current user is a member of the project + +- `created_by` , `updated_by` _uuid_ + + These values are auto saved and represent the id of the user that created or updated the comment + +- `project` uuid + + It contains project uuid which is automatically saved. + +- `workspace` uuid + + It contains workspace uuid which is automatically saved + +- `issue` _uuid_ + + The work item the comment is attached to + +- `actor` _uuid_ + + UUID of the user who commented. + +
+
+ + + +```json +{ + "id": "f3e29f26-708d-40f0-9209-7e0de44abc49", + "created_at": "2023-11-20T09:26:10.383129Z", + "updated_at": "2023-11-20T09:26:10.383140Z", + "comment_stripped": "Initialf ThoughtsaMy initial thoughts on this are very good", + "comment_json": {}, + "comment_html": "

Initialf Thoughts

a

My initial thoughts on this are very good

", + "attachments": [], + "access": "INTERNAL", + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4", + "issue": "e1c25c66-5bb8-465e-a818-92a483423443", + "actor": "16c61a3a-512a-48ac-b0be-b6b46fe6f430" +} +``` + +
+ +
+
diff --git a/apps/developer-docs/docs/api-reference/issue-comment/update-issue-comment-detail.md b/apps/developer-docs/docs/api-reference/issue-comment/update-issue-comment-detail.md new file mode 100644 index 00000000..d0af82e3 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-comment/update-issue-comment-detail.md @@ -0,0 +1,205 @@ +--- +title: Update a work item comment +description: Update a work item comment via Plane API. HTTP request format, parameters, scopes, and example responses for update a work item comment. +keywords: plane, plane api, rest api, api integration, issue comment, update a work item comment +--- + +# Update a work item comment + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/comments/{resource_id}/ +
+ +
+
+ +Modify the content of an existing comment on a work item. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Comment json. + + + + + +Comment html. + + + + + +- `INTERNAL` - INTERNAL +- `EXTERNAL` - EXTERNAL + + + + + +External source. + + + + + +External id. + + + + + +Parent. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.comments:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "comment_html": "

Example content

", + "comment_json": { + "type": "doc", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This issue has been resolved by implementing OAuth 2.0 flow." + } + ] + } + ] + }, + "actor": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "first_name": "John", + "last_name": "Doe", + "display_name": "Example Name", + "avatar": "https://example.com/assets/example-image.png" + }, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/options/add-dropdown-options.md b/apps/developer-docs/docs/api-reference/issue-types/options/add-dropdown-options.md new file mode 100644 index 00000000..8b5d9aac --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/options/add-dropdown-options.md @@ -0,0 +1,194 @@ +--- +title: Add dropdown options +description: Add dropdown options via Plane API. HTTP request format, parameters, scopes, and example responses for add dropdown options. +keywords: plane, plane api, rest api, api integration, issue types, options, add dropdown options +--- + +# Add dropdown options + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-properties/{property_id}/options/ +
+ +
+
+ +Create a new issue property option + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Is active. + + + + + +Is default. + + + + + +External source. + + + + + +External id. + + + + + +Parent. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_property_options:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "description": "Example description", + "deleted_at": "2024-01-01T00:00:00Z", + "sort_order": 1, + "logo_props": "example-value", + "is_active": true, + "is_default": true, + "external_source": "github", + "external_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/options/delete-dropdown-options.md b/apps/developer-docs/docs/api-reference/issue-types/options/delete-dropdown-options.md new file mode 100644 index 00000000..fd5544e5 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/options/delete-dropdown-options.md @@ -0,0 +1,114 @@ +--- +title: Delete dropdown options +description: Delete dropdown options via Plane API. HTTP request format, parameters, scopes, and example responses for delete dropdown options. +keywords: plane, plane api, rest api, api integration, issue types, options, delete dropdown options +--- + +# Delete dropdown options + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-properties/{property_id}/options/{option_id}/ +
+ +
+
+ +Delete an issue property option + +
+ +### Path Parameters + +
+ + + +The unique identifier of the option. + + + + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_property_options:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/options/get-option-details.md b/apps/developer-docs/docs/api-reference/issue-types/options/get-option-details.md new file mode 100644 index 00000000..83c99178 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/options/get-option-details.md @@ -0,0 +1,129 @@ +--- +title: Retrieve option details +description: Retrieve option details via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve option details. +keywords: plane, plane api, rest api, api integration, issue types, options, retrieve option details +--- + +# Retrieve option details + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-properties/{property_id}/options/{option_id}/ +
+ +
+
+ +Get issue property option by id + +
+ +### Path Parameters + +
+ + + +The unique identifier of the option. + + + + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_property_options:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "description": "Example description", + "deleted_at": "2024-01-01T00:00:00Z", + "sort_order": 1, + "logo_props": "example-value", + "is_active": true, + "is_default": true, + "external_source": "github", + "external_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/options/list-dropdown-options.md b/apps/developer-docs/docs/api-reference/issue-types/options/list-dropdown-options.md new file mode 100644 index 00000000..6b3bab4c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/options/list-dropdown-options.md @@ -0,0 +1,125 @@ +--- +title: List all dropdown options +description: List all dropdown options via Plane API. HTTP request format, parameters, scopes, and example responses for list all dropdown options. +keywords: plane, plane api, rest api, api integration, issue types, options, list all dropdown options +--- + +# List all dropdown options + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-properties/{property_id}/options/ +
+ +
+
+ +List issue property options + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_property_options:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "description": "Example description", + "deleted_at": "2024-01-01T00:00:00Z", + "sort_order": 1, + "logo_props": "example-value", + "is_active": true, + "is_default": true, + "external_source": "github", + "external_id": "550e8400-e29b-41d4-a716-446655440000" + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/options/overview.md b/apps/developer-docs/docs/api-reference/issue-types/options/overview.md new file mode 100644 index 00000000..df9ac953 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/options/overview.md @@ -0,0 +1,69 @@ +--- +title: Overview +description: Plane Options API overview. Learn about endpoints, request/response format, and how to work with options via REST API. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks +--- + +# Overview + +Custom property options define the available choices for dropdown-style custom properties on work items. + +
+
+ +## The Options Object + +### Attributes + +- `workspace` _uuid_ + + The workspace which the issue is part of auto generated from backend + +- `project` _uuid_ + + The project which the issue is part of auto generated from backend + +- `created_at` , `updated_at` timestamp + + Timestamp of the issue when it was created and when it was last updated. + +- `created_by` & `updated_by` + + This values are auto saved and represent the id of the user that created or the updated the project. + +- `external_id` & `external_source` + + This values are auto saved and represent the id of the user that created or the updated the project. + +
+
+ + + +```json +{ + "id": "51a869d1-f612-4315-ac91-ffef3e96c20e", + "created_at": "2024-10-23T07:44:42.883820Z", + "updated_at": "2024-10-23T07:44:42.883855Z", + "deleted_at": null, + "name": "issue property option 3", + "sort_order": 10000.0, + "description": "issue property option 3 description", + "logo_props": {}, + "is_active": true, + "is_default": false, + "external_source": null, + "external_id": null, + "created_by": "9d6d1ecd-bf73-4169-80c8-7dee79b217f4", + "updated_by": "9d6d1ecd-bf73-4169-80c8-7dee79b217f4", + "workspace": "70b6599f-9313-4c0d-b5c0-406a13a05647", + "project": "03a9bf56-84f4-4afe-b232-9400eb9b7b6b", + "property": "f962febb-98bc-43ca-8bfb-8012e4d54dae", + "parent": null +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/issue-types/options/update-dropdown-options.md b/apps/developer-docs/docs/api-reference/issue-types/options/update-dropdown-options.md new file mode 100644 index 00000000..463921b8 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/options/update-dropdown-options.md @@ -0,0 +1,200 @@ +--- +title: Update dropdown options +description: Update dropdown options via Plane API. HTTP request format, parameters, scopes, and example responses for update dropdown options. +keywords: plane, plane api, rest api, api integration, issue types, options, update dropdown options +--- + +# Update dropdown options + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-properties/{property_id}/options/{option_id}/ +
+ +
+
+ +Update an issue property option + +
+ +### Path Parameters + +
+ + + +The unique identifier of the option. + + + + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Is active. + + + + + +Is default. + + + + + +External source. + + + + + +External id. + + + + + +Parent. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_property_options:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "description": "Example description", + "deleted_at": "2024-01-01T00:00:00Z", + "sort_order": 1, + "logo_props": "example-value", + "is_active": true, + "is_default": true, + "external_source": "github", + "external_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/properties/add-property.md b/apps/developer-docs/docs/api-reference/issue-types/properties/add-property.md new file mode 100644 index 00000000..e19de522 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/properties/add-property.md @@ -0,0 +1,249 @@ +--- +title: Create a custom property +description: Create a custom property via Plane API. HTTP request format, parameters, scopes, and example responses for create a custom property. +keywords: plane, plane api, rest api, api integration, issue types, properties, create a custom property +--- + +# Create a custom property + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-types/{type_id}/work-item-properties/ +
+ +
+
+ +Create a new issue property + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item type. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +- `ISSUE` - Issue +- `USER` - User + + + + + +List of options to create when property_type is OPTION. Each option should have 'name', optionally 'description', 'is_default', 'external_id', and 'external_source'. + + + + + +Display name. + + + + + +Description. + + + + + +- `TEXT` - Text +- `DATETIME` - Datetime +- `DECIMAL` - Decimal +- `BOOLEAN` - Boolean +- `OPTION` - Option +- `RELATION` - Relation +- `URL` - URL +- `EMAIL` - Email +- `FILE` - File +- `FORMULA` - Formula + + + + + +Is required. + + + + + +Default value. + + + + + +Settings. + + + + + +Is active. + + + + + +Is multi. + + + + + +Validation rules. + + + + + +External source. + + + + + +External id. + + + + + +Formula config. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_properties:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "display_name": "Example Name", + "description": "Example description", + "property_type": "TEXT", + "deleted_at": "2024-01-01T00:00:00Z", + "relation_type": "ISSUE", + "logo_props": "example-value", + "sort_order": 1, + "is_required": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/properties/delete-property.md b/apps/developer-docs/docs/api-reference/issue-types/properties/delete-property.md new file mode 100644 index 00000000..261746d7 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/properties/delete-property.md @@ -0,0 +1,114 @@ +--- +title: Delete a custom property +description: Delete a custom property via Plane API. HTTP request format, parameters, scopes, and example responses for delete a custom property. +keywords: plane, plane api, rest api, api integration, issue types, properties, delete a custom property +--- + +# Delete a custom property + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-types/{type_id}/work-item-properties/{property_id}/ +
+ +
+
+ +Delete an issue property + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item type. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_properties:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/properties/get-property-details.md b/apps/developer-docs/docs/api-reference/issue-types/properties/get-property-details.md new file mode 100644 index 00000000..826d9ef5 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/properties/get-property-details.md @@ -0,0 +1,129 @@ +--- +title: Retrieve a custom property +description: Retrieve a custom property via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a custom property. +keywords: plane, plane api, rest api, api integration, issue types, properties, retrieve a custom property +--- + +# Retrieve a custom property + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-types/{type_id}/work-item-properties/{property_id}/ +
+ +
+
+ +Get issue property by id + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item type. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_properties:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "display_name": "Example Name", + "description": "Example description", + "property_type": "TEXT", + "deleted_at": "2024-01-01T00:00:00Z", + "relation_type": "ISSUE", + "logo_props": "example-value", + "sort_order": 1, + "is_required": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/properties/list-properties.md b/apps/developer-docs/docs/api-reference/issue-types/properties/list-properties.md new file mode 100644 index 00000000..42e3adeb --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/properties/list-properties.md @@ -0,0 +1,125 @@ +--- +title: List custom properties +description: List custom properties via Plane API. HTTP request format, parameters, scopes, and example responses for list custom properties. +keywords: plane, plane api, rest api, api integration, issue types, properties, list custom properties +--- + +# List custom properties + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-types/{type_id}/work-item-properties/ +
+ +
+
+ +List issue properties + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item type. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_properties:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "display_name": "Example Name", + "description": "Example description", + "property_type": "TEXT", + "deleted_at": "2024-01-01T00:00:00Z", + "relation_type": "ISSUE", + "logo_props": "example-value", + "sort_order": 1, + "is_required": true + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/properties/overview.md b/apps/developer-docs/docs/api-reference/issue-types/properties/overview.md new file mode 100644 index 00000000..866be387 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/properties/overview.md @@ -0,0 +1,77 @@ +--- +title: Overview +description: Plane Properties API overview. Learn about endpoints, request/response format, and how to work with properties via REST API. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks +--- + +# Overview + +Custom properties allow you to extend work items with additional fields specific to your workflow and processes. + +[Learn more about Custom properties](https://docs.plane.so/core-concepts/issues/work-item-types#add-custom-properties) + +
+
+ +## The Properties Object + +### Attributes + +- `workspace` _uuid_ + + The workspace which the issue is part of auto generated from backend + +- `project` _uuid_ + + The project which the issue is part of auto generated from backend + +- `created_at` , `updated_at` timestamp + + Timestamp of the issue when it was created and when it was last updated. + +- `created_by` & `updated_by` + + This values are auto saved and represent the id of the user that created or the updated the project. + +- `external_id` & `external_source` + + This values are auto saved and represent the id of the user that created or the updated the project. + +
+
+ + + +```json +{ + "id": "f962febb-98bc-43ca-8bfb-8012e4d54dae", + "created_at": "2024-10-23T07:38:58.231897Z", + "updated_at": "2024-10-23T07:38:58.231920Z", + "deleted_at": null, + "name": "first-issue-property", + "display_name": "first issue property", + "description": "first issue property", + "logo_props": {}, + "sort_order": 75535.0, + "property_type": "OPTION", + "relation_type": null, + "is_required": false, + "default_value": [], + "settings": {}, + "is_active": false, + "is_multi": false, + "validation_rules": {}, + "external_source": null, + "external_id": null, + "created_by": "9d6d1ecd-bf73-4169-80c8-7dee79b217f4", + "updated_by": "9d6d1ecd-bf73-4169-80c8-7dee79b217f4", + "workspace": "70b6599f-9313-4c0d-b5c0-406a13a05647", + "project": "03a9bf56-84f4-4afe-b232-9400eb9b7b6b", + "issue_type": "1800681a-a749-487b-9003-3279031fea35" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/issue-types/properties/update-property.md b/apps/developer-docs/docs/api-reference/issue-types/properties/update-property.md new file mode 100644 index 00000000..14dabda2 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/properties/update-property.md @@ -0,0 +1,255 @@ +--- +title: Update a custom property +description: Update a custom property via Plane API. HTTP request format, parameters, scopes, and example responses for update a custom property. +keywords: plane, plane api, rest api, api integration, issue types, properties, update a custom property +--- + +# Update a custom property + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-types/{type_id}/work-item-properties/{property_id}/ +
+ +
+
+ +Update an issue property + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item type. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +- `ISSUE` - Issue +- `USER` - User + + + + + +List of options to create when property_type is OPTION. Each option should have 'name', optionally 'description', 'is_default', 'external_id', and 'external_source'. + + + + + +Display name. + + + + + +Description. + + + + + +- `TEXT` - Text +- `DATETIME` - Datetime +- `DECIMAL` - Decimal +- `BOOLEAN` - Boolean +- `OPTION` - Option +- `RELATION` - Relation +- `URL` - URL +- `EMAIL` - Email +- `FILE` - File +- `FORMULA` - Formula + + + + + +Is required. + + + + + +Default value. + + + + + +Settings. + + + + + +Is active. + + + + + +Is multi. + + + + + +Validation rules. + + + + + +External source. + + + + + +External id. + + + + + +Formula config. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_properties:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "display_name": "Example Name", + "description": "Example description", + "property_type": "TEXT", + "deleted_at": "2024-01-01T00:00:00Z", + "relation_type": "ISSUE", + "logo_props": "example-value", + "sort_order": 1, + "is_required": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/types/add-issue-type.md b/apps/developer-docs/docs/api-reference/issue-types/types/add-issue-type.md new file mode 100644 index 00000000..da3eb6ad --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/types/add-issue-type.md @@ -0,0 +1,182 @@ +--- +title: Create a work item type +description: Create a work item type via Plane API. HTTP request format, parameters, scopes, and example responses for create a work item type. +keywords: plane, plane api, rest api, api integration, issue types, types, create a work item type +--- + +# Create a work item type + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-types/ +
+ +
+
+ +Create a new issue type for a project + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Is epic. + + + + + +Is active. + + + + + +External source. + + + + + +External id. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_types:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "description": "Example description", + "deleted_at": "2024-01-01T00:00:00Z", + "project_ids": ["550e8400-e29b-41d4-a716-446655440000"], + "logo_props": "example-value", + "is_epic": true, + "is_default": true, + "is_active": true, + "level": 1 +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/types/delete-issue-type.md b/apps/developer-docs/docs/api-reference/issue-types/types/delete-issue-type.md new file mode 100644 index 00000000..a7940b04 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/types/delete-issue-type.md @@ -0,0 +1,108 @@ +--- +title: Delete a work item type +description: Delete a work item type via Plane API. HTTP request format, parameters, scopes, and example responses for delete a work item type. +keywords: plane, plane api, rest api, api integration, issue types, types, delete a work item type +--- + +# Delete a work item type + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-types/{type_id}/ +
+ +
+
+ +Delete an issue type + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item type. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_types:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/types/get-issue-type-details.md b/apps/developer-docs/docs/api-reference/issue-types/types/get-issue-type-details.md new file mode 100644 index 00000000..651aa1da --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/types/get-issue-type-details.md @@ -0,0 +1,123 @@ +--- +title: Retrieve a work item type +description: Retrieve a work item type via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a work item type. +keywords: plane, plane api, rest api, api integration, issue types, types, retrieve a work item type +--- + +# Retrieve a work item type + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-types/{type_id}/ +
+ +
+
+ +Retrieve an issue type by id + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item type. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_types:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "description": "Example description", + "deleted_at": "2024-01-01T00:00:00Z", + "project_ids": ["550e8400-e29b-41d4-a716-446655440000"], + "logo_props": "example-value", + "is_epic": true, + "is_default": true, + "is_active": true, + "level": 1 +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/types/get-work-item-type-schema.md b/apps/developer-docs/docs/api-reference/issue-types/types/get-work-item-type-schema.md new file mode 100644 index 00000000..3854e662 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/types/get-work-item-type-schema.md @@ -0,0 +1,198 @@ +--- +title: Get work item type schema +description: Get work item type schema via Plane API. HTTP request format, parameters, scopes, and example responses for get work item type schema. +keywords: plane, plane api, rest api, api integration, issue types, types, get work item type schema +--- + +# Get work item type schema + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-types/schema/ +
+ +
+
+ +Returns the complete schema for a work item type including all standard fields +and custom properties with their available options inline. + +This endpoint enables LLMs and MCP integrations to understand what fields are available +when creating/updating work items. + +**Standard fields** are always included: + +- name, description_html, priority, state_id, assignee_ids, label_ids, start_date, target_date, parent_id + +**Custom fields** are included when: + +- ISSUE_TYPES feature is enabled AND +- A type_id is provided or a default type exists for the project + +**Options behavior:** + +- state_id options are always included +- priority options are always included +- assignee_ids and label_ids options require `?include=members,labels` +- estimate_point_id options are included when project has estimates configured + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Comma-separated list of additional options to include: members, labels + + + + + +Work item type ID. If not provided, returns schema for default type (when types enabled) or standard fields only. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_types:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "type_id": "550e8400-e29b-41d4-a716-446655440000", + "type_name": "Bug", + "type_description": "Example description", + "type_logo_props": {}, + "fields": { + "name": { + "type": "string", + "required": true, + "max_length": 255 + }, + "priority": { + "type": "option", + "required": false, + "options": [ + { + "value": "urgent", + "label": "Urgent" + }, + { + "value": "high", + "label": "High" + } + ] + }, + "state_id": { + "type": "uuid", + "required": false, + "options": [ + { + "id": "...", + "name": "Example Name", + "group": "backlog" + } + ] + } + }, + "custom_fields": { + "custom_field_severity": { + "id": "...", + "type": "OPTION", + "name": "Example Name", + "display_name": "Example Name", + "required": true, + "is_multi": false, + "options": [ + { + "id": "...", + "name": "Example Name" + } + ] + } + } +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/types/list-issue-types.md b/apps/developer-docs/docs/api-reference/issue-types/types/list-issue-types.md new file mode 100644 index 00000000..de28f9e6 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/types/list-issue-types.md @@ -0,0 +1,119 @@ +--- +title: List all work item types +description: List all work item types via Plane API. HTTP request format, parameters, scopes, and example responses for list all work item types. +keywords: plane, plane api, rest api, api integration, issue types, types, list all work item types +--- + +# List all work item types + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-types/ +
+ +
+
+ +List all issue types for a project + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_types:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "description": "Example description", + "deleted_at": "2024-01-01T00:00:00Z", + "project_ids": ["550e8400-e29b-41d4-a716-446655440000"], + "logo_props": "example-value", + "is_epic": true, + "is_default": true, + "is_active": true, + "level": 1 + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/types/overview.md b/apps/developer-docs/docs/api-reference/issue-types/types/overview.md new file mode 100644 index 00000000..d6299a15 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/types/overview.md @@ -0,0 +1,118 @@ +--- +title: Overview +description: Plane Types API overview. Learn about endpoints, request/response format, and how to work with types via REST API. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks +--- + +# Overview + +Work item types categorize different kinds of work in your project (e.g., "Task", "Bug", "Feature"). + +[Learn more about Work Item Types](https://docs.plane.so/core-concepts/issues/work-item-types) + +
+
+ +## The Work Item Type object + +### Attributes + +- `id` _uuid_ + + Unique identifier for the work item type + +- `name` _string_ + + Name of the work item type + +- `description` _string_ + + Description of the work item type + +- `logo_props` _object_ + + Logo properties for the work item type + +- `is_epic` _boolean_ + + Whether this work item type is an epic + +- `is_default` _boolean_ + + Whether this is the default work item type + +- `is_active` _boolean_ + + Whether this work item type is active + +- `level` _number_ + + Level of the work item type + +- `workspace` _uuid_ + + The workspace which the work item type is part of (auto generated from backend) + +- `project` _uuid_ + + The project which the work item type is part of (auto generated from backend) + +- `created_at` _timestamp_ + + Timestamp when the work item type was created + +- `updated_at` _timestamp_ + + Timestamp when the work item type was last updated + +- `created_by` _uuid_ + + ID of the user who created the work item type (auto saved) + +- `updated_by` _uuid_ + + ID of the user who last updated the work item type (auto saved) + +- `deleted_at` _timestamp_ + + Timestamp when the work item type was deleted (null if not deleted) + +- `external_id` _string_ + + External ID for the work item type (auto saved) + +- `external_source` _string_ + + External source for the work item type (auto saved) + +
+
+ + + +```json +{ + "id": "d6af3c13-3459-43ab-b91c-c33ef2fd7131", + "name": "Postman work item type", + "description": "Postman work item type description", + "logo_props": {}, + "is_epic": false, + "is_default": false, + "is_active": true, + "level": 0, + "workspace": "70b6599f-9313-4c0d-b5c0-406a13a05647", + "project": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "created_at": "2024-10-23T06:54:46.169344Z", + "updated_at": "2024-10-23T06:54:46.169390Z", + "created_by": "9d6d1ecd-bf73-4169-80c8-7dee79b217f4", + "updated_by": "9d6d1ecd-bf73-4169-80c8-7dee79b217f4", + "deleted_at": null, + "external_id": null, + "external_source": null +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/issue-types/types/update-issue-types.md b/apps/developer-docs/docs/api-reference/issue-types/types/update-issue-types.md new file mode 100644 index 00000000..f491fe61 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/types/update-issue-types.md @@ -0,0 +1,188 @@ +--- +title: Update a work item type +description: Update a work item type via Plane API. HTTP request format, parameters, scopes, and example responses for update a work item type. +keywords: plane, plane api, rest api, api integration, issue types, types, update a work item type +--- + +# Update a work item type + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-item-types/{type_id}/ +
+ +
+
+ +Update an issue type + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item type. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Is epic. + + + + + +Is active. + + + + + +External source. + + + + + +External id. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_types:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "name": "Example Name", + "description": "Example description", + "deleted_at": "2024-01-01T00:00:00Z", + "project_ids": ["550e8400-e29b-41d4-a716-446655440000"], + "logo_props": "example-value", + "is_epic": true, + "is_default": true, + "is_active": true, + "level": 1 +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/values/add-property-values.md b/apps/developer-docs/docs/api-reference/issue-types/values/add-property-values.md new file mode 100644 index 00000000..2d69af73 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/values/add-property-values.md @@ -0,0 +1,170 @@ +--- +title: Add custom property values +description: Add custom property values via Plane API. HTTP request format, parameters, scopes, and example responses for add custom property values. +keywords: plane, plane api, rest api, api integration, issue types, values, add custom property values +--- + +# Add custom property values + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/work-item-properties/{property_id}/values/ +
+ +
+
+ +Create or update the property value for a work item. Acts as an upsert operation since only one value is allowed per work item/property combination. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +The value to set for the property. Type depends on property type: string for text/url/email/file fields, string (UUID) or list of UUIDs for relations/options (list only when is_multi=True), string (YYYY-MM-DD) for dates, number for decimals, boolean for booleans + + + + + +Optional external identifier for syncing with external systems + + + + + +Optional external source identifier (e.g., 'github', 'jira') + + + +
+
+ +
+ +### Scopes + +`projects.work_item_property_values:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "property_id": "550e8400-e29b-41d4-a716-446655440000", + "issue_id": "550e8400-e29b-41d4-a716-446655440000", + "value": "Example Name", + "value_type": "Example Name", + "external_id": "550e8400-e29b-41d4-a716-446655440000", + "external_source": "github", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/values/delete-property-value.md b/apps/developer-docs/docs/api-reference/issue-types/values/delete-property-value.md new file mode 100644 index 00000000..09513b78 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/values/delete-property-value.md @@ -0,0 +1,114 @@ +--- +title: Delete property value +description: Delete property value via Plane API. HTTP request format, parameters, scopes, and example responses for delete property value. +keywords: plane, plane api, rest api, api integration, issue types, values, delete property value +--- + +# Delete property value + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/work-item-properties/{property_id}/values/ +
+ +
+
+ +Delete the property value(s) for a work item. For multi-value properties, deletes all values. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_property_values:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/values/get-property-value-detail.md b/apps/developer-docs/docs/api-reference/issue-types/values/get-property-value-detail.md new file mode 100644 index 00000000..46faa584 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/values/get-property-value-detail.md @@ -0,0 +1,126 @@ +--- +title: Get property value +description: Get property value via Plane API. HTTP request format, parameters, scopes, and example responses for get property value. +keywords: plane, plane api, rest api, api integration, issue types, values, get property value +--- + +# Get property value + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/work-item-properties/{property_id}/values/ +
+ +
+
+ +Retrieve the property value(s) for a specific work item property. Returns a single value for non-multi properties, or a list for multi-value properties. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_property_values:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "property_id": "550e8400-e29b-41d4-a716-446655440000", + "issue_id": "550e8400-e29b-41d4-a716-446655440000", + "value": "Example Name", + "value_type": "Example Name", + "external_id": "550e8400-e29b-41d4-a716-446655440000", + "external_source": "github", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/values/list-property-values.md b/apps/developer-docs/docs/api-reference/issue-types/values/list-property-values.md new file mode 100644 index 00000000..d9559898 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/values/list-property-values.md @@ -0,0 +1,126 @@ +--- +title: List all custom property values +description: List all custom property values via Plane API. HTTP request format, parameters, scopes, and example responses for list all custom property values. +keywords: plane, plane api, rest api, api integration, issue types, values, list all custom property values +--- + +# List all custom property values + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/work-item-properties/{property_id}/values/ +
+ +
+
+ +Retrieve the property value(s) for a specific work item property. Returns a single value for non-multi properties, or a list for multi-value properties. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item. + + + +
+
+ +
+ +### Scopes + +`projects.work_item_property_values:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "property_id": "550e8400-e29b-41d4-a716-446655440000", + "issue_id": "550e8400-e29b-41d4-a716-446655440000", + "value": "Example Name", + "value_type": "Example Name", + "external_id": "550e8400-e29b-41d4-a716-446655440000", + "external_source": "github", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue-types/values/overview.md b/apps/developer-docs/docs/api-reference/issue-types/values/overview.md new file mode 100644 index 00000000..46e39fbb --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/values/overview.md @@ -0,0 +1,69 @@ +--- +title: Overview +description: Plane Values API overview. Learn about endpoints, request/response format, and how to work with values via REST API. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks +--- + +# Overview + +Custom property values store the actual data entered into custom properties for specific work items. + +
+
+ +## The Values Object + +### Attributes + +- `workspace` _uuid_ + + The workspace which the issue is part of auto generated from backend + +- `project` _uuid_ + + The project which the issue is part of auto generated from backend + +- `created_at` , `updated_at` timestamp + + Timestamp of the issue when it was created and when it was last updated. + +- `created_by` & `updated_by` + + This values are auto saved and represent the id of the user that created or the updated the project. + +- `external_id` & `external_source` + + This values are auto saved and represent the id of the user that created or the updated the project. + +
+
+ + + +```json +{ + "id": "51a869d1-f612-4315-ac91-ffef3e96c20e", + "created_at": "2024-10-23T07:44:42.883820Z", + "updated_at": "2024-10-23T07:44:42.883855Z", + "deleted_at": null, + "name": "issue property option 3", + "sort_order": 10000.0, + "description": "issue property option 3 description", + "logo_props": {}, + "is_active": true, + "is_default": false, + "external_source": null, + "external_id": null, + "created_by": "9d6d1ecd-bf73-4169-80c8-7dee79b217f4", + "updated_by": "9d6d1ecd-bf73-4169-80c8-7dee79b217f4", + "workspace": "70b6599f-9313-4c0d-b5c0-406a13a05647", + "project_ids": ["03a9bf56-84f4-4afe-b232-9400eb9b7b6b"], + "property": "f962febb-98bc-43ca-8bfb-8012e4d54dae", + "parent": null +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/issue-types/values/update-property-value.md b/apps/developer-docs/docs/api-reference/issue-types/values/update-property-value.md new file mode 100644 index 00000000..87e4635d --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue-types/values/update-property-value.md @@ -0,0 +1,164 @@ +--- +title: Update property value +description: Update property value via Plane API. HTTP request format, parameters, scopes, and example responses for update property value. +keywords: plane, plane api, rest api, api integration, issue types, values, update property value +--- + +# Update property value + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/work-item-properties/{property_id}/values/ +
+ +
+
+ +Update an existing property value for a work item (partial update) + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The unique identifier of the property. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +The value to set for the property. Type depends on property type: string for text/url/email/file fields, string (UUID) or list of UUIDs for relations/options (list only when is_multi=True), string (YYYY-MM-DD) for dates, number for decimals, boolean for booleans + + + + + +Optional external identifier for syncing with external systems + + + + + +Optional external source identifier (e.g., 'github', 'jira') + + + +
+
+ +
+ +### Scopes + +`projects.work_item_property_values:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "property_id": "550e8400-e29b-41d4-a716-446655440000", + "issue_id": "550e8400-e29b-41d4-a716-446655440000", + "value": "Example Name", + "value_type": "Example Name", + "external_id": "550e8400-e29b-41d4-a716-446655440000", + "external_source": "github", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue/add-issue.md b/apps/developer-docs/docs/api-reference/issue/add-issue.md new file mode 100644 index 00000000..ac89de6d --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue/add-issue.md @@ -0,0 +1,311 @@ +--- +title: Create a work item +description: Create a work item via Plane API. HTTP request format, parameters, scopes, and example responses for create a work item. +keywords: plane, plane api, rest api, api integration, issue, create a work item +--- + +# Create a work item + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/ +
+ +
+
+ +Create a new work item in the specified project with the provided details. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Assignees. + + + + + +Labels. + + + + + +Type id. + + + + + +Parent. + + + + + +Deleted at. + + + + + +Point. + + + + + +Name. + + + + + +Description html. + + + + + +Description stripped. + + + + + +- `urgent` - Urgent +- `high` - High +- `medium` - Medium +- `low` - Low +- `none` - None + + + + + +Start date. + + + + + +Target date. + + + + + +Sequence id. + + + + + +Sort order. + + + + + +Completed at. + + + + + +Archived at. + + + + + +Last activity at. + + + + + +Is draft. + + + + + +External source. + + + + + +External id. + + + + + +Created by. + + + + + +State. + + + + + +Estimate point. + + + + + +Type. + + + +
+
+ +
+ +### Scopes + +`projects.work_items:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "sequence_id": 1, + "priority": "high", + "assignees": ["550e8400-e29b-41d4-a716-446655440000"], + "labels": ["550e8400-e29b-41d4-a716-446655440000"], + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue/advanced-search-work-items.md b/apps/developer-docs/docs/api-reference/issue/advanced-search-work-items.md new file mode 100644 index 00000000..80349eee --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue/advanced-search-work-items.md @@ -0,0 +1,183 @@ +--- +title: Advanced search work items +description: Advanced search work items via Plane API. HTTP request format, parameters, scopes, and example responses for advanced search work items. +keywords: plane, plane api, rest api, api integration, issue, advanced search work items +--- + +# Advanced search work items + +
+ POST + /api/v1/workspaces/{workspace_slug}/work-items/advanced-search/ +
+ +
+
+ +Search for work items with advanced filters and search query. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Search query string for text-based search across issue fields + + + + + +Filter JSON passed through to IssueFilterSet for validation and application + + + + + +Maximum number of results to return + + + + + +Whether to search across all projects in the workspace + + + + + +Optional project ID to filter results to a specific project + + + +
+
+ +
+ +### Scopes + +API key authentication or an OAuth token with equivalent access. + +
+ +
+ +
+ + + + + + + + + +```json +[ + [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "sequence_id": 102, + "project_identifier": "WEB", + "project_id": "550e8400-e29b-41d4-a716-446655440000", + "workspace_id": "550e8400-e29b-41d4-a716-446655440000", + "type_id": "550e8400-e29b-41d4-a716-446655440000", + "state_id": "550e8400-e29b-41d4-a716-446655440000", + "priority": "high", + "target_date": "2024-01-01", + "start_date": "2024-01-01" + }, + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "sequence_id": 245, + "project_identifier": "API", + "project_id": "550e8400-e29b-41d4-a716-446655440000", + "workspace_id": "550e8400-e29b-41d4-a716-446655440000", + "type_id": "550e8400-e29b-41d4-a716-446655440000", + "state_id": "550e8400-e29b-41d4-a716-446655440000", + "priority": "medium", + "target_date": null, + "start_date": "2024-01-01" + } + ] +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue/delete-issue.md b/apps/developer-docs/docs/api-reference/issue/delete-issue.md new file mode 100644 index 00000000..9fa16845 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue/delete-issue.md @@ -0,0 +1,108 @@ +--- +title: Delete a work item +description: Delete a work item via Plane API. HTTP request format, parameters, scopes, and example responses for delete a work item. +keywords: plane, plane api, rest api, api integration, issue, delete a work item +--- + +# Delete a work item + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{resource_id}/ +
+ +
+
+ +Permanently delete an existing work item from the project. Only admins or the item creator can perform this action. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_items:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue/get-issue-detail.md b/apps/developer-docs/docs/api-reference/issue/get-issue-detail.md new file mode 100644 index 00000000..e3995ab6 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue/get-issue-detail.md @@ -0,0 +1,159 @@ +--- +title: Retrieve a work item by ID +description: Retrieve a work item by ID via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a work item by id. +keywords: plane, plane api, rest api, api integration, issue, retrieve a work item by id +--- + +# Retrieve a work item by ID + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{resource_id}/ +
+ +
+
+ +Retrieve details of a specific work item. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Comma-separated list of related fields to expand in response + + + + + +External system identifier for filtering or lookup + + + + + +External system source name for filtering or lookup + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + +
+
+ +
+ +### Scopes + +`projects.work_items:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "sequence_id": 1, + "priority": "high", + "assignees": ["550e8400-e29b-41d4-a716-446655440000"], + "labels": ["550e8400-e29b-41d4-a716-446655440000"], + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue/get-issue-sequence-id.md b/apps/developer-docs/docs/api-reference/issue/get-issue-sequence-id.md new file mode 100644 index 00000000..b5d78973 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue/get-issue-sequence-id.md @@ -0,0 +1,120 @@ +--- +title: Retrieve a work item by identifier +description: Retrieve a work item by identifier via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a work item by identifier. +keywords: plane, plane api, rest api, api integration, issue, retrieve a work item by identifier +--- + +# Retrieve a work item by identifier + +
+ GET + /api/v1/workspaces/{workspace_slug}/work-items/{project_identifier}-{issue_identifier}/ +
+ +
+
+ +Retrieve a specific work item using workspace slug, project identifier, and issue identifier. + +
+ +### Path Parameters + +
+ + + +The numeric issue identifier. + + + + + +The project identifier key. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_items:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "sequence_id": 1, + "priority": "high", + "assignees": ["550e8400-e29b-41d4-a716-446655440000"], + "labels": ["550e8400-e29b-41d4-a716-446655440000"], + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue/list-issues.md b/apps/developer-docs/docs/api-reference/issue/list-issues.md new file mode 100644 index 00000000..00c2871b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue/list-issues.md @@ -0,0 +1,184 @@ +--- +title: List all work items +description: List all work items via Plane API. HTTP request format, parameters, scopes, and example responses for list all work items. +keywords: plane, plane api, rest api, api integration, issue, list all work items +--- + +# List all work items + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/ +
+ +
+
+ +Retrieve a paginated list of all work items in a project. Supports filtering, ordering, and field selection through query parameters. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +External system identifier for filtering or lookup + + + + + +External system source name for filtering or lookup + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.work_items:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "priority": "high", + "sequence_id": 123, + "state": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "group": "started" + }, + "assignees": [], + "labels": [], + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue/overview.md b/apps/developer-docs/docs/api-reference/issue/overview.md new file mode 100644 index 00000000..6038a200 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue/overview.md @@ -0,0 +1,164 @@ +--- +title: Overview +description: Plane Issue API overview. Learn about endpoints, request/response format, and how to work with issue via REST API. +keywords: plane, plane api, rest api, api integration, work items, issues, tasks +--- + +# Overview + +Work items are the fundamental unit of work in Plane. They represent tasks that need to be accomplished — assignable, trackable, and actionable to-dos in your project management workflow. + +[Learn more about Work Items](https://docs.plane.so/core-concepts/issues/overview) + +
+
+ +## The Work Item object + +### Attributes + +- `name` _string_ **(required)** + + Name of the work item + +- `created_at` , `updated_at` _timestamp_ + + Timestamp of the work item when it was created and when it was last updated + +- `estimate_point` _integer_ or _null_ + + Total estimate points for the work item takes value between (0,7). + +- `description_html` _string_ + + HTML description of the work item + +- `description_stripped` _string_ + + Stripped version of the html description auto generated using the application. + +- `priority` _string_ + + Priority of the work item takes in 5 values + - none + - urgent + - high + - medium + - low + +- `start_date` _date_ + + Start date of the work item + +- `target_date` _date_ + + Target date of the work item + +- `sequence_id` _integer_ + + Auto generated from the system the unique identifier of the work item + +- `sort_order` _decimal_ + + Auto generated from the system during creation used for ordering + +- `completed_at` _timestamp_ or _null_ + + Timestamp when the work item is moved to any completed group state + +- `created_by` & `updated_by` + + This values are auto saved and represent the id of the user that created or the updated the project. + +- `project` _uuid_ + + The project which the work item is part of auto generated from backend + +- `workspace` _uuid_ + + The workspace which the work item is part of auto generated from backend + +- `parent` _uuid_ + + The uuid of the parent work item which should be part of the same workspace + +- `state` _uuid_ + + The uuid of the state which is present in the project where the work item is being created. + +- `assignees` - _\[uuid,\]_ + + The array of uuids of the users who are part of the project where the work item is being created or updated. + +- `labels` - _\[uuid,\]_ + + The array of uuids of the labels which are present in the project where the work item is being created or updated. + +- `type` _uuid_ + + The uuid of the work item type for the work item. + +- `module` _uuid_ + + The uuid of the module the work item belongs to. + +- `is_draft` _boolean_ + + Whether the work item is a draft. + +- `archived_at` _timestamp_ or _null_ + + Timestamp when the work item was archived. + +- `description_binary` _string_ + + Binary description of the work item. + +**Expandable Fields** + +The following fields can be expanded when retrieving a work item by including them in the `expand` query parameter: + +- `type` - Expands to full WorkItemType object +- `module` - Expands to full Module object +- `labels` - Expands to array of full Label objects +- `assignees` - Expands to array of full User objects +- `state` - Expands to full State object +- `project` - Expands to full Project object + +
+
+ + + +```json +{ + "id": "e1c25c66-5bb8-465e-a818-92a483423443", + "created_at": "2023-11-19T11:56:55.176802Z", + "updated_at": "2023-11-19T11:56:55.176809Z", + "estimate_point": null, + "name": "First Work Item", + "description_html": "

", + "description_stripped": "", + "priority": "none", + "start_date": "2023-09-01", + "target_date": "2023-10-04", + "sequence_id": 421, + "sort_order": 265535.0, + "completed_at": null, + "archived_at": null, + "is_draft": false, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4", + "parent": null, + "state": "f3f045db-7e74-49f2-b3b2-0b7dee4635ae", + "assignees": ["797b5aea-3f40-4199-be84-5f94e0d04501"], + "labels": [] +} +``` + +
+ +
+
diff --git a/apps/developer-docs/docs/api-reference/issue/search-issues.md b/apps/developer-docs/docs/api-reference/issue/search-issues.md new file mode 100644 index 00000000..aff9b9c0 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue/search-issues.md @@ -0,0 +1,150 @@ +--- +title: Search work items +description: Search work items via Plane API. HTTP request format, parameters, scopes, and example responses for search work items. +keywords: plane, plane api, rest api, api integration, issue, search work items +--- + +# Search work items + +
+ GET + /api/v1/workspaces/{workspace_slug}/work-items/search/ +
+ +
+
+ +Perform semantic search across issue names, sequence IDs, and project identifiers. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Maximum number of results to return + + + + + +Project ID for filtering results within a specific project + + + + + +Search query to filter results by name, description, or identifier + + + + + +Whether to search across entire workspace or within specific project + + + +
+
+ +
+ +### Scopes + +`projects.work_items:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "issues": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "sequence_id": 123, + "project__identifier": "MAB", + "project_id": "550e8400-e29b-41d4-a716-446655440000", + "workspace__slug": "my-workspace" + }, + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "sequence_id": 124, + "project__identifier": "MAB", + "project_id": "550e8400-e29b-41d4-a716-446655440000", + "workspace__slug": "my-workspace" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/issue/update-issue-detail.md b/apps/developer-docs/docs/api-reference/issue/update-issue-detail.md new file mode 100644 index 00000000..ecc0aa95 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/issue/update-issue-detail.md @@ -0,0 +1,311 @@ +--- +title: Update a work item +description: Update a work item via Plane API. HTTP request format, parameters, scopes, and example responses for update a work item. +keywords: plane, plane api, rest api, api integration, issue, update a work item +--- + +# Update a work item + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{resource_id}/ +
+ +
+
+ +Partially update an existing work item with the provided fields. Supports external ID validation to prevent conflicts. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Assignees. + + + + + +Labels. + + + + + +Type id. + + + + + +Parent. + + + + + +Deleted at. + + + + + +Point. + + + + + +Name. + + + + + +Description html. + + + + + +Description stripped. + + + + + +- `urgent` - Urgent +- `high` - High +- `medium` - Medium +- `low` - Low +- `none` - None + + + + + +Start date. + + + + + +Target date. + + + + + +Sequence id. + + + + + +Sort order. + + + + + +Completed at. + + + + + +Archived at. + + + + + +Last activity at. + + + + + +Is draft. + + + + + +External source. + + + + + +External id. + + + + + +Created by. + + + + + +State. + + + + + +Estimate point. + + + + + +Type. + + + +
+
+ +
+ +### Scopes + +`projects.work_items:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "sequence_id": 1, + "priority": "high", + "assignees": ["550e8400-e29b-41d4-a716-446655440000"], + "labels": ["550e8400-e29b-41d4-a716-446655440000"], + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/label/add-label.md b/apps/developer-docs/docs/api-reference/label/add-label.md new file mode 100644 index 00000000..f53faaba --- /dev/null +++ b/apps/developer-docs/docs/api-reference/label/add-label.md @@ -0,0 +1,185 @@ +--- +title: Create a label +description: Create a label via Plane API. HTTP request format, parameters, scopes, and example responses for create a label. +keywords: plane, plane api, rest api, api integration, label, create a label +--- + +# Create a label + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/labels/ +
+ +
+
+ +Create a new label in the specified project with name, color, and description. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Color. + + + + + +Description. + + + + + +External source. + + + + + +External id. + + + + + +Parent. + + + + + +Sort order. + + + +
+
+ +
+ +### Scopes + +`projects.labels:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "color": "#ff4444", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/label/delete-label.md b/apps/developer-docs/docs/api-reference/label/delete-label.md new file mode 100644 index 00000000..f63e3611 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/label/delete-label.md @@ -0,0 +1,108 @@ +--- +title: Delete a label +description: Delete a label via Plane API. HTTP request format, parameters, scopes, and example responses for delete a label. +keywords: plane, plane api, rest api, api integration, label, delete a label +--- + +# Delete a label + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/labels/{label_id}/ +
+ +
+
+ +Permanently remove a label from the project. This action cannot be undone. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the label. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.labels:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/label/get-label-detail.md b/apps/developer-docs/docs/api-reference/label/get-label-detail.md new file mode 100644 index 00000000..ab60a14b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/label/get-label-detail.md @@ -0,0 +1,117 @@ +--- +title: Retrieve a label +description: Retrieve a label via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a label. +keywords: plane, plane api, rest api, api integration, label, retrieve a label +--- + +# Retrieve a label + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/labels/{label_id}/ +
+ +
+
+ +Retrieve details of a specific label. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the label. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.labels:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "color": "#ff4444", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/label/list-labels.md b/apps/developer-docs/docs/api-reference/label/list-labels.md new file mode 100644 index 00000000..0b4fb338 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/label/list-labels.md @@ -0,0 +1,163 @@ +--- +title: List all labels +description: List all labels via Plane API. HTTP request format, parameters, scopes, and example responses for list all labels. +keywords: plane, plane api, rest api, api integration, label, list all labels +--- + +# List all labels + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/labels/ +
+ +
+
+ +Retrieve all labels in a project. Supports filtering by name and color. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.labels:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "color": "#ff4444", + "description": "Example description" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/label/overview.md b/apps/developer-docs/docs/api-reference/label/overview.md new file mode 100644 index 00000000..95b2efa3 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/label/overview.md @@ -0,0 +1,81 @@ +--- +title: Overview +description: Plane Label API overview. Learn about endpoints, request/response format, and how to work with label via REST API. +keywords: plane, plane api, rest api, api integration, labels, tags, categorization +--- + +# Overview + +Labels are tags that help you categorize and organize work items in your project. + +[Learn more about Labels](https://docs.plane.so/core-concepts/work-items/labels) + +
+
+ +## The Label Object + +### Attributes + +- `name` _string_ **(required)** + + Name of the label + +- `created_at` , `updated_at` _timestamp_ + + Timestamp of the issue when it was created and when it was last updated. + +- `description` _string_ + + Description of the Label + +- `color` _string_ + + Hex code of the color + +- `sort_order` _float_ + + Sort order of the label used for sorting + +- `created_by` & `updated_by` + + This values are auto saved and represent the id of the user that created or the updated the project. + +- `project` _uuid_ + + The project which the issue is part of auto generated from backend + +- `workspace` _uuid_ + + The workspace which the issue is part of auto generated from backend + +- `parent` _uuid or null_ + + Parent of the label which is also a Label + +
+
+ + + +```json +{ + "id": "c7146baf-7058-496b-aa3a-df6c25a7e929", + "created_at": "2023-11-20T06:01:03.538675Z", + "updated_at": "2023-11-20T06:01:03.538683Z", + "name": "High", + "description": "", + "color": "", + "sort_order": 72416.0, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4", + "parent": null +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/label/update-label-detail.md b/apps/developer-docs/docs/api-reference/label/update-label-detail.md new file mode 100644 index 00000000..cb06b163 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/label/update-label-detail.md @@ -0,0 +1,191 @@ +--- +title: Update a label +description: Update a label via Plane API. HTTP request format, parameters, scopes, and example responses for update a label. +keywords: plane, plane api, rest api, api integration, label, update a label +--- + +# Update a label + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/labels/{label_id}/ +
+ +
+
+ +Partially update an existing label's properties like name, color, or description. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the label. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Color. + + + + + +Description. + + + + + +External source. + + + + + +External id. + + + + + +Parent. + + + + + +Sort order. + + + +
+
+ +
+ +### Scopes + +`projects.labels:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "color": "#ff4444", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/link/add-link.md b/apps/developer-docs/docs/api-reference/link/add-link.md new file mode 100644 index 00000000..7042242c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/link/add-link.md @@ -0,0 +1,156 @@ +--- +title: Create a link +description: Create a link via Plane API. HTTP request format, parameters, scopes, and example responses for create a link. +keywords: plane, plane api, rest api, api integration, link, create a link +--- + +# Create a link + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/links/ +
+ +
+
+ +Add a new external link to a work item with URL, title, and metadata. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Title. + + + + + +Url. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.links:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "url": "https://example.com/resource", + "title": "Example Name", + "metadata": { + "title": "Example Name", + "description": "Example description", + "image": "https://example.com/assets/example-image.png" + }, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/link/delete-link.md b/apps/developer-docs/docs/api-reference/link/delete-link.md new file mode 100644 index 00000000..d0502193 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/link/delete-link.md @@ -0,0 +1,114 @@ +--- +title: Delete a link +description: Delete a link via Plane API. HTTP request format, parameters, scopes, and example responses for delete a link. +keywords: plane, plane api, rest api, api integration, link, delete a link +--- + +# Delete a link + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/links/{resource_id}/ +
+ +
+
+ +Permanently remove an external link from a work item. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.links:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/link/get-link-detail.md b/apps/developer-docs/docs/api-reference/link/get-link-detail.md new file mode 100644 index 00000000..2edabed9 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/link/get-link-detail.md @@ -0,0 +1,168 @@ +--- +title: Retrieve a link +description: Retrieve a link via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a link. +keywords: plane, plane api, rest api, api integration, link, retrieve a link +--- + +# Retrieve a link + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/links/{resource_id}/ +
+ +
+
+ +Retrieve details of a specific work item link. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.work_items.links:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/link/list-links.md b/apps/developer-docs/docs/api-reference/link/list-links.md new file mode 100644 index 00000000..ded612c2 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/link/list-links.md @@ -0,0 +1,168 @@ +--- +title: List all links +description: List all links via Plane API. HTTP request format, parameters, scopes, and example responses for list all links. +keywords: plane, plane api, rest api, api integration, link, list all links +--- + +# List all links + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/links/ +
+ +
+
+ +Retrieve all links associated with a work item. Supports filtering by URL, title, and metadata. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.work_items.links:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/link/overview.md b/apps/developer-docs/docs/api-reference/link/overview.md new file mode 100644 index 00000000..74c9ac5f --- /dev/null +++ b/apps/developer-docs/docs/api-reference/link/overview.md @@ -0,0 +1,76 @@ +--- +title: Overview +description: Plane Link API overview. Learn about endpoints, request/response format, and how to work with link via REST API. +keywords: plane api, rest api, work item links, issue links, url references, link management, plane integration +--- + +# Overview + +Links attach external resources to work items, allowing you to reference documentation, designs, or other relevant URLs. + +[Learn more about Links](https://docs.plane.so/core-concepts/work-items/overview#add-links-and-attachments) + +
+
+ +## The Link Object + +### Attributes + +- `title` _string_ + + Title of the url + +- `url` _url_ **(required)** + + Url of the external link + +- `metadata` _json_ + + Metadata from the resource + +- `created_at` , `updated_at` _timestamp_ + + Timestamp of the issue when it was created and when it was last updated. + +- `created_by` & `updated_by` + + This values are auto saved and represent the id of the user that created or the updated the project. + +- `project` _uuid_ + + The project which the issue is part of auto generated from backend + +- `workspace` _uuid_ + + The workspace which the issue is part of auto generated from backend + +- `issue` _uuid_ + + The issue which the link is attached to + +
+
+ + + +```json +{ + "id": "662dd6b2-2b01-4315-955f-480eb51baa14", + "created_at": "2023-11-20T06:23:10.270664Z", + "updated_at": "2023-11-20T06:23:10.270689Z", + "title": "Plane Website", + "url": "https://plane.so", + "metadata": {}, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4", + "issue": "e1c25c66-5bb8-465e-a818-92a483423443" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/link/update-link-detail.md b/apps/developer-docs/docs/api-reference/link/update-link-detail.md new file mode 100644 index 00000000..53a996c8 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/link/update-link-detail.md @@ -0,0 +1,162 @@ +--- +title: Update a link +description: Update a link via Plane API. HTTP request format, parameters, scopes, and example responses for update a link. +keywords: plane, plane api, rest api, api integration, link, update a link +--- + +# Update a link + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/links/{resource_id}/ +
+ +
+
+ +Modify the URL, title, or metadata of an existing issue link. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Title. + + + + + +Url. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.links:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "url": "https://example.com/resource", + "title": "Example Name", + "metadata": { + "title": "Example Name", + "description": "Example description", + "image": "https://example.com/assets/example-image.png" + }, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/members/add-project-member.md b/apps/developer-docs/docs/api-reference/members/add-project-member.md new file mode 100644 index 00000000..340b6e5e --- /dev/null +++ b/apps/developer-docs/docs/api-reference/members/add-project-member.md @@ -0,0 +1,145 @@ +--- +title: Create project member +description: Create project member via Plane API. HTTP request format, parameters, scopes, and example responses for create project member. +keywords: plane, plane api, rest api, api integration, members, create project member +--- + +# Create project member + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/project-members/ +
+ +
+
+ +Create a new project member + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Member. + + + + + +- `20` - Admin +- `15` - Member +- `5` - Guest + + + +
+
+ +
+ +### Scopes + +`projects.members:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "member": "550e8400-e29b-41d4-a716-446655440000", + "role": 20 +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/members/delete-project-member.md b/apps/developer-docs/docs/api-reference/members/delete-project-member.md new file mode 100644 index 00000000..54c6fde2 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/members/delete-project-member.md @@ -0,0 +1,108 @@ +--- +title: Delete project member +description: Delete project member via Plane API. HTTP request format, parameters, scopes, and example responses for delete project member. +keywords: plane, plane api, rest api, api integration, members, delete project member +--- + +# Delete project member + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/project-members/{member_id}/ +
+ +
+
+ +Delete a project member + +
+ +### Path Parameters + +
+ + + +The unique identifier of the member. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.members:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/members/get-project-member-detail.md b/apps/developer-docs/docs/api-reference/members/get-project-member-detail.md new file mode 100644 index 00000000..228d3dcd --- /dev/null +++ b/apps/developer-docs/docs/api-reference/members/get-project-member-detail.md @@ -0,0 +1,114 @@ +--- +title: Get project member +description: Get project member via Plane API. HTTP request format, parameters, scopes, and example responses for get project member. +keywords: plane, plane api, rest api, api integration, members, get project member +--- + +# Get project member + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/project-members/{member_id}/ +
+ +
+
+ +Retrieve a project member by ID. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the member. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.members:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "member": "550e8400-e29b-41d4-a716-446655440000", + "role": 20 +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/members/get-project-members.md b/apps/developer-docs/docs/api-reference/members/get-project-members.md new file mode 100644 index 00000000..da13c481 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/members/get-project-members.md @@ -0,0 +1,123 @@ +--- +title: List all project members +description: List all project members via Plane API. HTTP request format, parameters, scopes, and example responses for list all project members. +keywords: plane, plane api, rest api, api integration, members, list all project members +--- + +# List all project members + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/project-members/ +
+ +
+
+ +Retrieve all users who are members of the specified project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.members:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "first_name": "John", + "last_name": "Doe", + "display_name": "Example Name", + "email": "user@example.com", + "avatar": "https://example.com/assets/example-image.png" + }, + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "first_name": "Jane", + "last_name": "Smith", + "display_name": "Example Name", + "email": "user@example.com", + "avatar": "https://example.com/assets/example-image.png" + } + ] +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/members/get-workspace-members.md b/apps/developer-docs/docs/api-reference/members/get-workspace-members.md new file mode 100644 index 00000000..d1063221 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/members/get-workspace-members.md @@ -0,0 +1,114 @@ +--- +title: Get all workspace members +description: Get all workspace members via Plane API. HTTP request format, parameters, scopes, and example responses for get all workspace members. +keywords: plane, plane api, rest api, api integration, members, get all workspace members +--- + +# Get all workspace members + +
+ GET + /api/v1/workspaces/{workspace_slug}/members/ +
+ +
+
+ +Retrieve all users who are members of the specified workspace. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`workspaces.members:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "first_name": "John", + "last_name": "Doe", + "display_name": "Example Name", + "email": "user@example.com", + "avatar": "https://example.com/assets/example-image.png", + "role": 20 + }, + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "first_name": "Jane", + "last_name": "Smith", + "display_name": "Example Name", + "email": "user@example.com", + "avatar": "https://example.com/assets/example-image.png", + "role": 15 + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/members/overview.md b/apps/developer-docs/docs/api-reference/members/overview.md new file mode 100644 index 00000000..6f9c8604 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/members/overview.md @@ -0,0 +1,65 @@ +--- +title: Overview +description: Plane Members API overview. Learn about endpoints, request/response format, and how to work with members via REST API. +keywords: plane api, members, workspace members, team members, member roles, user management, rest api, api integration +--- + +# Overview + +Members represent users who belong to a workspace or project. The Members API allows you to retrieve information about workspace and project members. + +[Learn more about Members](https://docs.plane.so/core-concepts/workspaces/members) + +
+
+ +## The Members Object + +### Attributes + +- `id` _string_ + Unique identifier for the Member + +- `first_name` _string_ + First name of the Member + +- `last_name` _string_ + Last name of the Member + +- `email` _string_ + Email address of the Member + +- `avatar` _string_ + Optional avatar image file reference + +- `avatar_url` _string_ + Publicly accessible URL for the avatar image + +- `display_name` _string_ + Display name shown across the application + +- `role` _integer_ + Role of the Member in the Workspace or Project + +
+
+ + + +```json +{ + "id": "00000000-0000-0000-0000-000000000001", + "first_name": "User", + "last_name": "One", + "email": "user1@example.com", + "avatar": "", + "avatar_url": null, + "display_name": "user1", + "role": 15 +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/members/remove-workspace-member.md b/apps/developer-docs/docs/api-reference/members/remove-workspace-member.md new file mode 100644 index 00000000..6e395971 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/members/remove-workspace-member.md @@ -0,0 +1,96 @@ +--- +title: Remove workspace member +description: Remove workspace member via Plane API. HTTP request format, parameters, scopes, and example responses for remove workspace member. +keywords: plane, plane api, rest api, api integration, members, remove workspace member +--- + +# Remove workspace member + +
+ POST + /api/v1/workspaces/{workspace_slug}/members/remove/ +
+ +
+
+ +Remove a member from the workspace, deactivate them from all projects, and reduce the seat count. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`workspaces.members:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/members/update-project-member.md b/apps/developer-docs/docs/api-reference/members/update-project-member.md new file mode 100644 index 00000000..9da5afc3 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/members/update-project-member.md @@ -0,0 +1,151 @@ +--- +title: Update project member +description: Update project member via Plane API. HTTP request format, parameters, scopes, and example responses for update project member. +keywords: plane, plane api, rest api, api integration, members, update project member +--- + +# Update project member + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/project-members/{member_id}/ +
+ +
+
+ +Update a project member + +
+ +### Path Parameters + +
+ + + +The unique identifier of the member. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Member. + + + + + +- `20` - Admin +- `15` - Member +- `5` - Guest + + + +
+
+ +
+ +### Scopes + +`projects.members:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "member": "550e8400-e29b-41d4-a716-446655440000", + "role": 20 +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/milestones/add-milestone.md b/apps/developer-docs/docs/api-reference/milestones/add-milestone.md new file mode 100644 index 00000000..4337e772 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/milestones/add-milestone.md @@ -0,0 +1,165 @@ +--- +title: Create milestone +description: Create milestone via Plane API. HTTP request format, parameters, scopes, and example responses for create milestone. +keywords: plane, plane api, rest api, api integration, milestones, create milestone +--- + +# Create milestone + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/milestones/ +
+ +
+
+ +Create a new milestone in a project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Title. + + + + + +Target date. + + + + + +External id. + + + + + +External source. + + + +
+
+ +
+ +### Scopes + +API key authentication or an OAuth token with equivalent access. + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "title": "Example Name", + "target_date": "2024-01-01", + "external_id": "550e8400-e29b-41d4-a716-446655440000", + "external_source": "github", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/milestones/delete-milestone.md b/apps/developer-docs/docs/api-reference/milestones/delete-milestone.md new file mode 100644 index 00000000..6b8f3c02 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/milestones/delete-milestone.md @@ -0,0 +1,108 @@ +--- +title: Delete milestone +description: Delete milestone via Plane API. HTTP request format, parameters, scopes, and example responses for delete milestone. +keywords: plane, plane api, rest api, api integration, milestones, delete milestone +--- + +# Delete milestone + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/milestones/{milestone_id}/ +
+ +
+
+ +Delete a specific milestone by its ID. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the milestone. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +API key authentication or an OAuth token with equivalent access. + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/milestones/get-milestone-detail.md b/apps/developer-docs/docs/api-reference/milestones/get-milestone-detail.md new file mode 100644 index 00000000..2154b293 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/milestones/get-milestone-detail.md @@ -0,0 +1,118 @@ +--- +title: Get milestone +description: Get milestone via Plane API. HTTP request format, parameters, scopes, and example responses for get milestone. +keywords: plane, plane api, rest api, api integration, milestones, get milestone +--- + +# Get milestone + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/milestones/{milestone_id}/ +
+ +
+
+ +Retrieve a specific milestone by its ID. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the milestone. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +API key authentication or an OAuth token with equivalent access. + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "title": "Example Name", + "target_date": "2024-01-01", + "external_id": "550e8400-e29b-41d4-a716-446655440000", + "external_source": "github", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/milestones/list-milestone-work-items.md b/apps/developer-docs/docs/api-reference/milestones/list-milestone-work-items.md new file mode 100644 index 00000000..48f5d0cc --- /dev/null +++ b/apps/developer-docs/docs/api-reference/milestones/list-milestone-work-items.md @@ -0,0 +1,116 @@ +--- +title: List milestone work items +description: List milestone work items via Plane API. HTTP request format, parameters, scopes, and example responses for list milestone work items. +keywords: plane, plane api, rest api, api integration, milestones, list milestone work items +--- + +# List milestone work items + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/milestones/{milestone_id}/work-items/ +
+ +
+
+ +List all work items for a milestone. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the milestone. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +API key authentication or an OAuth token with equivalent access. + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "issue": "550e8400-e29b-41d4-a716-446655440000", + "milestone": "550e8400-e29b-41d4-a716-446655440000" + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/milestones/list-milestones.md b/apps/developer-docs/docs/api-reference/milestones/list-milestones.md new file mode 100644 index 00000000..9ea8033c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/milestones/list-milestones.md @@ -0,0 +1,114 @@ +--- +title: List milestones +description: List milestones via Plane API. HTTP request format, parameters, scopes, and example responses for list milestones. +keywords: plane, plane api, rest api, api integration, milestones, list milestones +--- + +# List milestones + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/milestones/ +
+ +
+
+ +List all milestones in a project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +API key authentication or an OAuth token with equivalent access. + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "title": "Example Name", + "target_date": "2024-01-01", + "external_id": "550e8400-e29b-41d4-a716-446655440000", + "external_source": "github", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/milestones/overview.md b/apps/developer-docs/docs/api-reference/milestones/overview.md new file mode 100644 index 00000000..eaba379d --- /dev/null +++ b/apps/developer-docs/docs/api-reference/milestones/overview.md @@ -0,0 +1,68 @@ +--- +title: Overview +description: Plane Milestones API overview. Learn how to manage milestones and milestone work items through the Plane API. +keywords: plane, plane api, rest api, api integration, milestones, project planning +--- + +# Overview + +Milestones help teams group work items around important dates, releases, and delivery checkpoints inside a project. + +[Learn more about Projects](https://docs.plane.so/core-concepts/projects/overview) + +
+
+ +## The Milestone Object + +### Attributes + +- `id` _string_ + + Id. + +- `title` _string_ + + Title. + +- `target_date` _string_ + + Target date. + +- `external_id` _string_ + + External id. + +- `external_source` _string_ + + External source. + +- `created_at` _string_ + + Created at. + +- `updated_at` _string_ + + Updated at. + +
+
+ + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "title": "Example Name", + "target_date": "2024-01-01", + "external_id": "550e8400-e29b-41d4-a716-446655440000", + "external_source": "github", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/milestones/update-milestone-detail.md b/apps/developer-docs/docs/api-reference/milestones/update-milestone-detail.md new file mode 100644 index 00000000..24cfefd4 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/milestones/update-milestone-detail.md @@ -0,0 +1,171 @@ +--- +title: Update milestone +description: Update milestone via Plane API. HTTP request format, parameters, scopes, and example responses for update milestone. +keywords: plane, plane api, rest api, api integration, milestones, update milestone +--- + +# Update milestone + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/milestones/{milestone_id}/ +
+ +
+
+ +Update a specific milestone by its ID. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the milestone. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Title. + + + + + +Target date. + + + + + +External id. + + + + + +External source. + + + +
+
+ +
+ +### Scopes + +API key authentication or an OAuth token with equivalent access. + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "title": "Example Name", + "target_date": "2024-01-01", + "external_id": "550e8400-e29b-41d4-a716-446655440000", + "external_source": "github", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/module/add-module-work-items.md b/apps/developer-docs/docs/api-reference/module/add-module-work-items.md new file mode 100644 index 00000000..913630e8 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/module/add-module-work-items.md @@ -0,0 +1,151 @@ +--- +title: Add work items to module +description: Add work items to module via Plane API. HTTP request format, parameters, scopes, and example responses for add work items to module. +keywords: plane, plane api, rest api, api integration, module, add work items to module +--- + +# Add work items to module + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/modules/{module_id}/module-issues/ +
+ +
+
+ +Assign multiple work items to a module or move them from another module. Automatically handles bulk creation and updates with activity tracking. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the module. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +List of issue IDs to add to the module + + + +
+
+ +
+ +### Scopes + +`projects.modules:write` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "module": "550e8400-e29b-41d4-a716-446655440000", + "issue": "550e8400-e29b-41d4-a716-446655440000", + "sub_issues_count": 2, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/module/add-module.md b/apps/developer-docs/docs/api-reference/module/add-module.md new file mode 100644 index 00000000..f4f81268 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/module/add-module.md @@ -0,0 +1,213 @@ +--- +title: Create a module +description: Create a module via Plane API. HTTP request format, parameters, scopes, and example responses for create a module. +keywords: plane, plane api, rest api, api integration, module, create a module +--- + +# Create a module + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/modules/ +
+ +
+
+ +Create a new project module with specified name, description, and timeline. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Start date. + + + + + +Target date. + + + + + +- `backlog` - Backlog +- `planned` - Planned +- `in-progress` - In Progress +- `paused` - Paused +- `completed` - Completed +- `cancelled` - Cancelled + + + + + +Lead. + + + + + +Members. + + + + + +External source. + + + + + +External id. + + + +
+
+ +
+ +### Scopes + +`projects.modules:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "start_date": "2024-01-01", + "target_date": "2024-01-01", + "status": "in-progress", + "total_issues": 12, + "completed_issues": 5, + "cancelled_issues": 0, + "started_issues": 4, + "unstarted_issues": 3, + "backlog_issues": 0, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/module/archive-module.md b/apps/developer-docs/docs/api-reference/module/archive-module.md new file mode 100644 index 00000000..92ee5cf3 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/module/archive-module.md @@ -0,0 +1,108 @@ +--- +title: Archive a module +description: Archive a module via Plane API. HTTP request format, parameters, scopes, and example responses for archive a module. +keywords: plane, plane api, rest api, api integration, module, archive a module +--- + +# Archive a module + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/modules/{resource_id}/archive/ +
+ +
+
+ +Move a module to archived status for historical tracking. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.modules:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/module/delete-module.md b/apps/developer-docs/docs/api-reference/module/delete-module.md new file mode 100644 index 00000000..639e3200 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/module/delete-module.md @@ -0,0 +1,108 @@ +--- +title: Delete a module +description: Delete a module via Plane API. HTTP request format, parameters, scopes, and example responses for delete a module. +keywords: plane, plane api, rest api, api integration, module, delete a module +--- + +# Delete a module + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/modules/{resource_id}/ +
+ +
+
+ +Permanently remove a module and all its associated issue relationships. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.modules:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/module/get-module-detail.md b/apps/developer-docs/docs/api-reference/module/get-module-detail.md new file mode 100644 index 00000000..8c35cae4 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/module/get-module-detail.md @@ -0,0 +1,125 @@ +--- +title: Retrieve a module +description: Retrieve a module via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a module. +keywords: plane, plane api, rest api, api integration, module, retrieve a module +--- + +# Retrieve a module + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/modules/{resource_id}/ +
+ +
+
+ +Retrieve details of a specific module. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.modules:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "start_date": "2024-01-01", + "target_date": "2024-01-01", + "status": "in-progress", + "total_issues": 12, + "completed_issues": 5, + "cancelled_issues": 0, + "started_issues": 4, + "unstarted_issues": 3, + "backlog_issues": 0, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/module/list-archived-modules.md b/apps/developer-docs/docs/api-reference/module/list-archived-modules.md new file mode 100644 index 00000000..2b7dd0e5 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/module/list-archived-modules.md @@ -0,0 +1,162 @@ +--- +title: List all archived modules +description: List all archived modules via Plane API. HTTP request format, parameters, scopes, and example responses for list all archived modules. +keywords: plane, plane api, rest api, api integration, module, list all archived modules +--- + +# List all archived modules + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/archived-modules/ +
+ +
+
+ +Retrieve all modules that have been archived in the project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.modules:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/module/list-module-work-items.md b/apps/developer-docs/docs/api-reference/module/list-module-work-items.md new file mode 100644 index 00000000..fbaa31fb --- /dev/null +++ b/apps/developer-docs/docs/api-reference/module/list-module-work-items.md @@ -0,0 +1,168 @@ +--- +title: List all work items in a module +description: List all work items in a module via Plane API. HTTP request format, parameters, scopes, and example responses for list all work items in a module. +keywords: plane, plane api, rest api, api integration, module, list all work items in a module +--- + +# List all work items in a module + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/modules/{module_id}/module-issues/ +
+ +
+
+ +Retrieve all work items assigned to a module with detailed information. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the module. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.modules:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/module/list-modules.md b/apps/developer-docs/docs/api-reference/module/list-modules.md new file mode 100644 index 00000000..9f0a3fa4 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/module/list-modules.md @@ -0,0 +1,165 @@ +--- +title: List all modules +description: List all modules via Plane API. HTTP request format, parameters, scopes, and example responses for list all modules. +keywords: plane, plane api, rest api, api integration, module, list all modules +--- + +# List all modules + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/modules/ +
+ +
+
+ +Retrieve all modules in a project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.modules:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "start_date": "2024-01-01", + "target_date": "2024-01-01", + "status": "in_progress" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/module/overview.md b/apps/developer-docs/docs/api-reference/module/overview.md new file mode 100644 index 00000000..52167e31 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/module/overview.md @@ -0,0 +1,131 @@ +--- +title: Overview +description: Plane Module API overview. Learn about endpoints, request/response format, and how to work with module via REST API. +keywords: plane, plane api, rest api, api integration, modules, features +--- + +# Overview + +Modules are smaller, focused projects that help you group and organize issues within a specific time frame. They allow you to break down your work into manageable chunks and track progress towards specific goals or objectives. + +[Learn more about Modules](https://docs.plane.so/core-concepts/modules) + +
+
+ +## The Module Object + +### Attributes + +- `name` string(required) + + Name of the module + +- `description` string + + Description of the module + +- `description_html` string + + Description in HTML format + +- `start_date` date + + Start date of the module + +- `target_date` date + + Estimated date to complete the module + +- `created_at` _timestamp_ + + The timestamp of the time when the project was created + +- `updated_at` _timestamp_ + + The timestamp of the time when the project was last updated + +- `status` + + It describes the status of the module + + The status can be + - backlog + - planned + - in-progress + - paused + - completed + - cancelled + +- `view_props` + + It store the filters and the display properties selected by the user to visualize the issues in the module + +- `sort_order` + + It gives the position of the module at which it should be displayed + +- `created_by` , `updated_by` _uuid_ + + These values are auto saved and represent the id of the user that created or updated the module + +- `Project` uuid + + It contains projects uuid which is automatically saved. + +- `Workspace` uuid + + It contains workspace uuid which is automatically saved + +- `lead` uuid + + Lead of the module + +- `members` string[] + + List of member user IDs assigned to the module + +- `archived_at` _timestamp_ + + The timestamp when the module was archived (if archived) + +- `logo_props` + + Logo properties for the module + +- `description_text` + + Description in plain text format + +
+
+ + + +```json +{ + "id": "b69b19ae-261f-428c-899f-dd58efaa36c0", + "created_at": "2023-11-19T11:48:21.130161Z", + "updated_at": "2023-11-19T11:48:21.130168Z", + "name": "module stesting", + "description": "", + "description_text": null, + "description_html": null, + "start_date": null, + "target_date": null, + "status": "planned", + "view_props": {}, + "sort_order": 55535.0, + "created_by": "0649cb9d-05c8-4ef4-8e8b-d108ccddd42c", + "updated_by": "0649cb9d-05c8-4ef4-8e8b-d108ccddd42c", + "project": "6436c4ae-fba7-45dc-ad4a-5440e17cb1b2", + "workspace": "c467e125-59e3-44ec-b5ee-f9c1e138c611", + "lead": null, + "members": [] +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/module/remove-module-work-item.md b/apps/developer-docs/docs/api-reference/module/remove-module-work-item.md new file mode 100644 index 00000000..16037965 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/module/remove-module-work-item.md @@ -0,0 +1,114 @@ +--- +title: Remove work item from module +description: Remove work item from module via Plane API. HTTP request format, parameters, scopes, and example responses for remove work item from module. +keywords: plane, plane api, rest api, api integration, module, remove work item from module +--- + +# Remove work item from module + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/modules/{module_id}/module-issues/{work_item_id}/ +
+ +
+
+ +Remove a work item from a module while keeping the work item in the project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the module. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.modules:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/module/unarchive-module.md b/apps/developer-docs/docs/api-reference/module/unarchive-module.md new file mode 100644 index 00000000..4eb04620 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/module/unarchive-module.md @@ -0,0 +1,108 @@ +--- +title: Restore a module +description: Restore a module via Plane API. HTTP request format, parameters, scopes, and example responses for restore a module. +keywords: plane, plane api, rest api, api integration, module, restore a module +--- + +# Restore a module + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/archived-modules/{resource_id}/unarchive/ +
+ +
+
+ +Restore an archived module to active status, making it available for regular use. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.modules:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/module/update-module-detail.md b/apps/developer-docs/docs/api-reference/module/update-module-detail.md new file mode 100644 index 00000000..f958d04b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/module/update-module-detail.md @@ -0,0 +1,219 @@ +--- +title: Update module details +description: Update module details via Plane API. HTTP request format, parameters, scopes, and example responses for update module details. +keywords: plane, plane api, rest api, api integration, module, update module details +--- + +# Update module details + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/modules/{resource_id}/ +
+ +
+
+ +Modify an existing module's properties like name, description, status, or timeline. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Start date. + + + + + +Target date. + + + + + +- `backlog` - Backlog +- `planned` - Planned +- `in-progress` - In Progress +- `paused` - Paused +- `completed` - Completed +- `cancelled` - Cancelled + + + + + +Lead. + + + + + +Members. + + + + + +External source. + + + + + +External id. + + + +
+
+ +
+ +### Scopes + +`projects.modules:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "start_date": "2024-01-01", + "target_date": "2024-01-01", + "status": "in-progress", + "total_issues": 12, + "completed_issues": 5, + "cancelled_issues": 0, + "started_issues": 4, + "unstarted_issues": 3, + "backlog_issues": 0, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/page/add-project-page.md b/apps/developer-docs/docs/api-reference/page/add-project-page.md new file mode 100644 index 00000000..b949e34e --- /dev/null +++ b/apps/developer-docs/docs/api-reference/page/add-project-page.md @@ -0,0 +1,226 @@ +--- +title: Create a project page +description: Create a project page via Plane API. HTTP request format, parameters, scopes, and example responses for create a project page. +keywords: plane, plane api, rest api, api integration, page, create a project page +--- + +# Create a project page + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/pages/ +
+ +
+
+ +Create a project page + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +- `0` - Public +- `1` - Private + + + + + +Color. + + + + + +Is locked. + + + + + +Archived at. + + + + + +View props. + + + + + +Logo props. + + + + + +External id. + + + + + +External source. + + + + + +Description html. + + + +
+
+ +
+ +### Scopes + +`projects.pages:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "description_html": "

Example content

", + "owned_by": "550e8400-e29b-41d4-a716-446655440000", + "access": 0, + "color": "Example Name", + "is_locked": true, + "archived_at": "2024-01-01", + "workspace": "550e8400-e29b-41d4-a716-446655440000", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/page/add-workspace-page.md b/apps/developer-docs/docs/api-reference/page/add-workspace-page.md new file mode 100644 index 00000000..eb258fa3 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/page/add-workspace-page.md @@ -0,0 +1,217 @@ +--- +title: Create a wiki page +description: Create a wiki page via Plane API. HTTP request format, parameters, scopes, and example responses for create a wiki page. +keywords: plane, plane api, rest api, api integration, page, create a wiki page +--- + +# Create a wiki page + +
+ POST + /api/v1/workspaces/{workspace_slug}/pages/ +
+ +
+
+ +Create a workspace page + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +- `0` - Public +- `1` - Private + + + + + +Color. + + + + + +Is locked. + + + + + +Archived at. + + + + + +View props. + + + + + +Logo props. + + + + + +External id. + + + + + +External source. + + + + + +Description html. + + + +
+
+ +
+ +### Scopes + +`wiki.pages:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "description_html": "

Example content

", + "owned_by": "550e8400-e29b-41d4-a716-446655440000", + "access": 0, + "color": "Example Name", + "is_locked": true, + "archived_at": "2024-01-01", + "workspace": "550e8400-e29b-41d4-a716-446655440000", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/page/get-project-page.md b/apps/developer-docs/docs/api-reference/page/get-project-page.md new file mode 100644 index 00000000..d7619e82 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/page/get-project-page.md @@ -0,0 +1,115 @@ +--- +title: Retrieve a project page +description: Retrieve a project page via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a project page. +keywords: plane, plane api, rest api, api integration, page, retrieve a project page +--- + +# Retrieve a project page + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/pages/{page_id}/ +
+ +
+
+ +Get a project page by ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the page. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.pages:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/page/get-workspace-page.md b/apps/developer-docs/docs/api-reference/page/get-workspace-page.md new file mode 100644 index 00000000..7816f635 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/page/get-workspace-page.md @@ -0,0 +1,109 @@ +--- +title: Retrieve a wiki page +description: Retrieve a wiki page via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a wiki page. +keywords: plane, plane api, rest api, api integration, page, retrieve a wiki page +--- + +# Retrieve a wiki page + +
+ GET + /api/v1/workspaces/{workspace_slug}/pages/{page_id}/ +
+ +
+
+ +Get a workspace page by ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the page. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`wiki.pages:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/page/list-project-pages.md b/apps/developer-docs/docs/api-reference/page/list-project-pages.md new file mode 100644 index 00000000..a583d6b9 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/page/list-project-pages.md @@ -0,0 +1,171 @@ +--- +title: List project pages +description: List project pages via Plane API. HTTP request format, parameters, scopes, and example responses for listing project pages. +keywords: plane, plane api, rest api, api integration, page, list project pages, project pages +--- + +# List project pages + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/pages/ +
+ +
+
+ +List all pages in a project with optional filtering and search. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the project. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Filter pages by scope. Defaults to `all`. + +- `all` — all pages the user has access to +- `public` — pages with public access, excluding archived +- `private` — pages owned by the user and not shared, excluding archived +- `shared` — private pages explicitly shared with the user +- `archived` — pages that have been archived + + + + + +Case-insensitive search on page title. + + + + + +Number of results per page. Defaults to `20`, maximum `100`. + + + + + +Pagination cursor for getting the next or previous set of results. + + + +
+
+ +
+ +### Scopes + +`projects.pages:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": null, + "sub_grouped_by": null, + "total_count": 4, + "next_cursor": "20:1:0", + "prev_cursor": "20:-1:1", + "next_page_results": false, + "prev_page_results": false, + "count": 4, + "total_pages": 1, + "total_results": 4, + "extra_stats": null, + "results": [ + { + "id": "b3478c56-31f6-4f7e-b445-8392a4b26621", + "name": "welcome 3 b", + "owned_by": "5b0af4aa-e310-408a-a480-868429af5701", + "access": 0, + "is_locked": false, + "archived_at": null, + "workspace": "8725ddfa-c181-49f6-9173-97b8d0b7d599", + "created_at": "2026-04-01T15:41:19.062280Z", + "updated_at": "2026-04-07T19:30:39.274060Z", + "logo_props": {}, + "parent_id": "a2819c8b-f7ac-4cbd-b971-682726c4f8cc" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/page/list-workspace-pages.md b/apps/developer-docs/docs/api-reference/page/list-workspace-pages.md new file mode 100644 index 00000000..0a0685bd --- /dev/null +++ b/apps/developer-docs/docs/api-reference/page/list-workspace-pages.md @@ -0,0 +1,165 @@ +--- +title: List workspace wiki pages +description: List workspace wiki pages via Plane API. HTTP request format, parameters, scopes, and example responses for listing workspace wiki pages. +keywords: plane, plane api, rest api, api integration, page, list workspace wiki pages, wiki pages +--- + +# List workspace wiki pages + +
+ GET + /api/v1/workspaces/{workspace_slug}/pages/ +
+ +
+
+ +List all wiki pages in a workspace with optional filtering and search. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Filter pages by scope. Defaults to `all`. + +- `all` — all pages the user has access to +- `public` — pages with public access, excluding archived +- `private` — pages owned by the user and not shared, excluding archived +- `shared` — private pages explicitly shared with the user +- `archived` — pages that have been archived + + + + + +Case-insensitive search on page title. + + + + + +Number of results per page. Defaults to `20`, maximum `100`. + + + + + +Pagination cursor for getting the next or previous set of results. + + + +
+
+ +
+ +### Scopes + +`wiki.pages:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": null, + "sub_grouped_by": null, + "total_count": 4, + "next_cursor": "20:1:0", + "prev_cursor": "20:-1:1", + "next_page_results": false, + "prev_page_results": false, + "count": 4, + "total_pages": 1, + "total_results": 4, + "extra_stats": null, + "results": [ + { + "id": "b3478c56-31f6-4f7e-b445-8392a4b26621", + "name": "welcome 3 b", + "owned_by": "5b0af4aa-e310-408a-a480-868429af5701", + "access": 0, + "is_locked": false, + "archived_at": null, + "workspace": "8725ddfa-c181-49f6-9173-97b8d0b7d599", + "created_at": "2026-04-01T15:41:19.062280Z", + "updated_at": "2026-04-07T19:30:39.274060Z", + "logo_props": {}, + "parent_id": "a2819c8b-f7ac-4cbd-b971-682726c4f8cc" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/page/overview.md b/apps/developer-docs/docs/api-reference/page/overview.md new file mode 100644 index 00000000..f3bb7938 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/page/overview.md @@ -0,0 +1,68 @@ +--- +title: Overview +description: Plane Page API overview. Learn about endpoints, request/response format, and how to work with page via REST API. +keywords: plane, plane api, rest api, api integration, pages, documentation, notes +--- + +# Overview + +Pages allow you to create and manage documentation at both workspace and project levels. Workspace pages are accessible across all projects, while project pages are specific to individual projects. + +**Documentation**: [Wiki](https://docs.plane.so/core-concepts/pages/wiki), [Pages](https://docs.plane.so/core-concepts/pages/overview) + +
+
+ +## The Pages Object + +### Attributes + +- `id` _uuid_ + + Unique identifier for the page + +- `name` _string_ + + Name of the page + +- `description_html` _string_ + + HTML description/content of the page + +- `created_at` _timestamp_ + + The timestamp when the page was created + +- `updated_at` _timestamp_ + + The timestamp when the page was last updated + +- `created_by` _uuid_ + + ID of the user who created the page + +- `updated_by` _uuid_ + + ID of the user who last updated the page + +
+
+ + + +```json +{ + "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "created_at": "2023-11-19T11:56:55.176802Z", + "updated_at": "2023-11-19T11:56:55.176809Z", + "name": "Getting Started", + "description_html": "

Welcome

This is a getting started guide.

", + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430" +} +``` + +
+ +
+
diff --git a/apps/developer-docs/docs/api-reference/project-features/get-project-features.md b/apps/developer-docs/docs/api-reference/project-features/get-project-features.md new file mode 100644 index 00000000..294f0b6f --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project-features/get-project-features.md @@ -0,0 +1,112 @@ +--- +title: Get project features +description: Get project features via Plane API. HTTP request format, parameters, scopes, and example responses for get project features. +keywords: plane, plane api, rest api, api integration, project features, get project features +--- + +# Get project features + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/features/ +
+ +
+
+ +Get the features of a project + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.features:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "epics": true, + "modules": true, + "cycles": true, + "views": true, + "pages": true, + "intakes": true, + "work_item_types": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project-features/overview.md b/apps/developer-docs/docs/api-reference/project-features/overview.md new file mode 100644 index 00000000..e7394807 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project-features/overview.md @@ -0,0 +1,68 @@ +--- +title: Overview +description: Plane Project Features API overview. Learn how to inspect and update project feature flags with the Plane API. +keywords: plane, plane api, rest api, api integration, project features, feature flags +--- + +# Overview + +Project features control which project-level capabilities are enabled for an individual project. + +[Learn more about Projects](https://docs.plane.so/core-concepts/projects/overview) + +
+
+ +## The Project Feature Object + +### Attributes + +- `epics` _boolean_ + + Epics. + +- `modules` _boolean_ + + Modules. + +- `cycles` _boolean_ + + Cycles. + +- `views` _boolean_ + + Views. + +- `pages` _boolean_ + + Pages. + +- `intakes` _boolean_ + + Intakes. + +- `work_item_types` _boolean_ + + Work item types. + +
+
+ + + +```json +{ + "epics": true, + "modules": true, + "cycles": true, + "views": true, + "pages": true, + "intakes": true, + "work_item_types": true +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/project-features/update-project-features.md b/apps/developer-docs/docs/api-reference/project-features/update-project-features.md new file mode 100644 index 00000000..d46209e9 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project-features/update-project-features.md @@ -0,0 +1,192 @@ +--- +title: Update project features +description: Update project features via Plane API. HTTP request format, parameters, scopes, and example responses for update project features. +keywords: plane, plane api, rest api, api integration, project features, update project features +--- + +# Update project features + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/features/ +
+ +
+
+ +Update the features of a project + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Epics. + + + + + +Modules. + + + + + +Cycles. + + + + + +Views. + + + + + +Pages. + + + + + +Intakes. + + + + + +Work item types. + + + +
+
+ +
+ +### Scopes + +`projects.features:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "epics": true, + "modules": true, + "cycles": true, + "views": true, + "pages": true, + "intakes": true, + "work_item_types": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project-labels/add-project-label.md b/apps/developer-docs/docs/api-reference/project-labels/add-project-label.md new file mode 100644 index 00000000..8569ea84 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project-labels/add-project-label.md @@ -0,0 +1,159 @@ +--- +title: Create project label +description: Create project label via Plane API. HTTP request format, parameters, scopes, and example responses for create project label. +keywords: plane, plane api, rest api, api integration, project labels, create project label +--- + +# Create project label + +
+ POST + /api/v1/workspaces/{workspace_slug}/project-labels/ +
+ +
+
+ +Create a new project label in the workspace with name, color, and description. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Color. + + + + + +Sort order. + + + +
+
+ +
+ +### Scopes + +`projects.labels:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "color": "#f39c12", + "description": "Example description", + "sort_order": 65535, + "workspace": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project-labels/delete-project-label.md b/apps/developer-docs/docs/api-reference/project-labels/delete-project-label.md new file mode 100644 index 00000000..922600a9 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project-labels/delete-project-label.md @@ -0,0 +1,102 @@ +--- +title: Delete project label +description: Delete project label via Plane API. HTTP request format, parameters, scopes, and example responses for delete project label. +keywords: plane, plane api, rest api, api integration, project labels, delete project label +--- + +# Delete project label + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/project-labels/{label_id}/ +
+ +
+
+ +Permanently delete an existing project label from the workspace. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the label. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.labels:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project-labels/get-project-label-detail.md b/apps/developer-docs/docs/api-reference/project-labels/get-project-label-detail.md new file mode 100644 index 00000000..394db3ab --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project-labels/get-project-label-detail.md @@ -0,0 +1,115 @@ +--- +title: Get project label +description: Get project label via Plane API. HTTP request format, parameters, scopes, and example responses for get project label. +keywords: plane, plane api, rest api, api integration, project labels, get project label +--- + +# Get project label + +
+ GET + /api/v1/workspaces/{workspace_slug}/project-labels/{label_id}/ +
+ +
+
+ +Retrieve details of a specific project label. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the label. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.labels:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "color": "#f39c12", + "description": "Example description", + "sort_order": 65535, + "workspace": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project-labels/list-project-labels.md b/apps/developer-docs/docs/api-reference/project-labels/list-project-labels.md new file mode 100644 index 00000000..116afcc3 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project-labels/list-project-labels.md @@ -0,0 +1,156 @@ +--- +title: List project labels +description: List project labels via Plane API. HTTP request format, parameters, scopes, and example responses for list project labels. +keywords: plane, plane api, rest api, api integration, project labels, list project labels +--- + +# List project labels + +
+ GET + /api/v1/workspaces/{workspace_slug}/project-labels/ +
+ +
+
+ +Retrieve all project labels in a workspace. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.labels:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project-labels/overview.md b/apps/developer-docs/docs/api-reference/project-labels/overview.md new file mode 100644 index 00000000..e5f29224 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project-labels/overview.md @@ -0,0 +1,83 @@ +--- +title: Overview +description: Plane Project Labels API overview. Learn how to manage workspace-level project labels with the Plane API. +keywords: plane, plane api, rest api, api integration, project labels, workspace labels +--- + +# Overview + +Project labels define reusable classifications that can be attached to projects across a workspace. + +[Learn more about Projects](https://developers.plane.so/api-reference/project/overview) + +
+
+ +## The Project Label Object + +### Attributes + +- `id` _string_ + + Id. + +- `name` _string_ + + Name. + +- `description` _string_ + + Description. + +- `color` _string_ + + Color. + +- `sort_order` _number_ + + Sort order. + +- `workspace` _string_ + + Workspace. + +- `created_at` _string_ + + Created at. + +- `updated_at` _string_ + + Updated at. + +- `created_by` _string_ + + Created by. + +- `updated_by` _string_ + + Updated by. + +
+
+ + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "color": "#f39c12", + "description": "Example description", + "sort_order": 65535, + "workspace": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/project-labels/update-project-label-detail.md b/apps/developer-docs/docs/api-reference/project-labels/update-project-label-detail.md new file mode 100644 index 00000000..79b305b9 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project-labels/update-project-label-detail.md @@ -0,0 +1,165 @@ +--- +title: Update project label +description: Update project label via Plane API. HTTP request format, parameters, scopes, and example responses for update project label. +keywords: plane, plane api, rest api, api integration, project labels, update project label +--- + +# Update project label + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/project-labels/{label_id}/ +
+ +
+
+ +Partially update an existing project label's properties like name, color, or description. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the label. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Color. + + + + + +Sort order. + + + +
+
+ +
+ +### Scopes + +`projects.labels:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "color": "#f39c12", + "description": "Example description", + "sort_order": 65535, + "workspace": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project/add-project.md b/apps/developer-docs/docs/api-reference/project/add-project.md new file mode 100644 index 00000000..278f45a3 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project/add-project.md @@ -0,0 +1,691 @@ +--- +title: Create a project +description: Create a project via Plane API. HTTP request format, parameters, scopes, and example responses for create a project. +keywords: plane, plane api, rest api, api integration, project, create a project +--- + +# Create a project + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/ +
+ +
+
+ +Create a new project in the workspace with default states and member assignments. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Project lead. + + + + + +Default assignee. + + + + + +Identifier. + + + + + +Icon prop. + + + + + +Emoji. + + + + + +Cover image. + + + + + +Module view. + + + + + +Cycle view. + + + + + +Issue views view. + + + + + +Page view. + + + + + +Intake view. + + + + + +Guest view all features. + + + + + +Archive in. + + + + + +Close in. + + + + + +- `Africa/Abidjan` - Africa/Abidjan +- `Africa/Accra` - Africa/Accra +- `Africa/Addis_Ababa` - Africa/Addis_Ababa +- `Africa/Algiers` - Africa/Algiers +- `Africa/Asmara` - Africa/Asmara +- `Africa/Bamako` - Africa/Bamako +- `Africa/Bangui` - Africa/Bangui +- `Africa/Banjul` - Africa/Banjul +- `Africa/Bissau` - Africa/Bissau +- `Africa/Blantyre` - Africa/Blantyre +- `Africa/Brazzaville` - Africa/Brazzaville +- `Africa/Bujumbura` - Africa/Bujumbura +- `Africa/Cairo` - Africa/Cairo +- `Africa/Casablanca` - Africa/Casablanca +- `Africa/Ceuta` - Africa/Ceuta +- `Africa/Conakry` - Africa/Conakry +- `Africa/Dakar` - Africa/Dakar +- `Africa/Dar_es_Salaam` - Africa/Dar_es_Salaam +- `Africa/Djibouti` - Africa/Djibouti +- `Africa/Douala` - Africa/Douala +- `Africa/El_Aaiun` - Africa/El_Aaiun +- `Africa/Freetown` - Africa/Freetown +- `Africa/Gaborone` - Africa/Gaborone +- `Africa/Harare` - Africa/Harare +- `Africa/Johannesburg` - Africa/Johannesburg +- `Africa/Juba` - Africa/Juba +- `Africa/Kampala` - Africa/Kampala +- `Africa/Khartoum` - Africa/Khartoum +- `Africa/Kigali` - Africa/Kigali +- `Africa/Kinshasa` - Africa/Kinshasa +- `Africa/Lagos` - Africa/Lagos +- `Africa/Libreville` - Africa/Libreville +- `Africa/Lome` - Africa/Lome +- `Africa/Luanda` - Africa/Luanda +- `Africa/Lubumbashi` - Africa/Lubumbashi +- `Africa/Lusaka` - Africa/Lusaka +- `Africa/Malabo` - Africa/Malabo +- `Africa/Maputo` - Africa/Maputo +- `Africa/Maseru` - Africa/Maseru +- `Africa/Mbabane` - Africa/Mbabane +- `Africa/Mogadishu` - Africa/Mogadishu +- `Africa/Monrovia` - Africa/Monrovia +- `Africa/Nairobi` - Africa/Nairobi +- `Africa/Ndjamena` - Africa/Ndjamena +- `Africa/Niamey` - Africa/Niamey +- `Africa/Nouakchott` - Africa/Nouakchott +- `Africa/Ouagadougou` - Africa/Ouagadougou +- `Africa/Porto-Novo` - Africa/Porto-Novo +- `Africa/Sao_Tome` - Africa/Sao_Tome +- `Africa/Tripoli` - Africa/Tripoli +- `Africa/Tunis` - Africa/Tunis +- `Africa/Windhoek` - Africa/Windhoek +- `America/Adak` - America/Adak +- `America/Anchorage` - America/Anchorage +- `America/Anguilla` - America/Anguilla +- `America/Antigua` - America/Antigua +- `America/Araguaina` - America/Araguaina +- `America/Argentina/Buenos_Aires` - America/Argentina/Buenos_Aires +- `America/Argentina/Catamarca` - America/Argentina/Catamarca +- `America/Argentina/Cordoba` - America/Argentina/Cordoba +- `America/Argentina/Jujuy` - America/Argentina/Jujuy +- `America/Argentina/La_Rioja` - America/Argentina/La_Rioja +- `America/Argentina/Mendoza` - America/Argentina/Mendoza +- `America/Argentina/Rio_Gallegos` - America/Argentina/Rio_Gallegos +- `America/Argentina/Salta` - America/Argentina/Salta +- `America/Argentina/San_Juan` - America/Argentina/San_Juan +- `America/Argentina/San_Luis` - America/Argentina/San_Luis +- `America/Argentina/Tucuman` - America/Argentina/Tucuman +- `America/Argentina/Ushuaia` - America/Argentina/Ushuaia +- `America/Aruba` - America/Aruba +- `America/Asuncion` - America/Asuncion +- `America/Atikokan` - America/Atikokan +- `America/Bahia` - America/Bahia +- `America/Bahia_Banderas` - America/Bahia_Banderas +- `America/Barbados` - America/Barbados +- `America/Belem` - America/Belem +- `America/Belize` - America/Belize +- `America/Blanc-Sablon` - America/Blanc-Sablon +- `America/Boa_Vista` - America/Boa_Vista +- `America/Bogota` - America/Bogota +- `America/Boise` - America/Boise +- `America/Cambridge_Bay` - America/Cambridge_Bay +- `America/Campo_Grande` - America/Campo_Grande +- `America/Cancun` - America/Cancun +- `America/Caracas` - America/Caracas +- `America/Cayenne` - America/Cayenne +- `America/Cayman` - America/Cayman +- `America/Chicago` - America/Chicago +- `America/Chihuahua` - America/Chihuahua +- `America/Ciudad_Juarez` - America/Ciudad_Juarez +- `America/Costa_Rica` - America/Costa_Rica +- `America/Creston` - America/Creston +- `America/Cuiaba` - America/Cuiaba +- `America/Curacao` - America/Curacao +- `America/Danmarkshavn` - America/Danmarkshavn +- `America/Dawson` - America/Dawson +- `America/Dawson_Creek` - America/Dawson_Creek +- `America/Denver` - America/Denver +- `America/Detroit` - America/Detroit +- `America/Dominica` - America/Dominica +- `America/Edmonton` - America/Edmonton +- `America/Eirunepe` - America/Eirunepe +- `America/El_Salvador` - America/El_Salvador +- `America/Fort_Nelson` - America/Fort_Nelson +- `America/Fortaleza` - America/Fortaleza +- `America/Glace_Bay` - America/Glace_Bay +- `America/Goose_Bay` - America/Goose_Bay +- `America/Grand_Turk` - America/Grand_Turk +- `America/Grenada` - America/Grenada +- `America/Guadeloupe` - America/Guadeloupe +- `America/Guatemala` - America/Guatemala +- `America/Guayaquil` - America/Guayaquil +- `America/Guyana` - America/Guyana +- `America/Halifax` - America/Halifax +- `America/Havana` - America/Havana +- `America/Hermosillo` - America/Hermosillo +- `America/Indiana/Indianapolis` - America/Indiana/Indianapolis +- `America/Indiana/Knox` - America/Indiana/Knox +- `America/Indiana/Marengo` - America/Indiana/Marengo +- `America/Indiana/Petersburg` - America/Indiana/Petersburg +- `America/Indiana/Tell_City` - America/Indiana/Tell_City +- `America/Indiana/Vevay` - America/Indiana/Vevay +- `America/Indiana/Vincennes` - America/Indiana/Vincennes +- `America/Indiana/Winamac` - America/Indiana/Winamac +- `America/Inuvik` - America/Inuvik +- `America/Iqaluit` - America/Iqaluit +- `America/Jamaica` - America/Jamaica +- `America/Juneau` - America/Juneau +- `America/Kentucky/Louisville` - America/Kentucky/Louisville +- `America/Kentucky/Monticello` - America/Kentucky/Monticello +- `America/Kralendijk` - America/Kralendijk +- `America/La_Paz` - America/La_Paz +- `America/Lima` - America/Lima +- `America/Los_Angeles` - America/Los_Angeles +- `America/Lower_Princes` - America/Lower_Princes +- `America/Maceio` - America/Maceio +- `America/Managua` - America/Managua +- `America/Manaus` - America/Manaus +- `America/Marigot` - America/Marigot +- `America/Martinique` - America/Martinique +- `America/Matamoros` - America/Matamoros +- `America/Mazatlan` - America/Mazatlan +- `America/Menominee` - America/Menominee +- `America/Merida` - America/Merida +- `America/Metlakatla` - America/Metlakatla +- `America/Mexico_City` - America/Mexico_City +- `America/Miquelon` - America/Miquelon +- `America/Moncton` - America/Moncton +- `America/Monterrey` - America/Monterrey +- `America/Montevideo` - America/Montevideo +- `America/Montserrat` - America/Montserrat +- `America/Nassau` - America/Nassau +- `America/New_York` - America/New_York +- `America/Nome` - America/Nome +- `America/Noronha` - America/Noronha +- `America/North_Dakota/Beulah` - America/North_Dakota/Beulah +- `America/North_Dakota/Center` - America/North_Dakota/Center +- `America/North_Dakota/New_Salem` - America/North_Dakota/New_Salem +- `America/Nuuk` - America/Nuuk +- `America/Ojinaga` - America/Ojinaga +- `America/Panama` - America/Panama +- `America/Paramaribo` - America/Paramaribo +- `America/Phoenix` - America/Phoenix +- `America/Port-au-Prince` - America/Port-au-Prince +- `America/Port_of_Spain` - America/Port_of_Spain +- `America/Porto_Velho` - America/Porto_Velho +- `America/Puerto_Rico` - America/Puerto_Rico +- `America/Punta_Arenas` - America/Punta_Arenas +- `America/Rankin_Inlet` - America/Rankin_Inlet +- `America/Recife` - America/Recife +- `America/Regina` - America/Regina +- `America/Resolute` - America/Resolute +- `America/Rio_Branco` - America/Rio_Branco +- `America/Santarem` - America/Santarem +- `America/Santiago` - America/Santiago +- `America/Santo_Domingo` - America/Santo_Domingo +- `America/Sao_Paulo` - America/Sao_Paulo +- `America/Scoresbysund` - America/Scoresbysund +- `America/Sitka` - America/Sitka +- `America/St_Barthelemy` - America/St_Barthelemy +- `America/St_Johns` - America/St_Johns +- `America/St_Kitts` - America/St_Kitts +- `America/St_Lucia` - America/St_Lucia +- `America/St_Thomas` - America/St_Thomas +- `America/St_Vincent` - America/St_Vincent +- `America/Swift_Current` - America/Swift_Current +- `America/Tegucigalpa` - America/Tegucigalpa +- `America/Thule` - America/Thule +- `America/Tijuana` - America/Tijuana +- `America/Toronto` - America/Toronto +- `America/Tortola` - America/Tortola +- `America/Vancouver` - America/Vancouver +- `America/Whitehorse` - America/Whitehorse +- `America/Winnipeg` - America/Winnipeg +- `America/Yakutat` - America/Yakutat +- `Antarctica/Casey` - Antarctica/Casey +- `Antarctica/Davis` - Antarctica/Davis +- `Antarctica/DumontDUrville` - Antarctica/DumontDUrville +- `Antarctica/Macquarie` - Antarctica/Macquarie +- `Antarctica/Mawson` - Antarctica/Mawson +- `Antarctica/McMurdo` - Antarctica/McMurdo +- `Antarctica/Palmer` - Antarctica/Palmer +- `Antarctica/Rothera` - Antarctica/Rothera +- `Antarctica/Syowa` - Antarctica/Syowa +- `Antarctica/Troll` - Antarctica/Troll +- `Antarctica/Vostok` - Antarctica/Vostok +- `Arctic/Longyearbyen` - Arctic/Longyearbyen +- `Asia/Aden` - Asia/Aden +- `Asia/Almaty` - Asia/Almaty +- `Asia/Amman` - Asia/Amman +- `Asia/Anadyr` - Asia/Anadyr +- `Asia/Aqtau` - Asia/Aqtau +- `Asia/Aqtobe` - Asia/Aqtobe +- `Asia/Ashgabat` - Asia/Ashgabat +- `Asia/Atyrau` - Asia/Atyrau +- `Asia/Baghdad` - Asia/Baghdad +- `Asia/Bahrain` - Asia/Bahrain +- `Asia/Baku` - Asia/Baku +- `Asia/Bangkok` - Asia/Bangkok +- `Asia/Barnaul` - Asia/Barnaul +- `Asia/Beirut` - Asia/Beirut +- `Asia/Bishkek` - Asia/Bishkek +- `Asia/Brunei` - Asia/Brunei +- `Asia/Chita` - Asia/Chita +- `Asia/Choibalsan` - Asia/Choibalsan +- `Asia/Colombo` - Asia/Colombo +- `Asia/Damascus` - Asia/Damascus +- `Asia/Dhaka` - Asia/Dhaka +- `Asia/Dili` - Asia/Dili +- `Asia/Dubai` - Asia/Dubai +- `Asia/Dushanbe` - Asia/Dushanbe +- `Asia/Famagusta` - Asia/Famagusta +- `Asia/Gaza` - Asia/Gaza +- `Asia/Hebron` - Asia/Hebron +- `Asia/Ho_Chi_Minh` - Asia/Ho_Chi_Minh +- `Asia/Hong_Kong` - Asia/Hong_Kong +- `Asia/Hovd` - Asia/Hovd +- `Asia/Irkutsk` - Asia/Irkutsk +- `Asia/Jakarta` - Asia/Jakarta +- `Asia/Jayapura` - Asia/Jayapura +- `Asia/Jerusalem` - Asia/Jerusalem +- `Asia/Kabul` - Asia/Kabul +- `Asia/Kamchatka` - Asia/Kamchatka +- `Asia/Karachi` - Asia/Karachi +- `Asia/Kathmandu` - Asia/Kathmandu +- `Asia/Khandyga` - Asia/Khandyga +- `Asia/Kolkata` - Asia/Kolkata +- `Asia/Krasnoyarsk` - Asia/Krasnoyarsk +- `Asia/Kuala_Lumpur` - Asia/Kuala_Lumpur +- `Asia/Kuching` - Asia/Kuching +- `Asia/Kuwait` - Asia/Kuwait +- `Asia/Macau` - Asia/Macau +- `Asia/Magadan` - Asia/Magadan +- `Asia/Makassar` - Asia/Makassar +- `Asia/Manila` - Asia/Manila +- `Asia/Muscat` - Asia/Muscat +- `Asia/Nicosia` - Asia/Nicosia +- `Asia/Novokuznetsk` - Asia/Novokuznetsk +- `Asia/Novosibirsk` - Asia/Novosibirsk +- `Asia/Omsk` - Asia/Omsk +- `Asia/Oral` - Asia/Oral +- `Asia/Phnom_Penh` - Asia/Phnom_Penh +- `Asia/Pontianak` - Asia/Pontianak +- `Asia/Pyongyang` - Asia/Pyongyang +- `Asia/Qatar` - Asia/Qatar +- `Asia/Qostanay` - Asia/Qostanay +- `Asia/Qyzylorda` - Asia/Qyzylorda +- `Asia/Riyadh` - Asia/Riyadh +- `Asia/Sakhalin` - Asia/Sakhalin +- `Asia/Samarkand` - Asia/Samarkand +- `Asia/Seoul` - Asia/Seoul +- `Asia/Shanghai` - Asia/Shanghai +- `Asia/Singapore` - Asia/Singapore +- `Asia/Srednekolymsk` - Asia/Srednekolymsk +- `Asia/Taipei` - Asia/Taipei +- `Asia/Tashkent` - Asia/Tashkent +- `Asia/Tbilisi` - Asia/Tbilisi +- `Asia/Tehran` - Asia/Tehran +- `Asia/Thimphu` - Asia/Thimphu +- `Asia/Tokyo` - Asia/Tokyo +- `Asia/Tomsk` - Asia/Tomsk +- `Asia/Ulaanbaatar` - Asia/Ulaanbaatar +- `Asia/Urumqi` - Asia/Urumqi +- `Asia/Ust-Nera` - Asia/Ust-Nera +- `Asia/Vientiane` - Asia/Vientiane +- `Asia/Vladivostok` - Asia/Vladivostok +- `Asia/Yakutsk` - Asia/Yakutsk +- `Asia/Yangon` - Asia/Yangon +- `Asia/Yekaterinburg` - Asia/Yekaterinburg +- `Asia/Yerevan` - Asia/Yerevan +- `Atlantic/Azores` - Atlantic/Azores +- `Atlantic/Bermuda` - Atlantic/Bermuda +- `Atlantic/Canary` - Atlantic/Canary +- `Atlantic/Cape_Verde` - Atlantic/Cape_Verde +- `Atlantic/Faroe` - Atlantic/Faroe +- `Atlantic/Madeira` - Atlantic/Madeira +- `Atlantic/Reykjavik` - Atlantic/Reykjavik +- `Atlantic/South_Georgia` - Atlantic/South_Georgia +- `Atlantic/St_Helena` - Atlantic/St_Helena +- `Atlantic/Stanley` - Atlantic/Stanley +- `Australia/Adelaide` - Australia/Adelaide +- `Australia/Brisbane` - Australia/Brisbane +- `Australia/Broken_Hill` - Australia/Broken_Hill +- `Australia/Darwin` - Australia/Darwin +- `Australia/Eucla` - Australia/Eucla +- `Australia/Hobart` - Australia/Hobart +- `Australia/Lindeman` - Australia/Lindeman +- `Australia/Lord_Howe` - Australia/Lord_Howe +- `Australia/Melbourne` - Australia/Melbourne +- `Australia/Perth` - Australia/Perth +- `Australia/Sydney` - Australia/Sydney +- `Canada/Atlantic` - Canada/Atlantic +- `Canada/Central` - Canada/Central +- `Canada/Eastern` - Canada/Eastern +- `Canada/Mountain` - Canada/Mountain +- `Canada/Newfoundland` - Canada/Newfoundland +- `Canada/Pacific` - Canada/Pacific +- `Europe/Amsterdam` - Europe/Amsterdam +- `Europe/Andorra` - Europe/Andorra +- `Europe/Astrakhan` - Europe/Astrakhan +- `Europe/Athens` - Europe/Athens +- `Europe/Belgrade` - Europe/Belgrade +- `Europe/Berlin` - Europe/Berlin +- `Europe/Bratislava` - Europe/Bratislava +- `Europe/Brussels` - Europe/Brussels +- `Europe/Bucharest` - Europe/Bucharest +- `Europe/Budapest` - Europe/Budapest +- `Europe/Busingen` - Europe/Busingen +- `Europe/Chisinau` - Europe/Chisinau +- `Europe/Copenhagen` - Europe/Copenhagen +- `Europe/Dublin` - Europe/Dublin +- `Europe/Gibraltar` - Europe/Gibraltar +- `Europe/Guernsey` - Europe/Guernsey +- `Europe/Helsinki` - Europe/Helsinki +- `Europe/Isle_of_Man` - Europe/Isle_of_Man +- `Europe/Istanbul` - Europe/Istanbul +- `Europe/Jersey` - Europe/Jersey +- `Europe/Kaliningrad` - Europe/Kaliningrad +- `Europe/Kirov` - Europe/Kirov +- `Europe/Kyiv` - Europe/Kyiv +- `Europe/Lisbon` - Europe/Lisbon +- `Europe/Ljubljana` - Europe/Ljubljana +- `Europe/London` - Europe/London +- `Europe/Luxembourg` - Europe/Luxembourg +- `Europe/Madrid` - Europe/Madrid +- `Europe/Malta` - Europe/Malta +- `Europe/Mariehamn` - Europe/Mariehamn +- `Europe/Minsk` - Europe/Minsk +- `Europe/Monaco` - Europe/Monaco +- `Europe/Moscow` - Europe/Moscow +- `Europe/Oslo` - Europe/Oslo +- `Europe/Paris` - Europe/Paris +- `Europe/Podgorica` - Europe/Podgorica +- `Europe/Prague` - Europe/Prague +- `Europe/Riga` - Europe/Riga +- `Europe/Rome` - Europe/Rome +- `Europe/Samara` - Europe/Samara +- `Europe/San_Marino` - Europe/San_Marino +- `Europe/Sarajevo` - Europe/Sarajevo +- `Europe/Saratov` - Europe/Saratov +- `Europe/Simferopol` - Europe/Simferopol +- `Europe/Skopje` - Europe/Skopje +- `Europe/Sofia` - Europe/Sofia +- `Europe/Stockholm` - Europe/Stockholm +- `Europe/Tallinn` - Europe/Tallinn +- `Europe/Tirane` - Europe/Tirane +- `Europe/Ulyanovsk` - Europe/Ulyanovsk +- `Europe/Vaduz` - Europe/Vaduz +- `Europe/Vatican` - Europe/Vatican +- `Europe/Vienna` - Europe/Vienna +- `Europe/Vilnius` - Europe/Vilnius +- `Europe/Volgograd` - Europe/Volgograd +- `Europe/Warsaw` - Europe/Warsaw +- `Europe/Zagreb` - Europe/Zagreb +- `Europe/Zurich` - Europe/Zurich +- `GMT` - GMT +- `Indian/Antananarivo` - Indian/Antananarivo +- `Indian/Chagos` - Indian/Chagos +- `Indian/Christmas` - Indian/Christmas +- `Indian/Cocos` - Indian/Cocos +- `Indian/Comoro` - Indian/Comoro +- `Indian/Kerguelen` - Indian/Kerguelen +- `Indian/Mahe` - Indian/Mahe +- `Indian/Maldives` - Indian/Maldives +- `Indian/Mauritius` - Indian/Mauritius +- `Indian/Mayotte` - Indian/Mayotte +- `Indian/Reunion` - Indian/Reunion +- `Pacific/Apia` - Pacific/Apia +- `Pacific/Auckland` - Pacific/Auckland +- `Pacific/Bougainville` - Pacific/Bougainville +- `Pacific/Chatham` - Pacific/Chatham +- `Pacific/Chuuk` - Pacific/Chuuk +- `Pacific/Easter` - Pacific/Easter +- `Pacific/Efate` - Pacific/Efate +- `Pacific/Fakaofo` - Pacific/Fakaofo +- `Pacific/Fiji` - Pacific/Fiji +- `Pacific/Funafuti` - Pacific/Funafuti +- `Pacific/Galapagos` - Pacific/Galapagos +- `Pacific/Gambier` - Pacific/Gambier +- `Pacific/Guadalcanal` - Pacific/Guadalcanal +- `Pacific/Guam` - Pacific/Guam +- `Pacific/Honolulu` - Pacific/Honolulu +- `Pacific/Kanton` - Pacific/Kanton +- `Pacific/Kiritimati` - Pacific/Kiritimati +- `Pacific/Kosrae` - Pacific/Kosrae +- `Pacific/Kwajalein` - Pacific/Kwajalein +- `Pacific/Majuro` - Pacific/Majuro +- `Pacific/Marquesas` - Pacific/Marquesas +- `Pacific/Midway` - Pacific/Midway +- `Pacific/Nauru` - Pacific/Nauru +- `Pacific/Niue` - Pacific/Niue +- `Pacific/Norfolk` - Pacific/Norfolk +- `Pacific/Noumea` - Pacific/Noumea +- `Pacific/Pago_Pago` - Pacific/Pago_Pago +- `Pacific/Palau` - Pacific/Palau +- `Pacific/Pitcairn` - Pacific/Pitcairn +- `Pacific/Pohnpei` - Pacific/Pohnpei +- `Pacific/Port_Moresby` - Pacific/Port_Moresby +- `Pacific/Rarotonga` - Pacific/Rarotonga +- `Pacific/Saipan` - Pacific/Saipan +- `Pacific/Tahiti` - Pacific/Tahiti +- `Pacific/Tarawa` - Pacific/Tarawa +- `Pacific/Tongatapu` - Pacific/Tongatapu +- `Pacific/Wake` - Pacific/Wake +- `Pacific/Wallis` - Pacific/Wallis +- `US/Alaska` - US/Alaska +- `US/Arizona` - US/Arizona +- `US/Central` - US/Central +- `US/Eastern` - US/Eastern +- `US/Hawaii` - US/Hawaii +- `US/Mountain` - US/Mountain +- `US/Pacific` - US/Pacific +- `UTC` - UTC + + + + + +External source. + + + + + +External id. + + + + + +Is issue type enabled. + + + + + +Is time tracking enabled. + + + +
+
+ +
+ +### Scopes + +`projects:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "identifier": "PROJ-123", + "network": 2, + "project_lead": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project/archive-project.md b/apps/developer-docs/docs/api-reference/project/archive-project.md new file mode 100644 index 00000000..0c02ce6b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project/archive-project.md @@ -0,0 +1,102 @@ +--- +title: Archive project +description: Archive project via Plane API. HTTP request format, parameters, scopes, and example responses for archive project. +keywords: plane, plane api, rest api, api integration, project, archive project +--- + +# Archive project + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/archive/ +
+ +
+
+ +Move a project to archived status, hiding it from active project lists. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project/create-project-with-template.md b/apps/developer-docs/docs/api-reference/project/create-project-with-template.md new file mode 100644 index 00000000..d37c3eda --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project/create-project-with-template.md @@ -0,0 +1,178 @@ +--- +title: Create project with template +description: Create a project from an existing project template via Plane API. HTTP request format, parameters, scopes, and example responses for create project with template. +keywords: plane, plane api, rest api, api integration, project, create project with template, project template +--- + +# Create project with template + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/templates/use/ +
+ +
+
+ +Create a new project from an existing project template. The template's states, labels, estimates, modules, and work items are copied into the new project. Fields provided in the request body override the template defaults. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +The ID of the project template to instantiate. Must belong to the same workspace. + + + + + +Name of the new project. Overrides the template default. + + + + + +Short identifier for the project (e.g. `MAR`). Overrides the template default. + + + + + +Description of the new project. Overrides the template default. + + + + + +Network visibility of the project. `0` for secret, `2` for public. Overrides the template default. + + + + + +User ID of the project lead. The lead is added as a project admin. Overrides the template default. + + + +
+
+ +
+ +### Scopes + +`write` or `projects:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Mobile App Revamp", + "description": "Project created from the Agile Project Setup template", + "identifier": "MAR", + "network": 2, + "project_lead": "0d8d8869-3ed1-4fb4-b5c4-ff672888f5e2", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project/delete-project.md b/apps/developer-docs/docs/api-reference/project/delete-project.md new file mode 100644 index 00000000..1c710ad1 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project/delete-project.md @@ -0,0 +1,102 @@ +--- +title: Delete a project +description: Delete a project via Plane API. HTTP request format, parameters, scopes, and example responses for delete a project. +keywords: plane, plane api, rest api, api integration, project, delete a project +--- + +# Delete a project + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{resource_id}/ +
+ +
+
+ +Permanently remove a project and all its associated data from the workspace. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project/get-project-detail.md b/apps/developer-docs/docs/api-reference/project/get-project-detail.md new file mode 100644 index 00000000..9057bf8e --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project/get-project-detail.md @@ -0,0 +1,113 @@ +--- +title: Retrieve a project +description: Retrieve a project via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a project. +keywords: plane, plane api, rest api, api integration, project, retrieve a project +--- + +# Retrieve a project + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{resource_id}/ +
+ +
+
+ +Retrieve details of a specific project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "identifier": "PROJ-123", + "network": 2, + "project_lead": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project/list-projects.md b/apps/developer-docs/docs/api-reference/project/list-projects.md new file mode 100644 index 00000000..40bb64d0 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project/list-projects.md @@ -0,0 +1,158 @@ +--- +title: List all projects +description: List all projects via Plane API. HTTP request format, parameters, scopes, and example responses for list all projects. +keywords: plane, plane api, rest api, api integration, project, list all projects +--- + +# List all projects + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/ +
+ +
+
+ +Retrieve all projects in a workspace or get details of a specific project. + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "identifier": "PROJ-123", + "network": 2 + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project/overview.md b/apps/developer-docs/docs/api-reference/project/overview.md new file mode 100644 index 00000000..321de20e --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project/overview.md @@ -0,0 +1,183 @@ +--- +title: Overview +description: Plane Project API overview. Learn about endpoints, request/response format, and how to work with project via REST API. +keywords: plane, plane api, rest api, api integration, projects, project management +--- + +# Overview + +Projects organize your team's work within a workspace. Each project contains work items, cycles, modules, and other resources. + +[Learn more about Projects](https://docs.plane.so/core-concepts/projects/overview) + +
+
+ +### The Project Object + +### Attributes + +- `name` _string_ (**required**) + + Name of the project + +- `identifier` _string_ (**required**) + + Unique Identifier of project for the workspace + +- `description` _string_ + + Project description + +- `total_members` _integer_ + + Total members present in the project. + +- `total_cycles` _integer_ + + Total number of cycles present in the project. + +- `total_modules` _integer_ + + Total number of modules present in the project. + +- `is_member` _boolean_ + + The current requesting user is a member of the project or not + +- `member_role` _integer_ + + The current requesting users role in the project. + +- `is_deployed` _integer_ + + Represents if the project is deployed and publicly visible. + +- `created_at` _timestamp_ + + The timestamp of the time when the project was created + +- `updated_at` _timestamp_ + + The timestamp of the time when the project was last updated + +- `network` _integer_ + + Is the project public or secret it takes in two values either (0,2) + - **0 - Secret** + - **2 - Public** + +- `emoji` _string_ + + HTML emoji DEX code without the `&#` + +- `icon_prop` _json_ + + saves the data of the project icon + +- `module_view` _bool_ + + Enable disable module for the project in the UI + +- `cycle_view` _bool_ + + Enable disable cycle for the project in the UI + +- `inbox_view` _bool_ + + Enable disable intake for the project in the UI + +- `page_view` _bool_ + + Enable disable pages for the project in the UI + +- `issue_views_view` _bool_ + + Enable disable project views for the project in the UI + +- `cover_image` _url_ + + URL for the image for the project cover + +- `archive_in` _integer_ + + Months in which the issue should be automatically archived can take values between (0,12) + +- `close_in` _integer_ + + Months in which the issue should be auto closed can take values between (0,12) + +- `created_by` , `updated_by` _uuid_ + + This values are auto saved and represent the id of the user that created or the updated the project + +- `workspace` _uuid_ + + The workspace uuid where the project is created saved automatically + +- `default_assignee` _uuid_ + + The uuid of the user who is a workspace member that have issues assigned automatically if the issue does not have any assignee + +- `project_lead` _uuid_ + + The uuid of the user who is a workspace member that leads the project + +- `estimate` _uuid_ + + UUID of the estimate of the project + +- `default_state` + + Default state which will be used when the issues will be auto closed + +- `template_id` _uuid_ + + UUID of the project template used to create this project. + +
+
+ + + +```json +{ + "id": "00918ea1-52f7-48bd-abe3-d3efe76ff7dd", + "total_members": 1, + "total_cycles": 0, + "total_modules": 0, + "is_member": true, + "member_role": 20, + "is_deployed": false, + "created_at": "2023-11-19T10:40:15.426652Z", + "updated_at": "2023-11-19T10:40:15.426672Z", + "name": "Project X", + "description": "", + "description_text": null, + "description_html": null, + "network": 2, + "identifier": "PROJX", + "emoji": null, + "icon_prop": null, + "module_view": true, + "cycle_view": true, + "issue_views_view": true, + "page_view": true, + "inbox_view": false, + "cover_image": null, + "archive_in": 0, + "close_in": 0, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4", + "default_assignee": null, + "project_lead": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "estimate": null, + "default_state": null +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/project/unarchive-project.md b/apps/developer-docs/docs/api-reference/project/unarchive-project.md new file mode 100644 index 00000000..9061ffd6 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project/unarchive-project.md @@ -0,0 +1,102 @@ +--- +title: Unarchive project +description: Unarchive project via Plane API. HTTP request format, parameters, scopes, and example responses for unarchive project. +keywords: plane, plane api, rest api, api integration, project, unarchive project +--- + +# Unarchive project + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/archive/ +
+ +
+
+ +Restore an archived project to active status, making it available in regular workflows. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/project/update-project-detail.md b/apps/developer-docs/docs/api-reference/project/update-project-detail.md new file mode 100644 index 00000000..75092dcd --- /dev/null +++ b/apps/developer-docs/docs/api-reference/project/update-project-detail.md @@ -0,0 +1,712 @@ +--- +title: Update a project +description: Update a project via Plane API. HTTP request format, parameters, scopes, and example responses for update a project. +keywords: plane, plane api, rest api, api integration, project, update a project +--- + +# Update a project + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{resource_id}/ +
+ +
+
+ +Partially update an existing project's properties like name, description, or settings. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Project lead. + + + + + +Default assignee. + + + + + +Identifier. + + + + + +Icon prop. + + + + + +Emoji. + + + + + +Cover image. + + + + + +Module view. + + + + + +Cycle view. + + + + + +Issue views view. + + + + + +Page view. + + + + + +Intake view. + + + + + +Guest view all features. + + + + + +Archive in. + + + + + +Close in. + + + + + +- `Africa/Abidjan` - Africa/Abidjan +- `Africa/Accra` - Africa/Accra +- `Africa/Addis_Ababa` - Africa/Addis_Ababa +- `Africa/Algiers` - Africa/Algiers +- `Africa/Asmara` - Africa/Asmara +- `Africa/Bamako` - Africa/Bamako +- `Africa/Bangui` - Africa/Bangui +- `Africa/Banjul` - Africa/Banjul +- `Africa/Bissau` - Africa/Bissau +- `Africa/Blantyre` - Africa/Blantyre +- `Africa/Brazzaville` - Africa/Brazzaville +- `Africa/Bujumbura` - Africa/Bujumbura +- `Africa/Cairo` - Africa/Cairo +- `Africa/Casablanca` - Africa/Casablanca +- `Africa/Ceuta` - Africa/Ceuta +- `Africa/Conakry` - Africa/Conakry +- `Africa/Dakar` - Africa/Dakar +- `Africa/Dar_es_Salaam` - Africa/Dar_es_Salaam +- `Africa/Djibouti` - Africa/Djibouti +- `Africa/Douala` - Africa/Douala +- `Africa/El_Aaiun` - Africa/El_Aaiun +- `Africa/Freetown` - Africa/Freetown +- `Africa/Gaborone` - Africa/Gaborone +- `Africa/Harare` - Africa/Harare +- `Africa/Johannesburg` - Africa/Johannesburg +- `Africa/Juba` - Africa/Juba +- `Africa/Kampala` - Africa/Kampala +- `Africa/Khartoum` - Africa/Khartoum +- `Africa/Kigali` - Africa/Kigali +- `Africa/Kinshasa` - Africa/Kinshasa +- `Africa/Lagos` - Africa/Lagos +- `Africa/Libreville` - Africa/Libreville +- `Africa/Lome` - Africa/Lome +- `Africa/Luanda` - Africa/Luanda +- `Africa/Lubumbashi` - Africa/Lubumbashi +- `Africa/Lusaka` - Africa/Lusaka +- `Africa/Malabo` - Africa/Malabo +- `Africa/Maputo` - Africa/Maputo +- `Africa/Maseru` - Africa/Maseru +- `Africa/Mbabane` - Africa/Mbabane +- `Africa/Mogadishu` - Africa/Mogadishu +- `Africa/Monrovia` - Africa/Monrovia +- `Africa/Nairobi` - Africa/Nairobi +- `Africa/Ndjamena` - Africa/Ndjamena +- `Africa/Niamey` - Africa/Niamey +- `Africa/Nouakchott` - Africa/Nouakchott +- `Africa/Ouagadougou` - Africa/Ouagadougou +- `Africa/Porto-Novo` - Africa/Porto-Novo +- `Africa/Sao_Tome` - Africa/Sao_Tome +- `Africa/Tripoli` - Africa/Tripoli +- `Africa/Tunis` - Africa/Tunis +- `Africa/Windhoek` - Africa/Windhoek +- `America/Adak` - America/Adak +- `America/Anchorage` - America/Anchorage +- `America/Anguilla` - America/Anguilla +- `America/Antigua` - America/Antigua +- `America/Araguaina` - America/Araguaina +- `America/Argentina/Buenos_Aires` - America/Argentina/Buenos_Aires +- `America/Argentina/Catamarca` - America/Argentina/Catamarca +- `America/Argentina/Cordoba` - America/Argentina/Cordoba +- `America/Argentina/Jujuy` - America/Argentina/Jujuy +- `America/Argentina/La_Rioja` - America/Argentina/La_Rioja +- `America/Argentina/Mendoza` - America/Argentina/Mendoza +- `America/Argentina/Rio_Gallegos` - America/Argentina/Rio_Gallegos +- `America/Argentina/Salta` - America/Argentina/Salta +- `America/Argentina/San_Juan` - America/Argentina/San_Juan +- `America/Argentina/San_Luis` - America/Argentina/San_Luis +- `America/Argentina/Tucuman` - America/Argentina/Tucuman +- `America/Argentina/Ushuaia` - America/Argentina/Ushuaia +- `America/Aruba` - America/Aruba +- `America/Asuncion` - America/Asuncion +- `America/Atikokan` - America/Atikokan +- `America/Bahia` - America/Bahia +- `America/Bahia_Banderas` - America/Bahia_Banderas +- `America/Barbados` - America/Barbados +- `America/Belem` - America/Belem +- `America/Belize` - America/Belize +- `America/Blanc-Sablon` - America/Blanc-Sablon +- `America/Boa_Vista` - America/Boa_Vista +- `America/Bogota` - America/Bogota +- `America/Boise` - America/Boise +- `America/Cambridge_Bay` - America/Cambridge_Bay +- `America/Campo_Grande` - America/Campo_Grande +- `America/Cancun` - America/Cancun +- `America/Caracas` - America/Caracas +- `America/Cayenne` - America/Cayenne +- `America/Cayman` - America/Cayman +- `America/Chicago` - America/Chicago +- `America/Chihuahua` - America/Chihuahua +- `America/Ciudad_Juarez` - America/Ciudad_Juarez +- `America/Costa_Rica` - America/Costa_Rica +- `America/Creston` - America/Creston +- `America/Cuiaba` - America/Cuiaba +- `America/Curacao` - America/Curacao +- `America/Danmarkshavn` - America/Danmarkshavn +- `America/Dawson` - America/Dawson +- `America/Dawson_Creek` - America/Dawson_Creek +- `America/Denver` - America/Denver +- `America/Detroit` - America/Detroit +- `America/Dominica` - America/Dominica +- `America/Edmonton` - America/Edmonton +- `America/Eirunepe` - America/Eirunepe +- `America/El_Salvador` - America/El_Salvador +- `America/Fort_Nelson` - America/Fort_Nelson +- `America/Fortaleza` - America/Fortaleza +- `America/Glace_Bay` - America/Glace_Bay +- `America/Goose_Bay` - America/Goose_Bay +- `America/Grand_Turk` - America/Grand_Turk +- `America/Grenada` - America/Grenada +- `America/Guadeloupe` - America/Guadeloupe +- `America/Guatemala` - America/Guatemala +- `America/Guayaquil` - America/Guayaquil +- `America/Guyana` - America/Guyana +- `America/Halifax` - America/Halifax +- `America/Havana` - America/Havana +- `America/Hermosillo` - America/Hermosillo +- `America/Indiana/Indianapolis` - America/Indiana/Indianapolis +- `America/Indiana/Knox` - America/Indiana/Knox +- `America/Indiana/Marengo` - America/Indiana/Marengo +- `America/Indiana/Petersburg` - America/Indiana/Petersburg +- `America/Indiana/Tell_City` - America/Indiana/Tell_City +- `America/Indiana/Vevay` - America/Indiana/Vevay +- `America/Indiana/Vincennes` - America/Indiana/Vincennes +- `America/Indiana/Winamac` - America/Indiana/Winamac +- `America/Inuvik` - America/Inuvik +- `America/Iqaluit` - America/Iqaluit +- `America/Jamaica` - America/Jamaica +- `America/Juneau` - America/Juneau +- `America/Kentucky/Louisville` - America/Kentucky/Louisville +- `America/Kentucky/Monticello` - America/Kentucky/Monticello +- `America/Kralendijk` - America/Kralendijk +- `America/La_Paz` - America/La_Paz +- `America/Lima` - America/Lima +- `America/Los_Angeles` - America/Los_Angeles +- `America/Lower_Princes` - America/Lower_Princes +- `America/Maceio` - America/Maceio +- `America/Managua` - America/Managua +- `America/Manaus` - America/Manaus +- `America/Marigot` - America/Marigot +- `America/Martinique` - America/Martinique +- `America/Matamoros` - America/Matamoros +- `America/Mazatlan` - America/Mazatlan +- `America/Menominee` - America/Menominee +- `America/Merida` - America/Merida +- `America/Metlakatla` - America/Metlakatla +- `America/Mexico_City` - America/Mexico_City +- `America/Miquelon` - America/Miquelon +- `America/Moncton` - America/Moncton +- `America/Monterrey` - America/Monterrey +- `America/Montevideo` - America/Montevideo +- `America/Montserrat` - America/Montserrat +- `America/Nassau` - America/Nassau +- `America/New_York` - America/New_York +- `America/Nome` - America/Nome +- `America/Noronha` - America/Noronha +- `America/North_Dakota/Beulah` - America/North_Dakota/Beulah +- `America/North_Dakota/Center` - America/North_Dakota/Center +- `America/North_Dakota/New_Salem` - America/North_Dakota/New_Salem +- `America/Nuuk` - America/Nuuk +- `America/Ojinaga` - America/Ojinaga +- `America/Panama` - America/Panama +- `America/Paramaribo` - America/Paramaribo +- `America/Phoenix` - America/Phoenix +- `America/Port-au-Prince` - America/Port-au-Prince +- `America/Port_of_Spain` - America/Port_of_Spain +- `America/Porto_Velho` - America/Porto_Velho +- `America/Puerto_Rico` - America/Puerto_Rico +- `America/Punta_Arenas` - America/Punta_Arenas +- `America/Rankin_Inlet` - America/Rankin_Inlet +- `America/Recife` - America/Recife +- `America/Regina` - America/Regina +- `America/Resolute` - America/Resolute +- `America/Rio_Branco` - America/Rio_Branco +- `America/Santarem` - America/Santarem +- `America/Santiago` - America/Santiago +- `America/Santo_Domingo` - America/Santo_Domingo +- `America/Sao_Paulo` - America/Sao_Paulo +- `America/Scoresbysund` - America/Scoresbysund +- `America/Sitka` - America/Sitka +- `America/St_Barthelemy` - America/St_Barthelemy +- `America/St_Johns` - America/St_Johns +- `America/St_Kitts` - America/St_Kitts +- `America/St_Lucia` - America/St_Lucia +- `America/St_Thomas` - America/St_Thomas +- `America/St_Vincent` - America/St_Vincent +- `America/Swift_Current` - America/Swift_Current +- `America/Tegucigalpa` - America/Tegucigalpa +- `America/Thule` - America/Thule +- `America/Tijuana` - America/Tijuana +- `America/Toronto` - America/Toronto +- `America/Tortola` - America/Tortola +- `America/Vancouver` - America/Vancouver +- `America/Whitehorse` - America/Whitehorse +- `America/Winnipeg` - America/Winnipeg +- `America/Yakutat` - America/Yakutat +- `Antarctica/Casey` - Antarctica/Casey +- `Antarctica/Davis` - Antarctica/Davis +- `Antarctica/DumontDUrville` - Antarctica/DumontDUrville +- `Antarctica/Macquarie` - Antarctica/Macquarie +- `Antarctica/Mawson` - Antarctica/Mawson +- `Antarctica/McMurdo` - Antarctica/McMurdo +- `Antarctica/Palmer` - Antarctica/Palmer +- `Antarctica/Rothera` - Antarctica/Rothera +- `Antarctica/Syowa` - Antarctica/Syowa +- `Antarctica/Troll` - Antarctica/Troll +- `Antarctica/Vostok` - Antarctica/Vostok +- `Arctic/Longyearbyen` - Arctic/Longyearbyen +- `Asia/Aden` - Asia/Aden +- `Asia/Almaty` - Asia/Almaty +- `Asia/Amman` - Asia/Amman +- `Asia/Anadyr` - Asia/Anadyr +- `Asia/Aqtau` - Asia/Aqtau +- `Asia/Aqtobe` - Asia/Aqtobe +- `Asia/Ashgabat` - Asia/Ashgabat +- `Asia/Atyrau` - Asia/Atyrau +- `Asia/Baghdad` - Asia/Baghdad +- `Asia/Bahrain` - Asia/Bahrain +- `Asia/Baku` - Asia/Baku +- `Asia/Bangkok` - Asia/Bangkok +- `Asia/Barnaul` - Asia/Barnaul +- `Asia/Beirut` - Asia/Beirut +- `Asia/Bishkek` - Asia/Bishkek +- `Asia/Brunei` - Asia/Brunei +- `Asia/Chita` - Asia/Chita +- `Asia/Choibalsan` - Asia/Choibalsan +- `Asia/Colombo` - Asia/Colombo +- `Asia/Damascus` - Asia/Damascus +- `Asia/Dhaka` - Asia/Dhaka +- `Asia/Dili` - Asia/Dili +- `Asia/Dubai` - Asia/Dubai +- `Asia/Dushanbe` - Asia/Dushanbe +- `Asia/Famagusta` - Asia/Famagusta +- `Asia/Gaza` - Asia/Gaza +- `Asia/Hebron` - Asia/Hebron +- `Asia/Ho_Chi_Minh` - Asia/Ho_Chi_Minh +- `Asia/Hong_Kong` - Asia/Hong_Kong +- `Asia/Hovd` - Asia/Hovd +- `Asia/Irkutsk` - Asia/Irkutsk +- `Asia/Jakarta` - Asia/Jakarta +- `Asia/Jayapura` - Asia/Jayapura +- `Asia/Jerusalem` - Asia/Jerusalem +- `Asia/Kabul` - Asia/Kabul +- `Asia/Kamchatka` - Asia/Kamchatka +- `Asia/Karachi` - Asia/Karachi +- `Asia/Kathmandu` - Asia/Kathmandu +- `Asia/Khandyga` - Asia/Khandyga +- `Asia/Kolkata` - Asia/Kolkata +- `Asia/Krasnoyarsk` - Asia/Krasnoyarsk +- `Asia/Kuala_Lumpur` - Asia/Kuala_Lumpur +- `Asia/Kuching` - Asia/Kuching +- `Asia/Kuwait` - Asia/Kuwait +- `Asia/Macau` - Asia/Macau +- `Asia/Magadan` - Asia/Magadan +- `Asia/Makassar` - Asia/Makassar +- `Asia/Manila` - Asia/Manila +- `Asia/Muscat` - Asia/Muscat +- `Asia/Nicosia` - Asia/Nicosia +- `Asia/Novokuznetsk` - Asia/Novokuznetsk +- `Asia/Novosibirsk` - Asia/Novosibirsk +- `Asia/Omsk` - Asia/Omsk +- `Asia/Oral` - Asia/Oral +- `Asia/Phnom_Penh` - Asia/Phnom_Penh +- `Asia/Pontianak` - Asia/Pontianak +- `Asia/Pyongyang` - Asia/Pyongyang +- `Asia/Qatar` - Asia/Qatar +- `Asia/Qostanay` - Asia/Qostanay +- `Asia/Qyzylorda` - Asia/Qyzylorda +- `Asia/Riyadh` - Asia/Riyadh +- `Asia/Sakhalin` - Asia/Sakhalin +- `Asia/Samarkand` - Asia/Samarkand +- `Asia/Seoul` - Asia/Seoul +- `Asia/Shanghai` - Asia/Shanghai +- `Asia/Singapore` - Asia/Singapore +- `Asia/Srednekolymsk` - Asia/Srednekolymsk +- `Asia/Taipei` - Asia/Taipei +- `Asia/Tashkent` - Asia/Tashkent +- `Asia/Tbilisi` - Asia/Tbilisi +- `Asia/Tehran` - Asia/Tehran +- `Asia/Thimphu` - Asia/Thimphu +- `Asia/Tokyo` - Asia/Tokyo +- `Asia/Tomsk` - Asia/Tomsk +- `Asia/Ulaanbaatar` - Asia/Ulaanbaatar +- `Asia/Urumqi` - Asia/Urumqi +- `Asia/Ust-Nera` - Asia/Ust-Nera +- `Asia/Vientiane` - Asia/Vientiane +- `Asia/Vladivostok` - Asia/Vladivostok +- `Asia/Yakutsk` - Asia/Yakutsk +- `Asia/Yangon` - Asia/Yangon +- `Asia/Yekaterinburg` - Asia/Yekaterinburg +- `Asia/Yerevan` - Asia/Yerevan +- `Atlantic/Azores` - Atlantic/Azores +- `Atlantic/Bermuda` - Atlantic/Bermuda +- `Atlantic/Canary` - Atlantic/Canary +- `Atlantic/Cape_Verde` - Atlantic/Cape_Verde +- `Atlantic/Faroe` - Atlantic/Faroe +- `Atlantic/Madeira` - Atlantic/Madeira +- `Atlantic/Reykjavik` - Atlantic/Reykjavik +- `Atlantic/South_Georgia` - Atlantic/South_Georgia +- `Atlantic/St_Helena` - Atlantic/St_Helena +- `Atlantic/Stanley` - Atlantic/Stanley +- `Australia/Adelaide` - Australia/Adelaide +- `Australia/Brisbane` - Australia/Brisbane +- `Australia/Broken_Hill` - Australia/Broken_Hill +- `Australia/Darwin` - Australia/Darwin +- `Australia/Eucla` - Australia/Eucla +- `Australia/Hobart` - Australia/Hobart +- `Australia/Lindeman` - Australia/Lindeman +- `Australia/Lord_Howe` - Australia/Lord_Howe +- `Australia/Melbourne` - Australia/Melbourne +- `Australia/Perth` - Australia/Perth +- `Australia/Sydney` - Australia/Sydney +- `Canada/Atlantic` - Canada/Atlantic +- `Canada/Central` - Canada/Central +- `Canada/Eastern` - Canada/Eastern +- `Canada/Mountain` - Canada/Mountain +- `Canada/Newfoundland` - Canada/Newfoundland +- `Canada/Pacific` - Canada/Pacific +- `Europe/Amsterdam` - Europe/Amsterdam +- `Europe/Andorra` - Europe/Andorra +- `Europe/Astrakhan` - Europe/Astrakhan +- `Europe/Athens` - Europe/Athens +- `Europe/Belgrade` - Europe/Belgrade +- `Europe/Berlin` - Europe/Berlin +- `Europe/Bratislava` - Europe/Bratislava +- `Europe/Brussels` - Europe/Brussels +- `Europe/Bucharest` - Europe/Bucharest +- `Europe/Budapest` - Europe/Budapest +- `Europe/Busingen` - Europe/Busingen +- `Europe/Chisinau` - Europe/Chisinau +- `Europe/Copenhagen` - Europe/Copenhagen +- `Europe/Dublin` - Europe/Dublin +- `Europe/Gibraltar` - Europe/Gibraltar +- `Europe/Guernsey` - Europe/Guernsey +- `Europe/Helsinki` - Europe/Helsinki +- `Europe/Isle_of_Man` - Europe/Isle_of_Man +- `Europe/Istanbul` - Europe/Istanbul +- `Europe/Jersey` - Europe/Jersey +- `Europe/Kaliningrad` - Europe/Kaliningrad +- `Europe/Kirov` - Europe/Kirov +- `Europe/Kyiv` - Europe/Kyiv +- `Europe/Lisbon` - Europe/Lisbon +- `Europe/Ljubljana` - Europe/Ljubljana +- `Europe/London` - Europe/London +- `Europe/Luxembourg` - Europe/Luxembourg +- `Europe/Madrid` - Europe/Madrid +- `Europe/Malta` - Europe/Malta +- `Europe/Mariehamn` - Europe/Mariehamn +- `Europe/Minsk` - Europe/Minsk +- `Europe/Monaco` - Europe/Monaco +- `Europe/Moscow` - Europe/Moscow +- `Europe/Oslo` - Europe/Oslo +- `Europe/Paris` - Europe/Paris +- `Europe/Podgorica` - Europe/Podgorica +- `Europe/Prague` - Europe/Prague +- `Europe/Riga` - Europe/Riga +- `Europe/Rome` - Europe/Rome +- `Europe/Samara` - Europe/Samara +- `Europe/San_Marino` - Europe/San_Marino +- `Europe/Sarajevo` - Europe/Sarajevo +- `Europe/Saratov` - Europe/Saratov +- `Europe/Simferopol` - Europe/Simferopol +- `Europe/Skopje` - Europe/Skopje +- `Europe/Sofia` - Europe/Sofia +- `Europe/Stockholm` - Europe/Stockholm +- `Europe/Tallinn` - Europe/Tallinn +- `Europe/Tirane` - Europe/Tirane +- `Europe/Ulyanovsk` - Europe/Ulyanovsk +- `Europe/Vaduz` - Europe/Vaduz +- `Europe/Vatican` - Europe/Vatican +- `Europe/Vienna` - Europe/Vienna +- `Europe/Vilnius` - Europe/Vilnius +- `Europe/Volgograd` - Europe/Volgograd +- `Europe/Warsaw` - Europe/Warsaw +- `Europe/Zagreb` - Europe/Zagreb +- `Europe/Zurich` - Europe/Zurich +- `GMT` - GMT +- `Indian/Antananarivo` - Indian/Antananarivo +- `Indian/Chagos` - Indian/Chagos +- `Indian/Christmas` - Indian/Christmas +- `Indian/Cocos` - Indian/Cocos +- `Indian/Comoro` - Indian/Comoro +- `Indian/Kerguelen` - Indian/Kerguelen +- `Indian/Mahe` - Indian/Mahe +- `Indian/Maldives` - Indian/Maldives +- `Indian/Mauritius` - Indian/Mauritius +- `Indian/Mayotte` - Indian/Mayotte +- `Indian/Reunion` - Indian/Reunion +- `Pacific/Apia` - Pacific/Apia +- `Pacific/Auckland` - Pacific/Auckland +- `Pacific/Bougainville` - Pacific/Bougainville +- `Pacific/Chatham` - Pacific/Chatham +- `Pacific/Chuuk` - Pacific/Chuuk +- `Pacific/Easter` - Pacific/Easter +- `Pacific/Efate` - Pacific/Efate +- `Pacific/Fakaofo` - Pacific/Fakaofo +- `Pacific/Fiji` - Pacific/Fiji +- `Pacific/Funafuti` - Pacific/Funafuti +- `Pacific/Galapagos` - Pacific/Galapagos +- `Pacific/Gambier` - Pacific/Gambier +- `Pacific/Guadalcanal` - Pacific/Guadalcanal +- `Pacific/Guam` - Pacific/Guam +- `Pacific/Honolulu` - Pacific/Honolulu +- `Pacific/Kanton` - Pacific/Kanton +- `Pacific/Kiritimati` - Pacific/Kiritimati +- `Pacific/Kosrae` - Pacific/Kosrae +- `Pacific/Kwajalein` - Pacific/Kwajalein +- `Pacific/Majuro` - Pacific/Majuro +- `Pacific/Marquesas` - Pacific/Marquesas +- `Pacific/Midway` - Pacific/Midway +- `Pacific/Nauru` - Pacific/Nauru +- `Pacific/Niue` - Pacific/Niue +- `Pacific/Norfolk` - Pacific/Norfolk +- `Pacific/Noumea` - Pacific/Noumea +- `Pacific/Pago_Pago` - Pacific/Pago_Pago +- `Pacific/Palau` - Pacific/Palau +- `Pacific/Pitcairn` - Pacific/Pitcairn +- `Pacific/Pohnpei` - Pacific/Pohnpei +- `Pacific/Port_Moresby` - Pacific/Port_Moresby +- `Pacific/Rarotonga` - Pacific/Rarotonga +- `Pacific/Saipan` - Pacific/Saipan +- `Pacific/Tahiti` - Pacific/Tahiti +- `Pacific/Tarawa` - Pacific/Tarawa +- `Pacific/Tongatapu` - Pacific/Tongatapu +- `Pacific/Wake` - Pacific/Wake +- `Pacific/Wallis` - Pacific/Wallis +- `US/Alaska` - US/Alaska +- `US/Arizona` - US/Arizona +- `US/Central` - US/Central +- `US/Eastern` - US/Eastern +- `US/Hawaii` - US/Hawaii +- `US/Mountain` - US/Mountain +- `US/Pacific` - US/Pacific +- `UTC` - UTC + + + + + +External source. + + + + + +External id. + + + + + +Is issue type enabled. + + + + + +Is time tracking enabled. + + + + + +Default state. + + + + + +Estimate. + + + +
+
+ +
+ +### Scopes + +`projects:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "identifier": "PROJ-123", + "network": 2, + "project_lead": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/state/add-state.md b/apps/developer-docs/docs/api-reference/state/add-state.md new file mode 100644 index 00000000..48c8b11b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/state/add-state.md @@ -0,0 +1,204 @@ +--- +title: Create a state +description: Create a state via Plane API. HTTP request format, parameters, scopes, and example responses for create a state. +keywords: plane, plane api, rest api, api integration, state, create a state +--- + +# Create a state + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/states/ +
+ +
+
+ +Create a new workflow state for a project with specified name, color, and group. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Color. + + + + + +Sequence. + + + + + +- `backlog` - Backlog +- `unstarted` - Unstarted +- `started` - Started +- `completed` - Completed +- `cancelled` - Cancelled +- `triage` - Triage + + + + + +Is triage. + + + + + +Default. + + + + + +External source. + + + + + +External id. + + + +
+
+ +
+ +### Scopes + +`projects.states:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "color": "#f39c12", + "group": "started", + "sequence": 2, + "default": false, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/state/delete-state.md b/apps/developer-docs/docs/api-reference/state/delete-state.md new file mode 100644 index 00000000..2db1cb47 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/state/delete-state.md @@ -0,0 +1,108 @@ +--- +title: Delete a state +description: Delete a state via Plane API. HTTP request format, parameters, scopes, and example responses for delete a state. +keywords: plane, plane api, rest api, api integration, state, delete a state +--- + +# Delete a state + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/states/{state_id}/ +
+ +
+
+ +Permanently remove a workflow state from a project. Default states and states with existing work items cannot be deleted. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the state. + + + +
+
+ +
+ +### Scopes + +`projects.states:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/state/get-state-detail.md b/apps/developer-docs/docs/api-reference/state/get-state-detail.md new file mode 100644 index 00000000..971ee4a3 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/state/get-state-detail.md @@ -0,0 +1,119 @@ +--- +title: Retrieve a state +description: Retrieve a state via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a state. +keywords: plane, plane api, rest api, api integration, state, retrieve a state +--- + +# Retrieve a state + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/states/{state_id}/ +
+ +
+
+ +Retrieve details of a specific state. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the state. + + + +
+
+ +
+ +### Scopes + +`projects.states:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "color": "#f39c12", + "group": "started", + "sequence": 2, + "default": false, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/state/list-states.md b/apps/developer-docs/docs/api-reference/state/list-states.md new file mode 100644 index 00000000..3dec94ba --- /dev/null +++ b/apps/developer-docs/docs/api-reference/state/list-states.md @@ -0,0 +1,158 @@ +--- +title: List all states +description: List all states via Plane API. HTTP request format, parameters, scopes, and example responses for list all states. +keywords: plane, plane api, rest api, api integration, state, list all states +--- + +# List all states + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/states/ +
+ +
+
+ +Retrieve all workflow states for a project. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.states:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "color": "#ffa500", + "group": "started", + "sequence": 2 + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/state/overview.md b/apps/developer-docs/docs/api-reference/state/overview.md new file mode 100644 index 00000000..91810750 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/state/overview.md @@ -0,0 +1,96 @@ +--- +title: Overview +description: Plane State API overview. Learn about endpoints, request/response format, and how to work with state via REST API. +keywords: plane, plane api, rest api, api integration, states, workflow, status +--- + +# Overview + +States represent the current status of a work item in your project workflow. + +[Learn more about States](https://docs.plane.so/core-concepts/work-items/states) + +
+
+ +## The State Object + +### Attributes + +- `name` _string_ **( required )** + + Name of the state + +- `created_at` , `updated_at` _timestamp_ + + Timestamp of the issue when it was created and when it was last updated + +- `description` _string_ + + Description of the state + +- `color` _string_ **(required)** + + String code of the color + +- `workspace_slug` _string_ + + Slugified name of the state auto generated from the system + +- `sequence` _string_ + + Auto generated sequence of the state for ordering. + +- `group` _string_ **(required)** + + Group to which the state belongs can only take values + - backlog + - unstarted + - started + - completed + - cancelled + +- `default` _boolean_ + + Is it the default state in which if the issues are not assigned any states all the issues are created in this state. + +- `created_by` & `updated_by` + + This values are auto saved and represent the id of the user that created or the updated the project. + +- `project` _uuid_ + + The project which the issue is part of auto generated from backend + +- `workspace` _uuid_ + + The workspace which the issue is part of auto generated from backend + +
+
+ + + +```json +{ + "id": "f960d3c2-8524-4a41-b8eb-055ce4be2a7f", + "created_at": "2023-11-19T17:41:45.478363Z", + "updated_at": "2023-11-19T17:41:45.478383Z", + "name": "Ideation", + "description": "", + "color": "#eb5757", + "workspace_slug": "ideation", + "sequence": 130000.0, + "group": "unstarted", + "default": false, + "created_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "updated_by": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "project": "4af68566-94a4-4eb3-94aa-50dc9427067b", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/state/update-state-detail.md b/apps/developer-docs/docs/api-reference/state/update-state-detail.md new file mode 100644 index 00000000..c7f75912 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/state/update-state-detail.md @@ -0,0 +1,210 @@ +--- +title: Update a state +description: Update a state via Plane API. HTTP request format, parameters, scopes, and example responses for update a state. +keywords: plane, plane api, rest api, api integration, state, update a state +--- + +# Update a state + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/states/{state_id}/ +
+ +
+
+ +Partially update an existing workflow state's properties like name, color, or group. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the state. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Name. + + + + + +Description. + + + + + +Color. + + + + + +Sequence. + + + + + +- `backlog` - Backlog +- `unstarted` - Unstarted +- `started` - Started +- `completed` - Completed +- `cancelled` - Cancelled +- `triage` - Triage + + + + + +Is triage. + + + + + +Default. + + + + + +External source. + + + + + +External id. + + + +
+
+ +
+ +### Scopes + +`projects.states:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "color": "#f39c12", + "group": "started", + "sequence": 2, + "default": false, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/sticky/add-sticky.md b/apps/developer-docs/docs/api-reference/sticky/add-sticky.md new file mode 100644 index 00000000..a0ef0463 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/sticky/add-sticky.md @@ -0,0 +1,216 @@ +--- +title: Create a sticky +description: Create a sticky via Plane API. HTTP request format, parameters, scopes, and example responses for create a sticky. +keywords: plane, plane api, rest api, api integration, sticky, create a sticky +--- + +# Create a sticky + +
+ POST + /api/v1/workspaces/{workspace_slug}/stickies/ +
+ +
+
+ +Create a new sticky in the workspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Deleted at. + + + + + +Name. + + + + + +Description. + + + + + +Description html. + + + + + +Description stripped. + + + + + +Logo props. + + + + + +Color. + + + + + +Background color. + + + + + +Sort order. + + + + + +Created by. + + + + + +Updated by. + + + +
+
+ +
+ +### Scopes + +`stickies:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description_html": "

Example content

", + "created_at": "2024-01-01T00:00:00Z" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/sticky/delete-sticky.md b/apps/developer-docs/docs/api-reference/sticky/delete-sticky.md new file mode 100644 index 00000000..073def41 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/sticky/delete-sticky.md @@ -0,0 +1,102 @@ +--- +title: Delete a sticky +description: Delete a sticky via Plane API. HTTP request format, parameters, scopes, and example responses for delete a sticky. +keywords: plane, plane api, rest api, api integration, sticky, delete a sticky +--- + +# Delete a sticky + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/stickies/{resource_id}/ +
+ +
+
+ +Delete a sticky by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`stickies:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/sticky/get-sticky-detail.md b/apps/developer-docs/docs/api-reference/sticky/get-sticky-detail.md new file mode 100644 index 00000000..4d155ea9 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/sticky/get-sticky-detail.md @@ -0,0 +1,109 @@ +--- +title: Retrieve a sticky +description: Retrieve a sticky via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a sticky. +keywords: plane, plane api, rest api, api integration, sticky, retrieve a sticky +--- + +# Retrieve a sticky + +
+ GET + /api/v1/workspaces/{workspace_slug}/stickies/{resource_id}/ +
+ +
+
+ +Retrieve a sticky by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`stickies:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description_html": "

Example content

", + "created_at": "2024-01-01T00:00:00Z" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/sticky/list-stickies.md b/apps/developer-docs/docs/api-reference/sticky/list-stickies.md new file mode 100644 index 00000000..3336343e --- /dev/null +++ b/apps/developer-docs/docs/api-reference/sticky/list-stickies.md @@ -0,0 +1,117 @@ +--- +title: List all stickies +description: List all stickies via Plane API. HTTP request format, parameters, scopes, and example responses for list all stickies. +keywords: plane, plane api, rest api, api integration, sticky, list all stickies +--- + +# List all stickies + +
+ GET + /api/v1/workspaces/{workspace_slug}/stickies/ +
+ +
+
+ +List all stickies in the workspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`stickies:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description_html": "

Example content

", + "created_at": "2024-01-01T00:00:00Z" + } + ] + } +] +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/sticky/overview.md b/apps/developer-docs/docs/api-reference/sticky/overview.md new file mode 100644 index 00000000..1eba0e3d --- /dev/null +++ b/apps/developer-docs/docs/api-reference/sticky/overview.md @@ -0,0 +1,108 @@ +--- +title: Overview +description: Plane Sticky API overview. Learn about endpoints, request/response format, and how to work with sticky via REST API. +keywords: plane api, sticky notes, quick notes, personal notes, workspace stickies, rest api, api integration +--- + +# Overview + +Stickies are workspace-level notes that allow you to capture quick thoughts, ideas, or important reminders. + +[Learn more about Stickies](https://docs.plane.so/core-concepts/stickies) + +
+
+ +## The Sticky Object + +### Attributes + +- `id` _uuid_ + + Unique identifier for the sticky + +- `name` _string_ + + Name of the sticky + +- `description` _object_ + + JSON description of the sticky + +- `description_html` _string_ + + HTML description of the sticky + +- `description_stripped` _string_ + + Stripped version of the HTML description + +- `description_binary` _string_ + + Binary description of the sticky + +- `logo_props` _object_ + + Logo properties for the sticky + +- `color` _string_ + + Color of the sticky + +- `background_color` _string_ + + Background color of the sticky + +- `workspace` _uuid_ + + Workspace UUID which is automatically saved + +- `owner` _uuid_ + + User ID of the sticky owner + +- `sort_order` _number_ + + Sort order for the sticky + +- `created_at` _timestamp_ + + The timestamp when the sticky was created + +- `updated_at` _timestamp_ + + The timestamp when the sticky was last updated + +- `created_by` _uuid_ + + ID of the user who created the sticky + +- `updated_by` _uuid_ + + ID of the user who last updated the sticky + +
+
+ + + +```json +{ + "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "created_at": "2023-11-19T11:56:55.176802Z", + "updated_at": "2023-11-19T11:56:55.176809Z", + "name": "Important Note", + "description": {}, + "description_html": "

This is an important note

", + "color": "#FF5733", + "background_color": "#FFF9E6", + "sort_order": 1000.0, + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4", + "owner": "16c61a3a-512a-48ac-b0be-b6b46fe6f430" +} +``` + +
+ +
+
diff --git a/apps/developer-docs/docs/api-reference/sticky/update-sticky-detail.md b/apps/developer-docs/docs/api-reference/sticky/update-sticky-detail.md new file mode 100644 index 00000000..3a7c391b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/sticky/update-sticky-detail.md @@ -0,0 +1,225 @@ +--- +title: Update a sticky +description: Update a sticky via Plane API. HTTP request format, parameters, scopes, and example responses for update a sticky. +keywords: plane, plane api, rest api, api integration, sticky, update a sticky +--- + +# Update a sticky + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/stickies/{resource_id}/ +
+ +
+
+ +Update a sticky by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Deleted at. + + + + + +Name. + + + + + +Description. + + + + + +Description html. + + + + + +Description stripped. + + + + + +Logo props. + + + + + +Color. + + + + + +Background color. + + + + + +Sort order. + + + + + +Created by. + + + + + +Updated by. + + + +
+
+ +
+ +### Scopes + +`stickies:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description_html": "

Example content

", + "created_at": "2024-01-01T00:00:00Z" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/teamspace/add-projects-to-teamspace.md b/apps/developer-docs/docs/api-reference/teamspace/add-projects-to-teamspace.md new file mode 100644 index 00000000..1d568659 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/teamspace/add-projects-to-teamspace.md @@ -0,0 +1,143 @@ +--- +title: Add projects to teamspace +description: Add projects to teamspace via Plane API. HTTP request format, parameters, scopes, and example responses for add projects to teamspace. +keywords: plane, plane api, rest api, api integration, teamspace, add projects to teamspace +--- + +# Add projects to teamspace + +
+ POST + /api/v1/workspaces/{workspace_slug}/teamspaces/{teamspace_id}/projects/ +
+ +
+
+ +Add projects to a teamspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the teamspace. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Project ids. + + + +
+
+ +
+ +### Scopes + +`teamspaces.projects:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "identifier": "PROJ-123", + "network": 2, + "project_lead": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/teamspace/add-teamspace-members.md b/apps/developer-docs/docs/api-reference/teamspace/add-teamspace-members.md new file mode 100644 index 00000000..a718549e --- /dev/null +++ b/apps/developer-docs/docs/api-reference/teamspace/add-teamspace-members.md @@ -0,0 +1,142 @@ +--- +title: Add members to teamspace +description: Add members to teamspace via Plane API. HTTP request format, parameters, scopes, and example responses for add members to teamspace. +keywords: plane, plane api, rest api, api integration, teamspace, add members to teamspace +--- + +# Add members to teamspace + +
+ POST + /api/v1/workspaces/{workspace_slug}/teamspaces/{teamspace_id}/members/ +
+ +
+
+ +Add members to a teamspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the teamspace. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Member ids. + + + +
+
+ +
+ +### Scopes + +`teamspaces.members:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "first_name": "John", + "last_name": "Doe", + "email": "user@example.com", + "avatar": "https://example.com/assets/example-image.png", + "avatar_url": "https://example.com/assets/example-image.png", + "display_name": "Example Name" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/teamspace/add-teamspace.md b/apps/developer-docs/docs/api-reference/teamspace/add-teamspace.md new file mode 100644 index 00000000..27e25445 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/teamspace/add-teamspace.md @@ -0,0 +1,188 @@ +--- +title: Create a teamspace +description: Create a teamspace via Plane API. HTTP request format, parameters, scopes, and example responses for create a teamspace. +keywords: plane, plane api, rest api, api integration, teamspace, create a teamspace +--- + +# Create a teamspace + +
+ POST + /api/v1/workspaces/{workspace_slug}/teamspaces/ +
+ +
+
+ +Create a new teamspace in the workspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Logo props. + + + + + +Name. + + + + + +Description json. + + + + + +Description html. + + + + + +Description stripped. + + + + + +Created by. + + + + + +Updated by. + + + + + +Lead. + + + +
+
+ +
+ +### Scopes + +`teamspaces:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/teamspace/delete-teamspace.md b/apps/developer-docs/docs/api-reference/teamspace/delete-teamspace.md new file mode 100644 index 00000000..0113429c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/teamspace/delete-teamspace.md @@ -0,0 +1,108 @@ +--- +title: Delete a teamspace +description: Delete a teamspace via Plane API. HTTP request format, parameters, scopes, and example responses for delete a teamspace. +keywords: plane, plane api, rest api, api integration, teamspace, delete a teamspace +--- + +# Delete a teamspace + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/teamspaces/{resource_id}/ +
+ +
+
+ +Delete a teamspace by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the teamspace. + + + +
+
+ +
+ +### Scopes + +`teamspaces:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/teamspace/get-teamspace-detail.md b/apps/developer-docs/docs/api-reference/teamspace/get-teamspace-detail.md new file mode 100644 index 00000000..2172e8fa --- /dev/null +++ b/apps/developer-docs/docs/api-reference/teamspace/get-teamspace-detail.md @@ -0,0 +1,114 @@ +--- +title: Retrieve a teamspace +description: Retrieve a teamspace via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve a teamspace. +keywords: plane, plane api, rest api, api integration, teamspace, retrieve a teamspace +--- + +# Retrieve a teamspace + +
+ GET + /api/v1/workspaces/{workspace_slug}/teamspaces/{resource_id}/ +
+ +
+
+ +Retrieve a teamspace by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the teamspace. + + + +
+
+ +
+ +### Scopes + +`teamspaces:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/teamspace/list-teamspace-members.md b/apps/developer-docs/docs/api-reference/teamspace/list-teamspace-members.md new file mode 100644 index 00000000..ce10f463 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/teamspace/list-teamspace-members.md @@ -0,0 +1,144 @@ +--- +title: List all teamspace members +description: List all teamspace members via Plane API. HTTP request format, parameters, scopes, and example responses for list all teamspace members. +keywords: plane, plane api, rest api, api integration, teamspace, list all teamspace members +--- + +# List all teamspace members + +
+ GET + /api/v1/workspaces/{workspace_slug}/teamspaces/{teamspace_id}/members/ +
+ +
+
+ +List all members in a teamspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the teamspace. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`teamspaces.members:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/teamspace/list-teamspace-projects.md b/apps/developer-docs/docs/api-reference/teamspace/list-teamspace-projects.md new file mode 100644 index 00000000..47c5afa8 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/teamspace/list-teamspace-projects.md @@ -0,0 +1,125 @@ +--- +title: List teamspace projects +description: List teamspace projects via Plane API. HTTP request format, parameters, scopes, and example responses for list teamspace projects. +keywords: plane, plane api, rest api, api integration, teamspace, list teamspace projects +--- + +# List teamspace projects + +
+ GET + /api/v1/workspaces/{workspace_slug}/teamspaces/{teamspace_id}/projects/ +
+ +
+
+ +List all projects in a teamspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the teamspace. + + + +
+
+ +
+ +### Scopes + +`teamspaces.projects:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "identifier": "PROJ-123", + "network": 2 + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/teamspace/list-teamspaces.md b/apps/developer-docs/docs/api-reference/teamspace/list-teamspaces.md new file mode 100644 index 00000000..9c6ff7e4 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/teamspace/list-teamspaces.md @@ -0,0 +1,141 @@ +--- +title: List all teamspaces +description: List all teamspaces via Plane API. HTTP request format, parameters, scopes, and example responses for list all teamspaces. +keywords: plane, plane api, rest api, api integration, teamspace, list all teamspaces +--- + +# List all teamspaces + +
+ GET + /api/v1/workspaces/{workspace_slug}/teamspaces/ +
+ +
+
+ +List all teamspaces in the workspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`teamspaces:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description", + "created_at": "2024-01-01T00:00:00Z" + } + ] + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/teamspace/overview.md b/apps/developer-docs/docs/api-reference/teamspace/overview.md new file mode 100644 index 00000000..3e82cfe6 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/teamspace/overview.md @@ -0,0 +1,92 @@ +--- +title: Overview +description: Plane Teamspace API overview. Learn about endpoints, request/response format, and how to work with teamspace via REST API. +keywords: plane api, teamspace, team management, team collaboration, workspace teams, rest api, api integration +--- + +# Overview + +Teamspaces allow you to organize teams, projects, and members within a workspace, providing a way to group related work and manage access at a team level. + +[Learn more about Teamspaces](https://docs.plane.so/core-concepts/workspaces/teamspaces) + +
+
+ +## The Teamspace Object + +### Attributes + +- `id` _uuid_ + + Unique identifier for the teamspace. + +- `name` _string_ **(required)** + + Name of the teamspace. + +- `description_json` _object_ + + JSON representation of the teamspace description. + +- `description_html` _string_ + + HTML-formatted description of the teamspace. + +- `description_stripped` _string_ + + Stripped version of the HTML description. + +- `description_binary` _string_ + + Binary representation of the description. + +- `logo_props` _object_ + + Logo properties for the teamspace. + +- `lead` _uuid_ + + ID of the user who leads the teamspace. + +- `workspace` _uuid_ + + ID of the workspace containing the teamspace. + +- `created_at` _timestamp_ + + Time at which the teamspace was created. + +- `updated_at` _timestamp_ + + Time at which the teamspace was last updated. + +- `created_by` _uuid_ + + ID of the user who created the teamspace. + +- `updated_by` _uuid_ + + ID of the user who last updated the teamspace. + +
+
+ + + +```json +{ + "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "created_at": "2023-11-19T11:56:55.176802Z", + "updated_at": "2023-11-19T11:56:55.176809Z", + "name": "Engineering Team", + "description_html": "

Engineering team workspace

", + "lead": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "workspace": "cd4ab5a2-1a5f-4516-a6c6-8da1a9fa5be4" +} +``` + +
+ +
+
diff --git a/apps/developer-docs/docs/api-reference/teamspace/remove-projects-from-teamspace.md b/apps/developer-docs/docs/api-reference/teamspace/remove-projects-from-teamspace.md new file mode 100644 index 00000000..f88bcf4a --- /dev/null +++ b/apps/developer-docs/docs/api-reference/teamspace/remove-projects-from-teamspace.md @@ -0,0 +1,102 @@ +--- +title: Remove projects from teamspace +description: Remove projects from teamspace via Plane API. HTTP request format, parameters, scopes, and example responses for remove projects from teamspace. +keywords: plane, plane api, rest api, api integration, teamspace, remove projects from teamspace +--- + +# Remove projects from teamspace + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/teamspaces/{teamspace_id}/projects/ +
+ +
+
+ +Remove projects from a teamspace by its ID + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the teamspace. + + + +
+
+ +
+ +### Scopes + +`teamspaces.projects:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/teamspace/remove-teamspace-members.md b/apps/developer-docs/docs/api-reference/teamspace/remove-teamspace-members.md new file mode 100644 index 00000000..f715e192 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/teamspace/remove-teamspace-members.md @@ -0,0 +1,102 @@ +--- +title: Remove members from teamspace +description: Remove members from teamspace via Plane API. HTTP request format, parameters, scopes, and example responses for remove members from teamspace. +keywords: plane, plane api, rest api, api integration, teamspace, remove members from teamspace +--- + +# Remove members from teamspace + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/teamspaces/{teamspace_id}/members/ +
+ +
+
+ +Delete members from a teamspace by its ID + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the teamspace. + + + +
+
+ +
+ +### Scopes + +`teamspaces.members:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/teamspace/update-teamspace-detail.md b/apps/developer-docs/docs/api-reference/teamspace/update-teamspace-detail.md new file mode 100644 index 00000000..d4e08b4d --- /dev/null +++ b/apps/developer-docs/docs/api-reference/teamspace/update-teamspace-detail.md @@ -0,0 +1,203 @@ +--- +title: Update a teamspace +description: Update a teamspace via Plane API. HTTP request format, parameters, scopes, and example responses for update a teamspace. +keywords: plane, plane api, rest api, api integration, teamspace, update a teamspace +--- + +# Update a teamspace + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/teamspaces/{resource_id}/ +
+ +
+
+ +Update a teamspace by its ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the resource. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the teamspace. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Logo props. + + + + + +Name. + + + + + +Description json. + + + + + +Description html. + + + + + +Description stripped. + + + + + +Created by. + + + + + +Updated by. + + + + + +Lead. + + + +
+
+ +
+ +### Scopes + +`teamspaces:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description": "Example description" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/user/get-current-user.md b/apps/developer-docs/docs/api-reference/user/get-current-user.md new file mode 100644 index 00000000..e83f13ec --- /dev/null +++ b/apps/developer-docs/docs/api-reference/user/get-current-user.md @@ -0,0 +1,88 @@ +--- +title: Retrieve current user +description: Retrieve current user via Plane API. HTTP request format, parameters, scopes, and example responses for retrieve current user. +keywords: plane, plane api, rest api, api integration, user, retrieve current user +--- + +# Retrieve current user + +
+ GET + /api/v1/users/me/ +
+ +
+
+ +Retrieve the authenticated user's profile information including basic details. + +
+ +### Scopes + +`profile:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "first_name": "John", + "last_name": "Doe", + "email": "user@example.com", + "avatar": "https://example.com/assets/example-image.png", + "avatar_url": "https://example.com/assets/example-image.png", + "display_name": "Example Name" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/user/overview.md b/apps/developer-docs/docs/api-reference/user/overview.md new file mode 100644 index 00000000..5e5e6140 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/user/overview.md @@ -0,0 +1,66 @@ +--- +title: Overview +description: Plane User API overview. Learn about endpoints, request/response format, and how to work with user via REST API. +keywords: plane api, user profile, current user, user account, authentication, rest api, api integration +--- + +# Overview + +Users represent the people who use Plane. The Users API allows you to retrieve information about the current authenticated user. + +
+
+ +## The User Object + +### Attributes + +- `id` _uuid_ + + Unique identifier for the user + +- `first_name` _string_ + + First name of the user + +- `last_name` _string_ + + Last name of the user + +- `email` _string_ + + Email address of the user + +- `avatar` _string_ + + Avatar identifier for the user + +- `avatar_url` _string_ + + URL of the user's avatar image + +- `display_name` _string_ + + Display name of the user + +
+
+ + + +```json +{ + "id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430", + "first_name": "John", + "last_name": "Doe", + "email": "john.doe@example.com", + "avatar": "avatar-123", + "avatar_url": "https://example.com/avatars/avatar-123.png", + "display_name": "John Doe" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/work-item-pages/add-work-item-page.md b/apps/developer-docs/docs/api-reference/work-item-pages/add-work-item-page.md new file mode 100644 index 00000000..c8dd859e --- /dev/null +++ b/apps/developer-docs/docs/api-reference/work-item-pages/add-work-item-page.md @@ -0,0 +1,160 @@ +--- +title: Create work item page link +description: Create work item page link via Plane API. HTTP request format, parameters, scopes, and example responses for create work item page link. +keywords: plane, plane api, rest api, api integration, work item pages, create work item page link +--- + +# Create work item page link + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/pages/ +
+ +
+
+ +Link a page to a work item. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +ID of the page to link to the work item + + + +
+
+ +
+ +### Scopes + +API key authentication or an OAuth token with equivalent access. + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "page": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description_html": "

Example content

", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "is_global": false, + "logo_props": {} + }, + "issue": "550e8400-e29b-41d4-a716-446655440000", + "project": "550e8400-e29b-41d4-a716-446655440000", + "workspace": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/work-item-pages/delete-work-item-page.md b/apps/developer-docs/docs/api-reference/work-item-pages/delete-work-item-page.md new file mode 100644 index 00000000..bc61744c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/work-item-pages/delete-work-item-page.md @@ -0,0 +1,120 @@ +--- +title: Delete work item page link +description: Delete work item page link via Plane API. HTTP request format, parameters, scopes, and example responses for delete work item page link. +keywords: plane, plane api, rest api, api integration, work item pages, delete work item page link +--- + +# Delete work item page link + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/pages/{page_id}/ +
+ +
+
+ +Remove a page link from a work item. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the page. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item. + + + +
+
+ +
+ +### Scopes + +API key authentication or an OAuth token with equivalent access. + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/work-item-pages/get-work-item-page-detail.md b/apps/developer-docs/docs/api-reference/work-item-pages/get-work-item-page-detail.md new file mode 100644 index 00000000..7d441b19 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/work-item-pages/get-work-item-page-detail.md @@ -0,0 +1,140 @@ +--- +title: Get work item page link +description: Get work item page link via Plane API. HTTP request format, parameters, scopes, and example responses for get work item page link. +keywords: plane, plane api, rest api, api integration, work item pages, get work item page link +--- + +# Get work item page link + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/pages/{page_id}/ +
+ +
+
+ +Retrieve details of a specific page link for a work item. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the page. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item. + + + +
+
+ +
+ +### Scopes + +API key authentication or an OAuth token with equivalent access. + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "page": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description_html": "

Example content

", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "is_global": false, + "logo_props": {} + }, + "issue": "550e8400-e29b-41d4-a716-446655440000", + "project": "550e8400-e29b-41d4-a716-446655440000", + "workspace": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +
+ +
+ +
diff --git a/apps/developer-docs/docs/api-reference/work-item-pages/list-work-item-pages.md b/apps/developer-docs/docs/api-reference/work-item-pages/list-work-item-pages.md new file mode 100644 index 00000000..8fd6370e --- /dev/null +++ b/apps/developer-docs/docs/api-reference/work-item-pages/list-work-item-pages.md @@ -0,0 +1,162 @@ +--- +title: List work item pages +description: List work item pages via Plane API. HTTP request format, parameters, scopes, and example responses for list work item pages. +keywords: plane, plane api, rest api, api integration, work item pages, list work item pages +--- + +# List work item pages + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/pages/ +
+ +
+
+ +Retrieve all page links associated with a work item. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + + + +The unique identifier of the work item. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +API key authentication or an OAuth token with equivalent access. + +
+ +
+ +
+ + + + + + + + + +```json +{ + "grouped_by": "state", + "sub_grouped_by": "priority", + "total_count": 150, + "next_cursor": "20:1:0", + "prev_cursor": "20:0:0", + "next_page_results": true, + "prev_page_results": false, + "count": 20, + "total_pages": 8, + "total_results": 150, + "extra_stats": null, + "results": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "created_at": "2024-01-01T00:00:00Z" + } + ] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/work-item-pages/overview.md b/apps/developer-docs/docs/api-reference/work-item-pages/overview.md new file mode 100644 index 00000000..139c09c6 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/work-item-pages/overview.md @@ -0,0 +1,85 @@ +--- +title: Overview +description: Plane Work Item Pages API overview. Learn how to link pages to work items using the Plane API. +keywords: plane, plane api, rest api, api integration, work item pages, wiki pages +--- + +# Overview + +Work item pages create links between work items and wiki pages so related documentation stays close to execution. + +[Learn more about Work Items](https://docs.plane.so/core-concepts/issues) + +
+
+ +## The Work Item Page Link Object + +### Attributes + +- `id` _string_ + + Id. + +- `page` _object_ + + Lightweight page serializer for work item page links. + +Provides essential page information including identifiers, +name, timestamps, and visual properties for work item page associations. + +- `issue` _string_ + + Issue. + +- `project` _string_ + + Project. + +- `workspace` _string_ + + Workspace. + +- `created_at` _string_ + + Created at. + +- `updated_at` _string_ + + Updated at. + +- `created_by` _string_ + + Created by. + +
+
+ + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "page": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "description_html": "

Example content

", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "is_global": false, + "logo_props": {} + }, + "issue": "550e8400-e29b-41d4-a716-446655440000", + "project": "550e8400-e29b-41d4-a716-446655440000", + "workspace": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +
+ +
+
diff --git a/apps/developer-docs/docs/api-reference/work-item-relations/create-work-item-relation.md b/apps/developer-docs/docs/api-reference/work-item-relations/create-work-item-relation.md new file mode 100644 index 00000000..42346c25 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/work-item-relations/create-work-item-relation.md @@ -0,0 +1,178 @@ +--- +title: Create work item relation +description: Create work item relation via Plane API. HTTP request format, parameters, scopes, and example responses for create work item relation. +keywords: plane, plane api, rest api, api integration, work item relations, create work item relation +--- + +# Create work item relation + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/relations/ +
+ +
+
+ +Create relationships between work items. Supports various relation types including blocking, blocked_by, duplicate, relates_to, start_before, start_after, finish_before, and finish_after. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Type of relationship between work items + +- `blocking` - Blocking +- `blocked_by` - Blocked By +- `duplicate` - Duplicate +- `relates_to` - Relates To +- `start_before` - Start Before +- `start_after` - Start After +- `finish_before` - Finish Before +- `finish_after` - Finish After + + + + + +Array of work item IDs to create relations with + + + +
+
+ +
+ +### Scopes + +`projects.work_items:write` + +
+ +
+ +
+ + + + + + + + + +```json +[ + [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "Example Name", + "sequence_id": 42, + "project_id": "550e8400-e29b-41d4-a716-446655440000", + "relation_type": "blocked_by", + "state_id": "550e8400-e29b-41d4-a716-446655440000", + "priority": "high", + "type_id": "550e8400-e29b-41d4-a716-446655440000", + "is_epic": false, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000" + } + ] +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/work-item-relations/list-work-item-relations.md b/apps/developer-docs/docs/api-reference/work-item-relations/list-work-item-relations.md new file mode 100644 index 00000000..54f5d3dd --- /dev/null +++ b/apps/developer-docs/docs/api-reference/work-item-relations/list-work-item-relations.md @@ -0,0 +1,158 @@ +--- +title: List work item relations +description: List work item relations via Plane API. HTTP request format, parameters, scopes, and example responses for list work item relations. +keywords: plane, plane api, rest api, api integration, work item relations, list work item relations +--- + +# List work item relations + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/relations/ +
+ +
+
+ +Retrieve all relationships for a work item including blocking, blocked_by, duplicate, relates_to, start_before, start_after, finish_before, and finish_after relations. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Query Parameters + +
+ + + +Pagination cursor for getting next set of results + + + + + +Comma-separated list of related fields to expand in response + + + + + +Comma-separated list of fields to include in response + + + + + +Field to order results by. Prefix with '-' for descending order + + + + + +Number of results per page (default: 20, max: 100) + + + +
+
+ +
+ +### Scopes + +`projects.work_items:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "blocking": ["550e8400-e29b-41d4-a716-446655440000", "550e8400-e29b-41d4-a716-446655440000"], + "blocked_by": ["550e8400-e29b-41d4-a716-446655440000"], + "duplicate": [], + "relates_to": ["550e8400-e29b-41d4-a716-446655440000"], + "start_after": [], + "start_before": ["550e8400-e29b-41d4-a716-446655440000"], + "finish_after": [], + "finish_before": [] +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/work-item-relations/overview.md b/apps/developer-docs/docs/api-reference/work-item-relations/overview.md new file mode 100644 index 00000000..e1c88421 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/work-item-relations/overview.md @@ -0,0 +1,73 @@ +--- +title: Overview +description: Plane Work Item Relations API overview. Learn how to list, create, and remove work item relationships using the Plane API. +keywords: plane, plane api, rest api, api integration, work item relations, dependencies, blocking relationships +--- + +# Overview + +Work item relations let you model dependencies and planning constraints between work items, including blocking, duplicate, relates-to, and scheduling relationships. + +[Learn more about Work Items](https://docs.plane.so/core-concepts/issues) + +
+
+ +## The Work Item Relations Response + +### Attributes + +- `blocking` _array_ + + List of issue IDs that are blocking this issue + +- `blocked_by` _array_ + + List of issue IDs that this issue is blocked by + +- `duplicate` _array_ + + List of issue IDs that are duplicates of this issue + +- `relates_to` _array_ + + List of issue IDs that relate to this issue + +- `start_after` _array_ + + List of issue IDs that start after this issue + +- `start_before` _array_ + + List of issue IDs that start before this issue + +- `finish_after` _array_ + + List of issue IDs that finish after this issue + +- `finish_before` _array_ + + List of issue IDs that finish before this issue + +
+
+ + + +```json +{ + "blocking": ["550e8400-e29b-41d4-a716-446655440000", "550e8400-e29b-41d4-a716-446655440000"], + "blocked_by": ["550e8400-e29b-41d4-a716-446655440000"], + "duplicate": [], + "relates_to": ["550e8400-e29b-41d4-a716-446655440000"], + "start_after": [], + "start_before": ["550e8400-e29b-41d4-a716-446655440000"], + "finish_after": [], + "finish_before": [] +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/work-item-relations/remove-work-item-relation.md b/apps/developer-docs/docs/api-reference/work-item-relations/remove-work-item-relation.md new file mode 100644 index 00000000..4c6eaafd --- /dev/null +++ b/apps/developer-docs/docs/api-reference/work-item-relations/remove-work-item-relation.md @@ -0,0 +1,134 @@ +--- +title: Remove work item relation +description: Remove work item relation via Plane API. HTTP request format, parameters, scopes, and example responses for remove work item relation. +keywords: plane, plane api, rest api, api integration, work item relations, remove work item relation +--- + +# Remove work item relation + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/relations/remove/ +
+ +
+
+ +Remove a relationship between work items by specifying the related work item ID. + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +ID of the related work item to remove relation with + + + +
+
+ +
+ +### Scopes + +`projects.work_items:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/worklogs/create-worklog.md b/apps/developer-docs/docs/api-reference/worklogs/create-worklog.md new file mode 100644 index 00000000..fe2bc751 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/worklogs/create-worklog.md @@ -0,0 +1,174 @@ +--- +title: Create a worklog +description: Create a worklog via Plane API. HTTP request format, parameters, scopes, and example responses for create a worklog. +keywords: plane, plane api, rest api, api integration, worklogs, create a worklog +--- + +# Create a worklog + +
+ POST + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/worklogs/ +
+ +
+
+ +Create a new worklog entry + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Description. + + + + + +Duration. + + + + + +Created by. + + + + + +Updated by. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.worklogs:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "description": "Example description", + "duration": 1, + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000", + "project_id": "550e8400-e29b-41d4-a716-446655440000", + "workspace_id": "550e8400-e29b-41d4-a716-446655440000", + "logged_by": "550e8400-e29b-41d4-a716-446655440000" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/worklogs/delete-worklog.md b/apps/developer-docs/docs/api-reference/worklogs/delete-worklog.md new file mode 100644 index 00000000..27cc4439 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/worklogs/delete-worklog.md @@ -0,0 +1,114 @@ +--- +title: Delete a worklog +description: Delete a worklog via Plane API. HTTP request format, parameters, scopes, and example responses for delete a worklog. +keywords: plane, plane api, rest api, api integration, worklogs, delete a worklog +--- + +# Delete a worklog + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/worklogs/{worklog_id}/ +
+ +
+
+ +Delete a worklog entry + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the worklog. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.worklogs:write` + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/worklogs/get-total-time.md b/apps/developer-docs/docs/api-reference/worklogs/get-total-time.md new file mode 100644 index 00000000..ea7c870b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/worklogs/get-total-time.md @@ -0,0 +1,109 @@ +--- +title: Get total time for each work item +description: Get total time for each work item via Plane API. HTTP request format, parameters, scopes, and example responses for get total time for each work item. +keywords: plane, plane api, rest api, api integration, worklogs, get total time for each work item +--- + +# Get total time for each work item + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/total-worklogs/ +
+ +
+
+ +Get project worklog summary + +
+ +### Path Parameters + +
+ + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.worklogs:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "issue_id": "550e8400-e29b-41d4-a716-446655440000", + "duration": 1 + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/worklogs/get-worklogs-for-issue.md b/apps/developer-docs/docs/api-reference/worklogs/get-worklogs-for-issue.md new file mode 100644 index 00000000..f7867448 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/worklogs/get-worklogs-for-issue.md @@ -0,0 +1,123 @@ +--- +title: List all worklogs for a work item +description: List all worklogs for a work item via Plane API. HTTP request format, parameters, scopes, and example responses for list all worklogs for a work item. +keywords: plane, plane api, rest api, api integration, worklogs, list all worklogs for a work item +--- + +# List all worklogs for a work item + +
+ GET + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/worklogs/ +
+ +
+
+ +List worklog entries + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.worklogs:read` + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "description": "Example description", + "duration": 1, + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000", + "project_id": "550e8400-e29b-41d4-a716-446655440000", + "workspace_id": "550e8400-e29b-41d4-a716-446655440000", + "logged_by": "550e8400-e29b-41d4-a716-446655440000" + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/worklogs/overview.md b/apps/developer-docs/docs/api-reference/worklogs/overview.md new file mode 100644 index 00000000..5640dbc2 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/worklogs/overview.md @@ -0,0 +1,87 @@ +--- +title: Overview +description: Plane Worklogs API overview. Learn about endpoints, request/response format, and how to work with worklogs via REST API. +keywords: plane, plane api, rest api, api integration, time tracking, worklogs, time management +--- + +# Overview + +Worklogs enable time tracking for work items within a project, recording time spent in minutes along with descriptions and user information. + +[Learn more about Time tracking](https://docs.plane.so/core-concepts/issues/time-tracking) + +
+
+ +## The Worklogs Object + +### Attributes + +- `id` _string_ + + Unique identifier for the worklog + +- `created_at` _timestamp_ + + Timestamp when the worklog was created + +- `updated_at` _timestamp_ + + Timestamp when the worklog was last modified + +- `deleted_at` _timestamp_ + + Timestamp when the worklog was deleted + +- `description` _string_ + + Description of the work done during the worklog + +- `duration` _integer_ + + Time spent on the issue, recorded in minutes + +- `created_by` _string_ + + ID of user who created the worklog + +- `updated_by` _string_ + + ID of user who last modified the worklog + +- `project_id` _string_ + + ID of project associated with the worklog + +- `workspace_id` _string_ + + ID of workspace associated with the worklog + +- `logged_by` _string_ + + ID of the user who logged the work + +
+
+ + + +```json +{ + "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "created_at": "2025-01-29T21:27:54.197306+05:30", + "updated_at": "2025-01-29T21:27:54.197320+05:30", + "description": "", + "duration": 1, + "created_by": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "updated_by": null, + "project_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "workspace_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "logged_by": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/worklogs/update-worklog.md b/apps/developer-docs/docs/api-reference/worklogs/update-worklog.md new file mode 100644 index 00000000..79d3f86f --- /dev/null +++ b/apps/developer-docs/docs/api-reference/worklogs/update-worklog.md @@ -0,0 +1,180 @@ +--- +title: Update a worklog +description: Update a worklog via Plane API. HTTP request format, parameters, scopes, and example responses for update a worklog. +keywords: plane, plane api, rest api, api integration, worklogs, update a worklog +--- + +# Update a worklog + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/worklogs/{worklog_id}/ +
+ +
+
+ +Update a worklog entry + +
+ +### Path Parameters + +
+ + + +The unique identifier of the work item. + + + + + +The unique identifier of the worklog. + + + + + +The unique identifier of the project. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Description. + + + + + +Duration. + + + + + +Created by. + + + + + +Updated by. + + + +
+
+ +
+ +### Scopes + +`projects.work_items.worklogs:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "description": "Example description", + "duration": 1, + "created_by": "550e8400-e29b-41d4-a716-446655440000", + "updated_by": "550e8400-e29b-41d4-a716-446655440000", + "project_id": "550e8400-e29b-41d4-a716-446655440000", + "workspace_id": "550e8400-e29b-41d4-a716-446655440000", + "logged_by": "550e8400-e29b-41d4-a716-446655440000" +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/workspace-features/get-workspace-features.md b/apps/developer-docs/docs/api-reference/workspace-features/get-workspace-features.md new file mode 100644 index 00000000..86fa942c --- /dev/null +++ b/apps/developer-docs/docs/api-reference/workspace-features/get-workspace-features.md @@ -0,0 +1,102 @@ +--- +title: Get workspace features +description: Get workspace features via Plane API. HTTP request format, parameters, scopes, and example responses for get workspace features. +keywords: plane, plane api, rest api, api integration, workspace features, get workspace features +--- + +# Get workspace features + +
+ GET + /api/v1/workspaces/{workspace_slug}/features/ +
+ +
+
+ +Get the features of a workspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +`workspaces.features:read` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "project_grouping": true, + "initiatives": true, + "teams": true, + "customers": true, + "wiki": true, + "pi": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/workspace-features/overview.md b/apps/developer-docs/docs/api-reference/workspace-features/overview.md new file mode 100644 index 00000000..079f5e38 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/workspace-features/overview.md @@ -0,0 +1,63 @@ +--- +title: Overview +description: Plane Workspace Features API overview. Learn how to inspect and update workspace feature flags with the Plane API. +keywords: plane, plane api, rest api, api integration, workspace features, feature flags +--- + +# Overview + +Workspace features control which major Plane capabilities are enabled for a workspace. + +[Learn more about using the Plane API](https://developers.plane.so/api-reference/introduction) + +
+
+ +## The Workspace Feature Object + +### Attributes + +- `project_grouping` _boolean_ + + Project grouping. + +- `initiatives` _boolean_ + + Initiatives. + +- `teams` _boolean_ + + Teams. + +- `customers` _boolean_ + + Customers. + +- `wiki` _boolean_ + + Wiki. + +- `pi` _boolean_ + + Pi. + +
+
+ + + +```json +{ + "project_grouping": true, + "initiatives": true, + "teams": true, + "customers": true, + "wiki": true, + "pi": true +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/workspace-features/update-workspace-features.md b/apps/developer-docs/docs/api-reference/workspace-features/update-workspace-features.md new file mode 100644 index 00000000..d1446d34 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/workspace-features/update-workspace-features.md @@ -0,0 +1,173 @@ +--- +title: Update workspace features +description: Update workspace features via Plane API. HTTP request format, parameters, scopes, and example responses for update workspace features. +keywords: plane, plane api, rest api, api integration, workspace features, update workspace features +--- + +# Update workspace features + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/features/ +
+ +
+
+ +Update the features of a workspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Project grouping. + + + + + +Initiatives. + + + + + +Teams. + + + + + +Customers. + + + + + +Wiki. + + + + + +Pi. + + + +
+
+ +
+ +### Scopes + +`workspaces.features:write` + +
+ +
+ +
+ + + + + + + + + +```json +{ + "project_grouping": true, + "initiatives": true, + "teams": true, + "customers": true, + "wiki": true, + "pi": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/workspace-invitations/add-workspace-invitation.md b/apps/developer-docs/docs/api-reference/workspace-invitations/add-workspace-invitation.md new file mode 100644 index 00000000..e79c69d3 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/workspace-invitations/add-workspace-invitation.md @@ -0,0 +1,140 @@ +--- +title: Create workspace invitation +description: Create workspace invitation via Plane API. HTTP request format, parameters, scopes, and example responses for create workspace invitation. +keywords: plane, plane api, rest api, api integration, workspace invitations, create workspace invitation +--- + +# Create workspace invitation + +
+ POST + /api/v1/workspaces/{workspace_slug}/invitations/ +
+ +
+
+ +Create a workspace invite + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Email. + + + + + +- `20` - Admin +- `15` - Member +- `5` - Guest + + + +
+
+ +
+ +### Scopes + +Workspace admin or owner permission required. + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "email": "Example Name", + "role": 20, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "responded_at": "2024-01-01T00:00:00Z", + "accepted": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/workspace-invitations/delete-workspace-invitation.md b/apps/developer-docs/docs/api-reference/workspace-invitations/delete-workspace-invitation.md new file mode 100644 index 00000000..46a9bb45 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/workspace-invitations/delete-workspace-invitation.md @@ -0,0 +1,102 @@ +--- +title: Delete workspace invitation +description: Delete workspace invitation via Plane API. HTTP request format, parameters, scopes, and example responses for delete workspace invitation. +keywords: plane, plane api, rest api, api integration, workspace invitations, delete workspace invitation +--- + +# Delete workspace invitation + +
+ DELETE + /api/v1/workspaces/{workspace_slug}/invitations/{invitation_id}/ +
+ +
+
+ +Delete a workspace invite + +
+ +### Path Parameters + +
+ + + +The unique identifier of the invitation. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +Workspace admin or owner permission required. + +
+ +
+ +
+ + + + + + + + + +No response body. + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/workspace-invitations/get-workspace-invitation-detail.md b/apps/developer-docs/docs/api-reference/workspace-invitations/get-workspace-invitation-detail.md new file mode 100644 index 00000000..0862eda2 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/workspace-invitations/get-workspace-invitation-detail.md @@ -0,0 +1,112 @@ +--- +title: Get workspace invitation +description: Get workspace invitation via Plane API. HTTP request format, parameters, scopes, and example responses for get workspace invitation. +keywords: plane, plane api, rest api, api integration, workspace invitations, get workspace invitation +--- + +# Get workspace invitation + +
+ GET + /api/v1/workspaces/{workspace_slug}/invitations/{invitation_id}/ +
+ +
+
+ +Get a workspace invite by ID + +
+ +### Path Parameters + +
+ + + +The unique identifier of the invitation. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +Workspace admin or owner permission required. + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "email": "Example Name", + "role": 20, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "responded_at": "2024-01-01T00:00:00Z", + "accepted": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/workspace-invitations/list-workspace-invitations.md b/apps/developer-docs/docs/api-reference/workspace-invitations/list-workspace-invitations.md new file mode 100644 index 00000000..a1444a9d --- /dev/null +++ b/apps/developer-docs/docs/api-reference/workspace-invitations/list-workspace-invitations.md @@ -0,0 +1,105 @@ +--- +title: List workspace invitations +description: List workspace invitations via Plane API. HTTP request format, parameters, scopes, and example responses for list workspace invitations. +keywords: plane, plane api, rest api, api integration, workspace invitations, list workspace invitations +--- + +# List workspace invitations + +
+ GET + /api/v1/workspaces/{workspace_slug}/invitations/ +
+ +
+
+ +List all workspace invites for a workspace + +
+ +### Path Parameters + +
+ + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Scopes + +Workspace admin or owner permission required. + +
+ +
+ +
+ + + + + + + + + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "email": "Example Name", + "role": 20, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "responded_at": "2024-01-01T00:00:00Z", + "accepted": true + } +] +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/api-reference/workspace-invitations/overview.md b/apps/developer-docs/docs/api-reference/workspace-invitations/overview.md new file mode 100644 index 00000000..f187290b --- /dev/null +++ b/apps/developer-docs/docs/api-reference/workspace-invitations/overview.md @@ -0,0 +1,70 @@ +--- +title: Overview +description: Plane Workspace Invitations API overview. Learn how to create and manage workspace invitations with the Plane API. +keywords: plane, plane api, rest api, api integration, workspace invitations, member invites +--- + +# Overview + +Workspace invitations let admins invite users to join a workspace with specific access settings. + +[Learn more about Members](https://developers.plane.so/api-reference/members/overview) + +
+
+ +## The Workspace Invitation Object + +### Attributes + +- `id` _string_ + + Id. + +- `email` _string_ + + Email. + +- `role` _integer_ + - `20` - Admin + +* `15` - Member +* `5` - Guest + +- `created_at` _string_ + + Created at. + +- `updated_at` _string_ + + Updated at. + +- `responded_at` _string_ + + Responded at. + +- `accepted` _boolean_ + + Accepted. + +
+
+ + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "email": "Example Name", + "role": 20, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "responded_at": "2024-01-01T00:00:00Z", + "accepted": true +} +``` + + + +
+
diff --git a/apps/developer-docs/docs/api-reference/workspace-invitations/update-workspace-invitation.md b/apps/developer-docs/docs/api-reference/workspace-invitations/update-workspace-invitation.md new file mode 100644 index 00000000..2bae5155 --- /dev/null +++ b/apps/developer-docs/docs/api-reference/workspace-invitations/update-workspace-invitation.md @@ -0,0 +1,149 @@ +--- +title: Update workspace invitation +description: Update workspace invitation via Plane API. HTTP request format, parameters, scopes, and example responses for update workspace invitation. +keywords: plane, plane api, rest api, api integration, workspace invitations, update workspace invitation +--- + +# Update workspace invitation + +
+ PATCH + /api/v1/workspaces/{workspace_slug}/invitations/{invitation_id}/ +
+ +
+
+ +Update a workspace invite + +
+ +### Path Parameters + +
+ + + +The unique identifier of the invitation. + + + + + +The workspace_slug represents the unique workspace identifier for a workspace in Plane. It can be found in the URL. For example, in the URL `https://app.plane.so/my-team/projects/`, the workspace slug is `my-team`. + + + +
+
+ +
+ +### Body Parameters + +
+ + + +Email. + + + + + +- `20` - Admin +- `15` - Member +- `5` - Guest + + + +
+
+ +
+ +### Scopes + +Workspace admin or owner permission required. + +
+ +
+ +
+ + + + + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "email": "Example Name", + "role": 20, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "responded_at": "2024-01-01T00:00:00Z", + "accepted": true +} +``` + + + +
+ +
diff --git a/apps/developer-docs/docs/dev-tools/agents/best-practices.md b/apps/developer-docs/docs/dev-tools/agents/best-practices.md new file mode 100644 index 00000000..82d14d87 --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/agents/best-practices.md @@ -0,0 +1,414 @@ +--- +title: Agent best practices +description: Guidelines for building responsive, user-friendly Plane agents that provide a seamless experience. Covers error handling, response patterns, and user communication. +keywords: plane agent best practices, agent design patterns, plane bot guidelines, agent error handling, responsive agents, plane agent development +--- + +# Best practices + +::: info +Plane Agents are currently in **Beta**. Please send any feedback to support@plane.so. +::: + +## Overview + +Building a great agent experience requires thoughtful design around responsiveness, error handling, and user communication. This guide covers best practices to ensure your agent feels native to Plane and provides a seamless experience for users. + +## Sending immediate thought activity + +When your agent receives a webhook, users are waiting for a response. The most important best practice is to **acknowledge the request immediately**. + +### Why immediate acknowledgment matters + +- Users see that your agent is active and processing their request +- Prevents the Agent Run from being marked as `stale` (5-minute timeout) +- Builds trust that the agent received and understood the request +- Provides visual feedback during potentially long processing times + +### Implementation + +Send a `thought` activity within the first few seconds of receiving a webhook: + +:::tabs key:language +== TypeScript {#typescript} + +```typescript +import { PlaneClient } from "@makeplane/plane-node-sdk"; + +async function handleWebhook( + webhook: AgentRunActivityWebhook, + credentials: { bot_token: string; workspace_slug: string }, +) { + const planeClient = new PlaneClient({ + baseUrl: process.env.PLANE_API_URL || "https://api.plane.so", + accessToken: credentials.bot_token, + }); + + const agentRunId = webhook.agent_run.id; + + // IMMEDIATELY acknowledge receipt + await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "thought", + content: { type: "thought", body: "Received your request. Analyzing..." }, + }); + + // Now proceed with actual processing + // This can take longer since user knows agent is working + const result = await processRequest(webhook); + + // ... rest of the logic +} +``` + +== Python {#python} + +```python +from plane import PlaneClient +from plane.models.agent_runs import CreateAgentRunActivity + +def handle_webhook(webhook: dict, credentials: dict): + plane_client = PlaneClient( + base_url=os.getenv("PLANE_API_URL", "https://api.plane.so"), + access_token=credentials["bot_token"], + ) + + agent_run_id = webhook["agent_run"]["id"] + + # IMMEDIATELY acknowledge receipt + plane_client.agent_runs.activities.create( + workspace_slug=credentials["workspace_slug"], + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="thought", + content={"type": "thought", "body": "Received your request. Analyzing..."}, + ), + ) + + # Now proceed with actual processing + result = process_request(webhook) + + # ... rest of the logic +``` + +::: + +### Thought activity best practices + +- Keep thoughts concise but informative +- Update thoughts as you progress through different stages +- Use thoughts to explain what the agent is doing, not technical details + +**Good examples:** + +- "Analyzing your question about project timelines..." +- "Searching for relevant work items..." +- "Preparing response with the requested data..." + +**Avoid:** + +- "Initializing LLM context with temperature 0.7..." +- "Executing database query SELECT \* FROM..." +- Generic messages like "Working..." repeated multiple times + +## Acknowledging important signals + +Signals communicate user intent beyond the message content. Your agent **must** handle the `stop` signal appropriately. + +### The stop signal + +When a user wants to stop an agent run, Plane sends a `stop` signal with the activity. Your agent should: + +1. **Recognize the signal immediately** +2. **Stop any ongoing processing** +3. **Send a confirmation response** + +:::tabs key:language +== TypeScript {#typescript} + +```typescript +async function handleWebhook( + webhook: AgentRunActivityWebhook, + credentials: { bot_token: string; workspace_slug: string }, +) { + const planeClient = new PlaneClient({ + baseUrl: process.env.PLANE_API_URL || "https://api.plane.so", + accessToken: credentials.bot_token, + }); + + const signal = webhook.agent_run_activity.signal; + const agentRunId = webhook.agent_run.id; + + // ALWAYS check for stop signal first + if (signal === "stop") { + // Cancel any ongoing work + cancelOngoingTasks(agentRunId); + + // Acknowledge the stop + await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "response", + content: { + type: "response", + body: "Understood. I've stopped processing your previous request.", + }, + }); + + return; // Exit early + } + + // Continue with normal processing... +} +``` + +== Python {#python} + +```python +def handle_webhook(webhook: dict, credentials: dict): + plane_client = PlaneClient( + base_url=os.getenv("PLANE_API_URL", "https://api.plane.so"), + access_token=credentials["bot_token"], + ) + + signal = webhook["agent_run_activity"]["signal"] + agent_run_id = webhook["agent_run"]["id"] + + # ALWAYS check for stop signal first + if signal == "stop": + # Cancel any ongoing work + cancel_ongoing_tasks(agent_run_id) + + # Acknowledge the stop + plane_client.agent_runs.activities.create( + workspace_slug=credentials["workspace_slug"], + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="response", + content={ + "type": "response", + "body": "Understood. I've stopped processing your previous request.", + }, + ), + ) + + return # Exit early + + # Continue with normal processing... +``` + +::: + +### Signal considerations + +| Signal | How to Handle | +| ---------- | ----------------------------------------- | +| `continue` | Default behavior, proceed with processing | +| `stop` | Immediately halt and confirm | + +## Progress communication + +For long-running tasks, keep users informed with progress updates. + +### Multi-step operations + +When your agent performs multiple steps, send thought activities for each: + +```typescript +// Step 1: Acknowledge +await createThought("Understanding your request..."); + +// Step 2: First action +await createAction("searchDocuments", { query: userQuery }); +const searchResults = await searchDocuments(userQuery); + +// Step 3: Processing +await createThought("Found relevant information. Analyzing..."); + +// Step 4: Additional work +await createAction("generateSummary", { data: searchResults }); +const summary = await generateSummary(searchResults); + +// Step 5: Final response +await createResponse(`Here's what I found: ${summary}`); +``` + +### Avoiding information overload + +While progress updates are important, too many can be overwhelming: + +- **Don't** send a thought for every internal function call +- **Do** send thoughts for user-meaningful milestones +- **Don't** expose technical implementation details +- **Do** explain what value is being created for the user + +## Error handling + +Graceful error handling is crucial for a good user experience. + +### Always catch and report errors + +```typescript +async function handleWebhook( + webhook: AgentRunActivityWebhook, + credentials: { bot_token: string; workspace_slug: string }, +) { + const planeClient = new PlaneClient({ + baseUrl: process.env.PLANE_API_URL || "https://api.plane.so", + accessToken: credentials.bot_token, + }); + + const agentRunId = webhook.agent_run.id; + + try { + await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "thought", + content: { type: "thought", body: "Processing your request..." }, + }); + + // Your logic here... + const result = await processRequest(webhook); + + await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "response", + content: { type: "response", body: result }, + }); + } catch (error) { + // ALWAYS inform the user about errors + await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "error", + content: { + type: "error", + body: getUserFriendlyErrorMessage(error), + }, + }); + } +} + +function getUserFriendlyErrorMessage(error: Error): string { + // Map technical errors to user-friendly messages + if (error.message.includes("rate limit")) { + return "I'm receiving too many requests right now. Please try again in a few minutes."; + } + if (error.message.includes("timeout")) { + return "The operation took too long. Please try a simpler request or try again later."; + } + // Generic fallback + return "I encountered an unexpected error. Please try again or contact support if the issue persists."; +} +``` + +### Error message guidelines + +**Do:** + +- Use clear, non-technical language +- Suggest next steps when possible +- Be honest about what went wrong (at a high level) + +**Don't:** + +- Expose stack traces or technical details +- Blame the user for errors +- Leave users without any feedback + +## Handling conversation context + +For multi-turn conversations, maintain context from previous activities. + +### Fetching previous activities + +```typescript +// Get all activities for context +const activities = await planeClient.agentRuns.activities.list( + credentials.workspace_slug, + agentRunId, +); + +// Build conversation history +const history = activities.results + .filter((a) => a.type === "prompt" || a.type === "response") + .map((a) => ({ + role: a.type === "prompt" ? "user" : "assistant", + content: a.content.body, + })); + +// Use history in your LLM call or logic +const response = await processWithContext(newPrompt, history); +``` + +### Context best practices + +- Retrieve relevant history, not every single activity +- Filter to meaningful exchanges (prompts and responses) +- Consider summarizing long histories to save tokens/processing +- Don't assume infinite context availability + +## Rate limiting and timeouts + +Be mindful of Plane's API limits and your own processing time. + +### Stale run prevention + +Agent Runs are marked as `stale` after 5 minutes of inactivity. For long operations: + +```typescript +async function longRunningTask(agentRunId: string) { + const HEARTBEAT_INTERVAL = 60000; // 1 minute + + const heartbeat = setInterval(async () => { + await createThought("Still working on your request..."); + }, HEARTBEAT_INTERVAL); + + try { + const result = await performLongOperation(); + return result; + } finally { + clearInterval(heartbeat); + } +} +``` + +### Webhook response time + +- Return HTTP 200 from your webhook handler quickly (within seconds) +- Process the actual agent logic asynchronously +- Don't block the webhook response waiting for LLM calls + +```typescript +// Good: Respond immediately, process async +app.post("/webhook", async (req, res) => { + res.status(200).json({ received: true }); + + // Process in background + processWebhookAsync(req.body).catch(console.error); +}); +``` + +## Summary checklist + +**Responsiveness** + +- Send thought within seconds of webhook +- Return webhook response quickly +- Send heartbeats for long operations + +**Signal handling** + +- Always check for `stop` signal first +- Handle all signal types appropriately +- Confirm when stopping + +**Error handling** + +- Wrap processing in try/catch +- Always send error activity on failure +- Use friendly error messages + +**User experience** + +- Progress updates for long tasks +- Clear, non-technical communication +- Maintain conversation context + +## Next steps + +- Learn about [Signals & Content Payload](/dev-tools/agents/signals-content-payload) for advanced activity handling +- Review the [Building an Agent](/dev-tools/agents/building-an-agent) guide for implementation details diff --git a/apps/developer-docs/docs/dev-tools/agents/building-an-agent.md b/apps/developer-docs/docs/dev-tools/agents/building-an-agent.md new file mode 100644 index 00000000..d8c63841 --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/agents/building-an-agent.md @@ -0,0 +1,591 @@ +--- +title: Building an Agent +description: Step-by-step guide to creating a Plane agent, including OAuth setup, webhook handling, and activity creation. +keywords: build plane agent, plane agent tutorial, oauth agent setup, plane webhook handler, agent activity api, plane bot development, ai agent integration +--- + +# Building an agent + +::: info +Plane Agents are currently in **Beta**. Please send any feedback to support@plane.so. +::: + +## Prerequisites + +Before building an agent, make sure you have completed the following: + +1. **Build a Plane app** — Follow the [Build a Plane App](/dev-tools/build-plane-app/overview) guide to understand OAuth flows, deployment, and webhook handling. + +2. **Get your bot token** — Complete the [Bot Token Flow](/dev-tools/build-plane-app/choose-token-flow) to obtain a `bot_token` for your agent. This token is used for all API calls. + +3. **Set up webhook handling** — Ensure your server can [receive and verify webhooks](/dev-tools/build-plane-app/webhooks) from Plane. + +::: info +This guide assumes you have a working OAuth app with webhook handling. If not, complete the [Build a Plane App](/dev-tools/build-plane-app/overview) guide first. +::: + +## Creating an agent + +Building a Plane agent involves three main steps: + +1. Create an OAuth application with agent capabilities enabled +2. Implement the OAuth flow to install your agent in workspaces +3. Handle webhooks and create activities to respond to users + +### OAuth app creation + +To create an agent, you first need to [register an OAuth application](/dev-tools/build-plane-app/create-oauth-application) with the **Enable App Mentions** checkbox enabled. + +1. Navigate to `https://app.plane.so//settings/integrations/` +2. Click on **Build your own** button +3. Fill out the required details: + - **Setup URL**: The URL users are redirected to when installing your app + - **Redirect URIs**: Where Plane sends the authorization code after consent + - **Webhook URL Endpoint**: Your service's webhook endpoint for receiving events +4. **Enable the "Enable App Mentions" checkbox** — This is required for agents +5. **Choose the "Agent Run" scopes** — This is required for agents to be able to create run activities and get run details. See [OAuth Scopes](/dev-tools/build-plane-app/oauth-scopes#agent-run-scopes) for more information on the available scopes. +6. Save and securely store your **Client ID** and **Client Secret** + +::: info +The "Enable App Mentions" checkbox is what transforms a regular OAuth app into an agent that can be @mentioned in work items. +::: + +### Setting is mentionable + +When you enable app mentions during OAuth app creation, your application becomes mentionable in work item comments. This means: + +- Users will see your agent in the mention picker when typing `@` +- Your agent can be assigned or delegated work items +- Webhooks will be triggered when users interact with your agent + +After installation, your agent appears alongside workspace members in the mention autocomplete. + +## Agent interaction + +Once your agent is installed via the [OAuth consent flow](/dev-tools/build-plane-app/choose-token-flow) and users start mentioning it, you need to handle the interactions through Agent Runs and Activities. + +### AgentRun + +An **AgentRun** tracks a complete interaction session between a user and your agent. + +#### Key fields + +| Field | Type | Description | +| ---------------- | -------- | ------------------------------------------------------------------------------------------------------------ | +| `id` | UUID | Unique identifier for the agent run | +| `agent_user` | UUID | The bot user ID representing your agent | +| `issue` | UUID | The work item where the interaction started | +| `project` | UUID | The project containing the work item | +| `workspace` | UUID | The workspace where the agent is installed | +| `comment` | UUID | The comment thread for this run | +| `source_comment` | UUID | The original comment that triggered the run | +| `creator` | UUID | The user who initiated the run | +| `status` | String | Current status (`created`, `in_progress`, `awaiting`, `completed`, `stopping`, `stopped`, `failed`, `stale`) | +| `started_at` | DateTime | When the run started | +| `ended_at` | DateTime | When the run ended (if applicable) | +| `stopped_at` | DateTime | When a stop was requested | +| `stopped_by` | UUID | User who requested the stop | +| `external_link` | URL | Optional link to external dashboard/logs | +| `error_metadata` | JSON | Error details if the run failed | +| `type` | String | Type of run (currently `comment_thread`) | + +### AgentRunActivity + +An **AgentRunActivity** represents a single message or action within an Agent Run. + +#### Key fields + +| Field | Type | Description | +| ------------------ | ------- | ------------------------------------------------------------------------------------ | +| `id` | UUID | Unique identifier for the activity | +| `agent_run` | UUID | The parent Agent Run | +| `type` | String | Activity type (`prompt`, `thought`, `action`, `response`, `elicitation`, `error`) | +| `content` | JSON | The activity content (structure varies by type) | +| `content_metadata` | JSON | Additional metadata about the content | +| `ephemeral` | Boolean | If true, the activity is temporary and won't create a comment | +| `signal` | String | Signal for how to handle the activity (`continue`, `stop`, `auth_request`, `select`) | +| `signal_metadata` | JSON | Additional signal data | +| `actor` | UUID | The user or bot that created the activity | +| `comment` | UUID | Associated comment (for non-ephemeral activities) | + +### Creating activities + +Your agent communicates back to users by creating activities. We recommend using the official SDKs which provide typed helpers and insulate your code from API changes. + +#### Install the SDK + +:::tabs key:language +== Node.js {#nodejs} + +```bash +npm install @makeplane/plane-node-sdk +``` + +== Python {#python} + +```bash +pip install plane-sdk +``` + +::: + +#### Activity examples + +:::tabs key:language +== TypeScript {#typescript} + +```typescript +import { PlaneClient } from "@makeplane/plane-node-sdk"; + +// Initialize the client with your bot token +const planeClient = new PlaneClient({ + baseUrl: process.env.PLANE_API_URL || "https://api.plane.so", + accessToken: botToken, +}); + +// Create a thought activity (ephemeral - shows agent's reasoning) +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "thought", + content: { + type: "thought", + body: "Analyzing the user's request about weather data...", + }, +}); + +// Create an action activity (ephemeral - shows tool usage) +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "action", + content: { + type: "action", + action: "getWeather", + parameters: { location: "San Francisco" }, + }, +}); + +// Create a response activity (creates a visible comment) +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "response", + content: { + type: "response", + body: "The weather in San Francisco is currently 68°F with partly cloudy skies.", + }, + signal: "continue", +}); + +// Create an elicitation activity (asks user for input) +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "elicitation", + content: { + type: "elicitation", + body: "Which city would you like me to check the weather for?", + }, +}); + +// Create an error activity +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "error", + content: { + type: "error", + body: "Unable to fetch weather data. Please try again later.", + }, +}); +``` + +== Python {#python} + +```python +from plane import PlaneClient +from plane.models.agent_runs import CreateAgentRunActivity + +# Initialize the client with your bot token +plane_client = PlaneClient( + base_url=os.getenv("PLANE_API_URL", "https://api.plane.so"), + access_token=bot_token, +) + +# Create a thought activity (ephemeral - shows agent's reasoning) +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="thought", + content={ + "type": "thought", + "body": "Analyzing the user's request about weather data...", + }, + ), +) + +# Create an action activity (ephemeral - shows tool usage) +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="action", + content={ + "type": "action", + "action": "getWeather", + "parameters": {"location": "San Francisco"}, + }, + ), +) + +# Create a response activity (creates a visible comment) +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="response", + content={ + "type": "response", + "body": "The weather in San Francisco is currently 68°F with partly cloudy skies.", + }, + signal="continue", + ), +) + +# Create an elicitation activity (asks user for input) +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="elicitation", + content={ + "type": "elicitation", + "body": "Which city would you like me to check the weather for?", + }, + ), +) + +# Create an error activity +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="error", + content={ + "type": "error", + "body": "Unable to fetch weather data. Please try again later.", + }, + ), +) +``` + +::: + +### Content payload types + +The `content` field structure varies based on the activity type: + +::: details Thought +Internal reasoning from the agent. Automatically marked as ephemeral. + +```json +{ + "type": "thought", + "body": "The user is asking about weather data for their location." +} +``` + +::: + +::: details Action +A tool invocation. Automatically marked as ephemeral. You can include results after execution. + +```json +{ + "type": "action", + "action": "searchDatabase", + "parameters": { + "query": "weather API", + "limit": "10" + } +} +``` + +With result: + +```json +{ + "type": "action", + "action": "searchDatabase", + "parameters": { + "query": "weather API", + "result": "Found 3 matching records" + } +} +``` + +::: + +::: details Response +A final response to the user. Creates a comment reply. + +```json +{ + "type": "response", + "body": "Here's the weather forecast for San Francisco..." +} +``` + +::: + +::: details Elicitation +A question requesting user input. Creates a comment and sets run to `awaiting`. + +```json +{ + "type": "elicitation", + "body": "Could you please specify which date range you're interested in?" +} +``` + +::: + +::: details Error +An error message. Creates a comment and sets run to `failed`. + +```json +{ + "type": "error", + "body": "I encountered an error while processing your request." +} +``` + +::: + +### Signals + +Signals provide additional context about how an activity should be interpreted: + +| Signal | Description | +| -------------- | --------------------------------------------------------- | +| `continue` | Default signal, indicates the conversation can continue | +| `stop` | User requested to stop the agent run | +| `auth_request` | Agent needs user to authenticate with an external service | +| `select` | Agent is presenting options for user to select from | + +See [Signals & Content Payload](/dev-tools/agents/signals-content-payload) for detailed information. + +### Ephemeral activities + +Activities with `ephemeral: true` are temporary and don't create comments. They're useful for showing agent progress without cluttering the conversation. + +The following activity types are automatically marked as ephemeral: + +- `thought` +- `action` +- `error` + +Ephemeral activities are displayed temporarily in the UI and replaced when the next activity arrives. + +## AgentRun webhooks + +Your agent receives webhooks when users interact with it. There are two main webhook events: + +### AgentRun create webhook + +Triggered when a new Agent Run is created (user first mentions your agent). + +**Event:** `agent_run_create` + +**Payload:** + +```json +{ + "action": "created", + "agent_run": { + "id": "uuid", + "agent_user": "uuid", + "issue": "uuid", + "project": "uuid", + "workspace": "uuid", + "status": "created", + "type": "comment_thread", + "started_at": "2025-01-15T10:30:00Z" + }, + "agent_user_id": "uuid", + "app_client_id": "your-client-id", + "issue_id": "uuid", + "project_id": "uuid", + "workspace_id": "uuid", + "comment_id": "uuid", + "type": "agent_run" +} +``` + +### AgentRun activity webhook + +Triggered when a user sends a prompt to your agent (initial mention or follow-up). + +**Event:** `agent_run_user_prompt` + +**Payload:** + +```json +{ + "action": "prompted", + "agent_run_activity": { + "id": "uuid", + "agent_run": "uuid", + "type": "prompt", + "content": { + "type": "prompt", + "body": "What's the weather like in San Francisco?" + }, + "ephemeral": false, + "signal": "continue", + "actor": "uuid", + "workspace": "uuid" + }, + "agent_run": { + "id": "uuid", + "agent_user": "uuid", + "issue": "uuid", + "project": "uuid", + "workspace": "uuid", + "status": "in_progress" + }, + "agent_user_id": "uuid", + "app_client_id": "your-client-id", + "comment_id": "uuid", + "issue_id": "uuid", + "project_id": "uuid", + "workspace_id": "uuid", + "type": "agent_run_activity" +} +``` + +### Handling webhooks + +Here's a complete example of handling agent webhooks using the SDKs: + +:::tabs key:language +== TypeScript {#typescript} + +```typescript +import { PlaneClient } from "@makeplane/plane-node-sdk"; + +interface AgentRunActivityWebhook { + action: string; + agent_run_activity: { + id: string; + content: { type: string; body?: string }; + signal: string; + }; + agent_run: { + id: string; + status: string; + }; + workspace_id: string; + project_id: string; + type: string; +} + +async function handleWebhook( + webhook: AgentRunActivityWebhook, + credentials: { bot_token: string; workspace_slug: string }, +) { + // Only handle agent_run_activity webhooks + if (webhook.type !== "agent_run_activity") { + return; + } + + const planeClient = new PlaneClient({ + baseUrl: process.env.PLANE_API_URL || "https://api.plane.so", + accessToken: credentials.bot_token, + }); + + const agentRunId = webhook.agent_run.id; + const userPrompt = webhook.agent_run_activity.content.body || ""; + const signal = webhook.agent_run_activity.signal; + + // Check for stop signal + if (signal === "stop") { + await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "response", + content: { type: "response", body: "Stopping as requested." }, + }); + return; + } + + // Send initial thought (ephemeral - shows processing status) + await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "thought", + content: { type: "thought", body: "Processing your request..." }, + }); + + // Process the request (implement your logic here) + const response = await processUserRequest(userPrompt); + + // Send the response (creates a visible comment) + await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "response", + content: { type: "response", body: response }, + }); +} +``` + +== Python {#python} + +```python +from plane import PlaneClient +from plane.models.agent_runs import CreateAgentRunActivity + +def handle_webhook(webhook: dict, credentials: dict): + """Handle incoming agent webhook.""" + # Only handle agent_run_activity webhooks + if webhook.get("type") != "agent_run_activity": + return + + plane_client = PlaneClient( + base_url=os.getenv("PLANE_API_URL", "https://api.plane.so"), + access_token=credentials["bot_token"], + ) + + agent_run_id = webhook["agent_run"]["id"] + user_prompt = webhook["agent_run_activity"]["content"].get("body", "") + signal = webhook["agent_run_activity"]["signal"] + + # Check for stop signal + if signal == "stop": + plane_client.agent_runs.activities.create( + workspace_slug=credentials["workspace_slug"], + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="response", + content={"type": "response", "body": "Stopping as requested."}, + ), + ) + return + + # Send initial thought (ephemeral - shows processing status) + plane_client.agent_runs.activities.create( + workspace_slug=credentials["workspace_slug"], + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="thought", + content={"type": "thought", "body": "Processing your request..."}, + ), + ) + + # Process the request (implement your logic here) + response = process_user_request(user_prompt) + + # Send the response (creates a visible comment) + plane_client.agent_runs.activities.create( + workspace_slug=credentials["workspace_slug"], + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="response", + content={"type": "response", "body": response}, + ), + ) +``` + +::: + +## Next steps + +- Learn about [Best Practices](/dev-tools/agents/best-practices) for building responsive agents +- Explore [Signals & Content Payload](/dev-tools/agents/signals-content-payload) for advanced activity handling diff --git a/apps/developer-docs/docs/dev-tools/agents/overview.md b/apps/developer-docs/docs/dev-tools/agents/overview.md new file mode 100644 index 00000000..dfeb7c8c --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/agents/overview.md @@ -0,0 +1,141 @@ +--- +title: Agents Overview +description: Learn how to build AI agents that integrate with Plane workspaces, enabling automated task handling, intelligent responses, and seamless collaboration. +keywords: plane agents, ai agents, plane automation, workspace integration, plane bot, ai-powered project management, plane developer tools, agent api +--- + +# Agents overview + +::: info +Plane Agents are currently in **Beta**. Please send any feedback to support@plane.so. +::: + +## What are agents in Plane? + +Agents in Plane are AI-powered applications that can interact with your workspace similar to how human users do. They can be @mentioned in work item comments, receive prompts from users, and respond with intelligent actions. Agents enable automation and AI assistance directly within your project management workflow. + +Key capabilities of Plane agents: + +- **Mentionable** — Users can @mention agents in work item comments to trigger interactions +- **Contextual awareness** — Agents receive full context about the work item, project, and conversation +- **Activity tracking** — All agent interactions are tracked through the Agent Run system +- **Real-time responses** — Agents can send thoughts, actions, and responses back to users + +## Agent installation + +Agents are installed as OAuth applications in your Plane workspace. When you create an OAuth app with the **Enable App Mentions** option enabled, it becomes an agent that users can mention and interact with. + +### Installation flow + +1. A workspace admin installs your agent via the OAuth consent flow +2. Plane creates a **bot user** for your agent in that workspace +3. The bot user ID is returned in the app installation details +4. Users can now @mention your agent in work item comments + +When installed, agents appear in the mention picker alongside regular workspace members. Users can tag them in comments just like any team member. + +::: info +Agents installed in your workspace do not count as billable users. +::: + +## Agent Run lifecycle + +The Agent Run system tracks the complete lifecycle of an agent interaction, from when a user mentions the agent to when the agent completes its task. + +### What is an Agent Run? + +An **Agent Run** represents a single interaction session between a user and an agent. When a user @mentions an agent in a comment, Plane automatically creates an Agent Run to track: + +- The triggering comment and work item +- The conversation thread between user and agent +- All activities (thoughts, actions, responses) from the agent +- The current status of the interaction + +### What is an Agent Run Activity? + +An **Agent Run Activity** is a single unit of communication within an Agent Run. Activities can be: + +- **Prompt** — A message from a user to the agent +- **Thought** — Internal reasoning from the agent (ephemeral) +- **Action** — A tool invocation by the agent (ephemeral) +- **Response** — A final response from the agent (creates a comment) +- **Elicitation** — A question from the agent requesting user input +- **Error** — An error message from the agent + +### How Agent Run works + +The Agent Run flow consists of three main phases: + +```mermaid +sequenceDiagram + participant User + participant Plane + participant Agent + + User->>Plane: @mentions agent in comment + Plane->>Plane: Create AgentRun + Activity (prompt) + Plane->>Agent: Send webhook (agent_run_activity) + + loop Agent Processing + Agent->>Plane: Create Activity (thought/action) + Agent->>Agent: Process and reason + end + + Agent->>Plane: Create Activity (response) + Plane->>User: Show response as comment +``` + +#### Phase 1: Trigger + +1. User @mentions the agent in a work item comment +2. Plane detects the mention and creates a new **Agent Run** +3. An **Agent Run Activity** is created with the user's prompt +4. The Agent Run status is set to `created` + +#### Phase 2: Webhook + +1. Plane triggers a webhook to your agent's webhook URL +2. The webhook payload includes: + - The Agent Run details + - The triggering activity (user prompt) + - Work item and project context + - Workspace information + +#### Phase 3: Agent response + +1. Your agent processes the webhook and starts working +2. The agent sends activities back to Plane via the API: + - `thought` activities to show reasoning (ephemeral, don't create comments) + - `action` activities to show tool usage (ephemeral) + - `response` or `elicitation` when complete (creates a comment) +3. Plane updates the Agent Run status based on activities +4. Non-ephemeral activities (response, elicitation) create comment replies visible to users + +### Agent Run states + +Agent Runs transition through various states based on activities: + +| Status | Description | +| ------------- | ----------------------------------------------------------------- | +| `created` | The run has been initiated but not yet started processing | +| `in_progress` | The agent is actively processing the request | +| `awaiting` | The agent is waiting for additional input from the user | +| `completed` | The agent has successfully finished processing | +| `stopping` | A stop request has been received and is being processed | +| `stopped` | The run has been successfully stopped | +| `failed` | The run encountered an error and cannot continue | +| `stale` | The run has not been updated in 5 minutes and is considered stale | + +### Continuing a conversation + +When a user replies to an agent's response: + +1. If an active Agent Run exists for that thread, the reply is added as a new activity +2. The webhook is triggered again with the updated context +3. The agent can continue the conversation with full history + +This enables multi-turn conversations where users and agents can have back-and-forth interactions. + +## Next steps + +Ready to build your own agent? Continue to [Building an Agent](/dev-tools/agents/building-an-agent) to learn how to create and deploy your first Plane agent. diff --git a/apps/developer-docs/docs/dev-tools/agents/signals-content-payload.md b/apps/developer-docs/docs/dev-tools/agents/signals-content-payload.md new file mode 100644 index 00000000..11d9da24 --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/agents/signals-content-payload.md @@ -0,0 +1,844 @@ +--- +title: Signals & Content Payload +description: Detailed reference for activity signals and content payload structures in Plane agents. Includes signal types, payload formats, and content examples. +keywords: plane agent signals, activity payload, agent content structure, plane agent events, agent activity signals, plane webhook payload, agent data format +--- + +# Signals and content payload + +::: info +Plane Agents are currently in **Beta**. Please send any feedback to support@plane.so. +::: + +## Overview + +Agent activities consist of two key components: + +1. **Content** — The message or action being communicated +2. **Signal** — Metadata indicating how the activity should be interpreted + +Understanding these components is essential for building agents that communicate effectively with users. + +## Signals + +Signals are metadata that modify how an activity should be interpreted or handled. They provide additional context about the sender's intent—guiding how the activity should be processed or responded to. + +### Available signals + +| Signal | Description | Use Case | +| -------------- | -------------------------------------- | ---------------------------------------- | +| `continue` | Default signal, indicates normal flow | Standard responses, ongoing conversation | +| `stop` | User requested to stop the agent | Cancellation, abort operations | +| `auth_request` | Agent needs external authentication | OAuth flows, API key collection | +| `select` | Agent presenting options for selection | Multiple choice questions | + +### Signal: `continue` + +The default signal for most activities. Indicates normal conversation flow where the agent can continue processing. + +```json +{ + "type": "response", + "content": { + "type": "response", + "body": "Here's the information you requested." + }, + "signal": "continue" +} +``` + +### Signal: `stop` + +Sent by Plane when a user requests to stop the agent. Your agent should: + +1. Immediately halt any ongoing processing +2. Clean up resources if needed +3. Send a confirmation response + +**Incoming webhook with stop signal:** + +```json +{ + "action": "prompted", + "agent_run_activity": { + "type": "prompt", + "content": { + "type": "prompt", + "body": "Stop" + }, + "signal": "stop" + } +} +``` + +**Handling the stop signal:** + +```typescript +if (webhook.agent_run_activity.signal === "stop") { + // Cancel ongoing work + await cancelAllPendingTasks(); + + // Acknowledge the stop - this transitions run to "stopped" status + await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "response", + content: { + type: "response", + body: "I've stopped working on your request.", + }, + }); + + return; +} +``` + +### Signal: `auth_request` + +Used when your agent needs the user to authenticate with an external service. Requires a URL in the `signal_metadata`. + +**Creating an auth request:** + +```typescript +await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "elicitation", + content: { + type: "elicitation", + body: "I need access to your GitHub account to proceed. Please authenticate using the link below.", + }, + signal: "auth_request", + signal_metadata: { + url: "https://your-agent.com/auth/github?session=abc123", + }, +}); +``` + +**Requirements:** + +- The URL must start with `https://` +- The URL should be a secure endpoint on your agent's server +- After authentication, redirect the user back or notify completion + +### Signal: `select` + +Used when presenting options for the user to choose from. Useful for disambiguation or multi-choice scenarios. + +```typescript +await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "elicitation", + content: { + type: "elicitation", + body: "Which project would you like me to search?\n\n1. Frontend App\n2. Backend API\n3. Mobile App", + }, + signal: "select", + signal_metadata: { + options: [ + { id: "frontend", label: "Frontend App" }, + { id: "backend", label: "Backend API" }, + { id: "mobile", label: "Mobile App" }, + ], + }, +}); +``` + +## Content payload types + +The `content` field contains the actual message or action. Its structure varies based on the activity type. + +### Type: `thought` + +Internal reasoning or progress updates from the agent. Automatically marked as ephemeral (won't create a comment). + +**Structure:** + +```typescript +interface ThoughtContent { + type: "thought"; + body: string; // The thought message +} +``` + +**Example:** + +```json +{ + "type": "thought", + "body": "The user is asking about deployment status. I'll check the CI/CD pipeline." +} +``` + +**Creating a thought activity:** + +:::tabs key:language +== TypeScript {#typescript} + +```typescript +await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "thought", + content: { + type: "thought", + body: "Analyzing the codebase for potential issues...", + }, +}); +``` + +== Python {#python} + +```python +from plane.models.agent_runs import CreateAgentRunActivity + +plane_client.agent_runs.activities.create( + workspace_slug=credentials["workspace_slug"], + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="thought", + content={ + "type": "thought", + "body": "Analyzing the codebase for potential issues...", + }, + ), +) +``` + +::: + +**Best practices for thoughts:** + +- Keep them concise and user-meaningful +- Use to show progress, not internal implementation +- Update as you move through stages of processing + +### Type: `action` + +Describes a tool invocation or external action. Automatically marked as ephemeral. + +**Structure:** + +```typescript +interface ActionContent { + type: "action"; + action: string; // Name of the tool/action + parameters: { + // Key-value pairs of parameters + [key: string]: string; + }; +} +``` + +**Example - Starting an action:** + +```json +{ + "type": "action", + "action": "searchDatabase", + "parameters": { + "query": "bug reports", + "status": "open" + } +} +``` + +**Example - Action with result:** + +```json +{ + "type": "action", + "action": "searchDatabase", + "parameters": { + "query": "bug reports", + "status": "open", + "result": "Found 12 matching work items" + } +} +``` + +**Creating action activities:** + +:::tabs key:language +== TypeScript {#typescript} + +```typescript +// Before executing the action +await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "action", + content: { + type: "action", + action: "fetchWeather", + parameters: { + location: "San Francisco", + }, + }, +}); + +// Execute the actual action +const weatherData = await fetchWeather("San Francisco"); + +// After execution, report the result +await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, { + type: "action", + content: { + type: "action", + action: "fetchWeather", + parameters: { + location: "San Francisco", + result: `Temperature: ${weatherData.temp}°F, Conditions: ${weatherData.conditions}`, + }, + }, + content_metadata: { + result: weatherData, // Store full result in metadata + }, +}); +``` + +== Python {#python} + +```python +from plane.models.agent_runs import CreateAgentRunActivity + +# Before executing the action +plane_client.agent_runs.activities.create( + workspace_slug=credentials["workspace_slug"], + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="action", + content={ + "type": "action", + "action": "fetchWeather", + "parameters": {"location": "San Francisco"}, + }, + ), +) + +# Execute the actual action +weather_data = fetch_weather("San Francisco") + +# After execution, report the result +plane_client.agent_runs.activities.create( + workspace_slug=credentials["workspace_slug"], + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="action", + content={ + "type": "action", + "action": "fetchWeather", + "parameters": { + "location": "San Francisco", + "result": f"Temperature: {weather_data['temp']}°F, Conditions: {weather_data['conditions']}", + }, + }, + content_metadata={"result": weather_data}, + ), +) +``` + +::: + +**Parameter requirements:** + +- All parameter keys must be strings +- All parameter values must be strings +- Use `content_metadata` to store complex/structured data + +### Type: `response` + +A final response to the user. Creates a comment reply visible to users. + +**Structure:** + +```typescript +interface ResponseContent { + type: "response"; + body: string; // The response message (supports Markdown) +} +``` + +**Example:** + +```json +{ + "type": "response", + "body": "Based on my analysis, here are the top 3 issues affecting your sprint:\n\n1. **AUTH-123**: Login timeout affecting 15% of users\n2. **API-456**: Rate limiting too aggressive\n3. **UI-789**: Dashboard loading slowly\n\nWould you like me to provide more details on any of these?" +} +``` + +**Creating a response:** + +:::tabs key:language +== TypeScript {#typescript} + +```typescript +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "response", + content: { + type: "response", + body: "Here's the weather in San Francisco:\n\n**68°F** - Partly Cloudy\n\nExpect mild conditions throughout the day.", + }, + signal: "continue", +}); +``` + +== Python {#python} + +```python +from plane.models.agent_runs import CreateAgentRunActivity + +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="response", + content={ + "type": "response", + "body": "Here's the weather in San Francisco:\n\n**68°F** - Partly Cloudy\n\nExpect mild conditions throughout the day.", + }, + signal="continue", + ), +) +``` + +::: + +**Response best practices:** + +- Use Markdown for formatting +- Be clear and concise +- Include relevant context +- End with a call-to-action if appropriate + +### Type: `elicitation` + +Requests clarification or input from the user. Creates a comment and sets the Agent Run status to `awaiting`. + +**Structure:** + +```typescript +interface ElicitationContent { + type: "elicitation"; + body: string; // The question or request (supports Markdown) +} +``` + +**Example:** + +```json +{ + "type": "elicitation", + "body": "I found multiple projects matching your query. Which one would you like me to focus on?\n\n1. Project Alpha (12 open work items)\n2. Project Beta (8 open work items)\n3. Project Gamma (23 open work items)" +} +``` + +**Creating an elicitation:** + +:::tabs key:language +== TypeScript {#typescript} + +```typescript +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "elicitation", + content: { + type: "elicitation", + body: "To generate the report, I need a few details:\n\n- What date range should I cover?\n- Should I include completed work items or only open ones?", + }, +}); +``` + +== Python {#python} + +```python +from plane.models.agent_runs import CreateAgentRunActivity + +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="elicitation", + content={ + "type": "elicitation", + "body": "To generate the report, I need a few details:\n\n- What date range should I cover?\n- Should I include completed work items or only open ones?", + }, + ), +) +``` + +::: + +**Elicitation best practices:** + +- Ask specific, answerable questions +- Provide options when possible +- Don't ask too many questions at once +- Consider using `select` signal for multiple choice + +### Type: `error` + +Reports an error or failure. Creates a comment and sets the Agent Run status to `failed`. + +**Structure:** + +```typescript +interface ErrorContent { + type: "error"; + body: string; // The error message (supports Markdown) +} +``` + +**Example:** + +```json +{ + "type": "error", + "body": "I couldn't complete your request due to a connection issue with the external service. Please try again in a few minutes." +} +``` + +**Creating an error activity:** + +:::tabs key:language +== TypeScript {#typescript} + +```typescript +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "error", + content: { + type: "error", + body: "I was unable to access the GitHub repository. Please ensure the integration is properly configured.", + }, + signal_metadata: { + error_code: "GITHUB_ACCESS_DENIED", + retryable: true, + }, +}); +``` + +== Python {#python} + +```python +from plane.models.agent_runs import CreateAgentRunActivity + +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="error", + content={ + "type": "error", + "body": "I was unable to access the GitHub repository. Please ensure the integration is properly configured.", + }, + signal_metadata={ + "error_code": "GITHUB_ACCESS_DENIED", + "retryable": True, + }, + ), +) +``` + +::: + +**Error best practices:** + +- Use friendly, non-technical language +- Suggest next steps when possible +- Store detailed error info in `signal_metadata` +- Don't expose stack traces or sensitive information + +### Type: `prompt` + +This type is **user-generated only**. Your agent cannot create prompt activities—they're created by Plane when a user sends a message. + +**Structure:** + +```typescript +interface PromptContent { + type: "prompt"; + body: string; // The user's message +} +``` + +**Example received in webhook:** + +```json +{ + "type": "prompt", + "body": "Can you check the status of our deployment pipeline?" +} +``` + +## Ephemeral activities + +Ephemeral activities are temporary and won't create comment replies. They're useful for showing agent progress without cluttering the conversation thread. + +### Automatically ephemeral types + +The following activity types are automatically marked as ephemeral: + +- `thought` +- `action` +- `error` + +### Ephemeral behavior + +- Ephemeral activities appear temporarily in the Agent UI +- They're replaced when the next activity arrives +- They don't create permanent comment replies +- Useful for real-time progress updates + +### Visual example + +``` +User: @WeatherBot What's the weather in Tokyo? + +[Ephemeral - disappears when next activity arrives] +Analyzing your request... + +[Ephemeral - disappears when next activity arrives] +getCoordinates("Tokyo") + +[Ephemeral - disappears when next activity arrives] +getWeather(35.6762, 139.6503) → 72°F, Clear + +[Permanent - stays as comment] +The weather in Tokyo is currently 72°F with clear skies. +``` + +## Content metadata + +Use `content_metadata` to store additional structured data about an activity: + +:::tabs key:language +== TypeScript {#typescript} + +```typescript +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "action", + content: { + type: "action", + action: "analyzeCode", + parameters: { + file: "src/index.ts", + result: "Found 3 potential issues", + }, + }, + content_metadata: { + analysis_results: { + issues: [ + { line: 42, severity: "warning", message: "Unused variable" }, + { line: 78, severity: "error", message: "Type mismatch" }, + { line: 156, severity: "info", message: "Consider refactoring" }, + ], + processing_time_ms: 1250, + }, + }, +}); +``` + +== Python {#python} + +```python +from plane.models.agent_runs import CreateAgentRunActivity + +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="action", + content={ + "type": "action", + "action": "analyzeCode", + "parameters": { + "file": "src/index.ts", + "result": "Found 3 potential issues", + }, + }, + content_metadata={ + "analysis_results": { + "issues": [ + {"line": 42, "severity": "warning", "message": "Unused variable"}, + {"line": 78, "severity": "error", "message": "Type mismatch"}, + {"line": 156, "severity": "info", "message": "Consider refactoring"}, + ], + "processing_time_ms": 1250, + }, + }, + ), +) +``` + +::: + +## Signal metadata + +Use `signal_metadata` to provide additional context for signals: + +```typescript +// Auth request with URL +{ + signal: "auth_request", + signal_metadata: { + url: "https://your-agent.com/auth/connect?session=xyz", + provider: "github", + scopes: ["repo", "read:user"], + } +} + +// Select with options +{ + signal: "select", + signal_metadata: { + options: [ + { id: "opt1", label: "Option 1", description: "First choice" }, + { id: "opt2", label: "Option 2", description: "Second choice" }, + ], + allow_multiple: false, + } +} + +// Error with details +{ + signal: "continue", + signal_metadata: { + error_code: "RATE_LIMIT_EXCEEDED", + retry_after: 60, + retryable: true, + } +} +``` + +## Complete activity creation reference + +Here's a comprehensive example showing all activity types: + +:::tabs key:language +== TypeScript {#typescript} + +```typescript +import { PlaneClient } from "@makeplane/plane-node-sdk"; + +const planeClient = new PlaneClient({ + baseUrl: process.env.PLANE_API_URL || "https://api.plane.so", + accessToken: botToken, +}); + +// 1. Thought - Show reasoning (ephemeral) +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "thought", + content: { type: "thought", body: "Analyzing the request..." }, +}); + +// 2. Action - Tool invocation (ephemeral) +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "action", + content: { + type: "action", + action: "searchWorkItems", + parameters: { query: "bug", status: "open" }, + }, +}); + +// 3. Response - Final answer (creates comment) +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "response", + content: { type: "response", body: "Found 5 open bugs." }, + signal: "continue", +}); + +// 4. Elicitation - Ask for input (creates comment, awaits response) +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "elicitation", + content: { type: "elicitation", body: "Which bug should I prioritize?" }, + signal: "select", + signal_metadata: { + options: [ + { id: "bug-1", label: "AUTH-123: Login timeout" }, + { id: "bug-2", label: "API-456: Rate limiting" }, + ], + }, +}); + +// 5. Error - Report failure (creates comment) +await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, { + type: "error", + content: { type: "error", body: "Unable to access the database." }, + signal_metadata: { error_code: "DB_CONNECTION_FAILED" }, +}); +``` + +== Python {#python} + +```python +from plane import PlaneClient +from plane.models.agent_runs import CreateAgentRunActivity + +plane_client = PlaneClient( + base_url=os.getenv("PLANE_API_URL", "https://api.plane.so"), + access_token=bot_token, +) + +# 1. Thought - Show reasoning (ephemeral) +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="thought", + content={"type": "thought", "body": "Analyzing the request..."}, + ), +) + +# 2. Action - Tool invocation (ephemeral) +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="action", + content={ + "type": "action", + "action": "searchWorkItems", + "parameters": {"query": "bug", "status": "open"}, + }, + ), +) + +# 3. Response - Final answer (creates comment) +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="response", + content={"type": "response", "body": "Found 5 open bugs."}, + signal="continue", + ), +) + +# 4. Elicitation - Ask for input (creates comment, awaits response) +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="elicitation", + content={"type": "elicitation", "body": "Which bug should I prioritize?"}, + signal="select", + signal_metadata={ + "options": [ + {"id": "bug-1", "label": "AUTH-123: Login timeout"}, + {"id": "bug-2", "label": "API-456: Rate limiting"}, + ], + }, + ), +) + +# 5. Error - Report failure (creates comment) +plane_client.agent_runs.activities.create( + workspace_slug=workspace_slug, + run_id=agent_run_id, + data=CreateAgentRunActivity( + type="error", + content={"type": "error", "body": "Unable to access the database."}, + signal_metadata={"error_code": "DB_CONNECTION_FAILED"}, + ), +) +``` + +::: + +## Next steps + +- Review [Best Practices](/dev-tools/agents/best-practices) for building responsive agents +- See [Building an Agent](/dev-tools/agents/building-an-agent) for implementation examples +- Check the [Overview](/dev-tools/agents/overview) for Agent Run lifecycle details diff --git a/apps/developer-docs/docs/dev-tools/build-plane-app/choose-token-flow.md b/apps/developer-docs/docs/dev-tools/build-plane-app/choose-token-flow.md new file mode 100644 index 00000000..ecd5b029 --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/build-plane-app/choose-token-flow.md @@ -0,0 +1,220 @@ +--- +title: Choose token flow +description: Decide between Bot Token and User Token flows for your Plane app. Compare client credentials and authorization code grant types for different use cases. +keywords: plane token flow, bot token, user token, oauth flow, client credentials, authorization code, plane api authentication, plane access token +--- + +# Choose token flow + +Plane supports two OAuth flows: + +| Flow | Use When | Token Type | +| ----------------------------------- | ---------------------------------------------- | -------------- | +| **Bot Token** (Client Credentials) | Agents, webhooks, automation, background tasks | `bot_token` | +| **User Token** (Authorization Code) | Actions on behalf of a specific user | `access_token` | + +::: info +Most integrations should use the **Bot Token flow**. Use User Token only when you need to perform actions as a specific user. +::: + +--- + +## Bot token flow + +Use this flow for agents, webhook handlers, and automation that acts autonomously. + +```mermaid +sequenceDiagram + participant User + participant Plane + participant YourApp + + User->>YourApp: Clicks "Install" + YourApp->>Plane: Redirects to consent screen + Plane->>User: Shows consent screen + User->>Plane: Approves + Plane->>YourApp: Redirects with app_installation_id + YourApp->>Plane: POST /auth/o/token/ (client_credentials) + Plane->>YourApp: Returns bot_token + YourApp->>YourApp: Store credentials +``` + +### 1. Redirect to authorization + +When a user clicks "Install", redirect them to Plane's consent screen: + +``` +GET https://api.plane.so/auth/o/authorize-app/ + ?client_id=YOUR_CLIENT_ID + &response_type=code + &redirect_uri=https://your-app.com/callback + &scope=scopeA scopeB scopeC +``` + +### 2. Handle the callback + +After the user approves, Plane redirects to your Redirect URI with: + +| Parameter | Description | +| --------------------- | ----------------------------------------- | +| `app_installation_id` | Unique identifier for this installation | +| `code` | Authorization code (not used in bot flow) | + +### 3. Exchange for bot token + +``` +POST https://api.plane.so/auth/o/token/ +Content-Type: application/x-www-form-urlencoded +Authorization: Basic base64(client_id:client_secret) + +grant_type=client_credentials +&app_installation_id=APP_INSTALLATION_ID +&scope=scopeA scopeB scopeC +``` + +**Response:** + +```json +{ + "access_token": "pln_bot_xxxxxxxxxxxx", + "token_type": "Bearer", + "expires_in": 86400, + "scope": "scopeA scopeB scopeC" +} +``` + +### 4. Get workspace details + +``` +GET https://api.plane.so/auth/o/app-installation/?id=APP_INSTALLATION_ID +Authorization: Bearer YOUR_BOT_TOKEN +``` + +**Response:** + +```json +[ + { + "id": "installation-uuid", + "workspace": "workspace-uuid", + "workspace_detail": { + "name": "My Workspace", + "slug": "my-workspace" + }, + "app_bot": "bot-user-uuid", + "status": "installed" + } +] +``` + +Store the `workspace_detail.slug` for API calls and `app_installation_id` for token refresh. + +### 5. Refresh bot token + +Bot tokens expire. Request a new one using the stored `app_installation_id`: + +``` +POST https://api.plane.so/auth/o/token/ +Content-Type: application/x-www-form-urlencoded +Authorization: Basic base64(client_id:client_secret) + +grant_type=client_credentials +&app_installation_id=APP_INSTALLATION_ID +&scope=scopeA scopeB scopeC +``` + +--- + +## User token flow + +Use this flow when your app needs to act on behalf of a specific user. + +```mermaid +sequenceDiagram + participant User + participant Plane + participant YourApp + + User->>YourApp: Clicks "Connect" + YourApp->>Plane: Redirects to consent screen + Plane->>User: Shows consent screen + User->>Plane: Approves + Plane->>YourApp: Redirects with code + YourApp->>Plane: POST /auth/o/token/ (authorization_code) + Plane->>YourApp: Returns access_token + refresh_token + YourApp->>YourApp: Store tokens for user +``` + +### 1. Redirect to authorization + +``` +GET https://api.plane.so/auth/o/authorize-app/ + ?client_id=YOUR_CLIENT_ID + &response_type=code + &redirect_uri=https://your-app.com/callback + &state=RANDOM_STATE_VALUE + &scope=scopeA scopeB scopeC +``` + +::: info +Include a random `state` parameter to prevent CSRF attacks. Verify it matches when handling the callback. +::: + +### 2. Handle the callback + +After approval, Plane redirects to your Redirect URI with: + +| Parameter | Description | +| --------- | ------------------------------------------ | +| `code` | Authorization code to exchange for tokens | +| `state` | Your state parameter (verify this matches) | + +### 3. Exchange code for tokens + +``` +POST https://api.plane.so/auth/o/token/ +Content-Type: application/x-www-form-urlencoded + +grant_type=authorization_code +&code=AUTHORIZATION_CODE +&client_id=YOUR_CLIENT_ID +&client_secret=YOUR_CLIENT_SECRET +&redirect_uri=https://your-app.com/callback +``` + +**Response:** + +```json +{ + "access_token": "pln_xxxxxxxxxxxx", + "refresh_token": "pln_refresh_xxxxxxxxxxxx", + "token_type": "Bearer", + "expires_in": 86400, + "scope": "scopeA scopeB scopeC" +} +``` + +### 4. Refresh user token + +``` +POST https://api.plane.so/auth/o/token/ +Content-Type: application/x-www-form-urlencoded + +grant_type=refresh_token +&refresh_token=YOUR_REFRESH_TOKEN +&client_id=YOUR_CLIENT_ID +&client_secret=YOUR_CLIENT_SECRET +``` + +--- + +## Making API requests + +Include the token in the `Authorization` header: + +``` +GET https://api.plane.so/api/v1/workspaces/{workspace_slug}/projects/ +Authorization: Bearer YOUR_TOKEN +``` + +See the [API Reference](/api-reference/introduction) for available endpoints. diff --git a/apps/developer-docs/docs/dev-tools/build-plane-app/create-oauth-application.md b/apps/developer-docs/docs/dev-tools/build-plane-app/create-oauth-application.md new file mode 100644 index 00000000..1d5f85b3 --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/build-plane-app/create-oauth-application.md @@ -0,0 +1,31 @@ +--- +title: Create an OAuth Application +description: Register your Plane OAuth application to get Client ID and Secret. Configure setup URL, redirect URI, and webhook endpoints for your integration. +keywords: plane oauth application, create plane app, client id secret, plane app registration, oauth credentials, plane workspace integration, plane api authentication +--- + +# Create an OAuth Application + +1. Navigate to **Workspace Settings** → **Integrations**. + +```text +https://app.plane.so//settings/integrations/ +``` + +2. Click **Build your own**. +3. Fill in the required details: + +| Field | Description | +| ---------------- | ------------------------------------------------------------------------------------------------------ | +| **App Name** | Display name shown to users | +| **Setup URL** | Entry point when users install your app. Your app redirects users to Plane's consent screen from here. | +| **Redirect URI** | Callback URL where Plane sends users after they approve access, along with the authorization code. | +| **Webhook URL** | Endpoint for receiving event notifications | + +4. For agents that respond to @mentions, enable **"Enable App Mentions"**. +5. Save and store your **Client ID** and **Client Secret** securely. +6. Select the scopes you need for your app from the **Scopes & Permissions** section. See [OAuth Scopes](/dev-tools/build-plane-app/oauth-scopes) for more information on the available scopes. + +::: warning +Never expose your Client Secret in client-side code or commit it to version control. +::: diff --git a/apps/developer-docs/docs/dev-tools/build-plane-app/examples.md b/apps/developer-docs/docs/dev-tools/build-plane-app/examples.md new file mode 100644 index 00000000..6aafed1a --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/build-plane-app/examples.md @@ -0,0 +1,248 @@ +--- +title: Plane app code examples +description: Full code examples for building Plane OAuth apps with Node.js (Express) and Python (Flask). Includes bot token flow, user token flow, and webhook handling. +keywords: plane app example, plane oauth example, plane node.js integration, plane python integration, plane express app, plane flask app, plane api code sample +--- + +# Complete examples + +::: code-group + +```typescript [TypeScript (Express)] +import express from "express"; +import axios from "axios"; +import crypto from "crypto"; + +const app = express(); + +const CLIENT_ID = process.env.PLANE_CLIENT_ID!; +const CLIENT_SECRET = process.env.PLANE_CLIENT_SECRET!; +const REDIRECT_URI = process.env.PLANE_REDIRECT_URI!; +const WEBHOOK_SECRET = process.env.PLANE_WEBHOOK_SECRET!; +const PLANE_API_URL = process.env.PLANE_API_URL || "https://api.plane.so"; + +// In-memory storage (use a database in production) +const installations = new Map< + string, + { + botToken: string; + workspaceSlug: string; + appInstallationId: string; + } +>(); + +// Setup URL - redirect to Plane's consent screen +app.get("/oauth/setup", (req, res) => { + const params = new URLSearchParams({ + client_id: CLIENT_ID, + response_type: "code", + redirect_uri: REDIRECT_URI, + }); + res.redirect(`${PLANE_API_URL}/auth/o/authorize-app/?${params}`); +}); + +// OAuth callback - exchange app_installation_id for bot token +app.get("/oauth/callback", async (req, res) => { + const appInstallationId = req.query.app_installation_id as string; + + if (!appInstallationId) { + return res.status(400).send("Missing app_installation_id"); + } + + try { + const basicAuth = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64"); + + // Exchange for bot token + const tokenRes = await axios.post( + `${PLANE_API_URL}/auth/o/token/`, + new URLSearchParams({ + grant_type: "client_credentials", + app_installation_id: appInstallationId, + }).toString(), + { + headers: { + Authorization: `Basic ${basicAuth}`, + "Content-Type": "application/x-www-form-urlencoded", + }, + }, + ); + + const botToken = tokenRes.data.access_token; + + // Get workspace details + const installRes = await axios.get( + `${PLANE_API_URL}/auth/o/app-installation/?id=${appInstallationId}`, + { + headers: { Authorization: `Bearer ${botToken}` }, + }, + ); + + const installation = installRes.data[0]; + const workspaceId = installation.workspace; + const workspaceSlug = installation.workspace_detail.slug; + + // Store credentials + installations.set(workspaceId, { botToken, workspaceSlug, appInstallationId }); + + console.log(`Installed in workspace: ${workspaceSlug}`); + res.send("Installation successful! You can close this window."); + } catch (error) { + console.error("OAuth error:", error); + res.status(500).send("Installation failed"); + } +}); + +// Webhook handler +app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => { + const signature = req.headers["x-plane-signature"] as string; + const payload = req.body.toString(); + + // Verify signature + const expected = crypto.createHmac("sha256", WEBHOOK_SECRET).update(payload).digest("hex"); + if (!crypto.timingSafeEqual(Buffer.from(signature || ""), Buffer.from(expected))) { + return res.status(403).send("Invalid signature"); + } + + const event = JSON.parse(payload); + console.log(`Received: ${event.event} ${event.action}`); + + // Get credentials for this workspace + const creds = installations.get(event.workspace_id); + if (creds) { + // Process the event with creds.botToken + } + + res.status(200).send("OK"); +}); + +app.listen(3000, () => console.log("Server running on http://localhost:3000")); +``` + +```python [Python (Flask)] +import os +import hmac +import hashlib +import base64 +import requests as http_requests +from flask import Flask, request, redirect +from urllib.parse import urlencode + +app = Flask(__name__) + +CLIENT_ID = os.getenv("PLANE_CLIENT_ID") +CLIENT_SECRET = os.getenv("PLANE_CLIENT_SECRET") +REDIRECT_URI = os.getenv("PLANE_REDIRECT_URI") +WEBHOOK_SECRET = os.getenv("PLANE_WEBHOOK_SECRET") +PLANE_API_URL = os.getenv("PLANE_API_URL", "https://api.plane.so") + +# In-memory storage (use a database in production) +installations = {} + + +@app.route("/oauth/setup") +def oauth_setup(): + """Redirect to Plane's consent screen.""" + params = urlencode({ + "client_id": CLIENT_ID, + "response_type": "code", + "redirect_uri": REDIRECT_URI, + }) + return redirect(f"{PLANE_API_URL}/auth/o/authorize-app/?{params}") + + +@app.route("/oauth/callback") +def oauth_callback(): + """Exchange app_installation_id for bot token.""" + app_installation_id = request.args.get("app_installation_id") + + if not app_installation_id: + return "Missing app_installation_id", 400 + + try: + # Exchange for bot token + credentials = f"{CLIENT_ID}:{CLIENT_SECRET}" + basic_auth = base64.b64encode(credentials.encode()).decode() + + token_response = http_requests.post( + f"{PLANE_API_URL}/auth/o/token/", + data={ + "grant_type": "client_credentials", + "app_installation_id": app_installation_id, + }, + headers={ + "Authorization": f"Basic {basic_auth}", + "Content-Type": "application/x-www-form-urlencoded", + }, + ) + token_response.raise_for_status() + bot_token = token_response.json()["access_token"] + + # Get workspace details + install_response = http_requests.get( + f"{PLANE_API_URL}/auth/o/app-installation/", + params={"id": app_installation_id}, + headers={"Authorization": f"Bearer {bot_token}"}, + ) + install_response.raise_for_status() + installation = install_response.json()[0] + + workspace_id = installation["workspace"] + workspace_slug = installation["workspace_detail"]["slug"] + + # Store credentials + installations[workspace_id] = { + "bot_token": bot_token, + "workspace_slug": workspace_slug, + "app_installation_id": app_installation_id, + } + + print(f"Installed in workspace: {workspace_slug}") + return "Installation successful! You can close this window." + + except Exception as e: + print(f"OAuth error: {e}") + return "Installation failed", 500 + + +@app.route("/webhook", methods=["POST"]) +def webhook(): + """Handle incoming webhooks.""" + signature = request.headers.get("X-Plane-Signature", "") + payload = request.get_data() + + # Verify signature + expected = hmac.new( + WEBHOOK_SECRET.encode(), payload, hashlib.sha256 + ).hexdigest() + + if not hmac.compare_digest(expected, signature): + return "Invalid signature", 403 + + event = request.get_json() + print(f"Received: {event['event']} {event['action']}") + + # Get credentials for this workspace + creds = installations.get(event["workspace_id"]) + if creds: + # Process the event with creds["bot_token"] + pass + + return "OK", 200 + + +if __name__ == "__main__": + app.run(port=3000) +``` + +::: + +## Next Steps + +- [Build an Agent](/dev-tools/agents/overview) - Create AI agents that respond to @mentions +- [API Reference](/api-reference/introduction) - Explore the full Plane API +- [Webhook Events](/dev-tools/intro-webhooks) - All webhook event types +- [Example: PRD Agent](https://github.com/makeplane/prd-agent) - Complete agent implementation + +## Publish to Marketplace + +Apps can be listed on the [Plane Marketplace](https://plane.so/marketplace/integrations). Contact [support@plane.so](mailto:support@plane.so) to list your app. diff --git a/apps/developer-docs/docs/dev-tools/build-plane-app/oauth-scopes.md b/apps/developer-docs/docs/dev-tools/build-plane-app/oauth-scopes.md new file mode 100644 index 00000000..a28a35de --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/build-plane-app/oauth-scopes.md @@ -0,0 +1,142 @@ +--- +title: OAuth Scopes +description: Complete reference of all OAuth scopes available when building a Plane app. Includes read and write permissions for projects, work items, cycles, modules, and more. +keywords: plane oauth scopes, plane api permissions, oauth read write scopes, plane app authorization, plane api access control, workspace scopes, project scopes +--- + +# OAuth scopes + +This document lists all OAuth scopes available when building a Plane app. Request only the scopes your app needs. + +## Project scopes + +| Scope | Description | +| ------------------------------------------- | -------------------------------------------- | +| `projects:read` | Read projects | +| `projects:write` | Create and update projects | +| `projects.features:read` | Read project features | +| `projects.features:write` | Create and update project features | +| `projects.members:read` | Read project members | +| `projects.members:write` | Manage project members | +| `projects.states:read` | Read project states | +| `projects.states:write` | Create and update project states | +| `projects.labels:read` | Read project labels | +| `projects.labels:write` | Create and update project labels | +| `projects.intakes:read` | Read project intakes | +| `projects.intakes:write` | Create and update project intakes | +| `projects.epics:read` | Read project epics | +| `projects.epics:write` | Create and update project epics | +| `projects.cycles:read` | Read project cycles | +| `projects.cycles:write` | Create and update project cycles | +| `projects.pages:read` | Read project pages | +| `projects.pages:write` | Create and update project pages | +| `projects.modules:read` | Read project modules | +| `projects.modules:write` | Create and update project modules | +| `projects.work_items:read` | Read project work items | +| `projects.work_items:write` | Create and update project work items | +| `projects.work_items.comments:read` | Read work item comments | +| `projects.work_items.comments:write` | Create and update work item comments | +| `projects.work_items.attachments:read` | Read work item attachments | +| `projects.work_items.attachments:write` | Create and update work item attachments | +| `projects.work_items.links:read` | Read work item links | +| `projects.work_items.links:write` | Create and update work item links | +| `projects.work_items.relations:read` | Read work item relations | +| `projects.work_items.relations:write` | Create and update work item relations | +| `projects.work_items.activities:read` | Read work item activities | +| `projects.work_items.activities:write` | Create and update work item activities | +| `projects.work_items.worklogs:read` | Read work item worklogs | +| `projects.work_items.worklogs:write` | Create and update work item worklogs | +| `projects.work_item_types:read` | Read work item types | +| `projects.work_item_types:write` | Create and update work item types | +| `projects.work_item_properties:read` | Read work item properties | +| `projects.work_item_properties:write` | Create and update work item properties | +| `projects.work_item_property_options:read` | Read work item property options | +| `projects.work_item_property_options:write` | Create and update work item property options | +| `projects.work_item_property_values:read` | Read work item property values | +| `projects.work_item_property_values:write` | Create and update work item property values | +| `projects.milestones:read` | Read project milestones | +| `projects.milestones:write` | Create and update project milestones | + +## Wiki scopes + +| Scope | Description | +| ------------------ | ---------------------------- | +| `wiki.pages:read` | Read wiki pages | +| `wiki.pages:write` | Create and update wiki pages | + +## Customer scopes + +| Scope | Description | +| --------------------------------- | ------------------------------------------ | +| `customers:read` | Read customers | +| `customers:write` | Create and update customers | +| `customers.requests:read` | Read customer requests | +| `customers.requests:write` | Create and update customer requests | +| `customers.properties:read` | Read customer properties | +| `customers.properties:write` | Create and update customer properties | +| `customers.property_values:read` | Read customer property values | +| `customers.property_values:write` | Create and update customer property values | +| `customers.work_items:read` | Read customer work items | +| `customers.work_items:write` | Create and update customer work items | + +## Initiatives scopes + +| Scope | Description | +| ---------------------------- | ------------------------------------- | +| `initiatives:read` | Read initiatives | +| `initiatives:write` | Create and update initiatives | +| `initiatives.projects:read` | Read initiative projects | +| `initiatives.projects:write` | Create and update initiative projects | +| `initiatives.epics:read` | Read initiative epics | +| `initiatives.epics:write` | Create and update initiative epics | +| `initiatives.labels:read` | Read initiative labels | +| `initiatives.labels:write` | Create and update initiative labels | + +## Workspace scopes + +| Scope | Description | +| --------------------------- | ------------------------------------ | +| `workspaces.members:read` | Read workspace members | +| `workspaces.members:write` | Manage workspace members | +| `workspaces.features:read` | Read workspace features | +| `workspaces.features:write` | Create and update workspace features | + +## Stickies scopes + +| Scope | Description | +| ---------------- | -------------------------- | +| `stickies:read` | Read stickies | +| `stickies:write` | Create and update stickies | + +## Teamspaces scopes + +| Scope | Description | +| --------------------------- | ------------------------------------ | +| `teamspaces:read` | Read teamspaces | +| `teamspaces:write` | Create and update teamspaces | +| `teamspaces.projects:read` | Read teamspace projects | +| `teamspaces.projects:write` | Create and update teamspace projects | +| `teamspaces.members:read` | Read teamspace members | +| `teamspaces.members:write` | Create and update teamspace members | + +## Profile scopes + +| Scope | Description | +| -------------- | ----------------- | +| `profile:read` | Read user profile | + +## Assets scopes + +| Scope | Description | +| -------------- | ------------------------ | +| `assets:read` | Read assets | +| `assets:write` | Create and update assets | + +## Agent Run scopes + +| Scope | Description | +| ----------------------------- | -------------------------------------- | +| `agents.runs:read` | Read agent runs | +| `agents.runs:write` | Create and update agent runs | +| `agents.run_activities:read` | Read agent run activities | +| `agents.run_activities:write` | Create and update agent run activities | diff --git a/apps/developer-docs/docs/dev-tools/build-plane-app/overview.md b/apps/developer-docs/docs/dev-tools/build-plane-app/overview.md new file mode 100644 index 00000000..a1e8600d --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/build-plane-app/overview.md @@ -0,0 +1,130 @@ +--- +title: Build a Plane App +description: Build and integrate an app with Plane using OAuth 2.0 authentication. Covers bot tokens, user tokens, webhooks, and API access for custom integrations. +keywords: build plane app, plane oauth app, plane integration, plane api app, oauth 2.0 plane, plane developer platform, custom plane integration, plane third-party app +--- + +# Build a Plane app + +::: info +Plane apps are currently in **Beta**. Please send any feedback to support@plane.so. +::: + +## Overview + +Plane uses OAuth 2.0 to allow applications to access workspace data on behalf of users or as an autonomous bot. This comprehensive guide covers everything you need to build, integrate, and deploy apps that extend Plane's functionality. + +## What you can build + +Plane apps enable you to: + +- **AI Agents** - Create intelligent agents that respond to @mentions in work item comments +- **Workflow Automation** - Build bots that automate repetitive tasks across your workspace +- **Integrations** - Connect Plane with external tools and services +- **Custom Dashboards** - Build analytics and reporting tools using Plane's data +- **Webhook Handlers** - React to events in real-time as they happen in Plane + +## Key concepts + +### OAuth 2.0 flows + +Plane supports two authentication flows: + +- **Bot Token Flow** (Client Credentials) - For autonomous apps, agents, and webhooks that act independently +- **User Token Flow** (Authorization Code) - For apps that need to act on behalf of specific users + +Most integrations should use the **Bot Token flow**. See [Choose Your Flow](/dev-tools/build-plane-app/choose-token-flow) for detailed implementation guides. + +### App components + +A complete Plane app typically includes: + +1. **OAuth Application** - Registered in Plane with Client ID and Secret +2. **Setup URL** - Entry point where users begin the installation process +3. **Redirect URI** - Callback endpoint that receives authorization codes +4. **Webhook URL** - Endpoint for receiving real-time event notifications +5. **API Integration** - Code that interacts with Plane's REST API + +## Getting started + +Follow these steps to build your first Plane app: + +### 1. Create an OAuth application + +Register your app in Plane to get credentials: + +- Navigate to **Workspace Settings** → **Integrations** +- Configure your app's URLs and permissions +- Store your **Client ID** and **Client Secret** securely + +[Learn more →](/dev-tools/build-plane-app/create-oauth-application) + +### 2. Choose your authentication flow + +Decide between Bot Token or User Token based on your use case: + +- **Bot Token** - For agents, webhooks, and automation +- **User Token** - For user-specific actions and permissions + +[Learn more →](/dev-tools/build-plane-app/choose-token-flow) + +### 3. Implement OAuth + +Set up the OAuth flow to obtain access tokens: + +- Redirect users to Plane's consent screen +- Handle the callback with authorization code +- Exchange code for access tokens +- Store tokens securely for API calls + +[Learn more →](/dev-tools/build-plane-app/choose-token-flow) + +### 4. Handle webhooks + +Set up webhook handlers to receive real-time events: + +- Verify webhook signatures for security +- Process events like work item updates, comments, and more +- Respond to events with automated actions + +[Learn more →](/dev-tools/build-plane-app/webhooks) + +## Development tools + +::: tip Local Development +For local development, use [ngrok](https://ngrok.com) to expose your server: + +```bash +ngrok http 3000 +``` + +Use the generated URL (e.g., `https://abc123.ngrok.io`) for your Setup URL, Redirect URI, and Webhook URL. + +Free ngrok URLs change on restart. Update your app settings when the URL changes. +::: + +### Official SDKs + +Speed up development with official SDKs for Node.js and Python: + +- OAuth helpers for token management +- Typed API clients for all endpoints +- Built-in error handling and retries + +[Learn more →](/dev-tools/build-plane-app/sdks) + +### Complete examples + +See full working implementations: + +- TypeScript (Express) example +- Python (Flask) example +- OAuth flow, webhooks, and API integration + +[Learn more →](/dev-tools/build-plane-app/examples) + +## Quick links + +- [API Reference](/api-reference/introduction) - Explore all available endpoints +- [Build an Agent](/dev-tools/agents/overview) - Create AI agents for Plane +- [Webhook Events](/dev-tools/intro-webhooks) - All webhook event types diff --git a/apps/developer-docs/docs/dev-tools/build-plane-app/sdks.md b/apps/developer-docs/docs/dev-tools/build-plane-app/sdks.md new file mode 100644 index 00000000..9644c14c --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/build-plane-app/sdks.md @@ -0,0 +1,69 @@ +--- +title: Plane SDKs +description: Official Plane SDKs for Node.js and Python. Get typed API clients and OAuth helpers to build Plane integrations faster. +keywords: plane sdk, plane node sdk, plane python sdk, plane api client, plane-node-sdk, plane-sdk, plane npm package, plane pypi package, plane developer tools +--- + +# SDKs + +Official SDKs provide OAuth helpers and typed API clients: + +| Language | Package | +| -------- | ------------------------------------------------------------------------------------ | +| Node.js | [@makeplane/plane-node-sdk](https://www.npmjs.com/package/@makeplane/plane-node-sdk) | +| Python | [plane-sdk](https://pypi.org/project/plane-sdk/) | + +```bash +npm install @makeplane/plane-node-sdk +# or +pip install plane-sdk +``` + +#### OAuth helper methods + +::: code-group + +```typescript [Node.js] +import { OAuthClient } from "@makeplane/plane-node-sdk"; + +const oauth = new OAuthClient({ + clientId: "your_client_id", + clientSecret: "your_client_secret", + redirectUri: "https://your-app.com/callback", +}); + +// Generate authorization URL +const authUrl = oauth.getAuthorizationUrl("code", "state"); + +// Exchange for bot token +const token = await oauth.getBotToken(appInstallationId); + +// Exchange code for user token +const userToken = await oauth.exchangeCodeForToken(code); + +// Refresh user token +const newToken = await oauth.getRefreshToken(refreshToken); +``` + +```python [Python] +from plane.client import OAuthClient + +oauth = OAuthClient( + client_id="your_client_id", + client_secret="your_client_secret", +) + +# Generate authorization URL +auth_url = oauth.get_authorization_url(redirect_uri="...", state="state") + +# Exchange for bot token +token = oauth.get_client_credentials_token(app_installation_id=app_installation_id) + +# Exchange code for user token +user_token = oauth.exchange_code(code=code, redirect_uri=redirect_uri) + +# Refresh user token +new_token = oauth.refresh_token(refresh_token) +``` + +::: diff --git a/apps/developer-docs/docs/dev-tools/build-plane-app/webhooks.md b/apps/developer-docs/docs/dev-tools/build-plane-app/webhooks.md new file mode 100644 index 00000000..dcc4d59e --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/build-plane-app/webhooks.md @@ -0,0 +1,63 @@ +--- +title: Handling webhooks +description: Receive and verify webhooks from Plane in your application. Learn about webhook headers, signature verification, event types, and payload handling. +keywords: plane webhooks, webhook verification, plane event notifications, webhook signature, plane webhook handler, real-time events, plane webhook payload +--- + +# Handling webhooks + +When events occur in Plane, webhooks are sent to your Webhook URL. + +## Webhook headers + +| Header | Description | +| ------------------- | ------------------------------------------- | +| `X-Plane-Delivery` | Unique delivery ID | +| `X-Plane-Event` | Event type (e.g., `issue`, `issue_comment`) | +| `X-Plane-Signature` | HMAC-SHA256 signature for verification | + +## Verify signature + +Always verify the `X-Plane-Signature` header: + +:::tabs key:language +== Python {#python} + +```python +import hmac +import hashlib + +def verify_signature(payload: bytes, signature: str, secret: str) -> bool: + expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest() + return hmac.compare_digest(expected, signature) +``` + +== TypeScript {#typescript} + +```typescript +import crypto from "crypto"; + +function verifySignature(payload: string, signature: string, secret: string): boolean { + const expected = crypto.createHmac("sha256", secret).update(payload).digest("hex"); + return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected)); +} +``` + +::: + +## Webhook payload + +```json +{ + "event": "issue", + "action": "created", + "webhook_id": "webhook-uuid", + "workspace_id": "workspace-uuid", + "data": { ... }, + "activity": { + "actor": { "id": "user-uuid", "display_name": "John Doe" } + } +} +``` + +See [Webhook Events](/dev-tools/intro-webhooks) for all event types. diff --git a/apps/developer-docs/docs/dev-tools/intro-webhooks.md b/apps/developer-docs/docs/dev-tools/intro-webhooks.md new file mode 100644 index 00000000..9f224a3c --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/intro-webhooks.md @@ -0,0 +1,474 @@ +--- +title: Webhooks +description: Configure webhooks for Plane. Setup real-time event notifications and automate workflows with webhook integrations. +keywords: plane, developer tools, integrations, extensions, webhooks, automation, events +--- + +# Webhooks + +Webhooks give you a way to push Plane events into other systems the moment they happen. Instead of polling the Plane API periodically to check for changes, your service registers a URL and Plane calls it, with a structured payload, whenever something occurs. + +The value of webhooks over polling is immediacy and simplicity. Your endpoint receives exactly what happened without having to query anything. A work item created at 14:00:01 triggers a request at 14:00:01. + +Webhooks in Plane work at the workspace level. A single webhook can subscribe to events from every project in the workspace. There is no per-project webhook configuration. Only Workspace Owners and Admins can create or manage them. + +The current webhook system is v2. V2 payloads use dot-notation event names (e.g., `workitem.created`) and include structured fields for deduplication, diffing, and filtering that were not available in the original version. If you have webhooks created before v2, they appear in the list with a **(deprecated)** tag. They still deliver but do not receive any v2 fields. Recreate them as new webhooks to get the full v2 feature set. + +:::warning Migrate your v1 webhooks to v2 +V1 webhooks are deprecated. They continue to deliver but will not receive v2 payload features — including `delivery_id` and `event_id` for deduplication, `previous_attributes` for diffs on updated events, and dot-notation event names. There is no in-place upgrade. To move to v2, recreate each v1 webhook as a new webhook and update your server to handle the v2 payload structure. + +**To migrate a v1 webhook:** + +1. Open the v1 webhook (marked **deprecated**) and note its URL and event subscriptions. +2. Create a new webhook with the same URL and event subscriptions. See [How to create a webhook](#how-to-create-a-webhook). +3. Save the new secret key from the CSV download — your server will need it to verify v2 requests. +4. Update your server to expect the v2 payload structure. See [Payload structure](#payload-structure) for the full field reference. +5. Test that deliveries are arriving and your server is handling them correctly. +6. Delete the original v1 webhook once you're confident the new one is working. + ::: + +## Creating a webhook + +![Plane architecture](/images/webhooks/create-webhook.webp#hero) + +### How to create a webhook + +1. Go to **Workspace Settings → Webhooks**. +2. Click **Add webhook**. +3. Enter a **Webhook title** and **Payload URL**. +4. Check the events you want this webhook to fire for. +5. Optionally, expand **Advanced configurations** to add a work item filter. +6. Click **Create webhook**. + +Plane downloads the secret key as a CSV file to your computer and returns you to the webhook list. The webhook is active immediately. + +If you lose the CSV, you can re-generate the secret key from the edit form - but the old key stops working the moment you do. + +### What you're configuring + +When you create a webhook, you're telling Plane two things: where to send events, and which events to send. + +- **Webhook title** is a label for your own reference - it appears in the webhook list and helps you tell multiple webhooks apart. + +- **Payload URL** is the endpoint that Plane will POST to. It must be a publicly reachable `http://` or `https://` address. Local addresses (localhost, private IPs) are not accepted. + +- **Events** control what triggers this webhook. The form groups events by type - Projects, Cycles, Modules, Work items, and so on. Check the specific actions you care about. You can subscribe to as many or as few as you need. + +- **Advanced configurations** lets you add a filter so the webhook only fires for work items that match specific conditions - for example, high-priority bugs in a particular project. See [Filtering work item events](#filtering-work-item-events) below. + +- **Secret key** is generated automatically when you save the webhook. Plane downloads it as a CSV file the moment you click **Create webhook** and then returns you to the webhook list. It is not displayed on screen - the download is the only time you receive it automatically. Save the file. You need the key to verify incoming requests. + +## Filtering work item events + +### How filtering works + +By default, a webhook fires for every work item event you subscribe to, across all projects in the workspace. Filters let you narrow that, for example, to fire only when a high-priority work item is created in a specific project. + +Filters apply only to work item events. Events for projects, cycles, pages, milestones, and other types are always delivered without filtering. + +Plane evaluates filters at delivery time. If the filter fails to evaluate for any reason, the delivery is skipped rather than defaulting to "deliver everything." If you're not receiving expected deliveries, check that your filter expression is valid. + +### Basic mode versus PQL mode + +The filter builder in **Advanced configurations** offers two modes you can switch between freely: + +- **Basic** - a visual picker. Select values from dropdowns and Plane converts your selections into a filter expression behind the scenes. +- **PQL** - direct text input. Type a PQL (Plane Query Language) expression. The expression shown is exactly what is stored and evaluated at delivery time. + +Switching between modes is lossless - your filter is not lost when you switch. + +### How to add a work item filter + +1. Create or edit a webhook. +2. Check at least one **Work items** event. +3. Scroll down to the **Work item v2 filters** section. +4. Use the filter builder to define your conditions in Basic mode, or switch to PQL mode to type an expression directly. +5. Save the webhook. + +### PQL syntax and supported fields + +| Filter field | PQL field name | Accepted values | +| -------------- | -------------- | --------------------------------------------------------------- | +| Work item type | `type_id` | Work item type UUID | +| State group | `state_group` | `backlog` · `unstarted` · `started` · `completed` · `cancelled` | +| Assignees | `assignee_id` | User UUID | +| Labels | `label_id` | Label UUID | +| Projects | `project_id` | Project UUID | +| Priority | `priority` | `none` · `low` · `medium` · `high` · `urgent` | +| Start date | `start_date` | ISO date | +| Due date | `target_date` | ISO date | + +**Expression syntax** + +``` +priority = "urgent" Single value +priority in ["urgent", "high"] Multiple values +state_group = "started" State group match +assignee_id = "" Specific assignee +project_id = "" Specific project +``` + +## Managing webhooks + +### Disabling versus deleting + +Disabling a webhook pauses delivery without removing any configuration. The webhook stays in the list, its event subscriptions are preserved, and you can re-enable it at any time. Events are not queued while the webhook is disabled - any event that fires during the disabled period is not delivered. Use this when your endpoint is temporarily down or you need to make changes to your receiving system. + +Deleting a webhook removes it permanently - configuration and delivery history are gone. There is no undo. + +### Edit a webhook + +1. Go to **Workspace Settings → Webhooks**. +2. Click **···** on the webhook row and select **Edit**. +3. Update the title, URL, event subscriptions, or filter. +4. Click **Update webhook**. + +### Disable or enable a webhook + +1. Go to **Workspace Settings → Webhooks**. +2. Click **···** on the webhook row. +3. Select **Disable webhook** to stop delivery, or **Enable webhook** to resume it. + +### Delete a webhook + +1. Go to **Workspace Settings → Webhooks**. +2. Click **···** on the webhook row and select **Delete webhook**. + +### View and copy the secret key + +The secret key is not displayed during creation - Plane downloads it as a CSV instead. To access it later: + +1. Go to **Workspace Settings → Webhooks**. +2. Click **···** on the webhook row and select **Edit**. +3. In the **Secret key** section, click the eye icon to reveal the key. +4. Click the copy icon to copy it. + +### Re-generate the secret key + +Re-generate if your secret key is compromised. The old key is invalidated the moment you re-generate - update your server before completing this step or your signature verification will break. + +1. Go to **Workspace Settings → Webhooks**. +2. Click **···** on the webhook row and select **Edit**. +3. In the **Secret key** section, click **Re-generate key**. + +Plane downloads the new key as a CSV. + +## Securing requests + +### Why Plane signs every request + +Any server on the internet can send a POST request to your endpoint. Without a way to verify the source, someone could send fake webhook payloads to your system and trigger whatever logic you've built around them. + +Plane solves this by signing every request with HMAC-SHA256 using your secret key. The signature is attached as an `X-Plane-Signature` header. Because only Plane and you know the secret, a valid signature proves the request came from Plane and was not modified in transit. + +Skipping verification means your endpoint will process any request that arrives - forged or not. + +### How to verify a webhook payload + +On your server, compute the expected signature from the **raw request body bytes** and compare it to the value in `X-Plane-Signature`. Use a constant-time comparison to prevent timing attacks. + +```python +import hashlib +import hmac + +def verify_webhook(request_body_bytes: bytes, secret: str, signature_header: str) -> bool: + expected = hmac.new( + secret.encode("utf-8"), + request_body_bytes, + hashlib.sha256, + ).hexdigest() + return hmac.compare_digest(expected, signature_header) +``` + +Use the raw bytes from the incoming request - not a parsed or re-serialized version. JSON re-serialization can change key ordering, spacing, or escaping, which will produce a different signature and cause verification to fail. Reject any request where the signature does not match before running any other logic. + +### Signature header reference + +| Header | Value | +| ------------------- | ------------------------------------------------------------------------------ | +| `X-Plane-Signature` | HMAC-SHA256 hex digest of the raw request body, keyed with your webhook secret | + +The secret key is formatted as `plane_wh_` followed by a random string. Plane masks it in the UI. To view the full key, open the edit form for the webhook and use the show/hide toggle in the **Secret key** section. + +## Delivery and monitoring + +### How Plane delivers events + +Plane sends webhook requests asynchronously. When an event occurs, Plane queues the delivery and sends a POST request to your endpoint. Any 2xx response is treated as a success. + +If your endpoint is unavailable or returns a server error, Plane retries using exponential backoff with a ~10-minute base and jitter. After **5 failed attempts**, Plane automatically disables the webhook and emails the webhook creator. Re-enable it from the webhook list once your endpoint is fixed. + +4xx responses are not retried. Plane treats them as a deliberate rejection from your server. + +Retry behavior is automatic. There is no way to trigger a manual retry for a failed delivery. + +### How to read delivery logs + +1. Go to **Workspace Settings → Webhooks**. +2. Click on a webhook to open its detail view. + +The top of the view shows four summary stats: + +| Stat | What it shows | +| ---------------- | ------------------------------------------------------ | +| Total deliveries | Total number of delivery attempts | +| Successful | Deliveries that received a 2xx response | +| Failed | Deliveries that returned an error or exhausted retries | +| Success rate | Successful deliveries as a percentage of total | + +Below the summary, the delivery log lists individual attempts: + +| Column | What it shows | +| ------------- | ------------------------------------------------------- | +| Events | The event type that triggered the delivery | +| Status | Successful or Failed | +| Response time | How long your endpoint took to respond, in milliseconds | +| Event time | When the delivery was sent | + +## Events and payload + +### Event reference + +| Group | Event key | Fires when | +| -------------------------- | ----------------------------- | -------------------------------------- | +| **Projects** | `project.created` | A project is created | +| | `project.updated` | A project is updated | +| | `project.archived` | A project is archived | +| | `project.deleted` | A project is deleted | +| **Cycles** | `cycle.created` | A cycle is created | +| | `cycle.updated` | A cycle is updated | +| | `cycle.archived` | A cycle is archived | +| | `cycle.deleted` | A cycle is deleted | +| **Modules** | `module.created` | A module is created | +| | `module.updated` | A module is updated | +| | `module.archived` | A module is archived | +| | `module.deleted` | A module is deleted | +| **Milestones** | `milestone.created` | A milestone is created | +| | `milestone.updated` | A milestone is updated | +| | `milestone.deleted` | A milestone is deleted | +| **Pages** | `page.created` | A page is created | +| | `page.updated` | A page is updated | +| | `page.archived` | A page is archived | +| | `page.deleted` | A page is deleted | +| **Page comments** | `page.comment.created` | A comment is added to a page | +| | `page.comment.updated` | A page comment is edited | +| | `page.comment.deleted` | A page comment is deleted | +| **Work items** | `workitem.created` | A work item is created | +| | `workitem.updated` | A work item is updated | +| | `workitem.archived` | A work item is archived | +| | `workitem.deleted` | A work item is deleted | +| **Work item comments** | `workitem.comment.created` | A comment is added to a work item | +| | `workitem.comment.updated` | A work item comment is edited | +| | `workitem.comment.deleted` | A work item comment is deleted | +| **Work item links** | `workitem.link.created` | A link is added to a work item | +| | `workitem.link.updated` | A work item link is updated | +| | `workitem.link.deleted` | A work item link is removed | +| **Work item votes** | `workitem.vote.created` | A vote is cast on a work item | +| | `workitem.vote.deleted` | A vote is removed | +| **Work item attachments** | `workitem.attachment.created` | A file is attached to a work item | +| | `workitem.attachment.updated` | A work item attachment is updated | +| | `workitem.attachment.deleted` | A work item attachment is removed | +| **Work item relations** | `workitem.relation.created` | A relation is added between work items | +| | `workitem.relation.deleted` | A relation is removed | +| **Work item dependencies** | `workitem.dependency.created` | A dependency is added | +| | `workitem.dependency.deleted` | A dependency is removed | +| **Work item page links** | `workitem.page_link.created` | A page link is added to a work item | +| | `workitem.page_link.deleted` | A page link is removed | + +### Request headers + +Every webhook request includes these headers: + +| Header | Value | +| ------------------- | ----------------------------------------------------------------------------- | +| `Content-Type` | `application/json` | +| `User-Agent` | `Autopilot` | +| `X-Plane-Delivery` | Unique UUID per delivery attempt. Matches `delivery_id` in the payload body. | +| `X-Plane-Event` | The event type, e.g. `workitem.created`. Matches `event` in the payload body. | +| `X-Plane-Signature` | HMAC-SHA256 signature of the request body | + +These headers are reserved and cannot be overridden with custom values: `host`, `content-length`, `content-type`, `user-agent`, `x-plane-delivery`, `x-plane-event`, `x-plane-signature`. + +### Payload structure + +All v2 payloads share this top-level structure: + +```json +{ + "version": "v2", + "delivery_id": "", + "event_id": "", + "entity_id": "", + "entity_type": "", + "event": "", + "webhook_id": "", + "workspace_id": "", + "data": {}, + "previous_attributes": {} +} +``` + +| Field | Description | +| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `version` | Always `"v2"` | +| `delivery_id` | Unique ID for this delivery attempt. Matches the `X-Plane-Delivery` header. A new UUID is generated for each retry. | +| `event_id` | Unique ID for the triggering event. Stable across retries - use this for deduplication. | +| `entity_id` | UUID of the primary entity affected by the event. | +| `entity_type` | Type of the entity, e.g. `issue`, `cycle`, `issue_comment`, `issue_link`. | +| `event` | Full dot-notation event name, e.g. `workitem.comment.updated`. | +| `webhook_id` | ID of the webhook configuration that triggered this delivery. | +| `workspace_id` | UUID of the workspace in which the event occurred. | +| `data` | Full entity object for create and update events. Empty object `{}` for delete events. | +| `previous_attributes` | Present on all events. For `updated` events, contains the previous values of changed fields. For `deleted` events, contains the full record before deletion. Empty object `{}` for all other events. | + +### Payload examples + +**workitem.updated** + +```json +{ + "version": "v2", + "delivery_id": "2a0d0510-9052-446e-a1c7-a704bbd68cba", + "event_id": "9d508cd9-36c2-44a5-928d-7ee2f2a3b8a8", + "entity_id": "775c5716-5302-4617-bb9f-2cd843911268", + "entity_type": "issue", + "event": "WebhookScope.ScopeChoices.WORK_ITEM_UPDATED", + "webhook_id": "8944ed18-1331-4eae-b9bb-7c40864b8abd", + "workspace_id": "b54ecb0d-e3eb-4986-b238-f83fd8665e65", + "data": { + "id": "775c5716-5302-4617-bb9f-2cd843911268", + "name": "webhook test 3", + "point": "None", + "type_id": "None", + "is_draft": false, + "priority": "none", + "state_id": "067b88e5-304b-4221-ba09-94340dcc36e5", + "label_ids": [], + "parent_id": "None", + "created_at": "2026-03-31T11:44:41.249292+00:00", + "deleted_at": "None", + "project_id": "59e3be42-87ec-4950-99a3-ae639cf2b089", + "sort_order": 75535, + "start_date": "None", + "updated_at": "2026-03-31T11:44:41.249304+00:00", + "archived_at": "None", + "external_id": "None", + "sequence_id": 3, + "target_date": "None", + "assignee_ids": [], + "completed_at": "None", + "workspace_id": "b54ecb0d-e3eb-4986-b238-f83fd8665e65", + "created_by_id": "754009ab-3fb5-424e-909a-b46e9c9d0c4f", + "updated_by_id": "None", + "external_source": "None", + "description_json": {}, + "last_activity_at": "2026-03-31T11:44:41.346305+00:00", + "estimate_point_id": "None" + }, + "previous_attributes": { + "last_activity_at": "2026-03-31 11:44:41.242868+00" + } +} +``` + +**workitem.comment.created** + +```json +{ + "version": "v2", + "delivery_id": "01ab9316-f978-4449-bad6-dce958be8454", + "event_id": "0afa042d-92a9-4326-bdca-5ff5490dbf09", + "entity_id": "088a83b9-a53f-4dda-b2bc-c860cf455997", + "entity_type": "issue", + "event": "workitem.comment.created", + "webhook_id": "285f087b-e1e0-4f90-b9f4-0b720acfac04", + "workspace_id": "d250cd44-fa71-42c2-b2b5-3c73227288fc", + "data": { + "id": "088a83b9-a53f-4dda-b2bc-c860cf455997", + "name": "Webhook Test Work Item 2", + "comment": { + "id": "4797f841-c731-4e55-971f-d9cfe1938dfb", + "access": "INTERNAL", + "actor_id": "88fc36c8-73b0-4547-81c7-96b70f61835e", + "issue_id": "088a83b9-a53f-4dda-b2bc-c860cf455997", + "edited_at": null, + "comment_stripped": "Webhook Test Comment" + } + }, + "previous_attributes": {} +} +``` + +**workitem.link.created** + +```json +{ + "version": "v2", + "delivery_id": "616d98fe-35a7-4431-a233-db40936c8339", + "event_id": "7b3c1e2a-8f94-4b12-a781-2c5e9d4f6a03", + "entity_id": "8661bdfa-098f-434d-8e44-b1f32de62406", + "entity_type": "issue_link", + "event": "workitem.link.created", + "webhook_id": "285f087b-e1e0-4f90-b9f4-0b720acfac04", + "workspace_id": "d250cd44-fa71-42c2-b2b5-3c73227288fc", + "data": { + "id": "a6f8e562-49d2-4c19-bc4b-2bcb9d917da1", + "url": "http://google.com", + "title": "", + "issue_id": "8661bdfa-098f-434d-8e44-b1f32de62406", + "created_at": "2026-05-20T09:51:27.373582+00:00", + "project_id": "45b87d89-0ce0-4d6f-8903-4070f1c67f1b", + "workspace_id": "d250cd44-fa71-42c2-b2b5-3c73227288fc", + "created_by_id": "88fc36c8-73b0-4547-81c7-96b70f61835e" + }, + "previous_attributes": {} +} +``` + +**workitem.link.updated** + +```json +{ + "version": "v2", + "delivery_id": "2a0d0510-9052-446e-a1c7-a704bbd68cba", + "event_id": "9d508cd9-36c2-44a5-928d-7ee2f2a3b8a8", + "entity_id": "775c5716-5302-4617-bb9f-2cd843911268", + "entity_type": "issue", + "event": "WebhookScope.ScopeChoices.WORK_ITEM_UPDATED", + "webhook_id": "8944ed18-1331-4eae-b9bb-7c40864b8abd", + "workspace_id": "b54ecb0d-e3eb-4986-b238-f83fd8665e65", + "data": { + "id": "775c5716-5302-4617-bb9f-2cd843911268", + "name": "webhook test 3", + "point": "None", + "type_id": "None", + "is_draft": false, + "priority": "none", + "state_id": "067b88e5-304b-4221-ba09-94340dcc36e5", + "label_ids": [], + "parent_id": "None", + "created_at": "2026-03-31T11:44:41.249292+00:00", + "deleted_at": "None", + "project_id": "59e3be42-87ec-4950-99a3-ae639cf2b089", + "sort_order": 75535, + "start_date": "None", + "updated_at": "2026-03-31T11:44:41.249304+00:00", + "archived_at": "None", + "external_id": "None", + "sequence_id": 3, + "target_date": "None", + "assignee_ids": [], + "completed_at": "None", + "workspace_id": "b54ecb0d-e3eb-4986-b238-f83fd8665e65", + "created_by_id": "754009ab-3fb5-424e-909a-b46e9c9d0c4f", + "updated_by_id": "None", + "external_source": "None", + "description_json": {}, + "last_activity_at": "2026-03-31T11:44:41.346305+00:00", + "estimate_point_id": "None" + }, + "previous_attributes": { + "last_activity_at": "2026-03-31 11:44:41.242868+00" + } +} +``` diff --git a/apps/developer-docs/docs/dev-tools/mcp-server-self-host.md b/apps/developer-docs/docs/dev-tools/mcp-server-self-host.md new file mode 100644 index 00000000..bc60f14c --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/mcp-server-self-host.md @@ -0,0 +1,356 @@ +--- +title: Self-host the MCP server +description: Deploy the Plane MCP server with Docker Compose or Helm, register OAuth callbacks, configure storage and security, and connect AI clients. +keywords: plane mcp server, self-hosted mcp, plane mcp deployment, docker compose mcp, helm mcp, plane oauth mcp, mcp server setup +--- + +# Self-host the MCP server + +This guide is for teams that want to run their own instance of the Plane MCP server — either because they use a +self-hosted Plane installation that needs OAuth against their own domain, or because they want full control over the +MCP infrastructure. + +If you're a Plane Cloud user connecting to `mcp.plane.so`, you don't need this. Use the +[MCP server setup guide](/dev-tools/mcp-server) instead. + +## Prerequisites + +- A running **Plane instance** (self-hosted or Cloud) with workspace admin access. OAuth application registration is + available on Plane Cloud and Plane Commercial Edition; Plane Community Edition does not include it, so the OAuth + transport cannot be used against a Community Edition instance. Community Edition users should run the server in + [local (stdio) mode](/dev-tools/mcp-server#local-stdio) with a personal access token instead. +- **Docker** and Docker Compose v2+, _or_ **Kubernetes** v1.21+ with Helm v3+ +- A **public URL** for the MCP server (e.g. `https://mcp.yourdomain.com`) — OAuth callbacks must reach it over HTTPS + +--- + +## Register an OAuth app in Plane + +The MCP server authenticates users through Plane's OAuth 2.0 system. You need to register an app to get a Client ID and Client Secret. + +1. Go to **Workspace settings → Integrations**: + + ```text + https:////settings/integrations/ + ``` + +2. Click **Build your own**. + +3. Fill in the application details: + + | Field | Value | + | ---------------- | ---------------------------------------------------------------- | + | **App Name** | Anything descriptive (e.g. `Plane MCP Server`) | + | **Setup URL** | Your MCP server's public URL (e.g. `https://mcp.yourdomain.com`) | + | **Redirect URI** | Both URIs listed below, space-separated | + | **Webhook URL** | Leave empty unless you need webhook events | + + ::: tip Add both redirect URIs + FastMCP exposes one callback under the HTTP mount and one under the SSE mount: + + | Transport | Redirect URI | + | ---------------- | ------------------------------------- | + | Streamable HTTP | `/http/auth/callback` | + | SSE (deprecated) | `/auth/callback` | + + For `https://mcp.yourdomain.com`, paste this into the Redirect URI field: + + ```text + https://mcp.yourdomain.com/http/auth/callback https://mcp.yourdomain.com/auth/callback + ``` + + A previously registered `https://mcp.yourdomain.com/callback` URI is harmless but unnecessary. + + ::: + +4. Under **Scopes & permissions**, select both **read** and **write** scopes. + +5. Save. Copy the generated **Client ID** and **Client Secret** - you'll need them in the next step. + +::: warning +Never expose the Client Secret in client-side code or commit it to version control. +::: + +For more detail on OAuth app creation, see [Create an OAuth Application](/dev-tools/build-plane-app/create-oauth-application). + +--- + +## Deploy + +### Option A: Docker Compose + +**1. Create a `docker-compose.yaml`:** + +```yaml +name: plane-mcp + +services: + mcp: + image: makeplane/plane-mcp-server:${APP_RELEASE_VERSION:-latest} + restart: always + ports: + - "8211:8211" + env_file: + - variables.env + environment: + REDIS_HOST: valkey + REDIS_PORT: "6379" + depends_on: + valkey: + condition: service_healthy + + valkey: + image: valkey/valkey:8-alpine + restart: always + volumes: + - valkey-data:/data + healthcheck: + test: ["CMD", "valkey-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 + +volumes: + valkey-data: +``` + +**2. Create a `variables.env` with your OAuth credentials from Step 1:** + +```env +# Image tag - pin to a specific version in production +APP_RELEASE_VERSION=latest + +# Plane API URL - use your self-hosted instance URL or https://api.plane.so for Cloud +PLANE_BASE_URL=https://api.plane.so + +# Optional: internal URL for server-to-server calls (same-network setups) +# PLANE_INTERNAL_BASE_URL= + +# OAuth credentials from Step 1 +PLANE_OAUTH_PROVIDER_CLIENT_ID=your-client-id +PLANE_OAUTH_PROVIDER_CLIENT_SECRET=your-client-secret + +# Public URL where MCP clients reach this server (must match what you registered in Step 1) +PLANE_OAUTH_PROVIDER_BASE_URL=https://mcp.yourdomain.com +``` + +**3. Start:** + +```bash +docker compose up -d +``` + +**4. Verify:** + +```bash +docker compose logs -f mcp # follow startup logs +curl http://localhost:8211/http/mcp # expect: 401 or MCP protocol response +``` + +::: warning Terminate TLS in front of this container +The container listens on plain HTTP at `:8211`. Put it behind a reverse proxy (nginx, Caddy, Traefik, Cloudflare) that handles TLS. OAuth callbacks will fail without HTTPS, and `PLANE_OAUTH_PROVIDER_BASE_URL` must be the `https://` URL that proxy exposes. +::: + +#### Environment variable reference + +| Variable | Required | Description | +| ------------------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------- | +| `APP_RELEASE_VERSION` | No | Image tag to deploy. Defaults to `latest`. Pin in production. | +| `PLANE_BASE_URL` | No | Public Plane API URL. Defaults to `https://api.plane.so`. | +| `PLANE_INTERNAL_BASE_URL` | No | Internal Plane URL for server-to-server calls. Falls back to `PLANE_BASE_URL`. | +| `PLANE_OAUTH_PROVIDER_CLIENT_ID` | Yes | OAuth Client ID from Step 1. | +| `PLANE_OAUTH_PROVIDER_CLIENT_SECRET` | Yes | OAuth Client Secret from Step 1. | +| `PLANE_OAUTH_PROVIDER_BASE_URL` | Yes | Public URL of **this MCP server**, not your Plane instance. | +| `PLANE_OAUTH_PROVIDER_ENABLE_CIMD` | No | Enables client ID metadata documents. Defaults to `false`. | +| `PLANE_OAUTH_ALLOWED_REDIRECT_URIS` | No | Comma-separated extra client redirect patterns. `*` can match a port, path segment, or subdomain; keep hosts pinned. | +| `MCP_PATH_PREFIX` | No | Prefix for every route. For example, `/plane` serves MCP at `/plane/http/mcp`. | +| `REDIS_HOST` | No | Redis or Valkey host for persistent OAuth token storage. Without it, tokens use in-memory storage. | +| `REDIS_PORT` | No | Redis or Valkey port. | +| `REDIS_PASSWORD` | No | Static Redis or Valkey password. | +| `REDIS_SSL` | No | Enables TLS for Redis or Valkey when set to `true`. | +| `ELASTICACHE_SECRET_ARN` | No | AWS Secrets Manager ARN containing a rotating ElastiCache authentication token. | +| `AWS_REGION` | No | AWS region for `ELASTICACHE_SECRET_ARN`. | +| `REDIS_AUTH_TOKEN_KEY` | No | JSON key that contains the rotating token in the AWS secret. | +| `LOG_USER_INFO` | No | Logs the user's display name when `true`. Defaults to `false`; the display name is PII. | + +#### Onboard a new MCP client + +The built-in redirect allowlist contains: + +- `http://localhost:*`, `http://localhost:*/*`, `http://127.0.0.1:*`, and + `http://127.0.0.1:*/*` +- `cursor://anysphere.cursor-mcp/oauth/*` and `https://www.cursor.com/*` +- `https://vscode.dev/redirect` and `https://insiders.vscode.dev/redirect` +- `https://antigravity.google/oauth-callback` +- `https://claude.ai/*` +- `https://chatgpt.com/connector/oauth/*` and `https://chatgpt.com/connector_platform_oauth_redirect` + +Append new client callbacks without releasing a new server version: + +```env +PLANE_OAUTH_ALLOWED_REDIRECT_URIS=https://newclient.com/cb,https://other.app/oauth/* +``` + +The `*` wildcard can match any port, path segment, or subdomain. Keep the host pinned to a domain you trust. + +#### Upgrading + +```bash +docker compose pull +docker compose up -d +``` + +--- + +### Option B: Helm + +**1. Add the Plane Helm repo:** + +```bash +helm repo add plane https://helm.plane.so +helm repo update +``` + +**2. Create a `values.yaml`:** + +```yaml +ingress: + enabled: true + host: mcp.yourdomain.com + ingressClass: nginx + ssl: + enabled: true + issuer: cloudflare # cloudflare | digitalocean | http + email: you@yourdomain.com + +services: + api: + plane_base_url: "https://api.plane.so" + plane_oauth: + enabled: true + client_id: "" + client_secret: "" + provider_base_url: "https://mcp.yourdomain.com" +``` + +**3. Install:** + +```bash +helm install plane-mcp plane/plane-mcp-server \ + --namespace plane-mcp \ + --create-namespace \ + -f values.yaml +``` + +#### Helm values reference + +| Value | Default | Description | +| -------------------------------------------- | ----------------- | --------------------------------------------------- | +| `dockerRegistry.default_tag` | `latest` | Image tag to deploy | +| `ingress.enabled` | `true` | Enable ingress | +| `ingress.host` | `mcp.example.com` | Public hostname | +| `ingress.ingressClass` | `nginx` | Ingress class name | +| `ingress.ssl.enabled` | `false` | Enable TLS via cert-manager | +| `ingress.ssl.issuer` | `cloudflare` | ACME issuer (`cloudflare`, `digitalocean`, `http`) | +| `services.api.replicas` | `1` | Number of MCP server replicas | +| `services.api.plane_base_url` | `""` | Plane API URL | +| `services.api.plane_oauth.enabled` | `false` | Enable OAuth endpoints | +| `services.api.plane_oauth.client_id` | `""` | OAuth Client ID | +| `services.api.plane_oauth.client_secret` | `""` | OAuth Client Secret | +| `services.api.plane_oauth.provider_base_url` | `""` | Public URL this server is reachable on | +| `services.redis.local_setup` | `true` | Deploy Valkey in-cluster | +| `services.redis.external_redis_url` | `""` | External Valkey/Redis URL (if not using in-cluster) | + +Environment variables that have no Helm value — for example `PLANE_OAUTH_ALLOWED_REDIRECT_URIS` or `LOG_USER_INFO` — +must be set as environment variables on the MCP server deployment. + +#### Upgrading + +```bash +helm upgrade plane-mcp plane/plane-mcp-server \ + --namespace plane-mcp \ + -f values.yaml +``` + +#### Uninstalling + +```bash +helm uninstall plane-mcp --namespace plane-mcp +``` + +--- + +## Logging and observability + +The server emits structured JSON logs with the tool name, duration, status, opaque user ID, and workspace slug. + +`LOG_USER_INFO` defaults to `false`. Setting it to `true` also logs the user's display name, which is personally +identifiable information. + +Even with `LOG_USER_INFO=false`, log entries contain the opaque user ID and the workspace slug, which can identify a +person or organisation when combined with other data. Treat log storage as sensitive: restrict who can read it, set a +retention period, and redact those fields before sharing logs outside your team. + +## Connect AI clients + +Once the server is running, your available endpoints are: + +| Endpoint | Auth | Description | +| --------------------------------------------- | --------------------------------------------------------- | -------------------------------- | +| `https://mcp.yourdomain.com/http/mcp` | OAuth | Recommended for most clients | +| `https://mcp.yourdomain.com/http/api-key/mcp` | `Authorization: Bearer `, `x-workspace-slug: ` | CI, scripts, and headless setups | +| `https://mcp.yourdomain.com/sse` | OAuth | Deprecated HTTP+SSE transport | + +Client configuration is identical to the [MCP server setup guide](/dev-tools/mcp-server). Swap +`https://mcp.plane.so` for your server's host in each configuration. + +--- + +## Troubleshooting + +**Server not starting:** + +```bash +docker compose logs mcp +``` + +**Valkey not reachable:** + +```bash +docker compose exec valkey valkey-cli ping +# Expect: PONG +``` + +If Valkey is unhealthy, tokens are stored in-memory and lost on restart. Verify `REDIS_HOST` and `REDIS_PORT` are set correctly in your environment. + +**OAuth errors:** + +- Confirm both redirect URIs are registered in your Plane OAuth app: `/http/auth/callback` and `/auth/callback`. An + existing `/callback` registration is harmless but unnecessary. +- Check that `PLANE_OAUTH_PROVIDER_CLIENT_ID` and `PLANE_OAUTH_PROVIDER_CLIENT_SECRET` match what Plane generated. +- Check that `PLANE_OAUTH_PROVIDER_BASE_URL` is the publicly reachable `https://` URL of this MCP server - not your Plane instance URL. +- If the client reports `redirect_uri is not allowed`, add its exact callback or a host-pinned pattern to + `PLANE_OAUTH_ALLOWED_REDIRECT_URIS`, then restart the deployment. +- Clear any cached auth tokens on the client side: + + ```bash + rm -rf ~/.mcp-auth + ``` + +**Reset Docker Compose** (deletes Valkey data): + +```bash +docker compose down -v +docker compose up -d +``` + +**Still stuck:** + +1. Double-check OAuth credentials and redirect URIs in Plane workspace settings. +2. Check the [plane-mcp-server](https://github.com/makeplane/plane-mcp-server) repo for known issues. +3. Contact support@plane.so. + +--- + +→ For client configuration details, see the [MCP server setup guide](/dev-tools/mcp-server). +→ For the full list of available tools, see the [tool reference](/dev-tools/mcp-server-tools). diff --git a/apps/developer-docs/docs/dev-tools/mcp-server-tools.md b/apps/developer-docs/docs/dev-tools/mcp-server-tools.md new file mode 100644 index 00000000..46e3c9ed --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/mcp-server-tools.md @@ -0,0 +1,855 @@ +--- +title: MCP server tool reference +description: Reference for the 28 tools and 183 actions exposed by the Plane MCP server — work items, cycles, modules, releases, customers, pages, and more. +keywords: plane mcp tools, mcp tool reference, plane mcp actions, workitem tool, cycle tool, release tool, customer tool +--- + +# Tool reference + +The Plane MCP server exposes 28 tools, one per resource. Resource tools take an `action` parameter that selects one of 183 operations; `get_pql_reference` is the only exception. Every transport—hosted OAuth, hosted access token, local stdio, and deprecated SSE—exposes the same surface. + +The description your MCP client receives is generated from each tool's action declarations. It is the authoritative reference at call time, including required and optional parameters. + +**Totals:** 28 tools, 183 actions, and 169 retired-name aliases. + +For connection and authentication instructions, see [MCP server](/dev-tools/mcp-server). + +## How to read this page + +Each tool table has these columns: + +- **Action** is the value to pass as `action`. +- **Required** parameters must be present for that action. +- **Optional** parameters are accepted only for that action. +- **Notes** include action-specific behavior and the **Read-only** or **Destructive** flags. + +Actions with `cursor` and `per_page` return a `next_cursor` when another page is available. Follow it until it is empty. Update actions change only the fields you pass. + +Parameter values are plain strings, numbers, booleans, and lists. The server also coerces string-encoded lists, integers, and booleans. If you pass an argument that belongs to another action, validation rejects it and names the permitted parameters. + +## Conventions + +### Identifiers + +Most actions take UUIDs for projects, work items, states, labels, members, and other resources. List or resolve the relevant resource first when you have only its name or short identifier. + +`workitem retrieve_by_identifier` is the exception: its `workitem_identifier` accepts a readable identifier such as `ENG-42`. Other `workitem` actions use the work item's UUID in `workitem_id`. + +### Project vs workspace scope + +Pages, work item types, and work item properties can belong to a project or the workspace. Supply `project_id` for the project's own set; omit it for the workspace's set. + +Some workspaces centrally govern the type and property vocabulary. In that mode, project-scoped writes are refused. Use `workitem_type resolve` to find or create a usable type without duplicates, or `workitem_type import_to_project` to link existing workspace types to a project. + +### PQL + +`workitem list`, `workitem list_archived`, `workitem count`, `cycle list_workitems`, and `module list_workitems` accept a `pql` filter. Call `get_pql_reference` with `detail="brief"` or `detail="full"` before composing a query, and see the [Plane Query Language guide](https://docs.plane.so/core-concepts/issues/plane-query-language). + +UUID-backed PQL fields—such as project, assignee, state, label, cycle, module, type, milestone, and creator—need UUIDs. Resolve names before inserting them into a query. + +### Epics + +Plane represents an epic as a work item whose type is named **Epic**. There is no separate epic tool. + +1. Call `workitem_type resolve` with `project_id` and `name="Epic"`, then keep the returned type `id`. +2. Call `workitem create` with the project, name, and that `type_id`. +3. List epics with `workitem list` and `pql='type = ""'`. +4. Read, edit, or delete an epic with `workitem retrieve`, `workitem update`, or `workitem delete`. To nest a work item under it, pass the epic's work item UUID as `parent` to `workitem update` or `workitem create`. +5. List an epic's children with `workitem list` and `pql='childOf("PROJ-12")'`, using the epic's readable identifier. + +### Plan availability + +The server declares feature gates for `work_log` (**Time tracking**), `workitem_type` (**Work item types**), `workitem_property` (**Work item properties**), and some `project` features. Customers, initiatives, releases, and pages can also be gated by the Plane API and your plan. When a feature is unavailable, the error names it. + +## Tools by resource group + +Tools are grouped by the Plane resource they manage. Each group opens with an example prompt. + +## Work items + +_Example prompt: “Summarize what changed on ENG-42 this week, including comments, state changes, and assignees.”_ + +### `workitem` — Work items + +Work items -- issues, tasks and epics. + +| Action | Required | Optional | Notes | +| ------------------------ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| `list` | — | `project_id`, `pql`, `order_by`, `per_page`, `cursor`, `expand`, `fields`, `external_id`, `external_source` | omit project_id to search the whole workspace; Read-only | +| `list_archived` | `project_id` | `pql`, `order_by`, `per_page`, `cursor`, `expand`, `fields`, `external_id`, `external_source` | Read-only | +| `retrieve` | `project_id`, `workitem_id` | `expand`, `fields`, `external_id`, `external_source`, `order_by` | Read-only | +| `retrieve_by_identifier` | `workitem_identifier` | `expand`, `fields`, `external_id`, `external_source`, `order_by` | identifier is PROJECT-N, e.g. ENG-42; Read-only | +| `search` | `query` | `expand`, `fields`, `external_id`, `external_source`, `order_by` | Read-only | +| `count` | — | `project_id`, `pql`, `group_by`, `sub_group_by` | counts the whole workspace unless project_id narrows it; Read-only | +| `create` | `project_id`, `name` | `assignees`, `labels`, `type_id`, `point`, `description_html`, `description_stripped`, `priority`, `start_date`, `target_date`, `sort_order`, `is_draft`, `parent`, `state`, `estimate_point`, `external_source`, `external_id` | — | +| `update` | `project_id`, `workitem_id` | `name`, `assignees`, `labels`, `type_id`, `point`, `description_html`, `description_stripped`, `priority`, `start_date`, `target_date`, `sort_order`, `is_draft`, `parent`, `state`, `estimate_point`, `external_source`, `external_id` | only the fields you pass are changed | +| `delete` | `project_id`, `workitem_id` | — | Destructive | +| `archive` | `project_id`, `workitem_id` | `archive` | archive defaults to true; pass archive=false to unarchive. Only completed or cancelled items can be archived | +| `manage_assignee` | `project_id`, `workitem_id` | `add_user_id`, `remove_user_id` | each takes one id or several; the list is merged, not replaced, and removals apply first | +| `manage_label` | `project_id`, `workitem_id` | `add_label_id`, `remove_label_id` | each takes one id or several; the list is merged, not replaced, and removals apply first | + +**Notes:** priority: urgent, high, medium, low, none. UUID fields (assignees, labels, state, parent, type_id) need UUIDs -- list the relevant resource first if you only have a name. description_stripped is plain text and is wrapped into HTML on save; description_html wins if both are given. fields is a sparse fieldset: use `project`, not project_id, and `description_html`, not description. count group_by and sub_group_by accept: state_id, state\_\_group, priority, project_id, type_id, labels\_\_id, assignees\_\_id, issue_module\_\_module_id, release_work_items\_\_release_id, cycle_id, milestone_id, created_by, target_date, start_date. These are grouping keys only -- they are not PQL filter fields, and filtering on state\_\_group is rejected. + +### `workitem_comment` — Work item comments + +Comments on a work item. + +| Action | Required | Optional | Notes | +| ---------- | ------------------------------------------- | ---------------------------------------------------------- | ----------- | +| `list` | `project_id`, `workitem_id` | `cursor`, `per_page` | Read-only | +| `retrieve` | `project_id`, `workitem_id`, `comment_id` | — | Read-only | +| `create` | `project_id`, `workitem_id`, `comment_html` | `access`, `external_source`, `external_id` | — | +| `update` | `project_id`, `workitem_id`, `comment_id` | `comment_html`, `access`, `external_source`, `external_id` | — | +| `delete` | `project_id`, `workitem_id`, `comment_id` | — | Destructive | + +**Notes:** comment_html is HTML, e.g. '<p>Looks good.</p>'. access is INTERNAL or EXTERNAL. + +### `workitem_activity` — Work item activity + +Change history for a work item. + +| Action | Required | Optional | Notes | +| ---------- | ------------------------------------------ | -------------------- | --------- | +| `list` | `project_id`, `workitem_id` | `cursor`, `per_page` | Read-only | +| `retrieve` | `project_id`, `workitem_id`, `activity_id` | — | Read-only | + +### `workitem_attachment` — Work item attachments + +Files attached to a work item. + +| Action | Required | Optional | Notes | +| ----------------- | -------------------------------------------- | -------- | ----------------------------------------------------------------------------- | +| `list` | `project_id`, `workitem_id` | — | Read-only | +| `read` | `project_id`, `workitem_id`, `attachment_id` | — | returns images and text inline; use download_url for anything else; Read-only | +| `download_url` | `project_id`, `workitem_id`, `attachment_id` | — | Read-only | +| `upload_from_url` | `project_id`, `workitem_id`, `url` | `name` | — | +| `delete` | `project_id`, `workitem_id`, `attachment_id` | — | Destructive | + +**Notes:** read supports PNG/JPEG/GIF/WEBP up to 5 MB and TXT/MD/CSV/HTML/XML/YAML/JSON up to 1 MB. Get attachment_id from the list action. upload_from_url fetches the file server-side, so the URL must be reachable without authentication and must not resolve to a private address. + +### `workitem_link` — Work item links + +External links attached to a work item. + +| Action | Required | Optional | Notes | +| ---------- | --------------------------------------------- | -------------------- | ----------- | +| `list` | `project_id`, `workitem_id` | `cursor`, `per_page` | Read-only | +| `retrieve` | `project_id`, `workitem_id`, `link_id` | — | Read-only | +| `create` | `project_id`, `workitem_id`, `url` | — | — | +| `update` | `project_id`, `workitem_id`, `link_id`, `url` | — | — | +| `delete` | `project_id`, `workitem_id`, `link_id` | — | Destructive | + +### `workitem_relation` — Work item relations + +Relations between work items, and the definitions that type them. + +| Action | Required | Optional | Notes | +| ------------------- | -------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `list` | `project_id`, `workitem_id` | — | Read-only | +| `create` | `project_id`, `workitem_id`, `workitem_ids` | `relation_type`, `relation_definition_id`, `relation_definition_label` | pass relation_type for a dependency, or definition id + label for a custom relation | +| `delete` | `project_id`, `workitem_id`, `related_workitem_id` | `is_dependency` | removes one relation; dependencies and custom relations are independent, so is_dependency must match the kind that was created (default false); Destructive | +| `list_definitions` | — | `is_default`, `is_active` | Read-only | +| `create_definition` | `name` | `outward`, `inward`, `is_active`, `color` | — | +| `update_definition` | `definition_id` | `name`, `outward`, `inward`, `is_active`, `color` | — | +| `delete_definition` | `definition_id` | — | Destructive | + +**Notes:** Call list_definitions first and match the user's wording to an entry. A built_in_dependencies value (blocking, blocked_by, start_before, start_after, finish_before, finish_after) goes in relation_type; a custom definition needs its id in relation_definition_id and the matched outward or inward label in relation_definition_label, which sets direction. + +### `work_log` — Work logs + +Time logged against a work item. + +| Action | Required | Optional | Notes | +| -------- | ------------------------------------------ | ------------------------- | ----------- | +| `list` | `project_id`, `workitem_id` | `cursor`, `per_page` | Read-only | +| `create` | `project_id`, `workitem_id`, `duration` | `description` | — | +| `update` | `project_id`, `workitem_id`, `work_log_id` | `duration`, `description` | — | +| `delete` | `project_id`, `workitem_id`, `work_log_id` | — | Destructive | + +**Notes:** duration is in minutes. + +## Types, properties and estimates + +_Example prompt: “Create an Epic type for ENG, add a Customer impact property, and set up point estimates.”_ + +### `workitem_type` — Work item types + +Work item types, at project or workspace scope. + +| Action | Required | Optional | Notes | +| ------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | +| `list` | — | `project_id`, `cursor`, `per_page` | workspace scope when project_id is omitted; Read-only | +| `retrieve` | `workitem_type_id` | `project_id` | Read-only | +| `resolve` | `project_id`, `name` | — | finds or creates a named type usable in the project; never duplicates | +| `create` | `name` | `project_id`, `description`, `project_ids`, `is_active`, `external_source`, `external_id` | — | +| `update` | `workitem_type_id` | `project_id`, `name`, `description`, `project_ids`, `is_active`, `external_source`, `external_id` | only the fields you pass are changed | +| `delete` | `workitem_type_id` | `project_id` | Destructive | +| `import_to_project` | `project_id`, `workitem_type_ids` | — | links workspace types to a project | + +**Notes:** Omit project_id to work at workspace scope. A type's id is the type_id for `workitem create` and the workitem_type_id for `workitem_property list`. Prefer resolve over create when you just need a usable type such as Epic or Initiative: it handles both modes, matches exactly (case-sensitive, whitespace-stripped) and never duplicates. Where the workspace owns the vocabulary, creating a type on a project is rejected and importing is the only valid path -- resolve does that for you. + +### `workitem_property` — Work item properties + +Custom work item properties and their options. + +| Action | Required | Optional | Notes | +| ------------------------ | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| `list` | — | `project_id`, `workitem_type_id`, `cursor`, `per_page` | no ids lists every workspace property in one call -- the fast path for PQL; Read-only | +| `retrieve` | `workitem_property_id` | `project_id`, `workitem_type_id` | Read-only | +| `create` | `display_name`, `property_type` | `project_id`, `workitem_type_id`, `description`, `relation_type`, `is_required`, `is_multi`, `is_active`, `default_value`, `options`, `display_format`, `external_source`, `external_id` | — | +| `update` | `workitem_property_id` | `project_id`, `workitem_type_id`, `display_name`, `property_type`, `description`, `relation_type`, `is_required`, `is_multi`, `is_active`, `default_value`, `display_format`, `external_source`, `external_id` | only the fields you pass are changed | +| `delete` | `workitem_property_id` | `project_id`, `workitem_type_id` | Destructive | +| `manage_type_properties` | `workitem_type_id` | `project_id`, `attach_ids`, `detach_ids` | omit project_id where the workspace owns types; detach removes the association only, it does not delete the property | +| `list_options` | `property_id` | `project_id` | Read-only | +| `retrieve_option` | `property_id`, `option_id` | `project_id` | Read-only | +| `create_option` | `property_id`, `name` | `project_id`, `description`, `color`, `is_default`, `external_source`, `external_id` | — | +| `update_option` | `property_id`, `option_id` | `project_id`, `name`, `description`, `color`, `is_default`, `external_source`, `external_id` | — | +| `delete_option` | `property_id`, `option_id` | `project_id` | Destructive | +| `get_value` | `project_id`, `workitem_id`, `property_id` | — | Read-only | +| `set_value` | `project_id`, `workitem_id`, `property_id`, `value` | `external_source`, `external_id` | upsert; for a multi-value property this replaces every existing value | +| `delete_value` | `project_id`, `workitem_id`, `property_id` | — | Destructive | + +**Notes:** property_type is one of: TEXT, DATETIME, DECIMAL, BOOLEAN, OPTION, RELATION, URL, EMAIL, FILE, FORMULA. relation_type (for RELATION properties) is one of: ISSUE, USER, RELEASE, RICH_TEXT. A property id is what goes in a PQL cf["<id>"] filter; for OPTION properties the value is an option id. options takes a JSON array of {"name", "color", "is_default"} objects. display_format is required by TEXT (single-line, multi-line, readonly) and DATETIME (MMM dd, yyyy, dd/MM/yyyy, MM/dd/yyyy, yyyy/MM/dd) properties. A property lives with its type: where the workspace owns types, pass workitem_type_id without project_id and it is created in the workspace catalogue and associated for you. list resolves scope in this order: project_id + workitem_type_id is type-scoped (falling back to project-flat then workspace when empty), project_id alone is every property in the project, and neither is every workspace property. To filter by property name in PQL, call list with no ids -- one workspace-wide fetch beats iterating types -- then match display_name in memory to get the id for a cf[] condition. The \*\_value actions read and write a property on one work item: pass value in the type the property expects -- TEXT/URL/EMAIL/FILE as a string; DATETIME as a YYYY-MM-DD or YYYY-MM-DD HH:MM:SS string; DECIMAL as a number; BOOLEAN as true or false; OPTION and RELATION as an option or record id string, or an array of them when the property is multi-value. Send the value's own type, not a stringified form: "007" stays the text 007, whereas 7 is the number. + +### `project_estimate` — Project estimates + +A project's estimate system and its points. + +| Action | Required | Optional | Notes | +| --------------- | ------------------------------------------------ | -------------------------------------------------------------------- | --------------------------------------------- | +| `retrieve` | `project_id` | — | a project has at most one estimate; Read-only | +| `create` | `project_id`, `name` | `type`, `description`, `last_used`, `external_source`, `external_id` | — | +| `update` | `project_id` | `name`, `description`, `external_source`, `external_id` | — | +| `delete` | `project_id` | — | Destructive | +| `link` | `project_id`, `estimate_id` | — | makes that estimate the project's active one | +| `list_points` | `project_id`, `estimate_id` | — | Read-only | +| `create_points` | `project_id`, `estimate_id`, `points` | — | — | +| `update_point` | `project_id`, `estimate_id`, `estimate_point_id` | `value`, `key`, `description`, `external_source`, `external_id` | — | +| `delete_point` | `project_id`, `estimate_id`, `estimate_point_id` | — | Destructive | + +**Notes:** type is one of: categories, points, time. A point's `value` is its display label ("5", "XL") and its `key` is the sort order. points takes a JSON array such as [{"value": "1", "key": 0}]. To set a work item's estimate: retrieve to get the estimate_id, list_points to see the available values, then pass the chosen point's id to `workitem update` as estimate_point. + +## Planning + +_Example prompt: “Move unfinished work from Sprint 14 to Sprint 15, then count it by priority.”_ + +### `cycle` — Cycles + +Cycles (time-boxed iterations) in a project. + +| Action | Required | Optional | Notes | +| -------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| `list` | `project_id` | `archived`, `status`, `cursor`, `per_page`, `order_by` | Read-only | +| `retrieve` | `project_id`, `cycle_id` | — | Read-only | +| `create` | `project_id`, `name`, `owned_by` | `description`, `start_date`, `end_date`, `timezone`, `external_source`, `external_id` | — | +| `update` | `project_id`, `cycle_id` | `name`, `description`, `start_date`, `end_date`, `owned_by`, `timezone`, `external_source`, `external_id` | only the fields you pass are changed | +| `delete` | `project_id`, `cycle_id` | — | Destructive | +| `list_workitems` | `project_id`, `cycle_id` | `pql`, `order_by`, `cursor`, `per_page`, `expand`, `fields` | Read-only | +| `manage_workitems` | `project_id`, `cycle_id` | `add_ids`, `remove_ids` | pass at least one of add_ids or remove_ids; returns nothing, read back with list_workitems | +| `transfer_workitems` | `project_id`, `cycle_id`, `new_cycle_id` | — | moves everything to new_cycle_id | +| `complete` | `project_id`, `cycle_id` | — | sets end_date to today | +| `archive` | `project_id`, `cycle_id` | — | ends the cycle first if it is still running | +| `unarchive` | `project_id`, `cycle_id` | — | — | + +**Notes:** status filters active cycles: current, upcoming, completed, draft, incomplete; it is ignored when archived is true. Dates are ISO 8601 (YYYY-MM-DD). owned_by is a member id. Optional Plane Query Language (PQL) filter. Examples: `priority = "urgent" AND assignee = currentUser()`, `stateGroup IN openStates() AND isOverdue()`. UUID fields (project, assignee, state, label, cycle, module, type, milestone, createdBy) need UUIDs — resolve a name to its UUID first if you only have a name or short identifier (e.g. `LSS` → `project list` and match `identifier` to get `id`). Call `get_pql_reference` for full PQL syntax before composing complex queries. + +### `module` — Modules + +Modules (feature groupings) in a project. + +| Action | Required | Optional | Notes | +| ------------------ | ------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| `list` | `project_id` | `archived`, `cursor`, `per_page`, `order_by` | Read-only | +| `retrieve` | `project_id`, `module_id` | — | Read-only | +| `create` | `project_id`, `name` | `description`, `start_date`, `target_date`, `status`, `lead`, `members`, `external_source`, `external_id` | — | +| `update` | `project_id`, `module_id` | `name`, `description`, `start_date`, `target_date`, `status`, `lead`, `members`, `external_source`, `external_id` | only the fields you pass are changed | +| `delete` | `project_id`, `module_id` | — | Destructive | +| `list_workitems` | `project_id`, `module_id` | `pql`, `order_by`, `cursor`, `per_page`, `expand`, `fields` | Read-only | +| `manage_workitems` | `project_id`, `module_id` | `add_ids`, `remove_ids` | pass at least one of add_ids or remove_ids; returns nothing, read back with list_workitems | +| `archive` | `project_id`, `module_id` | — | — | +| `unarchive` | `project_id`, `module_id` | — | — | + +**Notes:** status is one of: backlog, planned, in-progress, paused, completed, cancelled. Dates are ISO 8601 (YYYY-MM-DD). lead and members are member ids. Optional Plane Query Language (PQL) filter. Examples: `priority = "urgent" AND assignee = currentUser()`, `stateGroup IN openStates() AND isOverdue()`. UUID fields (project, assignee, state, label, cycle, module, type, milestone, createdBy) need UUIDs — resolve a name to its UUID first if you only have a name or short identifier (e.g. `LSS` → `project list` and match `identifier` to get `id`). Call `get_pql_reference` for full PQL syntax before composing complex queries. + +### `milestone` — Milestones + +Milestones within a project. + +| Action | Required | Optional | Notes | +| ------------------ | ---------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| `list` | `project_id` | `cursor`, `per_page` | Read-only | +| `retrieve` | `project_id`, `milestone_id` | — | Read-only | +| `create` | `project_id`, `title` | `target_date`, `external_source`, `external_id` | — | +| `update` | `project_id`, `milestone_id` | `title`, `target_date`, `external_source`, `external_id` | only the fields you pass are changed | +| `delete` | `project_id`, `milestone_id` | — | Destructive | +| `list_workitems` | `project_id`, `milestone_id` | `cursor`, `per_page` | Read-only | +| `manage_workitems` | `project_id`, `milestone_id` | `add_ids`, `remove_ids` | pass at least one of add_ids or remove_ids; returns nothing, read back with list_workitems | + +**Notes:** target_date is ISO 8601 (YYYY-MM-DD). add_ids and remove_ids take work item UUIDs. + +### `initiative` — Initiatives + +Workspace initiatives. + +| Action | Required | Optional | Notes | +| ----------------- | ------------------------------ | --------------------------------------------------------------------- | -------------------------------------------------------------------- | +| `list` | — | — | returns every initiative; this endpoint does not paginate; Read-only | +| `retrieve` | `initiative_id` | — | Read-only | +| `create` | `name` | `description_html`, `start_date`, `end_date`, `state`, `lead` | — | +| `update` | `initiative_id` | `name`, `description_html`, `start_date`, `end_date`, `state`, `lead` | only the fields you pass are changed | +| `delete` | `initiative_id` | — | Destructive | +| `list_projects` | `initiative_id` | `cursor`, `per_page` | Read-only | +| `add_projects` | `initiative_id`, `project_ids` | — | returns nothing, read back with list_projects | +| `remove_projects` | `initiative_id`, `project_ids` | — | returns nothing, read back with list_projects; Destructive | + +**Notes:** state is one of: DRAFT, PLANNED, ACTIVE, COMPLETED, CLOSED. Dates are ISO 8601 (YYYY-MM-DD). lead is a member id. project_ids takes project UUIDs. + +## Releases + +_Example prompt: “Create release v1.8.0, add ENG-40 through ENG-45, and draft its changelog.”_ + +### `release` — Releases + +Releases in the workspace. + +| Action | Required | Optional | Notes | +| ------------------ | ------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| `list` | — | `cursor`, `per_page` | Read-only | +| `retrieve` | `release_id` | — | Read-only | +| `create` | `name` | `description_html`, `status`, `release_date`, `target_date`, `tag_id`, `lead_id`, `is_prerelease`, `external_source`, `external_id` | — | +| `update` | `release_id` | `name`, `description_html`, `status`, `release_date`, `target_date`, `tag_id`, `lead_id`, `is_prerelease` | only the fields you pass are changed | +| `delete` | `release_id` | — | Destructive | +| `get_changelog` | `release_id` | — | Read-only | +| `update_changelog` | `release_id` | `description_html`, `description_stripped` | — | +| `list_workitems` | `release_id` | `cursor`, `per_page` | Read-only | +| `manage_workitems` | `release_id` | `add_ids`, `remove_ids` | pass at least one of add_ids or remove_ids; returns nothing, read back with list_workitems | + +**Notes:** status is one of: unreleased, released, cancelled, defaulting to unreleased. release_date is what the Plane UI labels "Target date" (YYYY-MM-DD); target_date is a separate stored date that the UI does not show. tag_id comes from `release_tag list`, lead_id from `member list_workspace`. For the changelog pass description_html, or description_stripped for plain text. A changelog is created empty with the release, so get_changelog always returns one. + +### `release_tag` — Release tags + +Release tags (version markers). + +| Action | Required | Optional | Notes | +| ---------- | --------- | -------------------------------------------------- | ------------------------------------ | +| `list` | — | `cursor`, `per_page` | Read-only | +| `retrieve` | `tag_id` | — | Read-only | +| `create` | `version` | `description`, `commit_hash`, `git_tag` | — | +| `update` | `tag_id` | `version`, `description`, `commit_hash`, `git_tag` | only the fields you pass are changed | +| `delete` | `tag_id` | — | Destructive | + +**Notes:** version is a version string such as "v1.2.0". A tag id is what release takes as tag_id. + +### `release_label` — Release labels + +Release labels, workspace palette and per release. + +| Action | Required | Optional | Notes | +| -------- | ------------------------- | ---------------------------------- | ----------------------------------------------------------- | +| `list` | — | `release_id`, `cursor`, `per_page` | the workspace palette unless release_id is given; Read-only | +| `create` | `name` | `color`, `sort_order` | adds to the workspace palette | +| `update` | `label_id` | `name`, `color`, `sort_order` | — | +| `delete` | `label_id` | — | removes it from the palette entirely; Destructive | +| `attach` | `release_id`, `label_ids` | — | returns nothing, read back with list | +| `detach` | `release_id`, `label_ids` | — | returns nothing, read back with list; Destructive | + +**Notes:** color is a hex code such as #4E5355. label_ids takes palette label ids. Detaching a label leaves it in the palette; delete removes it for everyone. + +## Projects and workspace + +_Example prompt: “List my active projects and show the work currently assigned to me.”_ + +### `project` — Projects + +Projects in a workspace. + +| Action | Required | Optional | Notes | +| ----------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | +| `list` | — | `cursor`, `per_page`, `order_by` | trimmed fields; use retrieve for full detail; Read-only | +| `retrieve` | `project_id` | — | Read-only | +| `create` | `name`, `identifier` | `description`, `project_lead`, `default_assignee`, `emoji`, `cover_image`, `timezone`, `archive_in`, `close_in`, `external_source`, `external_id` | — | +| `update` | `project_id` | `name`, `description`, `identifier`, `project_lead`, `default_assignee`, `emoji`, `cover_image`, `network`, `timezone`, `archive_in`, `close_in`, `default_state`, `estimate`, `is_time_tracking_enabled`, `external_source`, `external_id` | only the fields you pass are changed | +| `delete` | `project_id` | — | Destructive | +| `archive` | `project_id` | — | — | +| `unarchive` | `project_id` | — | — | +| `worklog_summary` | `project_id` | — | Read-only | +| `get_features` | `project_id` | — | Read-only | +| `update_features` | `project_id` | `modules`, `cycles`, `views`, `pages`, `intakes`, `workitem_types`, `epics`, `parallel_cycles`, `project_updates`, `workflows` | toggles project features on or off | + +**Notes:** identifier is the short work item prefix, such as ENG. network is 0 for secret or 2 for public. project_lead and default_assignee are member ids -- get them from `member list_workspace`. Feature toggles are booleans; omitted ones are left as they are. + +### `state` — Workflow states + +Workflow states within a project. + +| Action | Required | Optional | Notes | +| ---------- | ----------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------ | +| `list` | `project_id` | `cursor`, `per_page` | Read-only | +| `retrieve` | `project_id`, `state_id` | — | Read-only | +| `create` | `project_id`, `name`, `color` | `description`, `sequence`, `group`, `is_triage`, `default`, `external_source`, `external_id` | — | +| `update` | `project_id`, `state_id` | `name`, `color`, `description`, `sequence`, `group`, `is_triage`, `default` | only the fields you pass are changed | +| `delete` | `project_id`, `state_id` | — | Destructive | + +**Notes:** group is one of: backlog, unstarted, started, completed, cancelled, triage. color is a hex code such as #EF4444. + +### `label` — Labels + +Labels within a project. + +| Action | Required | Optional | Notes | +| ---------- | ------------------------ | ---------------------------------------------------------------------------------------- | ------------------------------------ | +| `list` | `project_id` | `cursor`, `per_page` | Read-only | +| `retrieve` | `project_id`, `label_id` | — | Read-only | +| `create` | `project_id`, `name` | `color`, `description`, `parent`, `sort_order`, `external_source`, `external_id` | — | +| `update` | `project_id`, `label_id` | `name`, `color`, `description`, `parent`, `sort_order`, `external_source`, `external_id` | only the fields you pass are changed | +| `delete` | `project_id`, `label_id` | — | Destructive | + +**Notes:** color is a hex code such as #EF4444. parent is the UUID of another label, for nesting. + +### `member` — Members and roles + +Workspace and project members, and role definitions. + +| Action | Required | Optional | Notes | +| ---------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- | +| `me` | — | — | the authenticated user; Read-only | +| `list_workspace` | — | `first_name`, `last_name`, `email`, `display_name`, `role_slug`, `is_active`, `is_bot`, `cursor`, `per_page`, `order_by` | name filters match case-insensitively and combine with AND; Read-only | +| `list_project` | `project_id` | — | Read-only | +| `list_roles` | — | `namespace`, `cursor`, `per_page` | Read-only | +| `retrieve_role` | `role_id` | — | Read-only | + +**Notes:** namespace is 'workspace' (Owner/Admin/Member/Guest) or 'project' (Admin/Contributor/Commenter/Guest); omit for both. A role slug is stable but not globally unique -- key on (namespace, slug). + +### `workspace` — Workspace settings + +Workspace-level feature flags. + +| Action | Required | Optional | Notes | +| ----------------- | -------- | --------------------------------------------------------------------- | -------------------------------------------------- | +| `get_features` | — | — | feature flags for the current workspace; Read-only | +| `update_features` | — | `project_grouping`, `initiatives`, `teams`, `customers`, `wiki`, `pi` | only the flags you pass are changed | + +**Notes:** For a project's feature flags use `project get_features` and `project update_features`. + +### `intake` — Intake queue + +The intake (triage) queue for a project. + +| Action | Required | Optional | Notes | +| ---------- | --------------------------- | ------------------------------------------------------------------ | ------------------------------------- | +| `list` | `project_id` | `cursor`, `per_page` | Read-only | +| `retrieve` | `project_id`, `workitem_id` | — | Read-only | +| `create` | `project_id`, `name` | `description_html`, `priority` | — | +| `update` | `project_id`, `workitem_id` | `status`, `snoozed_till`, `duplicate_to`, `source`, `source_email` | pass status to make a triage decision | +| `delete` | `project_id`, `workitem_id` | — | Destructive | + +**Notes:** workitem_id is the `issue` field of an intake record, not the record's own id. status: -2 pending, -1 declined, 0 snoozed (needs snoozed_till), 1 accepted, 2 duplicate (needs duplicate_to). priority is one of: urgent, high, medium, low, none. + +### `page` — Pages + +Pages at workspace or project scope. + +| Action | Required | Optional | Notes | +| ---------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | +| `list` | — | `project_id`, `cursor`, `per_page` | workspace pages unless project_id is given; Read-only | +| `retrieve` | `page_id` | `project_id` | Read-only | +| `create` | `name`, `description_html` | `project_id`, `access`, `color`, `is_locked`, `external_source`, `external_id` | — | +| `list_workitem_pages` | `project_id`, `workitem_id` | — | Read-only | +| `attach_to_workitem` | `project_id`, `workitem_id`, `page_id` | — | — | +| `detach_from_workitem` | `project_id`, `workitem_id`, `workitem_page_id` | — | workitem_page_id is the link id from list_workitem_pages, not the page id; Destructive | + +**Notes:** description_html is the page body as HTML. access is the page access level. Omit project_id to work with workspace-level pages. + +## Customers + +_Example prompt: “Create Acme as a customer and link its checkout request to the relevant work items.”_ + +### `customer` — Customers + +Customers in the workspace. + +| Action | Required | Optional | Notes | +| ------------------ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| `list` | — | `query`, `cursor`, `per_page` | Read-only | +| `retrieve` | `customer_id` | — | Read-only | +| `create` | `name` | `description_html`, `email`, `website_url`, `domain`, `employees`, `stage`, `contract_status`, `revenue`, `external_source`, `external_id` | upsert: matches on external_source + external_id, else on name, so it never duplicates | +| `update` | `customer_id` | `name`, `description_html`, `email`, `website_url`, `domain`, `employees`, `stage`, `contract_status`, `revenue`, `external_source`, `external_id` | only the fields you pass are changed | +| `delete` | — | `customer_id`, `external_source`, `external_id` | address by customer_id, or by external_source plus external_id; Destructive | +| `list_workitems` | `customer_id` | `customer_request_id`, `search` | Read-only | +| `manage_workitems` | `customer_id` | `link_ids`, `unlink_ids`, `customer_request_id` | pass at least one of link_ids or unlink_ids; returns nothing, read back with list_workitems | + +**Notes:** domain is the customer's industry, shown as "Industry" in Plane -- the website goes in website_url. stage renders as one of: lead, sales_qualified_lead, contract_negotiation, closed_won, closed_lost. contract_status renders as one of: active, pre_contract, signed, inactive. Both are stored free-form; anything else is kept but not displayed. revenue is annual revenue as a string. + +### `customer_request` — Customer requests + +Requests raised by a customer. + +| Action | Required | Optional | Notes | +| ---------- | --------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------- | +| `list` | `customer_id` | `query`, `cursor`, `per_page` | Read-only | +| `retrieve` | `customer_id`, `request_id` | — | Read-only | +| `create` | `customer_id`, `name` | `description_html`, `link`, `workitem_ids` | workitem_ids can only be set here; change links afterwards with customer manage_workitems | +| `update` | `customer_id`, `request_id` | `name`, `description_html`, `link` | only the fields you pass are changed | +| `delete` | `customer_id`, `request_id` | — | Destructive | + +**Notes:** link is a URL associated with the request. workitem_ids is never echoed back -- read the links with `customer list_workitems`. + +### `customer_property` — Customer properties + +Custom properties on customers. + +| Action | Required | Optional | Notes | +| ------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- | +| `list` | — | `cursor`, `per_page` | Read-only | +| `retrieve` | `property_id` | — | Read-only | +| `create` | `display_name`, `property_type` | `relation_type`, `description`, `is_required`, `is_multi`, `is_active`, `default_value`, `options`, `display_format`, `external_source`, `external_id` | — | +| `update` | `property_id` | `display_name`, `relation_type`, `description`, `is_required`, `is_multi`, `is_active`, `default_value`, `options`, `external_source`, `external_id` | only the fields you pass are changed | +| `delete` | `property_id` | — | Destructive | +| `get_values` | `customer_id` | `property_id` | omit property_id to read them all; Read-only | +| `set_values` | `customer_id`, `values` | — | replaces the values of the properties named; others keep theirs | + +**Notes:** display_name is the user-facing label and must be unique in the workspace -- the stored name is derived from it. property_type is one of: TEXT, DATETIME, DECIMAL, BOOLEAN, OPTION, RELATION, URL, EMAIL, FILE, FORMULA. relation_type (required for RELATION) is one of: ISSUE, USER, RELEASE, RICH_TEXT. display_format is required by TEXT (single-line, multi-line, readonly) and DATETIME (MMM dd, yyyy, dd/MM/yyyy, MM/dd/yyyy, yyyy/MM/dd). options takes a JSON array of {"name", "description", "is_default"} objects. values takes a JSON object of property id to a list of strings, e.g. {"<id>": ["Enterprise"]} -- every value is a string whatever the property type, and a single-item list unless is_multi. + +## Query + +_Example prompt: “Show the PQL syntax for overdue, in-progress work assigned to the current user.”_ + +### `get_pql_reference` — PQL reference + +Plane Query Language (PQL) syntax reference. Call this before composing a `pql` filter for the workitem list, list_archived or count actions. + +Takes `detail`: `full` (default) or `brief`. This tool has no `action` parameter. + +**Notes:** detail 'full' gives operators, functions, common mistakes and worked examples; 'brief' gives the compact field and operator quick reference. + +## Retired tool names + +Plane MCP server 0.3.0 consolidated 177 per-operation tools into 28 resource tools. Of those 177 names, the 169 aliases below still resolve, stay hidden from tool listings, and accept their original parameter names; `get_pql_reference` is unchanged; and seven cannot be mapped to one action and instead return a message naming the replacement. The server logs each alias resolution. + +### Names without an alias + +| Retired name | Use instead | +| ---------------------------- | ----------------------------------------------------------- | +| `manage_customer_work_items` | `customer manage_workitems` with `link_ids` or `unlink_ids` | +| `manage_cycle_archive` | `cycle archive` or `cycle unarchive` | +| `manage_initiative_projects` | `initiative add_projects` or `initiative remove_projects` | +| `manage_module_archive` | `module archive` or `module unarchive` | +| `manage_project_archive` | `project archive` or `project unarchive` | +| `manage_release_labels` | `release_label attach` or `release_label detach` | +| `manage_release_work_items` | `release manage_workitems` with `add_ids` or `remove_ids` | + +### Alias table + +::: details Show all 169 aliases + +#### `customer` + +| Retired name | Now | +| -------------------------- | ------------------------- | +| `list_customers` | `customer list` | +| `retrieve_customer` | `customer retrieve` | +| `create_customer` | `customer create` | +| `update_customer` | `customer update` | +| `delete_customer` | `customer delete` | +| `list_customer_work_items` | `customer list_workitems` | + +#### `customer_property` + +| Retired name | Now | +| ------------------------------ | ------------------------------ | +| `list_customer_properties` | `customer_property list` | +| `retrieve_customer_property` | `customer_property retrieve` | +| `create_customer_property` | `customer_property create` | +| `update_customer_property` | `customer_property update` | +| `delete_customer_property` | `customer_property delete` | +| `get_customer_property_values` | `customer_property get_values` | +| `set_customer_property_values` | `customer_property set_values` | + +#### `customer_request` + +| Retired name | Now | +| --------------------------- | --------------------------- | +| `list_customer_requests` | `customer_request list` | +| `retrieve_customer_request` | `customer_request retrieve` | +| `create_customer_request` | `customer_request create` | +| `update_customer_request` | `customer_request update` | +| `delete_customer_request` | `customer_request delete` | + +#### `cycle` + +| Retired name | Now | +| --------------------------- | -------------------------- | +| `list_cycles` | `cycle list` | +| `retrieve_cycle` | `cycle retrieve` | +| `create_cycle` | `cycle create` | +| `update_cycle` | `cycle update` | +| `delete_cycle` | `cycle delete` | +| `list_cycle_work_items` | `cycle list_workitems` | +| `manage_cycle_work_items` | `cycle manage_workitems` | +| `transfer_cycle_work_items` | `cycle transfer_workitems` | +| `complete_cycle` | `cycle complete` | + +#### `initiative` + +| Retired name | Now | +| -------------------------- | -------------------------- | +| `list_initiatives` | `initiative list` | +| `retrieve_initiative` | `initiative retrieve` | +| `create_initiative` | `initiative create` | +| `update_initiative` | `initiative update` | +| `delete_initiative` | `initiative delete` | +| `list_initiative_projects` | `initiative list_projects` | + +#### `intake` + +| Retired name | Now | +| --------------------------- | ----------------- | +| `list_intake_work_items` | `intake list` | +| `retrieve_intake_work_item` | `intake retrieve` | +| `create_intake_work_item` | `intake create` | +| `update_intake_work_item` | `intake update` | +| `delete_intake_work_item` | `intake delete` | + +#### `label` + +| Retired name | Now | +| ---------------- | ---------------- | +| `list_labels` | `label list` | +| `retrieve_label` | `label retrieve` | +| `create_label` | `label create` | +| `update_label` | `label update` | +| `delete_label` | `label delete` | + +#### `member` + +| Retired name | Now | +| ----------------------- | ----------------------- | +| `get_me` | `member me` | +| `get_workspace_members` | `member list_workspace` | +| `get_project_members` | `member list_project` | +| `list_roles` | `member list_roles` | +| `retrieve_role` | `member retrieve_role` | + +#### `milestone` + +| Retired name | Now | +| ----------------------------- | ---------------------------- | +| `list_milestones` | `milestone list` | +| `retrieve_milestone` | `milestone retrieve` | +| `create_milestone` | `milestone create` | +| `update_milestone` | `milestone update` | +| `delete_milestone` | `milestone delete` | +| `list_milestone_work_items` | `milestone list_workitems` | +| `manage_milestone_work_items` | `milestone manage_workitems` | + +#### `module` + +| Retired name | Now | +| -------------------------- | ------------------------- | +| `list_modules` | `module list` | +| `retrieve_module` | `module retrieve` | +| `create_module` | `module create` | +| `update_module` | `module update` | +| `delete_module` | `module delete` | +| `list_module_work_items` | `module list_workitems` | +| `manage_module_work_items` | `module manage_workitems` | + +#### `page` + +| Retired name | Now | +| ---------------------------- | --------------------------- | +| `list_pages` | `page list` | +| `retrieve_page` | `page retrieve` | +| `create_page` | `page create` | +| `list_work_item_pages` | `page list_workitem_pages` | +| `attach_page_to_work_item` | `page attach_to_workitem` | +| `detach_page_from_work_item` | `page detach_from_workitem` | + +#### `project` + +| Retired name | Now | +| ----------------------------- | ------------------------- | +| `list_projects` | `project list` | +| `retrieve_project` | `project retrieve` | +| `create_project` | `project create` | +| `update_project` | `project update` | +| `delete_project` | `project delete` | +| `get_project_worklog_summary` | `project worklog_summary` | +| `update_project_features` | `project update_features` | + +#### `project_estimate` + +| Retired name | Now | +| -------------------------------- | -------------------------------- | +| `get_project_estimate` | `project_estimate retrieve` | +| `create_project_estimate` | `project_estimate create` | +| `update_project_estimate` | `project_estimate update` | +| `delete_project_estimate` | `project_estimate delete` | +| `link_estimate_to_project` | `project_estimate link` | +| `list_project_estimate_points` | `project_estimate list_points` | +| `create_project_estimate_points` | `project_estimate create_points` | +| `update_project_estimate_point` | `project_estimate update_point` | +| `delete_project_estimate_point` | `project_estimate delete_point` | + +#### `release` + +| Retired name | Now | +| -------------------------- | -------------------------- | +| `list_releases` | `release list` | +| `retrieve_release` | `release retrieve` | +| `create_release` | `release create` | +| `update_release` | `release update` | +| `delete_release` | `release delete` | +| `get_release_changelog` | `release get_changelog` | +| `update_release_changelog` | `release update_changelog` | +| `list_release_work_items` | `release list_workitems` | + +#### `release_label` + +| Retired name | Now | +| ---------------------- | ---------------------- | +| `list_release_labels` | `release_label list` | +| `create_release_label` | `release_label create` | +| `update_release_label` | `release_label update` | +| `delete_release_label` | `release_label delete` | + +#### `release_tag` + +| Retired name | Now | +| ---------------------- | ---------------------- | +| `list_release_tags` | `release_tag list` | +| `retrieve_release_tag` | `release_tag retrieve` | +| `create_release_tag` | `release_tag create` | +| `update_release_tag` | `release_tag update` | +| `delete_release_tag` | `release_tag delete` | + +#### `state` + +| Retired name | Now | +| ---------------- | ---------------- | +| `list_states` | `state list` | +| `retrieve_state` | `state retrieve` | +| `create_state` | `state create` | +| `update_state` | `state update` | +| `delete_state` | `state delete` | + +#### `work_log` + +| Retired name | Now | +| ----------------- | ----------------- | +| `list_work_logs` | `work_log list` | +| `create_work_log` | `work_log create` | +| `update_work_log` | `work_log update` | +| `delete_work_log` | `work_log delete` | + +#### `workitem` + +| Retired name | Now | +| ---------------------------------- | --------------------------------- | +| `list_work_items` | `workitem list` | +| `list_archived_work_items` | `workitem list_archived` | +| `retrieve_work_item` | `workitem retrieve` | +| `retrieve_work_item_by_identifier` | `workitem retrieve_by_identifier` | +| `search_work_items` | `workitem search` | +| `count_work_items` | `workitem count` | +| `create_work_item` | `workitem create` | +| `update_work_item` | `workitem update` | +| `delete_work_item` | `workitem delete` | +| `manage_work_item_archive` | `workitem archive` | +| `manage_work_item_assignee` | `workitem manage_assignee` | +| `manage_work_item_label` | `workitem manage_label` | + +#### `workitem_activity` + +| Retired name | Now | +| ----------------------------- | ---------------------------- | +| `list_work_item_activities` | `workitem_activity list` | +| `retrieve_work_item_activity` | `workitem_activity retrieve` | + +#### `workitem_attachment` + +| Retired name | Now | +| --------------------------------------- | ------------------------------------- | +| `list_work_item_attachments` | `workitem_attachment list` | +| `read_work_item_attachment` | `workitem_attachment read` | +| `get_work_item_attachment_download_url` | `workitem_attachment download_url` | +| `upload_work_item_attachment_from_url` | `workitem_attachment upload_from_url` | +| `delete_work_item_attachment` | `workitem_attachment delete` | + +#### `workitem_comment` + +| Retired name | Now | +| ---------------------------- | --------------------------- | +| `list_work_item_comments` | `workitem_comment list` | +| `retrieve_work_item_comment` | `workitem_comment retrieve` | +| `create_work_item_comment` | `workitem_comment create` | +| `update_work_item_comment` | `workitem_comment update` | +| `delete_work_item_comment` | `workitem_comment delete` | + +#### `workitem_link` + +| Retired name | Now | +| ------------------------- | ------------------------ | +| `list_work_item_links` | `workitem_link list` | +| `retrieve_work_item_link` | `workitem_link retrieve` | +| `create_work_item_link` | `workitem_link create` | +| `update_work_item_link` | `workitem_link update` | +| `delete_work_item_link` | `workitem_link delete` | + +#### `workitem_property` + +| Retired name | Now | +| ------------------------------------ | ------------------------------------------ | +| `list_work_item_properties` | `workitem_property list` | +| `retrieve_work_item_property` | `workitem_property retrieve` | +| `create_work_item_property` | `workitem_property create` | +| `update_work_item_property` | `workitem_property update` | +| `delete_work_item_property` | `workitem_property delete` | +| `manage_work_item_type_properties` | `workitem_property manage_type_properties` | +| `list_work_item_property_options` | `workitem_property list_options` | +| `retrieve_work_item_property_option` | `workitem_property retrieve_option` | +| `create_work_item_property_option` | `workitem_property create_option` | +| `update_work_item_property_option` | `workitem_property update_option` | +| `delete_work_item_property_option` | `workitem_property delete_option` | +| `get_work_item_property_value` | `workitem_property get_value` | +| `set_work_item_property_value` | `workitem_property set_value` | +| `delete_work_item_property_value` | `workitem_property delete_value` | + +#### `workitem_relation` + +| Retired name | Now | +| -------------------------------------- | ------------------------------------- | +| `list_work_item_relations` | `workitem_relation list` | +| `create_work_item_relation` | `workitem_relation create` | +| `remove_work_item_relation` | `workitem_relation delete` | +| `list_work_item_relation_definitions` | `workitem_relation list_definitions` | +| `create_work_item_relation_definition` | `workitem_relation create_definition` | +| `update_work_item_relation_definition` | `workitem_relation update_definition` | +| `delete_work_item_relation_definition` | `workitem_relation delete_definition` | + +#### `workitem_type` + +| Retired name | Now | +| ----------------------------------- | --------------------------------- | +| `list_work_item_types` | `workitem_type list` | +| `retrieve_work_item_type` | `workitem_type retrieve` | +| `resolve_work_item_type` | `workitem_type resolve` | +| `create_work_item_type` | `workitem_type create` | +| `update_work_item_type` | `workitem_type update` | +| `delete_work_item_type` | `workitem_type delete` | +| `import_work_item_types_to_project` | `workitem_type import_to_project` | + +#### `workspace` + +| Retired name | Now | +| --------------------------- | --------------------------- | +| `get_features` | `workspace get_features` | +| `update_workspace_features` | `workspace update_features` | + +::: + +## See also + +- [Set up the MCP server](/dev-tools/mcp-server) +- [Self-host the MCP server](/dev-tools/mcp-server-self-host) +- [Tool architecture and extension guide](https://github.com/makeplane/plane-mcp-server/blob/v0.3.0/plane_mcp/tools/README.md) diff --git a/apps/developer-docs/docs/dev-tools/mcp-server.md b/apps/developer-docs/docs/dev-tools/mcp-server.md new file mode 100644 index 00000000..60b21a00 --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/mcp-server.md @@ -0,0 +1,876 @@ +--- +title: MCP server +description: Connect Claude, ChatGPT, Codex, Cursor, VS Code, Windsurf, Zed, and Antigravity to Plane over MCP. Endpoints, OAuth and access-token auth, per-client setup, security, and troubleshooting. +keywords: plane mcp server, model context protocol, plane ai tools, claude plane, cursor plane, chatgpt plane, codex plane, mcp oauth, mcp access token +--- + +# MCP server + +Use Plane from the AI tool you already work in to create work items, plan cycles, and query projects in natural +language. The server is [open source](https://github.com/makeplane/plane-mcp-server) under the MIT license. + +::: tip Hosted server +Connect to `https://mcp.plane.so/http/mcp` and sign in with your Plane account. +::: + +::: tip +Just want to connect your AI tool? Use the [short setup guide](https://docs.plane.so/ai/mcp-server). +::: + +## How it works + +[Model Context Protocol (MCP)](https://modelcontextprotocol.io) is an open standard for how AI clients discover and call +external tools. The Plane MCP server sits between your client and Plane's REST API, then acts as the signed-in user. + +Version 0.3.0 exposes **28 tools, one per resource, covering 183 actions**. Pass `action` to select an operation: + +```python +workitem(action="create", project_id=..., name="Fix login") +workitem(action="list", project_id=..., pql='stateGroup = "started"') +cycle(action="archive", project_id=..., cycle_id=...) +``` + +Every tool description lists its actions and marks parameters as required or optional. Tools also carry MCP +`readOnlyHint` and `destructiveHint` annotations derived from their actions. + +### Hosted or self-hosted + +Plane Cloud users can connect to `mcp.plane.so`. For self-hosted Plane, run locally with `PLANE_BASE_URL` set to your +instance, or [deploy your own server](/dev-tools/mcp-server-self-host). + +## What you can do + +- [Work items](/dev-tools/mcp-server-tools#work-items): create, update, search, comment, attach, link, relate, nest, and + log time. +- [Types, properties, and estimates](/dev-tools/mcp-server-tools#types-properties-and-estimates): manage types, + custom properties, and estimates. +- [Planning](/dev-tools/mcp-server-tools#planning): plan cycles, modules, milestones, and initiatives. +- [Releases](/dev-tools/mcp-server-tools#releases): manage tags, labels, work items, and changelogs. +- [Projects and workspace](/dev-tools/mcp-server-tools#projects-and-workspace): manage projects, states, labels, members, + pages, features, and intake. +- [Customers](/dev-tools/mcp-server-tools#customers): manage customers, requests, properties, and linked work. +- [Query](/dev-tools/mcp-server-tools#query): retrieve the PQL language reference before composing filters. + +### Query with PQL + +`workitem list`, `workitem list_archived`, `workitem count`, `cycle list_workitems`, and `module list_workitems` accept +`pql`. UUID-backed fields require UUIDs, so resolve names first. Call `get_pql_reference` with `detail="brief"` or +`detail="full"`; see [Plane Query Language](https://docs.plane.so/core-concepts/issues/plane-query-language). + +There are no separate epic tools. Follow the [epics recipe](/dev-tools/mcp-server-tools#epics). + +## Endpoints and authentication + +| Endpoint | Auth | Use it for | +| --------------------------------------- | --------------------- | ---------------------------------------------------- | +| `https://mcp.plane.so/http/mcp` | OAuth | Streamable HTTP; recommended for interactive use | +| `https://mcp.plane.so/http/api-key/mcp` | PAT headers | Automations, CI, headless agents, shared team setups | +| `uvx plane-mcp-server stdio` | Environment variables | Self-hosted Plane and local or offline development | +| `https://mcp.plane.so/sse` | OAuth | Deprecated clients that still require HTTP+SSE | + +### OAuth + +Your client redirects you to Plane, where you sign in and choose a workspace. The server validates the resulting +token with `/api/v1/users/me/`, and the connection stays bound to that workspace. + +The default redirect allowlist covers Cursor, VS Code, Antigravity, Claude.ai, ChatGPT, and localhost callbacks. A +self-hosted server can add other clients through `PLANE_OAUTH_ALLOWED_REDIRECT_URIS`. + +Re-authenticate from your client's connector controls. In Claude Code, run `/mcp`; with `mcp-remote`, clear its cache: + +```bash +rm -rf ~/.mcp-auth +``` + +This removes cached OAuth credentials for every `mcp-remote` server, not only Plane. To keep Plane's cache separate, +set `MCP_REMOTE_CONFIG_DIR` in that server's `env` and remove that directory instead. + +### Personal access token + +Send both headers on every request to the PAT endpoint: + +| Header | Value | +| ------------------ | ------------------ | +| `Authorization` | `Bearer ` | +| `x-workspace-slug` | `` | + +::: warning Changed +Earlier versions of this page showed an `x-api-key` header. The server reads the standard `Authorization: Bearer` +header; update existing configs. +::: + +#### Get a token + +Create a personal access token under **Profile settings → Personal access tokens** and copy it when shown. For +automations, you can instead create a workspace access token under **Workspace settings → Access tokens**. + +#### Find your workspace slug + +The slug is the segment after `app.plane.so/` in your Plane URL. In `https://app.plane.so/acme-corp/`, it is +`acme-corp`. + +### Local (stdio) + +Local mode requires Python 3.10+ and [`uv`](https://docs.astral.sh/uv/). On macOS or Linux: + +```bash +curl -LsSf https://astral.sh/uv/install.sh | sh +``` + +On Windows: + +```powershell +powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" +``` + +| Variable | Required | Description | +| ---------------------- | -------- | --------------------------------------------------------------------------------- | +| `PLANE_API_KEY` | Yes | Your Plane personal or workspace access token | +| `PLANE_WORKSPACE_SLUG` | Yes | The workspace slug | +| `PLANE_BASE_URL` | No | Defaults to `https://api.plane.so`; set it to your self-hosted Plane instance URL | + +Prefer stdio when the client runs on the same machine, you need a self-hosted or private Plane instance, or you do +not want to expose an MCP HTTP service. + +### SSE (deprecated) + +The MCP specification deprecated the older HTTP+SSE transport. Keep `https://mcp.plane.so/sse` only for an existing +client that cannot use Streamable HTTP, and migrate when that client supports it. + +## Connect a client + +Replace `mcp.plane.so` with your own host if you self-host the server. Tabs stay in sync across this page. + +### General + +These are the common shapes. Some clients use `serverUrl`, `servers`, or `context_servers`; use the client-specific +schema below. + +:::tabs key:mcp-auth +== OAuth {#general-oauth} + +```json +{ + "mcpServers": { + "plane": { + "url": "https://mcp.plane.so/http/mcp" + } + } +} +``` + +== Access token {#general-token} + +```json +{ + "mcpServers": { + "plane": { + "url": "https://mcp.plane.so/http/api-key/mcp", + "headers": { + "Authorization": "Bearer ", + "x-workspace-slug": "" + } + } + } +} +``` + +== Local (stdio) {#general-stdio} + +```json +{ + "mcpServers": { + "plane": { + "command": "uvx", + "args": ["plane-mcp-server", "stdio"], + "env": { + "PLANE_API_KEY": "", + "PLANE_WORKSPACE_SLUG": "" + } + } + } +} +``` + +::: + +### Claude + +:::tabs key:mcp-auth +== OAuth {#claude-oauth} +On Claude Desktop or claude.ai: + +1. Open **Settings → Connectors → Add custom connector**. +2. Paste `https://mcp.plane.so/http/mcp`, select **Add**, then **Connect**. +3. Sign in to Plane. In a chat, choose **+ → Connectors** to enable Plane. + +Free plans allow one custom connector. On Team or Enterprise, an Owner adds it under +**Organization settings → Connectors**, then members select **Connect**. + +== Access token {#claude-token} +Desktop users who need a token instead of OAuth can bridge with `mcp-remote` (Node.js 22+ recommended): + +```json +{ + "mcpServers": { + "plane": { + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "https://mcp.plane.so/http/api-key/mcp", + "--header", + "Authorization:${PLANE_AUTH_HEADER}", + "--header", + "x-workspace-slug:${PLANE_WORKSPACE_SLUG}" + ], + "env": { + "PLANE_AUTH_HEADER": "Bearer ", + "PLANE_WORKSPACE_SLUG": "" + } + } + } +} +``` + +== Local (stdio) {#claude-stdio} +Use **Settings → Developer → Edit Config**, or edit `~/Library/Application Support/Claude/claude_desktop_config.json` +on macOS or `%APPDATA%\Claude\claude_desktop_config.json` on Windows: + +```json +{ + "mcpServers": { + "plane": { + "command": "uvx", + "args": ["plane-mcp-server", "stdio"], + "env": { + "PLANE_API_KEY": "", + "PLANE_WORKSPACE_SLUG": "" + } + } + } +} +``` + +Quit and relaunch Claude Desktop. This file supports stdio only: never put `url` or `type: http` in it. + +::: + +### Claude Code + +:::tabs key:mcp-auth +== OAuth {#claude-code-oauth} + +```bash +claude mcp add --transport http plane https://mcp.plane.so/http/mcp +# In a session, run /mcp and authenticate (or run: claude mcp login plane). +claude mcp list +``` + +== Access token {#claude-code-token} +Put `--header` after the URL: + +```bash +claude mcp add --transport http plane https://mcp.plane.so/http/api-key/mcp \ + --header "Authorization: Bearer " \ + --header "x-workspace-slug: " +``` + +== Local (stdio) {#claude-code-stdio} + +```bash +claude mcp add --transport stdio plane \ + --env PLANE_API_KEY= \ + --env PLANE_WORKSPACE_SLUG= \ + -- uvx plane-mcp-server stdio +``` + +::: + +Use `--scope local|project|user`; project scope writes a shareable `.mcp.json` with `mcpServers`, `type: "http"`, and +`url`. PAT entries add `headers`, and `${PLANE_PAT}` expands from the environment. Claude Code's SSE transport is deprecated. + +### ChatGPT + +ChatGPT supports OAuth on Plus, Pro, Business, Enterprise, and Edu plans: + +1. Open **Settings → Security and login** and turn on **Developer mode**. Business, Enterprise, and Edu workspaces + require an admin to allow it. +2. Open **chatgpt.com/plugins**, select **+**, name the connection "Plane", enter + `https://mcp.plane.so/http/mcp` under **Connection**, select **Create**, then sign in to Plane. +3. In a chat, open **+ → Developer mode** and enable Plane. + +The exact menu names may differ by workspace. ChatGPT does not accept custom headers, so use OAuth. + +### Codex + +The CLI, IDE extension, and ChatGPT desktop app share `~/.codex/config.toml`. + +:::tabs key:mcp-auth +== OAuth {#codex-oauth} + +```bash +codex mcp add plane --url https://mcp.plane.so/http/mcp +codex mcp login plane +codex mcp list +``` + +You can also run `/mcp` inside Codex. No experimental flag is required. + +== Access token {#codex-token} + +```toml +[mcp_servers.plane] +url = "https://mcp.plane.so/http/api-key/mcp" +bearer_token_env_var = "PLANE_PAT" +http_headers = { "x-workspace-slug" = "" } +``` + +`bearer_token_env_var` sends `Authorization: Bearer $PLANE_PAT`. The CLI supports +`codex mcp add … --bearer-token-env-var PLANE_PAT`; arbitrary headers are config-file only. + +== Local (stdio) {#codex-stdio} + +```toml +[mcp_servers.plane] +command = "uvx" +args = ["plane-mcp-server", "stdio"] + +[mcp_servers.plane.env] +PLANE_API_KEY = "" +PLANE_WORKSPACE_SLUG = "" +``` + +::: + +### Cursor + +Use `~/.cursor/mcp.json` globally or `.cursor/mcp.json` in a project. + +:::tabs key:mcp-auth +== OAuth {#cursor-oauth} +[![Install in Cursor](/images/mcp/install-in-cursor.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=plane&config=eyJ1cmwiOiJodHRwczovL21jcC5wbGFuZS5zby9odHRwL21jcCJ9) + +Or add the server manually: + +```json +{ + "mcpServers": { + "plane": { + "url": "https://mcp.plane.so/http/mcp" + } + } +} +``` + +Cursor shows **Login** or **Needs authentication** and completes OAuth. Manage servers from **Customize**. + +== Access token {#cursor-token} + +```json +{ + "mcpServers": { + "plane": { + "url": "https://mcp.plane.so/http/api-key/mcp", + "headers": { + "Authorization": "Bearer ${env:PLANE_PAT}", + "x-workspace-slug": "" + } + } + } +} +``` + +== Local (stdio) {#cursor-stdio} + +```json +{ + "mcpServers": { + "plane": { + "command": "uvx", + "args": ["plane-mcp-server", "stdio"], + "env": { + "PLANE_API_KEY": "${env:PLANE_API_KEY}", + "PLANE_WORKSPACE_SLUG": "" + } + } + } +} +``` + +::: + +Remote entries use `url` and must not include a `type` key. + +### VS Code + +Use `.vscode/mcp.json` for a workspace, or run **MCP: Open User Configuration** for the user file. + +:::tabs key:mcp-auth +== OAuth {#vs-code-oauth} +[![Install in VS Code](/images/mcp/install-in-vscode.svg)](https://vscode.dev/redirect/mcp/install?name=plane&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fmcp.plane.so%2Fhttp%2Fmcp%22%7D) + +[Install in VS Code Insiders](https://insiders.vscode.dev/redirect/mcp/install?name=plane&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fmcp.plane.so%2Fhttp%2Fmcp%22%7D&quality=insiders), or add it from the CLI: + +```bash +code --add-mcp '{"name":"plane","type":"http","url":"https://mcp.plane.so/http/mcp"}' +``` + +Trust the server on first start, verify it with **MCP: List Servers**, and use Copilot Chat in **Agent** mode. +Copilot Business and Enterprise organizations must enable the "MCP servers in Copilot" policy. + +== Access token {#vs-code-token} + +```json +{ + "inputs": [ + { + "type": "promptString", + "id": "plane-pat", + "description": "Plane personal access token", + "password": true + }, + { + "type": "promptString", + "id": "plane-slug", + "description": "Plane workspace slug" + } + ], + "servers": { + "plane": { + "type": "http", + "url": "https://mcp.plane.so/http/api-key/mcp", + "headers": { + "Authorization": "Bearer ${input:plane-pat}", + "x-workspace-slug": "${input:plane-slug}" + } + } + } +} +``` + +== Local (stdio) {#vs-code-stdio} + +```json +{ + "servers": { + "plane": { + "type": "stdio", + "command": "uvx", + "args": ["plane-mcp-server", "stdio"], + "env": { + "PLANE_API_KEY": "", + "PLANE_WORKSPACE_SLUG": "" + } + } + } +} +``` + +::: + +### Windsurf + +Current vendor docs call Windsurf **Devin Desktop**. Its configuration remains at +`~/.codeium/windsurf/mcp_config.json`; open Cascade's **MCPs → Manage MCPs** or +**Settings → Cascade → MCP Servers**. + +:::tabs key:mcp-auth +== OAuth {#windsurf-oauth} + +```json +{ + "mcpServers": { + "plane": { + "serverUrl": "https://mcp.plane.so/http/mcp" + } + } +} +``` + +If the OAuth sign-in does not complete, use the access-token configuration instead. + +== Access token {#windsurf-token} + +```json +{ + "mcpServers": { + "plane": { + "serverUrl": "https://mcp.plane.so/http/api-key/mcp", + "headers": { + "Authorization": "Bearer ${env:PLANE_PAT}", + "x-workspace-slug": "" + } + } + } +} +``` + +== Local (stdio) {#windsurf-stdio} + +```json +{ + "mcpServers": { + "plane": { + "command": "uvx", + "args": ["plane-mcp-server", "stdio"], + "env": { + "PLANE_API_KEY": "${env:PLANE_API_KEY}", + "PLANE_WORKSPACE_SLUG": "" + } + } + } +} +``` + +::: + +Remote entries use `serverUrl`. Refresh the server list after saving. + +### Zed + +Use **Settings → AI → MCP Servers → Add Server**, or edit `~/.config/zed/settings.json`. + +:::tabs key:mcp-auth +== OAuth {#zed-oauth} + +```json +{ + "context_servers": { + "plane": { + "url": "https://mcp.plane.so/http/mcp" + } + } +} +``` + +Zed prompts for OAuth through an allowlisted loopback callback. + +== Access token {#zed-token} + +```json +{ + "context_servers": { + "plane": { + "url": "https://mcp.plane.so/http/api-key/mcp", + "headers": { + "Authorization": "Bearer ", + "x-workspace-slug": "" + } + } + } +} +``` + +== Local (stdio) {#zed-stdio} + +```json +{ + "context_servers": { + "plane": { + "command": "uvx", + "args": ["plane-mcp-server", "stdio"], + "env": { + "PLANE_API_KEY": "", + "PLANE_WORKSPACE_SLUG": "" + } + } + } +} +``` + +::: + +Zed uses this flat schema; the old nested `command.path` and `source: custom` shape is outdated. + +### Antigravity + +The IDE and CLI share `~/.gemini/config/mcp_config.json` globally or `.agents/mcp_config.json` in a workspace. + +:::tabs key:mcp-auth +== OAuth {#antigravity-oauth} + +```json +{ + "mcpServers": { + "plane": { + "serverUrl": "https://mcp.plane.so/http/mcp" + } + } +} +``` + +OAuth is automatic. In the IDE, open **… → MCP Servers → Manage MCP Servers**. In Antigravity 2.0, use +**Settings → Customizations → Installed MCP Servers → Add MCP**; in the CLI, run `/mcp`. + +== Access token {#antigravity-token} + +```json +{ + "mcpServers": { + "plane": { + "serverUrl": "https://mcp.plane.so/http/api-key/mcp", + "headers": { + "Authorization": "Bearer ${env:PLANE_PAT}", + "x-workspace-slug": "" + } + } + } +} +``` + +== Local (stdio) {#antigravity-stdio} + +```json +{ + "mcpServers": { + "plane": { + "command": "uvx", + "args": ["plane-mcp-server", "stdio"], + "env": { + "PLANE_API_KEY": "${env:PLANE_API_KEY}", + "PLANE_WORKSPACE_SLUG": "" + } + } + } +} +``` + +::: + +Remote entries require `serverUrl`; `url` and `httpUrl` are unsupported. + +### Other clients + +For a stdio-only client, use `mcp-remote` with Node.js 22+ recommended. A client with native remote-MCP support only +needs the OAuth URL. + +:::tabs key:mcp-auth +== OAuth {#other-clients-oauth} + +```json +{ + "mcpServers": { + "plane": { + "command": "npx", + "args": ["-y", "mcp-remote", "https://mcp.plane.so/http/mcp"] + } + } +} +``` + +== Access token {#other-clients-token} + +```json +{ + "mcpServers": { + "plane": { + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "https://mcp.plane.so/http/api-key/mcp", + "--header", + "Authorization:${PLANE_AUTH_HEADER}", + "--header", + "x-workspace-slug:${PLANE_WORKSPACE_SLUG}" + ], + "env": { + "PLANE_AUTH_HEADER": "Bearer ", + "PLANE_WORKSPACE_SLUG": "" + } + } + } +} +``` + +::: + +`mcp-remote` reads headers from its `--header` arguments; a `headers` key on this stdio entry is ignored. Keep the +header values in `env` and write the arguments without spaces around the colon: on Windows, spaces inside `args` can be +mangled by some clients. To reset cached OAuth state, remove `~/.mcp-auth` (or the directory `MCP_REMOTE_CONFIG_DIR` +points to). + +## Common workflows + +**What's on my plate** + +```text +List work items assigned to me that are in progress or overdue, grouped by project. +``` + +_Trace: `member me` → `workitem list` without `project_id`, using +`pql='assignee = currentUser() AND (stateGroup = "started" OR isOverdue())'`._ + +**File a bug** + +```text +Create a high-priority bug in ENG called "Login times out on Safari 17". Description: the OAuth callback lands on a blank page. Assign it to me and add the "auth" label. +``` + +_Trace: `project list` → `member me` → `label list` → `workitem create`._ + +**Roll over a sprint** + +```text +Create Sprint 15 in ENG from June 2 to June 15, move everything unfinished from Sprint 14 into it, and give me a count by priority. +``` + +_Trace: `cycle create` with `owned_by` → `cycle list` to find Sprint 14 → `cycle transfer_workitems` → +`workitem count` with `pql` and `group_by="priority"`._ + +**Close the loop** + +```text +Log 90 minutes on ENG-42 with the note "Implemented retry logic", mark it Done, and comment "Fixed in abc1234, needs QA". +``` + +_Trace: `workitem retrieve_by_identifier` → `work_log create` → `state list` → `workitem update` → +`workitem_comment create`._ + +## Permissions and sessions + +- The server acts as the authenticated user. Plane enforces workspace and project roles, so a Guest cannot do more + through MCP. +- OAuth requests `read` and `write` scopes. The workspace chosen at consent binds that connection; reconnect to + switch workspaces. +- A PAT connection is scoped by `x-workspace-slug`. +- Hosted OAuth tokens are stored server-side in Redis or Valkey. A self-hosted server without Redis falls back to + in-memory storage. +- Revoke access by disconnecting the connector in your client, deleting a PAT in Plane, or clearing the + `mcp-remote` cache. + +## Security best practices + +- Use only `https://mcp.plane.so` or your own trusted host, and check the URL on Plane's consent screen. +- Treat work item titles, descriptions, comments, and attachments as untrusted model input. Prefer clients that + confirm writes; destructive actions are flagged with `destructiveHint`. +- Keep PATs out of shared or committed configs. Use environment variables or `${input:...}`, and never commit a + token in a project-scoped `.mcp.json`. +- Use a workspace access token with the minimum role needed for automations. +- Revoke tokens in Plane settings and audit API token events in the workspace audit log. +- Server logs are structured JSON with tool name, duration, status, opaque user ID, and workspace slug. Display + names are logged only when `LOG_USER_INFO=true`, because they are PII. + +## Self-hosted Plane + +The hosted `mcp.plane.so` service cannot reach private Plane instances. In stdio mode, set `PLANE_BASE_URL` to your +instance URL, then test the token against Plane's REST API. Read the key into a shell variable first so it stays out +of your command history: + +```bash +read -rs PLANE_API_KEY # paste the key and press Enter; nothing is echoed +curl -H "x-api-key: $PLANE_API_KEY" \ + "https://plane.yourcompany.com/api/v1/users/me/" +``` + +A `200` response confirms the key and URL. That header is the Plane REST API header, not the MCP PAT header. + +::: tip Running your own MCP server? +Follow the [self-hosting guide](/dev-tools/mcp-server-self-host) for Docker, Helm, OAuth, storage, and operations. +The OAuth transport needs Plane's OAuth application registration, which is available on Plane Cloud and Plane +Commercial Edition; on Community Edition, use stdio mode. +::: + +## Upgrading + +### From per-operation tools (0.2.x → 0.3.0) + +The 177 per-operation tools became 28 resource tools. Of the 177 names, 169 still resolve as hidden aliases and keep +their original parameter names, so saved prompts and scripts continue to work; `get_pql_reference` is unchanged; and +seven cannot be mapped and return a message naming their replacement. See +[retired tool names](/dev-tools/mcp-server-tools#retired-tool-names). + +`project list` is now paginated by default. Follow `next_cursor` or pass `per_page`. Archive actions now return an +explicit status object. + +### From the Node.js server + +The `@makeplane/plane-mcp-server` npm package is deprecated. Update environment variables, then use the stdio +configuration shown above: + +| Node.js server | Python server | +| ---------------------- | ---------------------- | +| `PLANE_API_KEY` | `PLANE_API_KEY` | +| `PLANE_API_HOST_URL` | `PLANE_BASE_URL` | +| `PLANE_WORKSPACE_SLUG` | `PLANE_WORKSPACE_SLUG` | + +Replace the old Node.js `command` and `args` with `uvx plane-mcp-server stdio`. + +## Troubleshooting + +| Symptom | Cause | Fix | +| ----------------------------------- | ------------------------------------------- | -------------------------------------------------------------------- | +| 401 with PAT | Token is wrong, revoked, or uses old header | Use `Authorization: Bearer ` instead of `x-api-key` | +| 401 with OAuth | Token expired | Re-authenticate from the client | +| "workspace slug missing" | PAT config omits the workspace header | Add `x-workspace-slug` | +| 404 | Workspace slug or resource ID is wrong | Check the slug or ID | +| 403 | Your Plane role is too low | Ask for the required workspace or project role | +| 400 | An argument is missing or invalid | Read the error; permitted enum values are in the tool description | +| "not available on your plan" or 402 | The Plane plan does not include the feature | Enable the feature or use an available action | +| `mcp-remote` fails to start | Node.js is too old | Use Node.js 22+ and run `npx -y mcp-remote@latest` | +| Server is not listed | JSON or client schema is invalid | Remove trailing commas; apply the client-specific schema notes above | +| Only the first page of projects | `project list` is paginated | Follow `next_cursor` or pass `per_page` | +| Tools look stale or out of order | Pinned tool order or client cache is stale | Restart the client after upgrades | + +For a server that is not listed, remember that Claude Desktop's JSON file cannot contain `url`, +Windsurf and Antigravity require `serverUrl`, and a Cursor remote entry must not contain `type`. + +Debug with: + +```bash +claude --debug +claude mcp list + +PLANE_API_KEY= PLANE_WORKSPACE_SLUG= uvx plane-mcp-server stdio + +curl -X POST http://localhost:8211/http/mcp + +rm -rf ~/.mcp-auth +``` + +The local HTTP request should return either `401` or an MCP response. + +## FAQ + +::: details Which Plane plans work? +The server follows your Plane plan and role. A plan-gated action returns a message naming the unavailable feature. +::: + +::: details Is the server free? +The MIT-licensed server is free to use. The Plane features it can access follow your Plane plan. +::: + +::: details Does it work with self-hosted Plane? +Yes. Use stdio with `PLANE_BASE_URL`, or [deploy your own MCP server](/dev-tools/mcp-server-self-host). +::: + +::: details Is there a read-only mode? +There is no separate read-only endpoint. Use your client's tool allow-list; read-only tools are annotated with +`readOnlyHint`. +::: + +::: details Can I limit which tools are available? +Yes. Use the client's tool allow-list or deny-list. +::: + +::: details How do epics work? +An epic is a work item whose type is "Epic". Follow the [epics recipe](/dev-tools/mcp-server-tools#epics). +::: + +::: details Does it use Plane AI credits? +No. The MCP server calls Plane's API directly; the AI model belongs to your MCP client. +::: + +::: details Where does my data go? +The hosted server proxies requests to `api.plane.so`. Self-host the MCP server if you need full infrastructure +control. +::: + +## See also + +- [Tool reference](/dev-tools/mcp-server-tools) +- [Self-host the MCP server](/dev-tools/mcp-server-self-host) +- [Short setup guide](https://docs.plane.so/ai/mcp-server) +- [Plane MCP server on GitHub](https://github.com/makeplane/plane-mcp-server) +- [Plane Query Language](https://docs.plane.so/core-concepts/issues/plane-query-language) diff --git a/apps/developer-docs/docs/dev-tools/openapi-specification.md b/apps/developer-docs/docs/dev-tools/openapi-specification.md new file mode 100644 index 00000000..932f43f4 --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/openapi-specification.md @@ -0,0 +1,67 @@ +--- +title: OpenAPI Specification +description: Generate and access the OpenAPI 3.0 specification for the Plane public REST API using drf-spectacular. +keywords: plane, developer tools, openapi, api specification, swagger, redoc, drf-spectacular, api documentation +--- + +# OpenAPI Specification + +Plane uses [drf-spectacular](https://drf-spectacular.readthedocs.io/) to generate an OpenAPI 3.0 specification for the public REST API (`/api/v1/`). The feature is **disabled by default** and must be explicitly enabled. + +## Enable the OpenAPI spec + +Add the following to your `.env` file (at the project root or `apps/api/.env`): + +```ini +ENABLE_DRF_SPECTACULAR=1 +``` + +Then restart the API server so it picks up the new variable. + +| Variable | Required value | Default | Description | +| ------------------------ | -------------- | ------- | ------------------------------------------------------------ | +| `ENABLE_DRF_SPECTACULAR` | `1` | `0` | Activates drf-spectacular and registers the schema endpoints | + +No other environment variables are needed — everything else (schema path prefix, tags, auth schemes, servers) is pre-configured in `apps/api/plane/settings/openapi.py`. + +## Access the OpenAPI spec + +> Replace `{domain_name}` below with your self-hosted Plane domain (e.g. `plane.example.com`). + +Once the API server is running with the variable enabled, three endpoints are available: + +| Endpoint | URL | Description | +| ----------------------------- | ---------------------------------------------- | -------------------------- | +| `GET /api/schema/` | `https://{domain_name}/api/schema/` | Raw OpenAPI schema (YAML) | +| `GET /api/schema/swagger-ui/` | `https://{domain_name}/api/schema/swagger-ui/` | Interactive Swagger UI | +| `GET /api/schema/redoc/` | `https://{domain_name}/api/schema/redoc/` | ReDoc documentation viewer | + +## Download the OpenAPI spec + +### Browser + +Open `https://{domain_name}/api/schema/` and save the page. The default format is YAML. + +For JSON, append the `format` query parameter: + +```text +https://{domain_name}/api/schema/?format=openapi-json +``` + +### curl + +```bash +# YAML +curl -o openapi.yaml https://{domain_name}/api/schema/ + +# JSON +curl -o openapi.json https://{domain_name}/api/schema/?format=openapi-json +``` + +### Management command (offline, no running server required) + +```bash +# From apps/api/ +ENABLE_DRF_SPECTACULAR=1 python manage.py spectacular --file openapi.yaml +ENABLE_DRF_SPECTACULAR=1 python manage.py spectacular --file openapi.json --format openapi-json +``` diff --git a/apps/developer-docs/docs/dev-tools/plane-compose.md b/apps/developer-docs/docs/dev-tools/plane-compose.md new file mode 100644 index 00000000..1a92ee08 --- /dev/null +++ b/apps/developer-docs/docs/dev-tools/plane-compose.md @@ -0,0 +1,1479 @@ +--- +title: Plane Compose +description: Define Plane projects, workflows, and work items in YAML files. Version control your project structure and sync bidirectionally with Plane using the command line. +keywords: plane compose, plane yaml, infrastructure as code, plane cli, plane project configuration, plane version control, plane sync, plane command line tool +--- + +# Plane Compose + +Plane Compose is a command-line tool that lets you define and manage Plane projects using YAML configuration files. Think of it as "project as code", you write your project structure, schema, and work items in files, version control them with Git, and sync them with Plane. + +## Prerequisites + +- Python 3.10 or later. Verify with `python3 --version`. +- pipx. Plane Compose is distributed as a Python package and pipx is the recommended installer. If you do not have pipx: + +```bash +# macOS +brew install pipx +pipx ensurepath + +# Linux / Windows (via pip) +pip install --user pipx +pipx ensurepath +``` + +- A Plane account with access to at least one workspace. +- An API token. You will need it during authentication. + +## Install Plane Compose + +```bash +pipx install plane-compose +``` + +The package is published at [https://pypi.org/project/plane-compose/](https://pypi.org/project/plane-compose/). + +To upgrade to the latest version: + +```bash +pipx upgrade plane-compose +``` + +## Authenticate + +```bash +plane auth login +``` + +You will be prompted for: + +- **Server URL** - leave blank for `https://api.plane.so`; enter your instance URL if self-hosted +- **Auth type** - `pat` for a Personal Access Token, `workspace` for a workspace-scoped token +- **Token** - your API key, generated at `https://app.plane.so//settings/account/api-tokens/` +- **Workspace** - your workspace slug (the URL segment after `app.plane.so/`) +- **Connection name** - a label for this connection (e.g. `personal`, `work`, `staging`), leave blank for default; used to identify it in `plane auth list-connections` + +Verify it worked: + +```bash +plane auth list-connections +``` + +To add credentials for a second workspace or a different Plane instance, run `plane auth login` again. Each login creates a separate connection. Link a workspace to a specific connection: + +```bash +plane auth connect-workspace --connection +``` + +Remove a connection: + +```bash +plane auth logout +``` + +## Project operations + +### Start a new project + +```bash +plane init --workspace +``` + +This creates the project directory with the given key and generates the full file structure inside it. + +To start from a template instead of the default schema: + +```bash +plane init --workspace --template ./templates/standard + +# or a Git URL: +plane init --workspace \ + --template https://github.com///templates/ +``` + +Before pushing, edit your schema files to define your project's structure. See the **Define your project schema** guide below. + +Then push the schema to create the project in Plane: + +```bash +cd +plane schema push +``` + +`plane.yaml` is updated with the project UUID after this runs. + +### Define your project schema + +After `plane init`, the `schema/` directory contains default files. Edit them to match your project's actual structure before pushing. + +**Edit the files in this order** - each file can only reference names defined in the files before it. + +1. **Define your states** + +Open `schema/states.yaml`. Every state belongs to one of five groups: `backlog`, `unstarted`, `started`, `completed`, `cancelled`. Exactly one state should have `is_default: true`. + +```yaml +states: + Backlog: + group: backlog + color: "#858585" + is_default: true + allow_issue_creation: true + In Progress: + group: started + color: "#f59e0b" + Done: + group: completed + color: "#22c55e" + Cancelled: + group: cancelled + color: "#ef4444" +``` + +See the `schema/states.yaml` reference for all fields. + +2. **Define your labels** + +Open `schema/labels.yaml`. Labels are flat - no nesting. + +```yaml +labels: + - name: backend + color: "#3b82f6" + - name: frontend + color: "#8b5cf6" +``` + +3. **Define your workflows** + +Open `schema/workflows.yaml`. A workflow ties a set of states together and optionally restricts which transitions are allowed. Use only state names defined in `schema/states.yaml`. + +```yaml +workflows: + default: + is_active: true + work_item_types: + - Story + - Bug + states: + - Backlog + - In Progress + - Done + - Cancelled +``` + +Without a `transitions` block, any state change is permitted. See the `schema/workflows.yaml` reference for transition and approval syntax. + +4. **Define your work item types** + +Open `schema/types.yaml`. Each type references a workflow by name. Use only workflow names defined in `schema/workflows.yaml`. + +```yaml +work_item_types: + Story: + description: A unit of user-facing work + workflow: default + is_epic: false + Bug: + description: A defect requiring correction + workflow: default +``` + +To add custom properties to a type, see the `schema/types.yaml` reference. + +5. **Set your default type in `plane.yaml`** + +Open `plane.yaml` and set `defaults.type` to the type name used when a work item does not specify one: + +```yaml +defaults: + type: Story + workflow: default +``` + +6. **Toggle features** + +Open `schema/features.yaml` and disable features your project does not need. Disabling a feature hides it from the Plane UI and causes Plane Compose to skip its corresponding work file on push and pull. + +```yaml +features: + cycles: true + modules: true + pages: false +``` + +7. **Validate** + +Check for errors without making any API calls: + +```bash +plane schema validate +``` + +Fix any reported errors before proceeding. + +8. **Push the schema** + +Then push the schema to create the project in Plane: + +```bash +cd my-project +plane schema push +``` + +`plane.yaml` is updated with the project UUID after this runs. + +This creates the project in Plane if it does not exist yet and pushes your types, states, labels, and workflows. `plane.yaml` is updated with the project UUID after this runs. + +### Clone an existing project + +Use this when the project already exists in Plane and you want to manage it locally. + +```bash +plane clone --workspace +``` + +:::info +Use `--connection ` if the workspace slug is common across multiple connections. +::: + +This downloads `plane.yaml`, all schema files, and all work files into a new local directory. The schema files will reflect what is currently in Plane, you can edit them and push changes back. + +### Push changes to Plane + +`plane push` pushes everything - schema and work files - in the correct order. `plane schema push` pushes schema only and is equivalent to `plane push --schema-only`. Use `plane schema push` when you are setting up a project for the first time and have no work items yet, or when you want to push schema changes without touching work items. Use `plane push` for all other cases. + +From inside the project directory: + +```bash +plane push +``` + +Preview what will change before pushing: + +```bash +plane push --dry-run +``` + +If you have only changed schema files: + +```bash +plane push --schema-only +``` + +If you have only changed work items: + +```bash +plane push --work-only +``` + +### Pull remote changes from Plane + +Use this when changes have been made in Plane (via the UI or by other users) and you want to bring them into your local files. + +```bash +plane pull +``` + +To keep local additions and apply remote changes without losing local-only items: + +```bash +plane pull --merge +``` + +To overwrite local files entirely with what is in Plane: + +```bash +plane pull --force +``` + +### Import schema changes made in Plane + +Use this when someone has modified work item types, states, labels, or workflows directly in the Plane UI and your local schema files are now out of sync. + +To reconnect local names to their remote IDs without changing any YAML (safe, no file changes): + +```bash +plane schema import +``` + +To add items that exist in Plane but are absent from your local files, without touching what you already have: + +```bash +plane schema import --merge +``` + +To replace your local schema files entirely with whatever is in Plane: + +```bash +plane schema import --force +``` + +### Upgrade a project to a new template version + +Use this when your team has updated the standard template and you want to bring an existing project in line with it. + +Preview the changes first: + +```bash +plane upgrade --template https://github.com///templates/ --dry-run +``` + +Apply the upgrade: + +```bash +plane upgrade --template https://github.com///templates/ +``` + +The template merges over your local schema. Items unique to your project are preserved; conflicts are resolved in favour of the template. + +### Run Plane Compose in CI/CD + +Authenticate non-interactively using flags: + +```bash +plane auth login \ + --server-url https://api.plane.so \ + --auth-type pat \ + --token "$PLANE_TOKEN" \ + --workspace +``` + +Push without prompts and with a machine-readable exit code: + +```bash +plane push --force --no-conflict-check --exit-code +``` + +Exit code values: `0` - no changes needed, `1` - error, `2` - changes were applied. + +## Workspace operations + +### Manage workspace configuration + +Clone workspace-level configuration to a local directory: + +```bash +plane ws clone -c +``` + +After making changes locally, push them to Plane: + +```bash +plane ws push +``` + +To pull the latest workspace state from Plane: + +```bash +plane ws pull +``` + +To pull and preserve local additions: + +```bash +plane ws pull --merge +``` + +### Work with multiple projects + +From a directory containing multiple project subdirectories: + +```bash +plane push --all +plane pull --all +plane status --all +``` + +To limit to a specific workspace: + +```bash +plane push --all --workspace +``` + +To filter by project name pattern: + +```bash +plane push --all --filter "" +``` + +## Recover from sync problems + +**State file deleted or corrupted:** + +```bash +plane schema import # reconnects schema names to remote IDs +plane pull # restores work item entries +``` + +**A specific item is stuck or needs to be re-created:** + +```bash +plane state remove types. +plane state remove work_items. +``` + +**All work items need to be re-pushed:** + +```bash +plane state clear-items +plane push +``` + +**A push was interrupted and some items failed:** + +```bash +plane push --resume +``` + +**Diagnosing what went wrong:** + +```bash +plane --debug push +tail -f ~/.config/plane-compose/plane.log +``` + +--- + +## Reference + +### CLI commands + +--- + +#### `plane init` + +Initialises a new project directory with the standard file structure: `plane.yaml`, `schema/`, `work/`, and `.plane/state.json`. If a template is specified, schema and work files are pre-populated from the template source. If called without arguments, the command runs interactively and prompts for workspace and project values. + +``` +plane init [PROJECT] [--workspace WS] [--connection CONN] + [--path PATH] [--template TEMPLATE] +``` + +| Option | Description | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PROJECT` | Name of the directory to create and the project key written into `plane.yaml` under `project.key`. | +| `--workspace WS` | Slug of the Plane workspace this project belongs to. Written into `plane.yaml`. | +| `--connection CONN` | ID of the connection to use for API calls during initialisation. Defaults to the connection linked to the workspace. | +| `--path PATH` | Parent directory in which to create the project folder. Defaults to the current working directory. | +| `--template TEMPLATE` | Source for pre-populating the schema and work files. Accepts a built-in name (e.g. `default`), a local filesystem path, a Git HTTPS URL, or a Git SSH URL. The resolved value is written to `plane.yaml` under `template` so that `plane upgrade` can reference it later. | + +--- + +#### `plane auth login` + +Stores a new set of credentials as a connection in `~/.config/plane-compose/config.json` and links it to a workspace. When all flags are provided, the command runs non-interactively, making it suitable for CI/CD pipelines. On success, prints the generated connection ID. + +``` +plane auth login [--connection CONN] [--server-url URL] [--auth-type TYPE] + [--token TOKEN] [--workspace WS] +``` + +| Option | Description | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `--connection CONN` | Name or ID to assign to this connection. Used to identify it in `plane auth list-connections` and to reference it with `--connection` in other commands. If omitted, a name is prompted interactively. | +| `--server-url URL` | Base URL of the Plane API. Defaults to `https://api.plane.so`. Set this to your instance URL when using a self-hosted deployment. | +| `--auth-type TYPE` | Token type. `pat` for a Personal Access Token scoped to a user. `workspace` for a workspace-scoped token. | +| `--token TOKEN` | The API token value. | +| `--workspace WS` | Workspace slug to associate with this connection. The association is stored so that commands can resolve credentials from `plane.yaml` automatically. | + +--- + +#### `plane auth logout` + +Removes a stored connection and all its associated workspace links from `~/.config/plane-compose/config.json`. This operation is irreversible without re-authenticating. + +``` +plane auth logout CONNECTION_ID [--force] +``` + +| Option | Description | +| --------------- | -------------------------------------------------------------------------- | +| `CONNECTION_ID` | ID of the connection to remove, as shown by `plane auth list-connections`. | +| `--force` | Skips the confirmation prompt before deletion. | + +--- + +#### `plane auth list-connections` + +Prints all stored connections with their server URL, auth type, and linked workspaces. The default workspace is marked with ★. Aliases: `whoami`, `connections`, `list`. + +``` +plane auth list-connections +``` + +--- + +#### `plane auth connect-workspace` + +Associates a workspace slug with an existing connection. After this, any command targeting that workspace will use the specified connection's credentials without requiring an explicit `--connection` flag. + +``` +plane auth connect-workspace WORKSPACE_SLUG --connection CONN_ID +``` + +| Option | Description | +| ---------------------- | --------------------------------------------- | +| `WORKSPACE_SLUG` | The Plane workspace slug to link. | +| `--connection CONN_ID` | ID of the connection to link it to. Required. | + +--- + +#### `plane auth disconnect-workspace` + +Removes the association between a workspace slug and a connection. After this, commands targeting that workspace will require an explicit `--connection` flag or re-authentication. + +``` +plane auth disconnect-workspace WORKSPACE_SLUG [--connection CONN_ID] +``` + +| Option | Description | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| `WORKSPACE_SLUG` | The workspace slug to unlink. | +| `--connection CONN_ID` | ID of the connection to unlink from. If omitted and the workspace has only one associated connection, that connection is unlinked automatically. | + +--- + +#### `plane schema validate` + +Checks all schema files in the project for structural errors, unknown field types, missing required fields, and invalid references. Runs entirely offline - no API connection is made. Exits with a non-zero code if any errors are found. + +``` +plane schema validate [PROJECT] [--path PATH] +``` + +| Option | Description | +| ------------- | ------------------------------------------------------------ | +| `PROJECT` | Project key or directory. Defaults to the current directory. | +| `--path PATH` | Explicit filesystem path to the project root. | + +--- + +#### `plane schema push` + +Pushes local schema files to Plane. Creates the project in Plane if it does not already exist. Applies changes to work item types, states, workflows, and labels in the order required by the Plane API. On first push, writes the project UUID back into `plane.yaml`. Updates `.plane/state.json` with remote ID mappings for all pushed schema items. + +``` +plane schema push [PROJECT] [--path PATH] [--dry-run] [--force] +``` + +| Option | Description | +| ------------- | ----------------------------------------------------------------------------------------------- | +| `PROJECT` | Project key or directory. Defaults to the current directory. | +| `--path PATH` | Explicit filesystem path to the project root. | +| `--dry-run` | Computes and displays the full change plan without making any API calls or modifying any files. | +| `--force` | Skips the interactive confirmation prompt before applying changes. | + +--- + +#### `plane schema import` + +Reads the current schema from the Plane remote and reconciles it with local files. Without flags, only `.plane/state.json` is updated - no YAML files are modified. This is the safe mode for reconnecting local names to remote IDs after out-of-band changes. + +``` +plane schema import [PROJECT] [--path PATH] [--merge] [--force] +``` + +| Option | Description | +| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PROJECT` | Project key or directory. Defaults to the current directory. | +| `--path PATH` | Explicit filesystem path to the project root. | +| `--merge` | Writes remote schema items that are absent from local files into the appropriate YAML files. Existing local items are not modified. Additive only. | +| `--force` | Replaces the contents of local schema files entirely with what is returned from the Plane API. Any local-only items are lost. | + +--- + +#### `plane schema diff` + +Fetches the current schema from Plane and compares it to local schema files. Prints a structured diff showing which types, states, workflows, and labels differ. Makes no changes to local files or the remote. + +``` +plane schema diff [PROJECT] [--path PATH] +``` + +| Option | Description | +| ------------- | ------------------------------------------------------------ | +| `PROJECT` | Project key or directory. Defaults to the current directory. | +| `--path PATH` | Explicit filesystem path to the project root. | + +--- + +#### `plane push` + +Pushes schema and work data to Plane in dependency order: schema first (types, states, workflows, labels), then work items, then cycles and modules, then milestones. Skips items whose content hash matches the hash stored in `.plane/state.json`. Updates state after each successful push. If the schema push fails, work data push does not proceed. + +``` +plane push [PROJECT] [--path PATH] [--connection CONN] + [--dry-run] [--force] [--schema-only] [--work-only] + [--skip SECTION] [--all] [--workspace WS] [--filter PATTERN] + [--no-conflict-check] [--exit-code] [--resume] +``` + +| Option | Description | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `PROJECT` | Project key or directory. Defaults to the current directory. | +| `--path PATH` | Explicit filesystem path to the project root. | +| `--connection CONN` | Overrides the connection resolved from `plane.yaml` for this invocation only. | +| `--dry-run` | Computes and displays the full change plan without making any API calls or writing to state. | +| `--force` | Skips the interactive confirmation prompt before applying changes. | +| `--schema-only` | Pushes only schema files. Equivalent to running `plane schema push`. Skips `workitems`, `cycles`, `modules`, and `milestones`. | +| `--work-only` | Skips the schema push phase and pushes only work files. Requires schema to already be in sync. | +| `--skip SECTION` | Excludes a specific section from the push. Repeatable. Valid values: `workitems`, `cycles`, `modules`, `milestones`. | +| `--all` | Discovers all project directories under the current directory and pushes each one. | +| `--workspace WS` | When used with `--all`, restricts discovery to projects belonging to this workspace. | +| `--filter PATTERN` | When used with `--all`, applies a glob pattern to filter project directory names. | +| `--no-conflict-check` | Skips the pre-push API call that detects remote conflicts. Reduces API usage. Recommended for CI/CD pipelines where conflicts are not expected. | +| `--exit-code` | Returns a differentiated exit code: `0` if no changes were needed, `1` on error, `2` if changes were successfully applied. Useful for scripting and CI gate logic. | +| `--resume` | Reads the failure log from the previous push and retries only the items that failed. Items that succeeded in the previous run are not re-pushed. | + +--- + +#### `plane pull` + +Fetches schema and work data from Plane and writes it to local files. Without flags, overwrites local files with remote content after prompting for confirmation. Updates `.plane/state.json` with the latest remote IDs and content hashes. + +``` +plane pull [PROJECT] [--path PATH] [--connection CONN] + [--merge] [--force] [--schema-only] [--work-only] + [--skip SECTION] [--with-properties] [--no-properties] + [--all] [--workspace WS] [--filter PATTERN] +``` + +| Option | Description | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `PROJECT` | Project key or directory. Defaults to the current directory. | +| `--path PATH` | Explicit filesystem path to the project root. | +| `--connection CONN` | Overrides the connection resolved from `plane.yaml` for this invocation only. | +| `--merge` | Applies remote changes to local files while preserving items that exist locally but not remotely. Local-only items are not deleted. | +| `--force` | Overwrites local files with remote content without prompting. Local-only items are lost. | +| `--schema-only` | Pulls only schema files. Skips `workitems`, `cycles`, `modules`, and `milestones`. | +| `--work-only` | Pulls only work files. Skips schema. | +| `--skip SECTION` | Excludes a specific section from the pull. Repeatable. Valid values: `workitems`, `cycles`, `modules`, `milestones`. | +| `--with-properties` | Includes custom property values in the pulled work items. Enabled by default. | +| `--no-properties` | Excludes custom property values from pulled work items. The `properties` map is omitted from each work item in the output file. | +| `--all` | Discovers all project directories under the current directory and pulls each one. | +| `--workspace WS` | When used with `--all`, restricts discovery to projects belonging to this workspace. | +| `--filter PATTERN` | When used with `--all`, applies a glob pattern to filter project directory names. | + +--- + +#### `plane clone` + +Downloads a complete Plane project - including `plane.yaml`, all schema files, and all work files - into a new local directory. Initialises `.plane/state.json` with the remote IDs of all cloned items. The project must already exist in Plane. + +``` +plane clone PROJECT [--directory DIR] [--path PATH] + [--workspace WS] [--connection CONN] + [--schema-only] [--skip SECTION] [--with-properties] +``` + +| Option | Description | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PROJECT` | Project identifier. Accepts three formats: `workspace/KEY` shorthand (e.g. `myteam/API`), a project key with `--workspace` flag, or a UUID with `--workspace` flag. | +| `--directory DIR` | Name of the local directory to create. Defaults to the project key. | +| `--path PATH` | Parent directory in which to create the project folder. Defaults to the current working directory. | +| `--workspace WS` | Workspace slug. Required when `PROJECT` is a key or UUID rather than a shorthand. | +| `--connection CONN` | Overrides the connection resolved from the workspace for this invocation only. | +| `--schema-only` | Downloads schema files only. Skips `workitems`, `cycles`, `modules`, and `milestones`. | +| `--skip SECTION` | Excludes a specific section from the clone. Repeatable. Valid values: `workitems`, `cycles`, `modules`, `milestones`. | +| `--with-properties` | Includes custom property values in the cloned work items. Enabled by default. | + +--- + +#### `plane diff` + +Fetches work items from Plane and compares them to local work files. Classifies each item into one of six categories and prints a structured report. Makes no changes to local files or the remote. + +``` +plane diff [PROJECT] [--path PATH] [--connection CONN] +``` + +| Option | Description | +| ------------------- | ----------------------------------------------------------------------------- | +| `PROJECT` | Project key or directory. Defaults to the current directory. | +| `--path PATH` | Explicit filesystem path to the project root. | +| `--connection CONN` | Overrides the connection resolved from `plane.yaml` for this invocation only. | + +**Output categories:** + +| Category | Meaning | +| ----------------- | ------------------------------------------------------------------------------------------------ | +| `new_local` | Item exists in local files but has no corresponding remote record. | +| `modified_local` | Item exists both locally and remotely, and the local version differs from the remote. | +| `modified_remote` | Item exists both locally and remotely, and the remote version differs from what was last synced. | +| `conflicts` | Item has been modified both locally and remotely since the last sync. | +| `in_sync` | Local and remote versions are identical. | +| `deleted_remote` | Item has been deleted from Plane but still exists in local files. | + +--- + +#### `plane validate` + +Validates work item files against the project schema. Checks for unknown type names, unknown state names, unknown label names, invalid priority values, duplicate `id` values, malformed dates, and missing required fields. By default, fetches the current schema from Plane to validate against. Exits with a non-zero code if any errors are found. + +``` +plane validate [PATH] [--offline] [--json] +``` + +| Option | Description | +| ----------- | ----------------------------------------------------------------------------------------------------------- | +| `PATH` | Filesystem path to the project root. Defaults to the current directory. | +| `--offline` | Skips the API call to fetch the remote schema. Validates only against local schema files. | +| `--json` | Outputs validation errors as a JSON array instead of formatted text. Useful for scripting and CI pipelines. | + +--- + +#### `plane status` + +Reads `.plane/state.json` and the local work files to produce a summary of the project's sync state: schema sync status, number of work items pending push, number of items in sync, and the timestamp of the last successful push. Does not make any API calls. + +``` +plane status [PATH] [--all] [--workspace WS] [--filter PATTERN] [--json] +``` + +| Option | Description | +| ------------------ | ------------------------------------------------------------------------------------------ | +| `PATH` | Filesystem path to the project root. Defaults to the current directory. | +| `--all` | Discovers all project directories under the current directory and reports status for each. | +| `--workspace WS` | When used with `--all`, restricts discovery to projects belonging to this workspace. | +| `--filter PATTERN` | When used with `--all`, applies a glob pattern to filter project directory names. | +| `--json` | Outputs the status report as JSON. | + +--- + +#### `plane upgrade` + +Applies a template to an existing project's schema. Pulls the latest schema from Plane first (unless `--skip-pull` is set), then computes a three-way merge between the current local schema, the current remote schema, and the template. Items present only in the template are added. Items in conflict between template and local are resolved in favour of the template. Items present only locally are preserved. Presents a plan before applying. + +``` +plane upgrade [PROJECT] --template TEMPLATE [--path PATH] + [--include-data] [--dry-run] [--force] + [--skip-pull] [--schema-only] +``` + +| Option | Description | +| --------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| `PROJECT` | Project key or directory. Defaults to the current directory. | +| `--template TEMPLATE` | Template source. Required. Accepts a built-in name, a local path, a Git HTTPS URL, or a Git SSH URL. | +| `--path PATH` | Explicit filesystem path to the project root. | +| `--include-data` | Also copies cycles, modules, and work items from the template into the local work files. | +| `--dry-run` | Computes and displays the upgrade plan without modifying any files or making any API calls. | +| `--force` | Skips the interactive confirmation prompt before applying the upgrade. | +| `--skip-pull` | Skips pulling the latest schema from Plane before computing the merge. Uses the current local schema as the base. | +| `--schema-only` | Applies only schema changes from the template. Does not copy cycles, modules, or work items even if `--include-data` is set. | + +--- + +#### `plane state show` + +Prints the contents of `.plane/state.json` as a structured report showing remote ID mappings and content hashes for schema items and work items. Makes no API calls. + +``` +plane state show [PROJECT] [--path PATH] [--json] +``` + +| Option | Description | +| ------------- | ------------------------------------------------------------ | +| `PROJECT` | Project key or directory. Defaults to the current directory. | +| `--path PATH` | Explicit filesystem path to the project root. | +| `--json` | Outputs the state as raw JSON. | + +--- + +#### `plane state reset` + +Clears all entries from `.plane/state.json`. After a reset, the next `plane push` treats every local item as new and attempts to create it in Plane. Use with caution - this can result in duplicate remote items if the project already exists in Plane. + +``` +plane state reset [PROJECT] [--path PATH] [--force] +``` + +| Option | Description | +| ------------- | ------------------------------------------------------------ | +| `PROJECT` | Project key or directory. Defaults to the current directory. | +| `--path PATH` | Explicit filesystem path to the project root. | +| `--force` | Skips the confirmation prompt. | + +--- + +#### `plane state clear-items` + +Removes only the `work_items` section of `.plane/state.json`, leaving schema state intact. The next `plane push` re-pushes all work items as if they are new, but schema items are not affected. + +``` +plane state clear-items [PROJECT] [--path PATH] [--force] +``` + +| Option | Description | +| ------------- | ------------------------------------------------------------ | +| `PROJECT` | Project key or directory. Defaults to the current directory. | +| `--path PATH` | Explicit filesystem path to the project root. | +| `--force` | Skips the confirmation prompt. | + +--- + +#### `plane state remove` + +Removes a single entry from `.plane/state.json` identified by a dot-separated path. On the next push, the removed item is treated as new. + +``` +plane state remove PATH_STR [PROJECT] [--path PATH] +``` + +| Option | Description | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `PATH_STR` | Dot-separated path into the state object. For example: `types.Story` removes the Story type mapping; `states.Done` removes the Done state mapping; `work_items.AUTH-1` removes a specific work item mapping. | +| `PROJECT` | Project key or directory. Defaults to the current directory. | +| `--path PATH` | Explicit filesystem path to the project root. | + +--- + +#### `plane ws clone` + +Downloads workspace-level configuration - including `workspace.yaml`, workspace schema files, and member data - into a new local directory. Initialises `.plane/state.json` with remote ID mappings. + +``` +plane ws clone WORKSPACE [--directory DIR] [--path PATH] + [--connection CONN] [--force] [--skip SECTION] +``` + +| Option | Description | +| ------------------- | ------------------------------------------------------------------------------------- | +| `WORKSPACE` | Workspace slug to clone. | +| `--directory DIR` | Name of the local directory to create. Defaults to the workspace slug. | +| `--path PATH` | Parent directory for the workspace folder. Defaults to the current working directory. | +| `--connection CONN` | Overrides the connection resolved from the workspace slug for this invocation only. | +| `--force` | Overwrites an existing local directory without prompting. | +| `--skip SECTION` | Excludes a section from the clone. Valid values: `releases`. | + +--- + +#### `plane ws pull` + +Fetches workspace-level configuration from Plane and writes it to local workspace files. Behaviour with respect to local content is controlled by `--merge` and `--force`, identical in semantics to `plane pull`. + +``` +plane ws pull [WORKSPACE] [--path PATH] [--merge] [--force] [--skip SECTION] +``` + +| Option | Description | +| ---------------- | ---------------------------------------------------------------------------------------- | +| `WORKSPACE` | Workspace slug. Defaults to the value in `workspace.yaml`. | +| `--path PATH` | Explicit filesystem path to the workspace root. | +| `--merge` | Preserves local-only items while applying remote changes. | +| `--force` | Overwrites local files with remote content without prompting. Local-only items are lost. | +| `--skip SECTION` | Excludes a section from the pull. Valid values: `releases`. | + +--- + +#### `plane ws push` + +Pushes local workspace configuration files to Plane. Updates `.plane/state.json` with remote ID mappings for all pushed items. + +``` +plane ws push [WORKSPACE] [--path PATH] [--dry-run] [--force] +``` + +| Option | Description | +| ------------- | ------------------------------------------------------------------- | +| `WORKSPACE` | Workspace slug. Defaults to the value in `workspace.yaml`. | +| `--path PATH` | Explicit filesystem path to the workspace root. | +| `--dry-run` | Computes and displays the change plan without making any API calls. | +| `--force` | Skips the interactive confirmation prompt. | + +--- + +#### `plane ws diff` + +Fetches workspace configuration from Plane and compares it to local workspace files. Prints a structured diff. Makes no changes. + +``` +plane ws diff [WORKSPACE] [--path PATH] +``` + +| Option | Description | +| ------------- | ---------------------------------------------------------- | +| `WORKSPACE` | Workspace slug. Defaults to the value in `workspace.yaml`. | +| `--path PATH` | Explicit filesystem path to the workspace root. | + +--- + +#### `plane ws upgrade` + +Applies a template to an existing workspace's schema. Follows the same merge logic as `plane upgrade`. + +``` +plane ws upgrade [WORKSPACE] [--path PATH] [--template TEMPLATE] + [--dry-run] [--force] [--skip-pull] [--schema-only] +``` + +| Option | Description | +| --------------------- | ------------------------------------------------------------------------------------ | +| `WORKSPACE` | Workspace slug. Defaults to the value in `workspace.yaml`. | +| `--path PATH` | Explicit filesystem path to the workspace root. | +| `--template TEMPLATE` | Template source. Accepts a built-in name, local path, Git HTTPS URL, or Git SSH URL. | +| `--dry-run` | Displays the upgrade plan without applying it. | +| `--force` | Skips the confirmation prompt. | +| `--skip-pull` | Skips pulling the latest workspace schema from Plane before computing the merge. | +| `--schema-only` | Applies only schema changes. Does not copy data from the template. | + +--- + +#### `plane ws state show / reset / clear / remove` + +Manage workspace sync state in `.plane/state.json` within the workspace directory. Semantics are identical to their project-level equivalents. + +``` +plane ws state show [WORKSPACE] [--path PATH] [--json] +plane ws state reset [WORKSPACE] [--path PATH] [--force] +plane ws state clear [WORKSPACE] [--path PATH] [--force] +plane ws state remove PATH_STR [WORKSPACE] [--path PATH] +``` + +`PATH_STR` examples for workspace state: `workitemtypes.types.Task`, `members.dev@example.com`, `releases.tags.v1.0`. + +--- + +#### `plane rate stats` + +Prints the current rate limit window statistics: total requests made, requests remaining, and the time until the window resets. Reads from local counters; does not make an API call. + +``` +plane rate stats +``` + +--- + +#### `plane rate reset` + +Resets the local rate limit counters to zero. Does not affect Plane's server-side rate limiting. + +``` +plane rate reset +``` + +--- + +#### Global options + +These options are accepted by every `plane` command. + +| Option | Description | +| ----------------- | -------------------------------------------------------------------------------------------- | +| `--version`, `-V` | Prints the installed version of Plane Compose and exits. | +| `--verbose`, `-v` | Enables verbose output. Prints additional detail about each operation as it runs. | +| `--debug` | Enables debug-level logging. Writes a structured log to `~/.config/plane-compose/plane.log`. | + +--- + +### Configuration files + +#### Project directory structure + +``` +/ +├── plane.yaml +├── schema/ +│ ├── types.yaml +│ ├── states.yaml +│ ├── workflows.yaml +│ ├── labels.yaml +│ ├── features.yaml +│ ├── members.yaml # populated on pull; read-only +│ ├── workitem_templates.yaml # populated on pull +│ └── page_templates.yaml # populated on pull +├── work/ +│ ├── workitems.yaml +│ ├── cycles.yaml +│ ├── modules.yaml +│ └── milestones.yaml +├── .plane/ +│ ├── state.json # sync state; do not edit manually +│ └── .state.lock # held during active sync operations +└── .gitignore +``` + +Workspace directory (created by `plane ws clone`): + +``` +/ +├── workspace.yaml +├── schema/ +│ └── workitem_types.yaml # workspace-level WIT definitions (Enterprise) +└── .plane/ + └── state.json +``` + +--- + +#### `plane.yaml` + +The primary configuration file for a project. Identifies the project, specifies the workspace and connection, and sets defaults used when work item fields are omitted. + +| Field | Type | Description | +| --------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `type` | string | Always `project`. Identifies this directory as a project as opposed to a workspace. | +| `workspace` | string | Slug of the Plane workspace this project belongs to. Used to resolve the correct connection from `~/.config/plane-compose/config.json`. | +| `connection` | string | Optional. ID of a specific connection to use for all operations on this project. Overrides the workspace-to-connection mapping. | +| `project.key` | string | Short project identifier, maximum 10 uppercase characters. Used as the prefix in work item sequence IDs (e.g. `API-42`). | +| `project.name` | string | Human-readable project name as displayed in Plane. | +| `project.uuid` | string | Remote UUID of the project. Populated automatically after the first `plane schema push`. Do not set manually. | +| `project.description` | string | Optional project description as displayed in Plane. | +| `project.network` | string | Visibility setting. `public` makes the project visible to all workspace members. `private` restricts visibility. Defaults to `public`. | +| `project.timezone` | string | IANA timezone string (e.g. `UTC`, `America/New_York`). Affects due date display and cycle date calculations in Plane. | +| `defaults.type` | string | Default work item type applied when a work item in `work/workitems.yaml` does not specify a `type` field. Must match a key in `schema/types.yaml`. | +| `defaults.workflow` | string | Default workflow applied when a work item type does not specify one. Must match a key in `schema/workflows.yaml`. | +| `template` | string | Source of the template used during `plane init` or `plane upgrade`. Written automatically; used by `plane upgrade` to know where to pull the template from. | + +```yaml +type: project +workspace: myteam +connection: conn-1 +project: + key: API + name: API Project + uuid: abc-123-def-456 + description: "" + network: public + timezone: UTC +defaults: + type: Story + workflow: default +template: default +``` + +--- + +#### `workspace.yaml` + +The configuration file for a workspace directory created by `plane ws clone`. Identifies the workspace and connection, and contains workspace-level member data. + +| Field | Type | Description | +| ------------------------ | ------ | --------------------------------------------------------------------------------------------------------------- | +| `workspace` | string | Slug of the Plane workspace. Used to resolve the correct connection from `~/.config/plane-compose/config.json`. | +| `connection` | string | Optional. ID of a specific connection to pin to this workspace directory. | +| `workspace_features` | map | Optional. Workspace-level feature flag overrides. Keys are feature names; values are `true` or `false`. | +| `members` | list | Workspace members. Populated automatically on `plane ws pull`. Read-only - do not edit manually. | +| `members[].id` | string | Remote UUID of the member. | +| `members[].email` | string | Email address of the member. | +| `members[].display_name` | string | Display name of the member as shown in Plane. | + +```yaml +workspace: myteam +connection: conn-1 +workspace_features: + epics: true +members: + - id: abc-123 + email: dev@example.com + display_name: Dev User +``` + +--- + +#### `schema/types.yaml` + +Defines the work item types available in the project. Each key is the type name. Types control which workflow applies, whether the type can act as an parent, its icon in the Plane UI, and which custom properties are attached. + +| Field | Type | Description | +| ----------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `` | map | Top-level key is the type name as referenced in work items and workflows. | +| `description` | string | Human-readable description of the type, displayed in the Plane UI. | +| `workflow` | string | Name of the workflow from `schema/workflows.yaml` that governs state transitions for this type. | +| `is_epic` | boolean | When `true`, work items of this type can act as parents for other work items, enabling hierarchy. Defaults to `false`. Requires `epics: true` in `schema/features.yaml`. | +| `logo_props.icon` | string | Name of the icon displayed for this type in the Plane UI. | +| `logo_props.background_color` | string | Hex colour string for the icon background (e.g. `#6366f1`). | +| `properties` | list | List of custom property definitions attached to this type. | +| `properties[].name` | string | Property name as displayed in the Plane UI and as the key in work item `properties` maps. | +| `properties[].type` | string | Data type of the property. See property type table below. | +| `properties[].required` | boolean | When `true`, the property must have a value before a work item of this type can be marked as done. | +| `properties[].options` | list | List of option strings. Required when `type` is `option`. | +| `properties[].is_multi` | boolean | When `true` and `type` is `option`, the property accepts multiple selected values. Defaults to `false`. | + +**Property types:** + +| Type | Description | Notes | +| ---------------- | ------------------------------------------------- | ------------------------------------------------------------------------------ | +| `text` | Single or multi-line text input. | Alias: `string`. | +| `number` | Integer numeric value. | | +| `decimal` | Floating-point numeric value. | | +| `date` | Calendar date in `YYYY-MM-DD` format. | | +| `datetime` | Date and time in ISO 8601 format. | | +| `option` | Dropdown selector, single or multi-select. | Alias: `enum`. Requires `options` list. Add `is_multi: true` for multi-select. | +| `boolean` | True/false checkbox. | | +| `url` | URL string with validation. | | +| `email` | Email address string with validation. | | +| `member_picker` | Reference to one or more Plane workspace members. | | +| `relation` | Reference to another work item or user. | Set `relation_type: user` or `relation_type: issue`. | +| `release_picker` | Reference to a Plane release tag. | Populated on pull; push is blocked by the Plane API. Read-only in practice. | +| `file` | File attachment reference. | | +| `formula` | Computed value derived from other fields. | Push not yet supported. | + +```yaml +work_item_types: + Story: + description: A unit of user-facing work + workflow: default + is_epic: false + logo_props: + icon: bookmark + background_color: "#6366f1" + properties: + - name: Severity + type: option + required: false + options: + - Minor + - Major + - Critical + is_multi: false + Bug: + description: A defect requiring correction + workflow: default + properties: + - name: Reproducible + type: boolean + required: true +``` + +--- + +#### `schema/states.yaml` + +Defines the states available in the project. Each key is the state name as referenced in work items and workflows. States are grouped into one of five standard Plane groups that determine how Plane treats them in reporting and cycle calculations. + +| Field | Type | Description | +| ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `` | map | Top-level key is the state name, referenced in `work/workitems.yaml` and `schema/workflows.yaml`. | +| `group` | string | Functional category of the state. One of: `backlog`, `unstarted`, `started`, `completed`, `cancelled`. Determines how Plane aggregates and reports on work items in this state. | +| `color` | string | Hex colour string used to represent this state in the Plane UI (e.g. `#22c55e`). | +| `allow_issue_creation` | boolean | When `true`, new work items can be created directly in this state. Defaults to `true`. | +| `is_default` | boolean | When `true`, this state is assigned to new work items that do not specify a state. Only one state per project should have `is_default: true`. | + +```yaml +states: + Backlog: + group: backlog + color: "#858585" + allow_issue_creation: true + is_default: true + Todo: + group: unstarted + color: "#d1d5db" + In Progress: + group: started + color: "#f59e0b" + Done: + group: completed + color: "#22c55e" + Cancelled: + group: cancelled + color: "#ef4444" +``` + +--- + +#### `schema/workflows.yaml` + +Defines the workflows available in the project. Each workflow associates a set of states with a set of work item types and optionally restricts which state transitions are permitted. When no transitions are defined, any state change is allowed. + +| Field | Type | Description | +| ------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `` | map | Top-level key is the workflow name, referenced from `schema/types.yaml` and `plane.yaml`. | +| `description` | string | Human-readable description of the workflow. | +| `is_active` | boolean | When `false`, the workflow is defined but not enforced by Plane. | +| `work_item_types` | list | Names of work item types from `schema/types.yaml` that this workflow governs. | +| `states` | list | Names of states from `schema/states.yaml` that are valid within this workflow. | +| `transitions` | map | Optional. Defines which state transitions are permitted. Keys are source state names; values are lists of allowed target transitions. When absent, all transitions between the workflow's states are permitted. | +| `transitions.[].to` | string | Name of the target state for this transition. Must be in the workflow's `states` list. | +| `transitions.[].type` | string | `transition` for a direct state change. `approval` for a change that requires approval before completing. | +| `transitions.[].required_approvals` | integer | For `approval` type only. Number of approvers required. `null` means all listed approvers must approve. | +| `transitions.[].approvers` | list | For `approval` type only. Email addresses of Plane members who can approve the transition. | + +```yaml +workflows: + default: + description: Standard engineering workflow + is_active: true + work_item_types: + - Story + - Bug + states: + - Backlog + - Todo + - In Progress + - Done + transitions: + Todo: + - to: In Progress + type: transition + In Progress: + - to: Done + type: approval + required_approvals: 1 + approvers: + - lead@example.com + - to: Todo + type: transition +``` + +--- + +#### `schema/labels.yaml` + +Defines the labels available in the project. Labels are flat - no nesting. Each entry in the list defines one label. + +| Field | Type | Description | +| ------- | ------ | -------------------------------------------------------------------------------------------- | +| `name` | string | Label name as referenced in work item `labels` lists. | +| `color` | string | Hex colour string used to render the label chip in the Plane UI. | +| `id` | string | Remote UUID of the label. Populated automatically after the first push. Do not set manually. | + +```yaml +labels: + - name: backend + color: "#3b82f6" + - name: frontend + color: "#8b5cf6" + - name: infrastructure + color: "#10b981" +``` + +--- + +#### `schema/features.yaml` + +Controls which Plane features are enabled for the project. Disabling a feature hides it from the Plane UI and prevents Plane Compose from pushing or pulling data for that section. + +| Field | Type | Description | +| ----------------- | ------- | ---------------------------------------------------------------------------------------------------- | +| `cycles` | boolean | Enables time-boxed sprint cycles. When `false`, `work/cycles.yaml` is ignored on push and pull. | +| `modules` | boolean | Enables modules for grouping work by feature area. When `false`, `work/modules.yaml` is ignored. | +| `pages` | boolean | Enables wiki-style pages within the project. | +| `views` | boolean | Enables saved filtered views. | +| `intakes` | boolean | Enables a public intake form for submitting work items from outside the workspace. | +| `epics` | boolean | Enables work item hierarchy. Requires at least one type with `is_epic: true` in `schema/types.yaml`. | +| `work_item_types` | boolean | Enables custom work item types. When `false`, Plane uses only the default type. | +| `workflows` | boolean | Enables custom workflow enforcement. When `false`, state transitions are unrestricted. | +| `parallel_cycles` | boolean | Allows multiple active cycles to run simultaneously. | +| `project_updates` | boolean | Enables the project updates feed. | + +```yaml +features: + cycles: true + modules: true + pages: true + views: true + intakes: false + epics: true + work_item_types: true + workflows: true + parallel_cycles: false + project_updates: false +``` + +--- + +#### `work/workitems.yaml` + +Defines the work items to be synced to Plane. Contains a single top-level `workitems` key whose value is a list. Each list entry represents one work item. + +| Field | Type | Required | Description | +| -------------- | ------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `id` | string | Recommended | Stable local identifier chosen by the author. Used as the tracking key in `.plane/state.json`. If omitted, Plane Compose derives the key from a hash of the item's content - title or description changes will then generate a new key and cause a duplicate to be created instead of an update. | +| `title` | string | Yes | Work item title as displayed in Plane. | +| `type` | string | No | Name of the work item type from `schema/types.yaml`. Defaults to `defaults.type` in `plane.yaml`. | +| `state` | string | No | Name of the state from `schema/states.yaml`. Defaults to the state with `is_default: true`. | +| `priority` | string | No | Priority level. One of: `urgent`, `high`, `medium`, `low`, `none`. Defaults to `none`. | +| `labels` | list | No | List of label names from `schema/labels.yaml`. | +| `assignees` | list | No | List of email addresses of Plane workspace members to assign to this work item. | +| `watchers` | list | No | List of email addresses of Plane workspace members who receive notifications for this work item. | +| `start_date` | string | No | Planned start date in `YYYY-MM-DD` format. | +| `due_date` | string | No | Planned due date in `YYYY-MM-DD` format. | +| `description` | string | No | Full description in Markdown format. | +| `parent` | string | No | Sequence ID of the parent work item (e.g. `API-5`). Requires `epics: true` in `schema/features.yaml` and a type with `is_epic: true`. | +| `blocked_by` | list | No | List of sequence IDs of work items that must be completed before this one can begin. | +| `blocking` | list | No | List of sequence IDs of work items that cannot begin until this one is completed. | +| `duplicate_of` | string | No | Sequence ID of the work item this one duplicates. | +| `relates_to` | list | No | List of sequence IDs of work items related to this one without a specific dependency relationship. | +| `properties` | map | No | Custom property values. Keys are property names as defined in the type's `properties` list in `schema/types.yaml`. Values must match the property type. | + +```yaml +workitems: + - id: "auth-oauth" + title: Implement OAuth2 login + type: Story + state: Backlog + priority: high + labels: + - backend + assignees: + - dev@example.com + watchers: + - pm@example.com + start_date: "2026-06-01" + due_date: "2026-06-15" + description: | + Add OAuth2 authentication using the provider SDK. + parent: "API-5" + blocked_by: + - "API-3" + blocking: + - "API-9" + properties: + Severity: Major +``` + +--- + +#### `work/cycles.yaml` + +Defines time-boxed sprint cycles. The `status` field is computed by Plane based on dates relative to the current time and is read-only - do not set it manually. The `id` field is populated automatically after the first push. + +| Field | Type | Description | +| ------------- | ------ | ---------------------------------------------------------------------------------- | +| `name` | string | Cycle name as displayed in Plane. Used as the tracking key in state. | +| `description` | string | Optional description of the cycle's goal or scope. | +| `start_date` | string | Cycle start date in `YYYY-MM-DD` format. | +| `end_date` | string | Cycle end date in `YYYY-MM-DD` format. | +| `id` | string | Remote UUID of the cycle. Populated automatically after push. Do not set manually. | + +```yaml +cycles: + - name: Sprint 1 + description: Foundation sprint + start_date: "2026-06-01" + end_date: "2026-06-14" + id: abc-123 +``` + +--- + +#### `work/modules.yaml` + +Defines modules that group work by feature or initiative. The `status` field is computed by Plane and is read-only. The `id` field is populated automatically after the first push. + +| Field | Type | Description | +| ------------- | ------ | ----------------------------------------------------------------------------------- | +| `name` | string | Module name as displayed in Plane. Used as the tracking key in state. | +| `description` | string | Optional description of the module's scope. | +| `start_date` | string | Module start date in `YYYY-MM-DD` format. | +| `end_date` | string | Module end date in `YYYY-MM-DD` format. | +| `id` | string | Remote UUID of the module. Populated automatically after push. Do not set manually. | + +```yaml +modules: + - name: Authentication + description: All auth-related work items + start_date: "2026-06-01" + end_date: "2026-07-01" + id: abc-123 +``` + +--- + +#### `work/milestones.yaml` + +Defines milestones that mark significant points in the project timeline. The `id` field is populated automatically after the first push. + +| Field | Type | Description | +| ------------- | ------ | -------------------------------------------------------------------------------------------------------------------- | +| `name` | string | Milestone name as displayed in Plane. Used as the tracking key in state. Maps to the `title` field in the Plane API. | +| `target_date` | string | Target completion date in `YYYY-MM-DD` format. | +| `work_items` | list | List of work item sequence IDs (e.g. `API-1`) to associate with this milestone. | +| `id` | string | Remote UUID of the milestone. Populated automatically after push. Do not set manually. | + +```yaml +milestones: + - name: v1.0 Release + target_date: "2026-08-01" + work_items: + - "API-1" + - "API-5" + id: abc-123 +``` + +### Troubleshooting + +#### Authentication failed (401) + +The stored token is invalid, expired, or has been revoked. Remove the connection and re-authenticate: + +```bash +plane auth logout +plane auth login +``` + +#### Permission denied (403) + +The authenticated user does not have access to the requested workspace or project. Verify workspace membership with `plane auth list-connections`. Contact the workspace administrator to request access. + +#### Project not found (404) + +The `project.uuid` in `plane.yaml` does not correspond to an existing project. This occurs when the project has been deleted from Plane or when `plane.yaml` from one environment is used in another. Remove the `uuid` field from `plane.yaml` and run `plane schema push` to create a new project and update the UUID. + +#### Rate limit exceeded (429) + +Plane Compose has exceeded the API request limit for the current time window. Run `plane rate stats` to see how many requests remain and when the window resets. To reduce the request rate: + +```bash +PLANE_RATE_LIMIT_PER_MINUTE=30 plane push +``` + +#### Duplicate work items + +Work items were pushed without a stable `id` field. When the title or description was subsequently changed, the content hash changed, causing Plane Compose to treat the item as new rather than an update. A second item was created in Plane. To fix: add a stable `id` to the item in the YAML file, remove the old state entry with `plane state remove work_items.`, delete the duplicate from Plane manually, then push. + +#### State file out of sync or deleted + +```bash +plane schema import # rebuilds schema entries from remote IDs +plane pull # rebuilds work item entries from remote data +``` + +#### Push fails partway through + +If a push is interrupted before completion, the next `plane push --resume` reads the failure log written during the interrupted run and retries only the items that did not succeed. Items that pushed successfully are not re-pushed. + +--- + +## Explanation + +### Why project as code + +Project management tools like Plane are rich and powerful, but they have a structural problem: the configuration of a project - its work item types, its workflows, its state definitions - lives exclusively inside the tool. There is no file you can open, no diff you can review, no commit history you can trace. When a workflow changes, you cannot see who changed it, when, or why. When a project template drifts across teams, you have no way to detect it. When you want to spin up a new project that mirrors an existing one, you configure it manually from memory. + +Plane Compose addresses this by treating the project as an artifact that lives in your repository. The schema and work items are YAML files. Changes go through pull requests. History is in Git. + +### The local-first model + +In a bidirectional sync tool, neither side is fully in control - changes can originate anywhere and the tool tries to merge them. This creates ambiguity: if a state is renamed both locally and in the UI at the same time, which one wins? Who is responsible for the project structure? + +Plane Compose takes a deliberate position: local files are the source of truth. The Plane remote is the target. You declare what you want; Plane Compose makes it so. Remote changes do not flow back automatically - you pull them deliberately when you choose to accept them, review the diff, and commit. + +This asymmetry is a feature, not a limitation. It means the project schema has a single authoritative home: your repository. It means changes are proposed through pull requests, reviewed by teammates, and tracked in Git history. It means a new team member can understand the entire project structure by reading YAML files rather than navigating a UI. + +The tradeoff is that Plane Compose requires discipline. If your team routinely reconfigures projects through the Plane UI and rarely pulls those changes back into local files, the local files drift out of date and lose their value as the source of truth. The model works best when local files are treated as the real project definition and the UI is used for day-to-day work on individual items, not for structural changes. + +### The connection model + +The simplest possible authentication design would be a single API key stored somewhere on disk. Plane Compose uses a more structured model - connections - because a single key assumption breaks quickly in practice. + +Different workspaces may require different credentials. A developer might have access to a `myteam` workspace with a personal token and a `client-project` workspace under a separate account. A CI/CD system may use a workspace-scoped service token. A self-hosted Plane instance has a different server URL entirely. + +A connection bundles three things: a server URL, an auth type, and a token. Each connection gets a name. Workspaces are then linked to connections, so that when a command reads `workspace: myteam` from `plane.yaml`, it knows which set of credentials to use without you specifying it each time. + +This design also separates identity from configuration. `plane.yaml` contains the workspace slug - a human-readable project identity - but not the credentials. The credentials live in `~/.config/plane-compose/config.json`, separate from the repository. You can commit `plane.yaml` to Git without leaking tokens. + +### Schema and work as separate concerns + +Plane Compose separates project content into two categories with different natures and different lifecycles. + +Schema files define _what is possible_: the types of work items that exist, the states they can move through, the labels available, the features enabled. Schema changes infrequently, is owned by leads or architects, and has consequences across the entire project. + +Work files define _what is happening_: the actual items, sprints, modules, and milestones. Work changes constantly - every day, by everyone on the team. + +This distinction shapes how you use Plane Compose. Schema is the part of the project you want to version-control rigorously, review carefully, and propagate from a template. Work is the part you might generate programmatically, import from another source, or let the team manage through the Plane UI. Some teams commit both to Git. Others commit only schema and treat work items as data managed through Plane directly. Both are valid. + +The separation also clarifies the dependency direction: schema must exist before work can be pushed, because work items reference type names, state names, and label names that need to resolve to remote UUIDs. This is why `plane push` always applies schema before work. diff --git a/apps/developer-docs/docs/index.md b/apps/developer-docs/docs/index.md new file mode 100644 index 00000000..c97d145b --- /dev/null +++ b/apps/developer-docs/docs/index.md @@ -0,0 +1,113 @@ +--- +layout: doc +title: Plane Developer Documentation - API Reference & Self-Hosting Guides +description: Build integrations with Plane's REST API and deploy on your infrastructure. Complete guides for self-hosting with Docker, Kubernetes, webhooks, and OAuth apps. +keywords: plane developer docs, plane api, self-hosting plane, kubernetes deployment, docker compose, plane webhooks, plane oauth, project management api +aside: false +prev: false +next: false +copyPage: false +--- + +
+ +# Developer docs + +
Build, deploy, and integrate
+ +

Everything you need to self-host Plane, integrate with the REST API, and build powerful custom workflows.

+ +
+ + + +
+ + + + + +Deploy Plane on your infrastructure with Docker, Kubernetes, or other methods. Complete guides for configuration, authentication, and management. + + + + + +180+ endpoints to manage projects, work items, cycles, modules, and more. + + + + + +Automate workflows with real-time webhooks for project events, work item updates, and team activities. + + + + + +Build custom integrations using OAuth 2.0. Complete guides for app registration, token management, and API access. + + + + + +Connect Claude, ChatGPT, Cursor, VS Code, and other AI tools to Plane. 28 tools covering work items, cycles, releases, customers, and more. + + + + + +Create and deploy agents that work with Plane using signals, webhooks, and the REST API. + + + + + +
+ +## Quick start guides + +
+ + + + + +Get Plane running in minutes with Docker Compose. + + + + + + + +Set up authentication and connect external services to your Plane deployment. + + + + + + + +Keep your instance up to date with the latest features and security patches. + + + + + + + +
diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-Bold.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Bold.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-Bold.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Bold.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-BoldItalic.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-BoldItalic.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-BoldItalic.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-BoldItalic.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLight.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLight.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLight.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLight.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLightItalic.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLightItalic.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLightItalic.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLightItalic.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-Italic.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Italic.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-Italic.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Italic.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-Light.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Light.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-Light.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Light.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-LightItalic.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-LightItalic.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-LightItalic.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-LightItalic.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-Medium.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Medium.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-Medium.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Medium.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-MediumItalic.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-MediumItalic.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-MediumItalic.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-MediumItalic.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-Regular.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Regular.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-Regular.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Regular.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBold.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBold.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBold.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBold.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBoldItalic.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBoldItalic.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBoldItalic.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBoldItalic.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-Thin.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Thin.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-Thin.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Thin.ttf diff --git a/docs/public/fonts/IBMPlexMono/IBMPlexMono-ThinItalic.ttf b/apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ThinItalic.ttf similarity index 100% rename from docs/public/fonts/IBMPlexMono/IBMPlexMono-ThinItalic.ttf rename to apps/developer-docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ThinItalic.ttf diff --git a/docs/public/fonts/Inter/InterVariable.woff2 b/apps/developer-docs/docs/public/fonts/Inter/InterVariable.woff2 similarity index 100% rename from docs/public/fonts/Inter/InterVariable.woff2 rename to apps/developer-docs/docs/public/fonts/Inter/InterVariable.woff2 diff --git a/apps/developer-docs/docs/public/images/1.gif b/apps/developer-docs/docs/public/images/1.gif new file mode 100644 index 00000000..cfc86477 Binary files /dev/null and b/apps/developer-docs/docs/public/images/1.gif differ diff --git a/apps/developer-docs/docs/public/images/2.gif b/apps/developer-docs/docs/public/images/2.gif new file mode 100644 index 00000000..e52331d2 Binary files /dev/null and b/apps/developer-docs/docs/public/images/2.gif differ diff --git a/apps/developer-docs/docs/public/images/3.gif b/apps/developer-docs/docs/public/images/3.gif new file mode 100644 index 00000000..7ceb858e Binary files /dev/null and b/apps/developer-docs/docs/public/images/3.gif differ diff --git a/apps/developer-docs/docs/public/images/4.gif b/apps/developer-docs/docs/public/images/4.gif new file mode 100644 index 00000000..db4c1961 Binary files /dev/null and b/apps/developer-docs/docs/public/images/4.gif differ diff --git a/apps/developer-docs/docs/public/images/5.gif b/apps/developer-docs/docs/public/images/5.gif new file mode 100644 index 00000000..65cb8f1d Binary files /dev/null and b/apps/developer-docs/docs/public/images/5.gif differ diff --git a/apps/developer-docs/docs/public/images/account/account-dashboard.png b/apps/developer-docs/docs/public/images/account/account-dashboard.png new file mode 100644 index 00000000..30619a3c Binary files /dev/null and b/apps/developer-docs/docs/public/images/account/account-dashboard.png differ diff --git a/apps/developer-docs/docs/public/images/account/account-email-preferences.png b/apps/developer-docs/docs/public/images/account/account-email-preferences.png new file mode 100644 index 00000000..b49a4f0d Binary files /dev/null and b/apps/developer-docs/docs/public/images/account/account-email-preferences.png differ diff --git a/apps/developer-docs/docs/public/images/account/account-notifications.png b/apps/developer-docs/docs/public/images/account/account-notifications.png new file mode 100644 index 00000000..b9af6e45 Binary files /dev/null and b/apps/developer-docs/docs/public/images/account/account-notifications.png differ diff --git a/apps/developer-docs/docs/public/images/account/account-profile-activity.png b/apps/developer-docs/docs/public/images/account/account-profile-activity.png new file mode 100644 index 00000000..9be4c528 Binary files /dev/null and b/apps/developer-docs/docs/public/images/account/account-profile-activity.png differ diff --git a/apps/developer-docs/docs/public/images/account/account-profile-dropdown.png b/apps/developer-docs/docs/public/images/account/account-profile-dropdown.png new file mode 100644 index 00000000..dc533c07 Binary files /dev/null and b/apps/developer-docs/docs/public/images/account/account-profile-dropdown.png differ diff --git a/apps/developer-docs/docs/public/images/account/account-theme.png b/apps/developer-docs/docs/public/images/account/account-theme.png new file mode 100644 index 00000000..84b89f46 Binary files /dev/null and b/apps/developer-docs/docs/public/images/account/account-theme.png differ diff --git a/apps/developer-docs/docs/public/images/activate-license/activate-enterprise-plan.webp b/apps/developer-docs/docs/public/images/activate-license/activate-enterprise-plan.webp new file mode 100644 index 00000000..cb18e99c Binary files /dev/null and b/apps/developer-docs/docs/public/images/activate-license/activate-enterprise-plan.webp differ diff --git a/apps/developer-docs/docs/public/images/activate-license/activate-pro-license.webp b/apps/developer-docs/docs/public/images/activate-license/activate-pro-license.webp new file mode 100644 index 00000000..8c3611ba Binary files /dev/null and b/apps/developer-docs/docs/public/images/activate-license/activate-pro-license.webp differ diff --git a/apps/developer-docs/docs/public/images/activate-license/billing-and-plans-cloud.webp b/apps/developer-docs/docs/public/images/activate-license/billing-and-plans-cloud.webp new file mode 100644 index 00000000..06e4d41b Binary files /dev/null and b/apps/developer-docs/docs/public/images/activate-license/billing-and-plans-cloud.webp differ diff --git a/apps/developer-docs/docs/public/images/activate-license/copy-license-key.webp b/apps/developer-docs/docs/public/images/activate-license/copy-license-key.webp new file mode 100644 index 00000000..f4a0b34e Binary files /dev/null and b/apps/developer-docs/docs/public/images/activate-license/copy-license-key.webp differ diff --git a/apps/developer-docs/docs/public/images/activate-license/download-license.webp b/apps/developer-docs/docs/public/images/activate-license/download-license.webp new file mode 100644 index 00000000..a9b4598b Binary files /dev/null and b/apps/developer-docs/docs/public/images/activate-license/download-license.webp differ diff --git a/apps/developer-docs/docs/public/images/activate-license/enter-license-key-selfhosted.webp b/apps/developer-docs/docs/public/images/activate-license/enter-license-key-selfhosted.webp new file mode 100644 index 00000000..65604241 Binary files /dev/null and b/apps/developer-docs/docs/public/images/activate-license/enter-license-key-selfhosted.webp differ diff --git a/apps/developer-docs/docs/public/images/activate-license/pro-activated-cloud.webp b/apps/developer-docs/docs/public/images/activate-license/pro-activated-cloud.webp new file mode 100644 index 00000000..7648422b Binary files /dev/null and b/apps/developer-docs/docs/public/images/activate-license/pro-activated-cloud.webp differ diff --git a/apps/developer-docs/docs/public/images/activate-license/upload-airgapped-enterprise.webp b/apps/developer-docs/docs/public/images/activate-license/upload-airgapped-enterprise.webp new file mode 100644 index 00000000..36ca1fb7 Binary files /dev/null and b/apps/developer-docs/docs/public/images/activate-license/upload-airgapped-enterprise.webp differ diff --git a/apps/developer-docs/docs/public/images/activate-license/upload-airgapped-license-file.webp b/apps/developer-docs/docs/public/images/activate-license/upload-airgapped-license-file.webp new file mode 100644 index 00000000..37854ba9 Binary files /dev/null and b/apps/developer-docs/docs/public/images/activate-license/upload-airgapped-license-file.webp differ diff --git a/apps/developer-docs/docs/public/images/activate-license/workspace-settings.webp b/apps/developer-docs/docs/public/images/activate-license/workspace-settings.webp new file mode 100644 index 00000000..b63730f4 Binary files /dev/null and b/apps/developer-docs/docs/public/images/activate-license/workspace-settings.webp differ diff --git a/apps/developer-docs/docs/public/images/airgapped/airgapped-cluster.webp b/apps/developer-docs/docs/public/images/airgapped/airgapped-cluster.webp new file mode 100644 index 00000000..b308bc3a Binary files /dev/null and b/apps/developer-docs/docs/public/images/airgapped/airgapped-cluster.webp differ diff --git a/apps/developer-docs/docs/public/images/airgapped/plane-architecture.webp b/apps/developer-docs/docs/public/images/airgapped/plane-architecture.webp new file mode 100644 index 00000000..c54526e5 Binary files /dev/null and b/apps/developer-docs/docs/public/images/airgapped/plane-architecture.webp differ diff --git a/apps/developer-docs/docs/public/images/analytics/analytics-custom-analytics.png b/apps/developer-docs/docs/public/images/analytics/analytics-custom-analytics.png new file mode 100644 index 00000000..d75d8a3c Binary files /dev/null and b/apps/developer-docs/docs/public/images/analytics/analytics-custom-analytics.png differ diff --git a/apps/developer-docs/docs/public/images/analytics/analytics-scope-demand.png b/apps/developer-docs/docs/public/images/analytics/analytics-scope-demand.png new file mode 100644 index 00000000..26f7d2c8 Binary files /dev/null and b/apps/developer-docs/docs/public/images/analytics/analytics-scope-demand.png differ diff --git a/apps/developer-docs/docs/public/images/api-reference/add-api-key-plane.png b/apps/developer-docs/docs/public/images/api-reference/add-api-key-plane.png new file mode 100644 index 00000000..5e83a2f2 Binary files /dev/null and b/apps/developer-docs/docs/public/images/api-reference/add-api-key-plane.png differ diff --git a/apps/developer-docs/docs/public/images/api-reference/api-tokens-plane.png b/apps/developer-docs/docs/public/images/api-reference/api-tokens-plane.png new file mode 100644 index 00000000..53995db2 Binary files /dev/null and b/apps/developer-docs/docs/public/images/api-reference/api-tokens-plane.png differ diff --git a/apps/developer-docs/docs/public/images/api-reference/profile-settings.png b/apps/developer-docs/docs/public/images/api-reference/profile-settings.png new file mode 100644 index 00000000..cb17581a Binary files /dev/null and b/apps/developer-docs/docs/public/images/api-reference/profile-settings.png differ diff --git a/apps/developer-docs/docs/public/images/authentication/github/github-auth-1.png b/apps/developer-docs/docs/public/images/authentication/github/github-auth-1.png new file mode 100644 index 00000000..471391b8 Binary files /dev/null and b/apps/developer-docs/docs/public/images/authentication/github/github-auth-1.png differ diff --git a/apps/developer-docs/docs/public/images/authentication/github/github-auth-2.png b/apps/developer-docs/docs/public/images/authentication/github/github-auth-2.png new file mode 100644 index 00000000..27d8cbeb Binary files /dev/null and b/apps/developer-docs/docs/public/images/authentication/github/github-auth-2.png differ diff --git a/apps/developer-docs/docs/public/images/authentication/google/google-auth-1.png b/apps/developer-docs/docs/public/images/authentication/google/google-auth-1.png new file mode 100644 index 00000000..6d28fc3d Binary files /dev/null and b/apps/developer-docs/docs/public/images/authentication/google/google-auth-1.png differ diff --git a/apps/developer-docs/docs/public/images/authentication/google/google-auth-2.png b/apps/developer-docs/docs/public/images/authentication/google/google-auth-2.png new file mode 100644 index 00000000..c47de15d Binary files /dev/null and b/apps/developer-docs/docs/public/images/authentication/google/google-auth-2.png differ diff --git a/apps/developer-docs/docs/public/images/authentication/google/google-auth-3.png b/apps/developer-docs/docs/public/images/authentication/google/google-auth-3.png new file mode 100644 index 00000000..af7844d1 Binary files /dev/null and b/apps/developer-docs/docs/public/images/authentication/google/google-auth-3.png differ diff --git a/apps/developer-docs/docs/public/images/authentication/google/google-auth-4.png b/apps/developer-docs/docs/public/images/authentication/google/google-auth-4.png new file mode 100644 index 00000000..43689e64 Binary files /dev/null and b/apps/developer-docs/docs/public/images/authentication/google/google-auth-4.png differ diff --git a/apps/developer-docs/docs/public/images/custom-sso/github-oauth.png b/apps/developer-docs/docs/public/images/custom-sso/github-oauth.png new file mode 100644 index 00000000..1b99b793 Binary files /dev/null and b/apps/developer-docs/docs/public/images/custom-sso/github-oauth.png differ diff --git a/apps/developer-docs/docs/public/images/custom-sso/google-oauth.png b/apps/developer-docs/docs/public/images/custom-sso/google-oauth.png new file mode 100644 index 00000000..db23a916 Binary files /dev/null and b/apps/developer-docs/docs/public/images/custom-sso/google-oauth.png differ diff --git a/apps/developer-docs/docs/public/images/custom-sso/instance-login.png b/apps/developer-docs/docs/public/images/custom-sso/instance-login.png new file mode 100644 index 00000000..db787364 Binary files /dev/null and b/apps/developer-docs/docs/public/images/custom-sso/instance-login.png differ diff --git a/apps/developer-docs/docs/public/images/custom-sso/oidc-oauth.png b/apps/developer-docs/docs/public/images/custom-sso/oidc-oauth.png new file mode 100644 index 00000000..0559edc9 Binary files /dev/null and b/apps/developer-docs/docs/public/images/custom-sso/oidc-oauth.png differ diff --git a/apps/developer-docs/docs/public/images/custom-sso/okta-signin.webp b/apps/developer-docs/docs/public/images/custom-sso/okta-signin.webp new file mode 100644 index 00000000..5c111ed5 Binary files /dev/null and b/apps/developer-docs/docs/public/images/custom-sso/okta-signin.webp differ diff --git a/apps/developer-docs/docs/public/images/custom-sso/plane-login.png b/apps/developer-docs/docs/public/images/custom-sso/plane-login.png new file mode 100644 index 00000000..93334ada Binary files /dev/null and b/apps/developer-docs/docs/public/images/custom-sso/plane-login.png differ diff --git a/apps/developer-docs/docs/public/images/custom-sso/saml-oauth.png b/apps/developer-docs/docs/public/images/custom-sso/saml-oauth.png new file mode 100644 index 00000000..6df64f3b Binary files /dev/null and b/apps/developer-docs/docs/public/images/custom-sso/saml-oauth.png differ diff --git a/apps/developer-docs/docs/public/images/cycles/active-cycle-ui.png b/apps/developer-docs/docs/public/images/cycles/active-cycle-ui.png new file mode 100644 index 00000000..eeaa2ab5 Binary files /dev/null and b/apps/developer-docs/docs/public/images/cycles/active-cycle-ui.png differ diff --git a/apps/developer-docs/docs/public/images/cycles/active-cycle.png b/apps/developer-docs/docs/public/images/cycles/active-cycle.png new file mode 100644 index 00000000..56256a20 Binary files /dev/null and b/apps/developer-docs/docs/public/images/cycles/active-cycle.png differ diff --git a/apps/developer-docs/docs/public/images/cycles/active-cycles-ui.png b/apps/developer-docs/docs/public/images/cycles/active-cycles-ui.png new file mode 100644 index 00000000..f50f180c Binary files /dev/null and b/apps/developer-docs/docs/public/images/cycles/active-cycles-ui.png differ diff --git a/apps/developer-docs/docs/public/images/cycles/create-cycles.png b/apps/developer-docs/docs/public/images/cycles/create-cycles.png new file mode 100644 index 00000000..4d0dc8af Binary files /dev/null and b/apps/developer-docs/docs/public/images/cycles/create-cycles.png differ diff --git a/apps/developer-docs/docs/public/images/cycles/cycle-empty-state.png b/apps/developer-docs/docs/public/images/cycles/cycle-empty-state.png new file mode 100644 index 00000000..96dd2a07 Binary files /dev/null and b/apps/developer-docs/docs/public/images/cycles/cycle-empty-state.png differ diff --git a/apps/developer-docs/docs/public/images/disable-telemetry.webp b/apps/developer-docs/docs/public/images/disable-telemetry.webp new file mode 100644 index 00000000..db776a25 Binary files /dev/null and b/apps/developer-docs/docs/public/images/disable-telemetry.webp differ diff --git a/apps/developer-docs/docs/public/images/docker-compose/download-complete.png b/apps/developer-docs/docs/public/images/docker-compose/download-complete.png new file mode 100644 index 00000000..42b1d1cc Binary files /dev/null and b/apps/developer-docs/docs/public/images/docker-compose/download-complete.png differ diff --git a/apps/developer-docs/docs/public/images/docker-compose/download-docker.png b/apps/developer-docs/docs/public/images/docker-compose/download-docker.png new file mode 100644 index 00000000..acdfa598 Binary files /dev/null and b/apps/developer-docs/docs/public/images/docker-compose/download-docker.png differ diff --git a/apps/developer-docs/docs/public/images/docker-compose/migrate-error.png b/apps/developer-docs/docs/public/images/docker-compose/migrate-error.png new file mode 100644 index 00000000..35e701d2 Binary files /dev/null and b/apps/developer-docs/docs/public/images/docker-compose/migrate-error.png differ diff --git a/apps/developer-docs/docs/public/images/docker-compose/restart-docker.png b/apps/developer-docs/docs/public/images/docker-compose/restart-docker.png new file mode 100644 index 00000000..47ad68d7 Binary files /dev/null and b/apps/developer-docs/docs/public/images/docker-compose/restart-docker.png differ diff --git a/apps/developer-docs/docs/public/images/docker-compose/stopped-docker.png b/apps/developer-docs/docs/public/images/docker-compose/stopped-docker.png new file mode 100644 index 00000000..0d18f19c Binary files /dev/null and b/apps/developer-docs/docs/public/images/docker-compose/stopped-docker.png differ diff --git a/apps/developer-docs/docs/public/images/docker-compose/upgrade-docker.png b/apps/developer-docs/docs/public/images/docker-compose/upgrade-docker.png new file mode 100644 index 00000000..e11ff2ec Binary files /dev/null and b/apps/developer-docs/docs/public/images/docker-compose/upgrade-docker.png differ diff --git a/apps/developer-docs/docs/public/images/faq-2.png b/apps/developer-docs/docs/public/images/faq-2.png new file mode 100644 index 00000000..41470a1f Binary files /dev/null and b/apps/developer-docs/docs/public/images/faq-2.png differ diff --git a/apps/developer-docs/docs/public/images/github-imp/comments.png b/apps/developer-docs/docs/public/images/github-imp/comments.png new file mode 100644 index 00000000..fc3b21f1 Binary files /dev/null and b/apps/developer-docs/docs/public/images/github-imp/comments.png differ diff --git a/apps/developer-docs/docs/public/images/github-imp/configure.png b/apps/developer-docs/docs/public/images/github-imp/configure.png new file mode 100644 index 00000000..c12a52da Binary files /dev/null and b/apps/developer-docs/docs/public/images/github-imp/configure.png differ diff --git a/apps/developer-docs/docs/public/images/github-imp/confirm.png b/apps/developer-docs/docs/public/images/github-imp/confirm.png new file mode 100644 index 00000000..4b8941be Binary files /dev/null and b/apps/developer-docs/docs/public/images/github-imp/confirm.png differ diff --git a/apps/developer-docs/docs/public/images/github-imp/import.png b/apps/developer-docs/docs/public/images/github-imp/import.png new file mode 100644 index 00000000..f07a7f9e Binary files /dev/null and b/apps/developer-docs/docs/public/images/github-imp/import.png differ diff --git a/apps/developer-docs/docs/public/images/github-imp/integration.png b/apps/developer-docs/docs/public/images/github-imp/integration.png new file mode 100644 index 00000000..df1159e4 Binary files /dev/null and b/apps/developer-docs/docs/public/images/github-imp/integration.png differ diff --git a/apps/developer-docs/docs/public/images/github-imp/map.png b/apps/developer-docs/docs/public/images/github-imp/map.png new file mode 100644 index 00000000..bf11e271 Binary files /dev/null and b/apps/developer-docs/docs/public/images/github-imp/map.png differ diff --git a/apps/developer-docs/docs/public/images/github-imp/merge.png b/apps/developer-docs/docs/public/images/github-imp/merge.png new file mode 100644 index 00000000..afa057d7 Binary files /dev/null and b/apps/developer-docs/docs/public/images/github-imp/merge.png differ diff --git a/apps/developer-docs/docs/public/images/github-imp/sync.png b/apps/developer-docs/docs/public/images/github-imp/sync.png new file mode 100644 index 00000000..a2e873a7 Binary files /dev/null and b/apps/developer-docs/docs/public/images/github-imp/sync.png differ diff --git a/apps/developer-docs/docs/public/images/github-imp/verify.png b/apps/developer-docs/docs/public/images/github-imp/verify.png new file mode 100644 index 00000000..7d65176b Binary files /dev/null and b/apps/developer-docs/docs/public/images/github-imp/verify.png differ diff --git a/apps/developer-docs/docs/public/images/github-imp/workspace-settings.png b/apps/developer-docs/docs/public/images/github-imp/workspace-settings.png new file mode 100644 index 00000000..4639ada7 Binary files /dev/null and b/apps/developer-docs/docs/public/images/github-imp/workspace-settings.png differ diff --git a/apps/developer-docs/docs/public/images/importers/github/comments.png b/apps/developer-docs/docs/public/images/importers/github/comments.png new file mode 100644 index 00000000..4d6c7f18 Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/github/comments.png differ diff --git a/apps/developer-docs/docs/public/images/importers/github/configure.png b/apps/developer-docs/docs/public/images/importers/github/configure.png new file mode 100644 index 00000000..3509173f Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/github/configure.png differ diff --git a/apps/developer-docs/docs/public/images/importers/github/confirm.png b/apps/developer-docs/docs/public/images/importers/github/confirm.png new file mode 100644 index 00000000..ba34b8d0 Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/github/confirm.png differ diff --git a/apps/developer-docs/docs/public/images/importers/github/import.png b/apps/developer-docs/docs/public/images/importers/github/import.png new file mode 100644 index 00000000..021dd6b0 Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/github/import.png differ diff --git a/apps/developer-docs/docs/public/images/importers/github/integration.png b/apps/developer-docs/docs/public/images/importers/github/integration.png new file mode 100644 index 00000000..568255d1 Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/github/integration.png differ diff --git a/apps/developer-docs/docs/public/images/importers/github/map.png b/apps/developer-docs/docs/public/images/importers/github/map.png new file mode 100644 index 00000000..83b8457c Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/github/map.png differ diff --git a/apps/developer-docs/docs/public/images/importers/github/merge.png b/apps/developer-docs/docs/public/images/importers/github/merge.png new file mode 100644 index 00000000..0c834d31 Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/github/merge.png differ diff --git a/apps/developer-docs/docs/public/images/importers/github/sync.png b/apps/developer-docs/docs/public/images/importers/github/sync.png new file mode 100644 index 00000000..02ce3440 Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/github/sync.png differ diff --git a/apps/developer-docs/docs/public/images/importers/github/verify.png b/apps/developer-docs/docs/public/images/importers/github/verify.png new file mode 100644 index 00000000..b7124cda Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/github/verify.png differ diff --git a/apps/developer-docs/docs/public/images/importers/github/workspace-settings.png b/apps/developer-docs/docs/public/images/importers/github/workspace-settings.png new file mode 100644 index 00000000..2984a7fd Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/github/workspace-settings.png differ diff --git a/apps/developer-docs/docs/public/images/importers/jira/jira-importer-first-step.png b/apps/developer-docs/docs/public/images/importers/jira/jira-importer-first-step.png new file mode 100644 index 00000000..d6296186 Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/jira/jira-importer-first-step.png differ diff --git a/apps/developer-docs/docs/public/images/importers/jira/jira-importer-second-step.png b/apps/developer-docs/docs/public/images/importers/jira/jira-importer-second-step.png new file mode 100644 index 00000000..e7ba5639 Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/jira/jira-importer-second-step.png differ diff --git a/apps/developer-docs/docs/public/images/importers/jira/jira-importer-step-fore.png b/apps/developer-docs/docs/public/images/importers/jira/jira-importer-step-fore.png new file mode 100644 index 00000000..c0e66593 Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/jira/jira-importer-step-fore.png differ diff --git a/apps/developer-docs/docs/public/images/importers/jira/jira-importer-step-three.png b/apps/developer-docs/docs/public/images/importers/jira/jira-importer-step-three.png new file mode 100644 index 00000000..421a4ee3 Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/jira/jira-importer-step-three.png differ diff --git a/apps/developer-docs/docs/public/images/importers/jira/jira-workspace-setting.png b/apps/developer-docs/docs/public/images/importers/jira/jira-workspace-setting.png new file mode 100644 index 00000000..eddd3b19 Binary files /dev/null and b/apps/developer-docs/docs/public/images/importers/jira/jira-workspace-setting.png differ diff --git a/apps/developer-docs/docs/public/images/inbox/RTE-reactions-and-mentions.png b/apps/developer-docs/docs/public/images/inbox/RTE-reactions-and-mentions.png new file mode 100644 index 00000000..48fdb181 Binary files /dev/null and b/apps/developer-docs/docs/public/images/inbox/RTE-reactions-and-mentions.png differ diff --git a/apps/developer-docs/docs/public/images/inbox/accept-intake-issue.webp b/apps/developer-docs/docs/public/images/inbox/accept-intake-issue.webp new file mode 100644 index 00000000..d782d5b2 Binary files /dev/null and b/apps/developer-docs/docs/public/images/inbox/accept-intake-issue.webp differ diff --git a/apps/developer-docs/docs/public/images/inbox/create-issue-intake.webp b/apps/developer-docs/docs/public/images/inbox/create-issue-intake.webp new file mode 100644 index 00000000..8cad1e1d Binary files /dev/null and b/apps/developer-docs/docs/public/images/inbox/create-issue-intake.webp differ diff --git a/apps/developer-docs/docs/public/images/inbox/enable-intake-feature.webp b/apps/developer-docs/docs/public/images/inbox/enable-intake-feature.webp new file mode 100644 index 00000000..a0aa3b16 Binary files /dev/null and b/apps/developer-docs/docs/public/images/inbox/enable-intake-feature.webp differ diff --git a/apps/developer-docs/docs/public/images/inbox/enter-issue-details.webp b/apps/developer-docs/docs/public/images/inbox/enter-issue-details.webp new file mode 100644 index 00000000..d2bc8ea2 Binary files /dev/null and b/apps/developer-docs/docs/public/images/inbox/enter-issue-details.webp differ diff --git a/apps/developer-docs/docs/public/images/inbox/inbox-activity.png b/apps/developer-docs/docs/public/images/inbox/inbox-activity.png new file mode 100644 index 00000000..89973f68 Binary files /dev/null and b/apps/developer-docs/docs/public/images/inbox/inbox-activity.png differ diff --git a/apps/developer-docs/docs/public/images/inbox/inbox-description-box.png b/apps/developer-docs/docs/public/images/inbox/inbox-description-box.png new file mode 100644 index 00000000..94b447a1 Binary files /dev/null and b/apps/developer-docs/docs/public/images/inbox/inbox-description-box.png differ diff --git a/apps/developer-docs/docs/public/images/inbox/inbox-navigate.png b/apps/developer-docs/docs/public/images/inbox/inbox-navigate.png new file mode 100644 index 00000000..ab0b837a Binary files /dev/null and b/apps/developer-docs/docs/public/images/inbox/inbox-navigate.png differ diff --git a/apps/developer-docs/docs/public/images/inbox/inbox-tabs.png b/apps/developer-docs/docs/public/images/inbox/inbox-tabs.png new file mode 100644 index 00000000..c61d1ad5 Binary files /dev/null and b/apps/developer-docs/docs/public/images/inbox/inbox-tabs.png differ diff --git a/apps/developer-docs/docs/public/images/inbox/mark-duplicate-intake-issues.webp b/apps/developer-docs/docs/public/images/inbox/mark-duplicate-intake-issues.webp new file mode 100644 index 00000000..20515bb0 Binary files /dev/null and b/apps/developer-docs/docs/public/images/inbox/mark-duplicate-intake-issues.webp differ diff --git a/apps/developer-docs/docs/public/images/inbox/snooze-intake-issue.webp b/apps/developer-docs/docs/public/images/inbox/snooze-intake-issue.webp new file mode 100644 index 00000000..56434701 Binary files /dev/null and b/apps/developer-docs/docs/public/images/inbox/snooze-intake-issue.webp differ diff --git a/apps/developer-docs/docs/public/images/instance-admin/access-god-mode.webp b/apps/developer-docs/docs/public/images/instance-admin/access-god-mode.webp new file mode 100644 index 00000000..40b4a034 Binary files /dev/null and b/apps/developer-docs/docs/public/images/instance-admin/access-god-mode.webp differ diff --git a/apps/developer-docs/docs/public/images/instance-admin/email-settings.png b/apps/developer-docs/docs/public/images/instance-admin/email-settings.png new file mode 100644 index 00000000..036b425c Binary files /dev/null and b/apps/developer-docs/docs/public/images/instance-admin/email-settings.png differ diff --git a/apps/developer-docs/docs/public/images/instance-admin/god-mode-ai.webp b/apps/developer-docs/docs/public/images/instance-admin/god-mode-ai.webp new file mode 100644 index 00000000..3fc29881 Binary files /dev/null and b/apps/developer-docs/docs/public/images/instance-admin/god-mode-ai.webp differ diff --git a/apps/developer-docs/docs/public/images/instance-admin/god-mode-authentication.webp b/apps/developer-docs/docs/public/images/instance-admin/god-mode-authentication.webp new file mode 100644 index 00000000..222affa7 Binary files /dev/null and b/apps/developer-docs/docs/public/images/instance-admin/god-mode-authentication.webp differ diff --git a/apps/developer-docs/docs/public/images/instance-admin/god-mode-email.webp b/apps/developer-docs/docs/public/images/instance-admin/god-mode-email.webp new file mode 100644 index 00000000..bcf7a7e3 Binary files /dev/null and b/apps/developer-docs/docs/public/images/instance-admin/god-mode-email.webp differ diff --git a/apps/developer-docs/docs/public/images/instance-admin/god-mode-general.webp b/apps/developer-docs/docs/public/images/instance-admin/god-mode-general.webp new file mode 100644 index 00000000..b6548ca7 Binary files /dev/null and b/apps/developer-docs/docs/public/images/instance-admin/god-mode-general.webp differ diff --git a/apps/developer-docs/docs/public/images/instance-admin/god-mode-images.webp b/apps/developer-docs/docs/public/images/instance-admin/god-mode-images.webp new file mode 100644 index 00000000..c8651c71 Binary files /dev/null and b/apps/developer-docs/docs/public/images/instance-admin/god-mode-images.webp differ diff --git a/apps/developer-docs/docs/public/images/instance-admin/god-mode-workspaces.webp b/apps/developer-docs/docs/public/images/instance-admin/god-mode-workspaces.webp new file mode 100644 index 00000000..100d6146 Binary files /dev/null and b/apps/developer-docs/docs/public/images/instance-admin/god-mode-workspaces.webp differ diff --git a/apps/developer-docs/docs/public/images/instance-admin/invite-instance-admin.webp b/apps/developer-docs/docs/public/images/instance-admin/invite-instance-admin.webp new file mode 100644 index 00000000..1685a4df Binary files /dev/null and b/apps/developer-docs/docs/public/images/instance-admin/invite-instance-admin.webp differ diff --git a/apps/developer-docs/docs/public/images/instance-admin/user-actions.webp b/apps/developer-docs/docs/public/images/instance-admin/user-actions.webp new file mode 100644 index 00000000..e0695205 Binary files /dev/null and b/apps/developer-docs/docs/public/images/instance-admin/user-actions.webp differ diff --git a/apps/developer-docs/docs/public/images/instance-admin/user-management.webp b/apps/developer-docs/docs/public/images/instance-admin/user-management.webp new file mode 100644 index 00000000..b2ab3452 Binary files /dev/null and b/apps/developer-docs/docs/public/images/instance-admin/user-management.webp differ diff --git a/apps/developer-docs/docs/public/images/instance-ready.png b/apps/developer-docs/docs/public/images/instance-ready.png new file mode 100644 index 00000000..f9b1f488 Binary files /dev/null and b/apps/developer-docs/docs/public/images/instance-ready.png differ diff --git a/apps/developer-docs/docs/public/images/integrations/github/add-callback-url.webp b/apps/developer-docs/docs/public/images/integrations/github/add-callback-url.webp new file mode 100644 index 00000000..547fc41c Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/github/add-callback-url.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/github/add-setup-url.webp b/apps/developer-docs/docs/public/images/integrations/github/add-setup-url.webp new file mode 100644 index 00000000..5a40aef2 Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/github/add-setup-url.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/github/add-webhook-url.webp b/apps/developer-docs/docs/public/images/integrations/github/add-webhook-url.webp new file mode 100644 index 00000000..0cbaed39 Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/github/add-webhook-url.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/github/app-name-homepage-url.webp b/apps/developer-docs/docs/public/images/integrations/github/app-name-homepage-url.webp new file mode 100644 index 00000000..7a4f32c6 Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/github/app-name-homepage-url.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/github/create-github-app.webp b/apps/developer-docs/docs/public/images/integrations/github/create-github-app.webp new file mode 100644 index 00000000..abece663 Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/github/create-github-app.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/github/general-tab.webp b/apps/developer-docs/docs/public/images/integrations/github/general-tab.webp new file mode 100644 index 00000000..8241f8db Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/github/general-tab.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/github/private-keys.webp b/apps/developer-docs/docs/public/images/integrations/github/private-keys.webp new file mode 100644 index 00000000..17ff1975 Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/github/private-keys.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/github/setup-permissions.webp b/apps/developer-docs/docs/public/images/integrations/github/setup-permissions.webp new file mode 100644 index 00000000..55f3babd Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/github/setup-permissions.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/github/subscribe-to-events.webp b/apps/developer-docs/docs/public/images/integrations/github/subscribe-to-events.webp new file mode 100644 index 00000000..951c14d4 Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/github/subscribe-to-events.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/gitlab/add-app-details.webp b/apps/developer-docs/docs/public/images/integrations/gitlab/add-app-details.webp new file mode 100644 index 00000000..31394c7c Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/gitlab/add-app-details.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/gitlab/add-gitlab-application.webp b/apps/developer-docs/docs/public/images/integrations/gitlab/add-gitlab-application.webp new file mode 100644 index 00000000..0fcd8266 Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/gitlab/add-gitlab-application.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/gitlab/copy-credentials.webp b/apps/developer-docs/docs/public/images/integrations/gitlab/copy-credentials.webp new file mode 100644 index 00000000..6436f908 Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/gitlab/copy-credentials.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/slack/app-from-manifest.webp b/apps/developer-docs/docs/public/images/integrations/slack/app-from-manifest.webp new file mode 100644 index 00000000..fea8ceaa Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/slack/app-from-manifest.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/slack/choose-from-manifest.webp b/apps/developer-docs/docs/public/images/integrations/slack/choose-from-manifest.webp new file mode 100644 index 00000000..7b2a1e59 Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/slack/choose-from-manifest.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/slack/create-slack-app.webp b/apps/developer-docs/docs/public/images/integrations/slack/create-slack-app.webp new file mode 100644 index 00000000..ea6182fa Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/slack/create-slack-app.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/slack/event-subscriptions.webp b/apps/developer-docs/docs/public/images/integrations/slack/event-subscriptions.webp new file mode 100644 index 00000000..78f25a66 Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/slack/event-subscriptions.webp differ diff --git a/apps/developer-docs/docs/public/images/integrations/slack/review-summary.webp b/apps/developer-docs/docs/public/images/integrations/slack/review-summary.webp new file mode 100644 index 00000000..2ef12190 Binary files /dev/null and b/apps/developer-docs/docs/public/images/integrations/slack/review-summary.webp differ diff --git a/apps/developer-docs/docs/public/images/issues/activate-issue-type.webp b/apps/developer-docs/docs/public/images/issues/activate-issue-type.webp new file mode 100644 index 00000000..7f35841d Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/activate-issue-type.webp differ diff --git a/apps/developer-docs/docs/public/images/issues/add-issue-type.webp b/apps/developer-docs/docs/public/images/issues/add-issue-type.webp new file mode 100644 index 00000000..e9ae1a72 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/add-issue-type.webp differ diff --git a/apps/developer-docs/docs/public/images/issues/add-new-property.webp b/apps/developer-docs/docs/public/images/issues/add-new-property.webp new file mode 100644 index 00000000..376199aa Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/add-new-property.webp differ diff --git a/apps/developer-docs/docs/public/images/issues/archived-issues.webp b/apps/developer-docs/docs/public/images/issues/archived-issues.webp new file mode 100644 index 00000000..44119336 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/archived-issues.webp differ diff --git a/apps/developer-docs/docs/public/images/issues/create-issue-modal.png b/apps/developer-docs/docs/public/images/issues/create-issue-modal.png new file mode 100644 index 00000000..f863159d Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/create-issue-modal.png differ diff --git a/apps/developer-docs/docs/public/images/issues/create-issue-type.webp b/apps/developer-docs/docs/public/images/issues/create-issue-type.webp new file mode 100644 index 00000000..eb33d094 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/create-issue-type.webp differ diff --git a/apps/developer-docs/docs/public/images/issues/enable-issue-types.webp b/apps/developer-docs/docs/public/images/issues/enable-issue-types.webp new file mode 100644 index 00000000..5b36e01a Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/enable-issue-types.webp differ diff --git a/apps/developer-docs/docs/public/images/issues/issue-activity-comments.png b/apps/developer-docs/docs/public/images/issues/issue-activity-comments.png new file mode 100644 index 00000000..3232a761 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/issue-activity-comments.png differ diff --git a/apps/developer-docs/docs/public/images/issues/issue-calendar-layout.png b/apps/developer-docs/docs/public/images/issues/issue-calendar-layout.png new file mode 100644 index 00000000..25826fe7 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/issue-calendar-layout.png differ diff --git a/apps/developer-docs/docs/public/images/issues/issue-filters.png b/apps/developer-docs/docs/public/images/issues/issue-filters.png new file mode 100644 index 00000000..985f2bfb Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/issue-filters.png differ diff --git a/apps/developer-docs/docs/public/images/issues/issue-gantt-layout.png b/apps/developer-docs/docs/public/images/issues/issue-gantt-layout.png new file mode 100644 index 00000000..a9dc92cb Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/issue-gantt-layout.png differ diff --git a/apps/developer-docs/docs/public/images/issues/issue-kanban-layout.png b/apps/developer-docs/docs/public/images/issues/issue-kanban-layout.png new file mode 100644 index 00000000..b407b180 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/issue-kanban-layout.png differ diff --git a/apps/developer-docs/docs/public/images/issues/issue-list-layout.png b/apps/developer-docs/docs/public/images/issues/issue-list-layout.png new file mode 100644 index 00000000..f3dd8f34 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/issue-list-layout.png differ diff --git a/apps/developer-docs/docs/public/images/issues/issue-quick-add.png b/apps/developer-docs/docs/public/images/issues/issue-quick-add.png new file mode 100644 index 00000000..6b74e730 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/issue-quick-add.png differ diff --git a/apps/developer-docs/docs/public/images/issues/issue-side-peek.png b/apps/developer-docs/docs/public/images/issues/issue-side-peek.png new file mode 100644 index 00000000..2da9ee10 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/issue-side-peek.png differ diff --git a/apps/developer-docs/docs/public/images/issues/issue-spreadsheet-layout.png b/apps/developer-docs/docs/public/images/issues/issue-spreadsheet-layout.png new file mode 100644 index 00000000..cdf9ff91 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/issue-spreadsheet-layout.png differ diff --git a/apps/developer-docs/docs/public/images/issues/issue-sub-issues.png b/apps/developer-docs/docs/public/images/issues/issue-sub-issues.png new file mode 100644 index 00000000..269d4f3d Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/issue-sub-issues.png differ diff --git a/apps/developer-docs/docs/public/images/issues/property-details.webp b/apps/developer-docs/docs/public/images/issues/property-details.webp new file mode 100644 index 00000000..3def3a69 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/property-details.webp differ diff --git a/apps/developer-docs/docs/public/images/issues/setup-issue-types.webp b/apps/developer-docs/docs/public/images/issues/setup-issue-types.webp new file mode 100644 index 00000000..f7a8e887 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/setup-issue-types.webp differ diff --git a/apps/developer-docs/docs/public/images/issues/use-issue-type.webp b/apps/developer-docs/docs/public/images/issues/use-issue-type.webp new file mode 100644 index 00000000..b10893c7 Binary files /dev/null and b/apps/developer-docs/docs/public/images/issues/use-issue-type.webp differ diff --git a/apps/developer-docs/docs/public/images/ldap/enable-ldap.webp b/apps/developer-docs/docs/public/images/ldap/enable-ldap.webp new file mode 100644 index 00000000..cf3d64cd Binary files /dev/null and b/apps/developer-docs/docs/public/images/ldap/enable-ldap.webp differ diff --git a/apps/developer-docs/docs/public/images/ldap/ldap-configuration.webp b/apps/developer-docs/docs/public/images/ldap/ldap-configuration.webp new file mode 100644 index 00000000..18cb1ed9 Binary files /dev/null and b/apps/developer-docs/docs/public/images/ldap/ldap-configuration.webp differ diff --git a/apps/developer-docs/docs/public/images/ldap/sign-in-ldap.webp b/apps/developer-docs/docs/public/images/ldap/sign-in-ldap.webp new file mode 100644 index 00000000..23fa4e65 Binary files /dev/null and b/apps/developer-docs/docs/public/images/ldap/sign-in-ldap.webp differ diff --git a/apps/developer-docs/docs/public/images/mcp/install-in-cursor.svg b/apps/developer-docs/docs/public/images/mcp/install-in-cursor.svg new file mode 100644 index 00000000..3dacb7f1 --- /dev/null +++ b/apps/developer-docs/docs/public/images/mcp/install-in-cursor.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/apps/developer-docs/docs/public/images/mcp/install-in-vscode.svg b/apps/developer-docs/docs/public/images/mcp/install-in-vscode.svg new file mode 100644 index 00000000..0c1f164e --- /dev/null +++ b/apps/developer-docs/docs/public/images/mcp/install-in-vscode.svg @@ -0,0 +1 @@ +VS Code: Install ServerVS CodeInstall Server \ No newline at end of file diff --git a/apps/developer-docs/docs/public/images/modules/add-issues-to-module.png b/apps/developer-docs/docs/public/images/modules/add-issues-to-module.png new file mode 100644 index 00000000..00cdea3a Binary files /dev/null and b/apps/developer-docs/docs/public/images/modules/add-issues-to-module.png differ diff --git a/apps/developer-docs/docs/public/images/modules/create-module.png b/apps/developer-docs/docs/public/images/modules/create-module.png new file mode 100644 index 00000000..f3e4374c Binary files /dev/null and b/apps/developer-docs/docs/public/images/modules/create-module.png differ diff --git a/apps/developer-docs/docs/public/images/modules/module-gantt.png b/apps/developer-docs/docs/public/images/modules/module-gantt.png new file mode 100644 index 00000000..af2794ee Binary files /dev/null and b/apps/developer-docs/docs/public/images/modules/module-gantt.png differ diff --git a/apps/developer-docs/docs/public/images/modules/module-progress.png b/apps/developer-docs/docs/public/images/modules/module-progress.png new file mode 100644 index 00000000..df9d4073 Binary files /dev/null and b/apps/developer-docs/docs/public/images/modules/module-progress.png differ diff --git a/apps/developer-docs/docs/public/images/one-click-deploy/one-click-advanced.png b/apps/developer-docs/docs/public/images/one-click-deploy/one-click-advanced.png new file mode 100644 index 00000000..5daa7335 Binary files /dev/null and b/apps/developer-docs/docs/public/images/one-click-deploy/one-click-advanced.png differ diff --git a/apps/developer-docs/docs/public/images/one-click-deploy/one-click-help.png b/apps/developer-docs/docs/public/images/one-click-deploy/one-click-help.png new file mode 100644 index 00000000..c14603a4 Binary files /dev/null and b/apps/developer-docs/docs/public/images/one-click-deploy/one-click-help.png differ diff --git a/apps/developer-docs/docs/public/images/one-click-deploy/one-click-install.png b/apps/developer-docs/docs/public/images/one-click-deploy/one-click-install.png new file mode 100644 index 00000000..c8ba1e5f Binary files /dev/null and b/apps/developer-docs/docs/public/images/one-click-deploy/one-click-install.png differ diff --git a/apps/developer-docs/docs/public/images/open-search/opensearch-flow.webp b/apps/developer-docs/docs/public/images/open-search/opensearch-flow.webp new file mode 100644 index 00000000..e51c78e2 Binary files /dev/null and b/apps/developer-docs/docs/public/images/open-search/opensearch-flow.webp differ diff --git a/apps/developer-docs/docs/public/images/pages/page-add-content.png b/apps/developer-docs/docs/public/images/pages/page-add-content.png new file mode 100644 index 00000000..6583e241 Binary files /dev/null and b/apps/developer-docs/docs/public/images/pages/page-add-content.png differ diff --git a/apps/developer-docs/docs/public/images/pages/page-create.png b/apps/developer-docs/docs/public/images/pages/page-create.png new file mode 100644 index 00000000..b80b1b76 Binary files /dev/null and b/apps/developer-docs/docs/public/images/pages/page-create.png differ diff --git a/apps/developer-docs/docs/public/images/plane-github.png b/apps/developer-docs/docs/public/images/plane-github.png new file mode 100644 index 00000000..224d9ac2 Binary files /dev/null and b/apps/developer-docs/docs/public/images/plane-github.png differ diff --git a/apps/developer-docs/docs/public/images/plane-one/prime-all-licenses.png b/apps/developer-docs/docs/public/images/plane-one/prime-all-licenses.png new file mode 100644 index 00000000..d4a5c1c1 Binary files /dev/null and b/apps/developer-docs/docs/public/images/plane-one/prime-all-licenses.png differ diff --git a/apps/developer-docs/docs/public/images/plane-one/prime-inside-a-license.png b/apps/developer-docs/docs/public/images/plane-one/prime-inside-a-license.png new file mode 100644 index 00000000..138ff3f5 Binary files /dev/null and b/apps/developer-docs/docs/public/images/plane-one/prime-inside-a-license.png differ diff --git a/apps/developer-docs/docs/public/images/plane_analytics.png b/apps/developer-docs/docs/public/images/plane_analytics.png new file mode 100644 index 00000000..49625228 Binary files /dev/null and b/apps/developer-docs/docs/public/images/plane_analytics.png differ diff --git a/apps/developer-docs/docs/public/images/power-k/power-k1.png b/apps/developer-docs/docs/public/images/power-k/power-k1.png new file mode 100644 index 00000000..24a1519a Binary files /dev/null and b/apps/developer-docs/docs/public/images/power-k/power-k1.png differ diff --git a/apps/developer-docs/docs/public/images/power-k/power-k2.png b/apps/developer-docs/docs/public/images/power-k/power-k2.png new file mode 100644 index 00000000..888244f6 Binary files /dev/null and b/apps/developer-docs/docs/public/images/power-k/power-k2.png differ diff --git a/apps/developer-docs/docs/public/images/power-k/power-k3.png b/apps/developer-docs/docs/public/images/power-k/power-k3.png new file mode 100644 index 00000000..4431562b Binary files /dev/null and b/apps/developer-docs/docs/public/images/power-k/power-k3.png differ diff --git a/apps/developer-docs/docs/public/images/power-k/power-k4.png b/apps/developer-docs/docs/public/images/power-k/power-k4.png new file mode 100644 index 00000000..c312e29f Binary files /dev/null and b/apps/developer-docs/docs/public/images/power-k/power-k4.png differ diff --git a/apps/developer-docs/docs/public/images/power-k/power-k5.png b/apps/developer-docs/docs/public/images/power-k/power-k5.png new file mode 100644 index 00000000..7e9dc230 Binary files /dev/null and b/apps/developer-docs/docs/public/images/power-k/power-k5.png differ diff --git a/apps/developer-docs/docs/public/images/power-k/power-k6.png b/apps/developer-docs/docs/public/images/power-k/power-k6.png new file mode 100644 index 00000000..f99f2b24 Binary files /dev/null and b/apps/developer-docs/docs/public/images/power-k/power-k6.png differ diff --git a/apps/developer-docs/docs/public/images/power-k1.png b/apps/developer-docs/docs/public/images/power-k1.png new file mode 100644 index 00000000..30dcbf52 Binary files /dev/null and b/apps/developer-docs/docs/public/images/power-k1.png differ diff --git a/apps/developer-docs/docs/public/images/power-k2.png b/apps/developer-docs/docs/public/images/power-k2.png new file mode 100644 index 00000000..29e59e83 Binary files /dev/null and b/apps/developer-docs/docs/public/images/power-k2.png differ diff --git a/apps/developer-docs/docs/public/images/power-k3.png b/apps/developer-docs/docs/public/images/power-k3.png new file mode 100644 index 00000000..75dac179 Binary files /dev/null and b/apps/developer-docs/docs/public/images/power-k3.png differ diff --git a/apps/developer-docs/docs/public/images/power-k4.png b/apps/developer-docs/docs/public/images/power-k4.png new file mode 100644 index 00000000..f7ea7678 Binary files /dev/null and b/apps/developer-docs/docs/public/images/power-k4.png differ diff --git a/apps/developer-docs/docs/public/images/power-k5.png b/apps/developer-docs/docs/public/images/power-k5.png new file mode 100644 index 00000000..8bb989dd Binary files /dev/null and b/apps/developer-docs/docs/public/images/power-k5.png differ diff --git a/apps/developer-docs/docs/public/images/power-k6.png b/apps/developer-docs/docs/public/images/power-k6.png new file mode 100644 index 00000000..e3ddc47f Binary files /dev/null and b/apps/developer-docs/docs/public/images/power-k6.png differ diff --git a/apps/developer-docs/docs/public/images/projects/project-automations.png b/apps/developer-docs/docs/public/images/projects/project-automations.png new file mode 100644 index 00000000..63d6539c Binary files /dev/null and b/apps/developer-docs/docs/public/images/projects/project-automations.png differ diff --git a/apps/developer-docs/docs/public/images/projects/project-close-automation.png b/apps/developer-docs/docs/public/images/projects/project-close-automation.png new file mode 100644 index 00000000..3de6578e Binary files /dev/null and b/apps/developer-docs/docs/public/images/projects/project-close-automation.png differ diff --git a/apps/developer-docs/docs/public/images/projects/project-create.png b/apps/developer-docs/docs/public/images/projects/project-create.png new file mode 100644 index 00000000..07a06de2 Binary files /dev/null and b/apps/developer-docs/docs/public/images/projects/project-create.png differ diff --git a/apps/developer-docs/docs/public/images/projects/project-features.png b/apps/developer-docs/docs/public/images/projects/project-features.png new file mode 100644 index 00000000..d071550b Binary files /dev/null and b/apps/developer-docs/docs/public/images/projects/project-features.png differ diff --git a/apps/developer-docs/docs/public/images/projects/project-labels.png b/apps/developer-docs/docs/public/images/projects/project-labels.png new file mode 100644 index 00000000..5e7003ea Binary files /dev/null and b/apps/developer-docs/docs/public/images/projects/project-labels.png differ diff --git a/apps/developer-docs/docs/public/images/projects/project-members.png b/apps/developer-docs/docs/public/images/projects/project-members.png new file mode 100644 index 00000000..cefebee0 Binary files /dev/null and b/apps/developer-docs/docs/public/images/projects/project-members.png differ diff --git a/apps/developer-docs/docs/public/images/projects/project-new-state.png b/apps/developer-docs/docs/public/images/projects/project-new-state.png new file mode 100644 index 00000000..79830a91 Binary files /dev/null and b/apps/developer-docs/docs/public/images/projects/project-new-state.png differ diff --git a/apps/developer-docs/docs/public/images/projects/project-states.png b/apps/developer-docs/docs/public/images/projects/project-states.png new file mode 100644 index 00000000..35cf06a0 Binary files /dev/null and b/apps/developer-docs/docs/public/images/projects/project-states.png differ diff --git a/apps/developer-docs/docs/public/images/secure-instance.png b/apps/developer-docs/docs/public/images/secure-instance.png new file mode 100644 index 00000000..181d560e Binary files /dev/null and b/apps/developer-docs/docs/public/images/secure-instance.png differ diff --git a/apps/developer-docs/docs/public/images/sentry/public-integration.webp b/apps/developer-docs/docs/public/images/sentry/public-integration.webp new file mode 100644 index 00000000..18e4cd89 Binary files /dev/null and b/apps/developer-docs/docs/public/images/sentry/public-integration.webp differ diff --git a/apps/developer-docs/docs/public/images/sentry/sentry-permissions.webp b/apps/developer-docs/docs/public/images/sentry/sentry-permissions.webp new file mode 100644 index 00000000..dfaca9b2 Binary files /dev/null and b/apps/developer-docs/docs/public/images/sentry/sentry-permissions.webp differ diff --git a/apps/developer-docs/docs/public/images/sentry/sentry-webhook-config.webp b/apps/developer-docs/docs/public/images/sentry/sentry-webhook-config.webp new file mode 100644 index 00000000..6cd8c8f7 Binary files /dev/null and b/apps/developer-docs/docs/public/images/sentry/sentry-webhook-config.webp differ diff --git a/apps/developer-docs/docs/public/images/set-password.png b/apps/developer-docs/docs/public/images/set-password.png new file mode 100644 index 00000000..8209bfcb Binary files /dev/null and b/apps/developer-docs/docs/public/images/set-password.png differ diff --git a/apps/developer-docs/docs/public/images/signup-signin.png b/apps/developer-docs/docs/public/images/signup-signin.png new file mode 100644 index 00000000..a28e9d19 Binary files /dev/null and b/apps/developer-docs/docs/public/images/signup-signin.png differ diff --git a/apps/developer-docs/docs/public/images/time-tracking/enable-time-tracking.webp b/apps/developer-docs/docs/public/images/time-tracking/enable-time-tracking.webp new file mode 100644 index 00000000..b45070de Binary files /dev/null and b/apps/developer-docs/docs/public/images/time-tracking/enable-time-tracking.webp differ diff --git a/apps/developer-docs/docs/public/images/time-tracking/filter-and-download-worklogs.webp b/apps/developer-docs/docs/public/images/time-tracking/filter-and-download-worklogs.webp new file mode 100644 index 00000000..760ba41b Binary files /dev/null and b/apps/developer-docs/docs/public/images/time-tracking/filter-and-download-worklogs.webp differ diff --git a/apps/developer-docs/docs/public/images/time-tracking/log-work.webp b/apps/developer-docs/docs/public/images/time-tracking/log-work.webp new file mode 100644 index 00000000..ea58b26f Binary files /dev/null and b/apps/developer-docs/docs/public/images/time-tracking/log-work.webp differ diff --git a/apps/developer-docs/docs/public/images/time-tracking/project-settings.webp b/apps/developer-docs/docs/public/images/time-tracking/project-settings.webp new file mode 100644 index 00000000..5ac83d7b Binary files /dev/null and b/apps/developer-docs/docs/public/images/time-tracking/project-settings.webp differ diff --git a/apps/developer-docs/docs/public/images/time-tracking/view-worklogs.webp b/apps/developer-docs/docs/public/images/time-tracking/view-worklogs.webp new file mode 100644 index 00000000..340bffaf Binary files /dev/null and b/apps/developer-docs/docs/public/images/time-tracking/view-worklogs.webp differ diff --git a/apps/developer-docs/docs/public/images/time-tracking/worklog-created.webp b/apps/developer-docs/docs/public/images/time-tracking/worklog-created.webp new file mode 100644 index 00000000..b133ce1e Binary files /dev/null and b/apps/developer-docs/docs/public/images/time-tracking/worklog-created.webp differ diff --git a/apps/developer-docs/docs/public/images/time-tracking/workspace-settings.webp b/apps/developer-docs/docs/public/images/time-tracking/workspace-settings.webp new file mode 100644 index 00000000..b63730f4 Binary files /dev/null and b/apps/developer-docs/docs/public/images/time-tracking/workspace-settings.webp differ diff --git a/apps/developer-docs/docs/public/images/update-plane/docker-volumes.png b/apps/developer-docs/docs/public/images/update-plane/docker-volumes.png new file mode 100644 index 00000000..886fed4d Binary files /dev/null and b/apps/developer-docs/docs/public/images/update-plane/docker-volumes.png differ diff --git a/apps/developer-docs/docs/public/images/view-logs/container-logs.webp b/apps/developer-docs/docs/public/images/view-logs/container-logs.webp new file mode 100644 index 00000000..081a1fb5 Binary files /dev/null and b/apps/developer-docs/docs/public/images/view-logs/container-logs.webp differ diff --git a/apps/developer-docs/docs/public/images/views/create-view-modal.png b/apps/developer-docs/docs/public/images/views/create-view-modal.png new file mode 100644 index 00000000..f3090b2a Binary files /dev/null and b/apps/developer-docs/docs/public/images/views/create-view-modal.png differ diff --git a/apps/developer-docs/docs/public/images/views/view-create-from-existing.png b/apps/developer-docs/docs/public/images/views/view-create-from-existing.png new file mode 100644 index 00000000..2590c9e9 Binary files /dev/null and b/apps/developer-docs/docs/public/images/views/view-create-from-existing.png differ diff --git a/apps/developer-docs/docs/public/images/views/views-overview.png b/apps/developer-docs/docs/public/images/views/views-overview.png new file mode 100644 index 00000000..ebc7042f Binary files /dev/null and b/apps/developer-docs/docs/public/images/views/views-overview.png differ diff --git a/apps/developer-docs/docs/public/images/webhooks/create-webhook.webp b/apps/developer-docs/docs/public/images/webhooks/create-webhook.webp new file mode 100644 index 00000000..d6e07c13 Binary files /dev/null and b/apps/developer-docs/docs/public/images/webhooks/create-webhook.webp differ diff --git a/apps/developer-docs/docs/public/images/workspaces/add-user.png b/apps/developer-docs/docs/public/images/workspaces/add-user.png new file mode 100644 index 00000000..92ee8f0f Binary files /dev/null and b/apps/developer-docs/docs/public/images/workspaces/add-user.png differ diff --git a/apps/developer-docs/docs/public/images/workspaces/create-workspace.png b/apps/developer-docs/docs/public/images/workspaces/create-workspace.png new file mode 100644 index 00000000..1fd1b7a4 Binary files /dev/null and b/apps/developer-docs/docs/public/images/workspaces/create-workspace.png differ diff --git a/apps/developer-docs/docs/public/images/workspaces/remove-user.png b/apps/developer-docs/docs/public/images/workspaces/remove-user.png new file mode 100644 index 00000000..6e4ce712 Binary files /dev/null and b/apps/developer-docs/docs/public/images/workspaces/remove-user.png differ diff --git a/apps/developer-docs/docs/public/images/workspaces/update-user.png b/apps/developer-docs/docs/public/images/workspaces/update-user.png new file mode 100644 index 00000000..e6d8e024 Binary files /dev/null and b/apps/developer-docs/docs/public/images/workspaces/update-user.png differ diff --git a/apps/developer-docs/docs/public/images/workspaces/workspace-settings.png b/apps/developer-docs/docs/public/images/workspaces/workspace-settings.png new file mode 100644 index 00000000..c8ec3be8 Binary files /dev/null and b/apps/developer-docs/docs/public/images/workspaces/workspace-settings.png differ diff --git a/apps/developer-docs/docs/public/logo/dev-logo-dark.png b/apps/developer-docs/docs/public/logo/dev-logo-dark.png new file mode 100644 index 00000000..b1496918 Binary files /dev/null and b/apps/developer-docs/docs/public/logo/dev-logo-dark.png differ diff --git a/apps/developer-docs/docs/public/logo/dev-logo-light.png b/apps/developer-docs/docs/public/logo/dev-logo-light.png new file mode 100644 index 00000000..d3289dc9 Binary files /dev/null and b/apps/developer-docs/docs/public/logo/dev-logo-light.png differ diff --git a/apps/developer-docs/docs/public/logo/dev-logo-watermark-dark.png b/apps/developer-docs/docs/public/logo/dev-logo-watermark-dark.png new file mode 100644 index 00000000..b1325e92 Binary files /dev/null and b/apps/developer-docs/docs/public/logo/dev-logo-watermark-dark.png differ diff --git a/apps/developer-docs/docs/public/logo/dev-logo-watermark-light.png b/apps/developer-docs/docs/public/logo/dev-logo-watermark-light.png new file mode 100644 index 00000000..f49a6177 Binary files /dev/null and b/apps/developer-docs/docs/public/logo/dev-logo-watermark-light.png differ diff --git a/apps/developer-docs/docs/public/logo/favicon-32x32.png b/apps/developer-docs/docs/public/logo/favicon-32x32.png new file mode 100644 index 00000000..7f87b144 Binary files /dev/null and b/apps/developer-docs/docs/public/logo/favicon-32x32.png differ diff --git a/apps/developer-docs/docs/public/robots.txt b/apps/developer-docs/docs/public/robots.txt new file mode 100644 index 00000000..4b66bd3f --- /dev/null +++ b/apps/developer-docs/docs/public/robots.txt @@ -0,0 +1,30 @@ +# robots.txt for Plane Developer Documentation +# https://developers.plane.so + +# Allow all search engines to crawl all content +User-agent: * +Allow: / + +# Disallow crawling of search results (if any) +Disallow: /search + +# Content Signals — AI content usage preferences (comment only; not a standard robots directive) +# https://contentsignals.org/ +# Content-Signal: search=yes, ai-train=yes, ai-input=yes + +# Disallow crawling of any internal/private paths (add as needed) +# Disallow: /private/ +# Disallow: /admin/ +# Disallow: /plane-one/ + +# Sitemap location +Sitemap: https://developers.plane.so/sitemap.xml + +# LLMs.txt — AI-friendly site documentation (discoverable via Link response header) +# https://llmstxt.org/ +# https://developers.plane.so/llms.txt + +# Crawl-delay for polite crawling (optional) +# Crawl-delay: 1 + + diff --git a/apps/developer-docs/docs/self-hosting/editions-and-versions.md b/apps/developer-docs/docs/self-hosting/editions-and-versions.md new file mode 100644 index 00000000..f505aac8 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/editions-and-versions.md @@ -0,0 +1,86 @@ +--- +title: Understanding Plane's editions +description: Compare Plane Community, Pro, Business, and Enterprise editions. Understand features, pricing tiers, and version differences for self-hosted deployments. +keywords: plane editions, plane community edition, plane pro, plane enterprise, plane business, self-hosting comparison, plane pricing tiers +--- + +# Understanding Plane's editions + +Plane comes in four editions by how its deployed. Our Cloud is our only hosted edition as of 2025. Additionally, we offer three unique self-hosted editions tailored to meet two sets of unique needs—the open-source Community Edition, the recommended Commercial Edition, and the Airgapped Edition. + +## About our self-hosted editions + +### Community + +Built with transparency in mind, the Community Edition, + +- Is governed by the AGPL v3.0 license, ensuring free and open usage + +- Allows contribution to the repo by way of modifications and customizations + +- Has no code dependencies or restrictions on and from the Commercial Edition + +It’s ideal for those who want to try Plane first, audit the code for security, and see how each one of services works with the others. Several tens of thousands of uses have used it and a significant number have contributed to it. + +The Community Edition is at par with the Free tier of the Cloud edition in its feature availability. To upgrade to paid plans, you must first switch to the Commercial Edition. + +### Commercial + +Designed for teams that want governance, compliance, and privacy controls, the Commercial Edition is ideal for teams that want to try Plane with an intent to unlock advanced work management and security features. + +This edition also comes with a Free tier, but also lets you upgrade seamlessly to all our paid plans. It offers, + +- Full feature parity with our Cloud + +- A bundle of 12 Free user seats per workspace so there are no surprises when you upgrade + +- An intuitive upgrade flow that automatically calculates the number of seats you need by the number of users with paid roles in your workspace, so you never have to guess + +### Airgapped + +Built for organizations with strict security and compliance requirements, the Airgapped Commercial Edition provides the same powerful features as the Commercial Edition but operates in completely isolated environments without internet connectivity. + +The Airgapped Edition offers: + +- **Complete isolation** + Operates entirely within your network perimeter with no external dependencies or outbound connections. + +- **Full feature parity** + Includes all features available in the standard Commercial Edition, including advanced work management, security controls, and governance tools + +- **Version updates** + Updates from your own docker registry. + +- **Self-contained architecture** + All services, dependencies, and resources are bundled for deployment in restricted networks + +- **Compliance-ready** + Designed to meet requirements for environments that prohibit external network communication + +## Why we separate editions + +We’ve designed Plane’s editions to serve diverse user needs while staying true to the ethos of open source. + +- The **Community Edition** is completely open-source, with no restrictions beyond those outlined in the [AGPL v3.0 license](https://github.com/makeplane/plane/blob/preview/LICENSE.txt). This is the edition that is now ranking at #1 in our space on GitHub. + +- The **Commercial Edition** remains closed-source to offer enterprise-grade features and seamless scalability for businesses. + +- The **Airgapped Edition** extends the Commercial Edition's capabilities to isolated environments, ensuring organizations with strict security requirements can still benefit from Plane's full feature set. + +Unlike some open-core companies, we’ve adopted a clean separation to keep things simple and transparent. There’s no hidden code that limits modifications on the Community Edition, and no forced migrations from one edition to another. + +## Differences in versions between editions + +Each of our editions is built on a distinct codebase. Versions with each differ for how we ship new code per our three separate release cycles. This distinction allows us to + +- Use the Cloud as a test bed for new features before they come to our self-hosted editions + +- Innovate quickly on a more controlled Commercial Edition + +- Be intentful and deliberate with changes to the Community Edition + +For both the Commercial, Airgapped, and Community Editions, version updates are in your control. Regular updates ensure you’re benefiting from the latest features and improvements. See [Update Plane](/self-hosting/manage/upgrade-plane) for how to upgrade your versions. + +## Changelog + +We maintain a detailed changelog for all editions. [Check it out](https://plane.so/changelog) and bookmark it to stay informed about the latest features, bug fixes, and improvements by edition. diff --git a/apps/developer-docs/docs/self-hosting/govern/advanced-search.md b/apps/developer-docs/docs/self-hosting/govern/advanced-search.md new file mode 100644 index 00000000..403e04da --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/advanced-search.md @@ -0,0 +1,291 @@ +--- +title: Configure OpenSearch for advanced search +description: Enable full-text search in Plane with OpenSearch. Configure advanced search indexing for work items, projects, and pages in your self-hosted instance. +keywords: plane opensearch, full-text search, advanced search, search indexing, self-hosting, plane search +--- + +# Configure OpenSearch for advanced search + +Plane uses OpenSearch to provide advanced search capabilities across your workspace. This guide walks you through setting up OpenSearch integration on your self-hosted instance. + +## Before you begin + +You'll need: + +- An OpenSearch instance running version 2.19 or later (self-hosted or managed service like AWS OpenSearch). + +## What you get with advanced search + +Once configured, advanced search provides: + +- **Full-text search** across work items, projects, cycles, modules, pages, and more +- **Fuzzy matching** that tolerates typos and variations in spelling +- **Autocomplete** with instant suggestions as you type +- **Multi-entity search** that searches across all content types in a single query + +Users can access advanced search using the global search shortcut (Cmd/Ctrl + K) or search within specific projects and sections. + +## Configure OpenSearch + +Set environment variables in your Plane configuration. See [Environment variables reference](/self-hosting/govern/environment-variables#opensearch) for details. + +### For Docker deployments + +1. **Add configuration to your environment file** + + Edit `/opt/plane/plane.env`. + + ```bash + # OpenSearch Settings + OPENSEARCH_ENABLED=1 + OPENSEARCH_URL=https://your-opensearch-instance:9200/ + OPENSEARCH_USERNAME=admin + OPENSEARCH_PASSWORD=your-secure-password + OPENSEARCH_INDEX_PREFIX=plane + ``` + +2. **Restart Plane services** + + ```bash + prime-cli restart + ``` + + or if managing containers directly: + + ```bash + docker compose down + docker compose up -d + ``` + +3. **Create search indices** + + Access the API container and create the necessary indices: + + ```bash + # Access the API container + docker exec -it plane-api-1 sh + + # Create all search indices (run once) + python manage.py manage_search_index index rebuild --force + ``` + +4. **Index your existing data** + + Index all existing content into OpenSearch: + + ```bash + # For small datasets + python manage.py manage_search_index document index --force + + # For large datasets (recommended) + python manage.py manage_search_index --background document index --force + ``` + + The background option processes indexing through Celery workers, which is better for instances with large amounts of data. + +### For Kubernetes deployments + +The Plane Helm chart provides auto-setup for OpenSearch. If you're using your own OpenSearch instance, configure it through Helm values. + +1. **Configure Helm values** + + Get the current values file: + + ```bash + helm show values plane/plane-enterprise > values.yaml + ``` + + Edit `values.yaml` to add OpenSearch configuration: + + ```yaml + env: + # OpenSearch configuration + opensearch_remote_url: "https://your-opensearch-instance:9200/" + opensearch_remote_username: "admin" + opensearch_remote_password: "your-secure-password" + opensearch_index_prefix: "plane" + ``` + + Refer to the [Plane Helm chart documentation](https://artifacthub.io/packages/helm/makeplane/plane-enterprise?modal=values&path=env.opensearch_remote_url) for complete values structure. + +2. **Upgrade your deployment** + + ```bash + helm upgrade --install plane-app plane/plane-enterprise \ + --create-namespace \ + --namespace plane \ + -f values.yaml \ + --timeout 10m \ + --wait \ + --wait-for-jobs + ``` + +3. **Create search indices** + + Run these commands in the API pod. + + ```bash + # Get the API pod name + API_POD=$(kubectl get pods -n plane --no-headers | grep api | head -1 | awk '{print $1}') + + # Create all search indices (run once) + kubectl exec -n plane $API_POD -- python manage.py manage_search_index index rebuild --force + ``` + +4. **Index your existing data** + Run these commands in the API pod. + + ```bash + # For small datasets + kubectl exec -n plane $API_POD -- python manage.py manage_search_index document index --force + + # For large datasets (recommended) + kubectl exec -n plane $API_POD -- python manage.py manage_search_index --background document index --force + ``` + +## Verify the setup + +### Check OpenSearch connection + +Test that Plane can connect to your OpenSearch instance: + +```bash +# Access your API container or pod +docker exec -it plane-api-1 sh # For Docker +# OR +kubectl exec -n plane $API_POD -- sh # For Kubernetes + +# Start Python shell +python manage.py shell +``` + +Then run: + +```python +from django.conf import settings +from opensearchpy import OpenSearch + +client = OpenSearch( + hosts=[settings.OPENSEARCH_DSL['default']['hosts']], + http_auth=settings.OPENSEARCH_DSL['default']['http_auth'], + use_ssl=True, + verify_certs=False +) + +# Check cluster health +print(client.cluster.health()) + +# List indices - you should see your Plane indices +print(client.cat.indices(format='json')) +``` + +### Verify indices were created + +List all created indices: + +```bash +python manage.py manage_search_index list +``` + +You should see indices for work items, projects, cycles, modules, pages, and other searchable entities. + +### Test search functionality + +1. Sign in to your Plane instance. +2. Press **Cmd/Ctrl + K** to open global search. +3. Type a search query and verify results appear. +4. Test search within projects, work items, and pages. + +## Maintenance + +### Resync data + +If search results become stale or inconsistent, resync your data: + +```bash +python manage.py manage_search_index document index --force +``` + +This reindexes all content without recreating the index structure. + +### Complete rebuild + +For a complete reset (recreates indices and reindexes all data): + +```bash +# Recreate all indices +python manage.py manage_search_index index rebuild --force + +# Reindex all documents +python manage.py manage_search_index document index --force +``` + +Use this if index structure needs updating or if you're experiencing persistent issues. + +### Monitor logs + +Check API logs OpenSearch-related errors: + +**Docker:** + +```bash +docker compose logs api | grep -i opensearch +``` + +**Kubernetes:** + +```bash +kubectl logs -n plane -l app.kubernetes.io/component=api | grep -i opensearch +``` + +## Understanding how it works + +Advanced search in Plane maintains search indices separately from your main database. This separation is why search can be fast even with thousands of work items - OpenSearch is purpose-built for search operations, while your database handles transactional operations. + +### Why Plane uses OpenSearch + +Traditional database searches struggle with fuzzy matching and typos. If you search for "authentcation" (with a typo), a database won't find "authentication". OpenSearch handles this naturally because it analyzes text differently - it breaks words into tokens, normalizes variations, and understands linguistic patterns. + +This is why autocomplete feels instant. OpenSearch pre-processes text to match partial words, while your database would need to scan entire tables to achieve similar results. + +### The synchronization challenge + +The trade-off with separate search indices is keeping them synchronized with your database. When someone updates a work item, that change must reach OpenSearch for search results to remain accurate. + +Plane solves this through an event-driven architecture. Every time data changes in your database, Django emits a signal. These signals trigger updates to OpenSearch. + +### Batching for efficiency + +Direct, immediate updates would overwhelm both your database and OpenSearch. Imagine a user creating 50 work items in quick succession, that would mean 50 separate API calls to OpenSearch, each with network overhead. + +Instead, Plane batches updates through Redis. When a signal fires, the update goes into a Redis queue. A Celery worker processes this queue every 5 seconds, combining multiple updates into efficient batch operations. This is why you might notice a brief delay (up to 5 seconds) before new content appears in search results. + +The batching pattern also provides resilience. If OpenSearch is temporarily unavailable, updates accumulate in Redis and process once connectivity returns. This requires Redis 6.2+ which supports the LPOP count operation needed for efficient batch retrieval. + +### The complete flow + +![OpenSeach flow](/images/open-search/opensearch-flow.webp#hero) + +When you search, queries bypass this synchronization process entirely. The Plane API sends your search query directly to OpenSearch, which returns results almost instantly. Your database isn't involved in search queries at all — this is the key to search performance. + +### Index organization + +Plane creates nine separate indices in OpenSearch, one for each searchable entity type. This separation might seem redundant - why not put everything in one index? + +The answer lies in how different entities need different search behaviors. Work items use fuzzy matching and field prioritization (title matches rank higher than description matches). Projects emphasize metadata filtering—status, member counts, and timelines. Pages analyze long-form content structure. + +Each index is optimized for its content type: + +| Index | Content | Search Features | +| ------------------------- | ----------- | ------------------------------------------------------------------------ | +| `{prefix}_issues` | Work items | Full-text search, field weighting (title > description), state filtering | +| `{prefix}_issue_comments` | Comments | Comment search within work items, parent-child relationships | +| `{prefix}_projects` | Projects | Project discovery, metadata filtering (dates, counts, status) | +| `{prefix}_cycles` | Cycles | Cycle search, time-based filtering and aggregations | +| `{prefix}_modules` | Modules | Module/sprint search, planning aggregations | +| `{prefix}_pages` | Pages | Page content search, rich text analysis for long-form content | +| `{prefix}_workspaces` | Workspaces | Workspace search and discovery | +| `{prefix}_issue_views` | Saved views | Saved view search and filtering | +| `{prefix}_teamspaces` | Teamspaces | Teamspace discovery | + +The `{prefix}` is whatever you configured in `OPENSEARCH_INDEX_PREFIX`, or empty if you didn't set a prefix. This prefix exists because you might run multiple Plane instances pointing to the same OpenSearch cluster. The prefix prevents different instances from accidentally sharing or conflicting with each other's indices. diff --git a/apps/developer-docs/docs/self-hosting/govern/authentication.md b/apps/developer-docs/docs/self-hosting/govern/authentication.md new file mode 100644 index 00000000..23198e95 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/authentication.md @@ -0,0 +1,23 @@ +--- +title: Overview +description: Configure authentication methods for self-hosted Plane. Setup OAuth, SSO, SAML, OIDC, LDAP and other authentication providers. +keywords: plane authentication, auth providers, sso setup, oauth configuration, saml, oidc, ldap, self-hosting security +--- + +# Overview + +Plane offers several methods you can choose from to let your users log in to your Plane instance. Configure these methods in Authentication on /god-mode of your instance. + + + +## Authentication methods + +### Unique code + +Plane lets your users log in with codes sent over email. This is disabled if SMTP is not configured for your instance. See [Communication](https://app.plane.so/plane/projects/e3ea12b0-62e3-4b8d-8ada-3379f4efc563/pages/e83af23e-b120-47b0-b241-2bee39037505) to set up SMTP if you wish to enable unique codes. + +### Passwords + +Your users can log in with passwords that they or you set for them. This is toggled on when SMTP isn't configured for your instance. Disable it if you would like to use another authentication method below. diff --git a/apps/developer-docs/docs/self-hosting/govern/communication.md b/apps/developer-docs/docs/self-hosting/govern/communication.md new file mode 100644 index 00000000..e4a0d34f --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/communication.md @@ -0,0 +1,56 @@ +--- +title: Configure SMTP for email notifications +description: Configure SMTP email settings for Plane. Setup email notifications and communication for your self-hosted instance. +keywords: plane smtp, email notifications, smtp configuration, email setup, self-hosting, plane email, mail server +--- + +# Configure SMTP for email notifications + +Either during your set-up or sometime later, you will want to set SMTP settings to let your users get emails to reset passwords, onboard themselves right, and get notifications for changes, and receive exports of your data. + +::: info +Plane currently supports SMTP authentication only via email and password. OAuth-based SMTP configurations aren’t supported. +::: + +## Configuration + +Plane offers an interface to configure Simple Mail Transfer Protocol (SMTP) and SSL for encrypted email communication. + +Navigate to `Email` in `/god-mode`and you will see ↓. +![](/images/instance-admin/email-settings.png) + +- **Host**\ + The address of your SMTP server. +- **Port**\ + The port for outgoing emails. +- **Sender email address**\ + The email address you wish to use as the sender of emails. +- **Email security**\ + Toggle `TLS` or `SSL` as the email security layer for your emails. If you do not wish to use either of them, you can choose the `No email security` option. +- **Authentication**\ + You can configure the username and password to authenticate the SMTP server to send emails. It's an optional configuration, but we would advise you to provide authentication details for a secure email delivery experience. + - **Username**\ + Specify the username for the SMTP configuration here. + - **Password**\ + Specify the password for the SMTP configuration here. + +::: tip +**Google Workspaces** + +If your Plane instance is not accessible on the internet, Gmail may block profile photos or other embedded images in email notifications. This occurs because Gmail uses Google's secure image proxy to serve images for security purposes. + +To resolve this issue, you must configure the Image URL proxy allowlist in your Google Workspace settings to include your Plane instance's URL. Refer to Google’s documentation for instructions: [Allowlist image URLs](https://support.google.com/a/answer/3299041?hl=en). +::: + +## Configuration for popular email services providers + +### Amazon SES + +1. Sign in to [**https://console.aws.amazon.com/ses**](https://console.aws.amazon.com/ses). +2. Navigate to **SMTP Settings** in the sidebar. +3. Click **Create My SMTP Credentials**. +4. Follow prompts to create a user in the **Create User for SMTP** dialog box, then click **Create**. +5. Select **Show User SMTP Credentials** to view the user's SMTP credentials. +6. Return to your Plane instance's `/god-mode` and enter the obtained details. + +Ensure to review [**email quotas**](https://docs.aws.amazon.com/ses/latest/dg/quotas.html) for your Amazon SES server. Consider managing email recipients using groups to optimize usage. diff --git a/apps/developer-docs/docs/self-hosting/govern/configure-dns-email-service.md b/apps/developer-docs/docs/self-hosting/govern/configure-dns-email-service.md new file mode 100644 index 00000000..ac0e2320 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/configure-dns-email-service.md @@ -0,0 +1,183 @@ +--- +title: Configure DNS for Intake Email +description: Configure DNS MX records for Plane intake email. Route incoming emails to create work items automatically in your self-hosted instance. +keywords: plane dns configuration, mx records, intake email, email to issue, self-hosting, plane email setup, dns setup +--- + +# Configure DNS for Intake Email + +This guide explains how to configure DNS settings to enable the [Intake Email](https://docs.plane.so/intake/intake-email) feature for your self-hosted Plane instance. These configurations enable your server to accept messages sent to your project's dedicated Intake address, which are then converted into work items in your project's Intake section. + +## Prerequisites + +Ensure that the Plane server allows inbound traffic on the following email-related ports: `25`, `465`, and `587`. + +If any of these ports are currently in use, you can free them by running: + +```bash +fuser -k 25/tcp 465/tcp 587/tcp +``` + +## Generate SSL/TLS Certificate for Email Domain + +::: warning +Mandatory for Docker Compose deployments only. +::: +Before configuring DNS records for Intake Email, secure your email domain with an SSL/TLS certificate. This ensures encrypted communication between mail servers and improves email trust and deliverability. + +1. **Install Certbot** + Update your system and install Certbot. + + ```bash + sudo apt update && sudo apt install certbot + ``` + + For NGINX: + + ```bash + sudo apt install python3-certbot-nginx + ``` + + For Apache: + + ```bash + sudo apt install python3-certbot-apache + ``` + +2. **Generate SSL Certificate** + Choose the method that matches your web server setup: + + For NGINX: + + ```bash + sudo certbot --nginx -d + ``` + + For Apache: + + ```bash + sudo certbot --apache -d + ``` + + For standalone (no web server): + + ```bash + sudo certbot certonly --standalone -d + ``` + +3. **Copy Certificate Files** + Copy the generated certificate files to Plane's expected directory: + + ```bash + sudo cp /etc/letsencrypt/live//fullchain.pem /opt/plane/data/email/tls/cert.pem + sudo cp /etc/letsencrypt/live//privkey.pem /opt/plane/data/email/tls/key.pem + ``` + +4. **Configure Environment Variables** + Add the following settings to your plane.env file: + + ```bash + # If using SMTP_DOMAIN as FQDN (e.g., intake.example.com), + # generate a valid SSL certificate and set these paths accordingly. + SMTP_DOMAIN=intake.example.com + TLS_CERT_PATH=tls/cert.pem + TLS_PRIV_KEY_PATH=tls/key.pem + INTAKE_EMAIL_DOMAIN=intake.example.com + ``` + + ::: warning + Important: `SMTP_DOMAIN` and `INTAKE_EMAIL_DOMAIN` must be identical. + ::: + +## Configure DNS records + +1. **Create an A Record** + This record points to the server running your email service. + + ```bash + Type: A + Host: # Example: plane.example.com + Value: # Your server's public IP address + TTL: Auto | 3600 + ``` + + ::: tip + You can alternatively use a CNAME record if you're using a cloud load balancer. + ::: + +2. **Add an MX Record** + This record directs email traffic to your mail server. + + ```bash + Type: MX + Host: # Example: intake.example.com + Value: # Same as your A record host + Priority: 10 + TTL: Auto | 3600 + ``` + +3. **Configure an SPF Record** + This record helps prevent email spoofing. + + ```bash + Type: TXT + Host: # Example: intake.example.com + Value: "v=spf1 ip4: -all" + TTL: Auto | 3600 + ``` + +4. **Set Up a DMARC record** + This record specifies how receiving mail servers should handle authentication failures. + + ```bash + Type: TXT + Host: _dmarc. # Example: _dmarc.intake.example.com + Value: "v=DMARC1; p=reject; rua=mailto:" + TTL: Auto | 3600 + ``` + +## Verify your configuration + +After setting up your DNS records, verify that they're correctly configured: + +```bash +# Verify A record +dig A + +# Verify MX record +dig MX + +# Verify SPF record +dig TXT + +# Verify DMARC record +dig TXT _dmarc. +``` + +You can also use [MXToolbox](https://mxtoolbox.com) to check for any issues with your DNS configuration. + +## Test your mail server + +Once your DNS records have propagated, test your SMTP connections: + +```bash +# Test SMTP connection on standard ports +telnet 25 +telnet 465 +telnet 587 +``` + +## Troubleshooting + +- MX Record issues + - Ensure there's a proper dot at the end of the domain. + - Check that the priority number is correct (lower = higher priority). + - Allow 24-48 hours for DNS changes to fully propagate. + +- A Record issues + - Verify that the IP address is correct. + - Ensure your mail subdomain matches the MX record. + +## See also + +[Intake Email](https://docs.plane.so/intake/intake-email) diff --git a/apps/developer-docs/docs/self-hosting/govern/configure-ssl.md b/apps/developer-docs/docs/self-hosting/govern/configure-ssl.md new file mode 100644 index 00000000..d2159769 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/configure-ssl.md @@ -0,0 +1,113 @@ +--- +title: Set up SSL +description: Configure SSL/TLS certificates for Plane. Setup HTTPS encryption for secure self-hosted Plane deployment. +keywords: plane ssl, https setup, tls certificate, ssl configuration, lets encrypt, secure deployment, self-hosting security +--- + +# Set up SSL + +This guide shows you how to configure SSL/TLS certificates for your self-hosted Plane instance. Plane handles certificate provisioning and renewal automatically using Let's Encrypt. + +::: info +**Applies to:** Docker deployments of Plane Commercial Edition without an external reverse proxy. + +If you're using an external reverse proxy (nginx, Caddy, Traefik) or a load balancer, configure SSL there instead and skip this guide. +::: + +## Before you begin + +Ensure you have: + +- A registered domain name pointing to your Plane server +- DNS records configured (A or CNAME record pointing to your server's IP) +- Ports 80 and 443 open on your server's firewall +- Prime CLI installed (included with Plane Commercial Edition) + +::: warning +**DNS must be configured first.** Let's Encrypt validates domain ownership by making HTTP requests to your domain. Ensure your domain resolves to your server's IP address before proceeding. +::: + +## Configure SSL settings + +### Open the configuration file + +Edit your Plane environment configuration: + +```bash +vim /opt/plane/plane.env +``` + +### Set required variables + +Add or update these environment variables: + +```bash +# SSL Configuration +CERT_EMAIL=admin@yourcompany.com +SITE_ADDRESS=plane.yourcompany.com +WEB_URL=https://plane.yourcompany.com +``` + +**Variable explanations:** + +**CERT_EMAIL** +A valid email address for Let's Encrypt certificate registration. Let's Encrypt uses this to send renewal reminders and important notices about your certificates. + +**SITE_ADDRESS** +Your domain name **without** protocol. Use only the domain (e.g., `plane.company.com`), not `https://plane.company.com`. Plane's built-in proxy uses this to request certificates from Let's Encrypt. + +**WEB_URL** +Your full Plane URL **with** the `https://` protocol. This tells Plane services how to construct URLs for redirects, emails, and API responses. + +### DNS provider configuration (optional) + +If you're using Cloudflare or another DNS provider with API access, you can use DNS validation instead of HTTP validation. This is useful if: + +- Your server is behind a firewall that blocks port 80 +- You need wildcard certificates +- HTTP validation isn't working due to network restrictions + +**For Cloudflare:** + +```bash +CERT_ACME_DNS=acme_dns cloudflare +``` + +Replace `` with your Cloudflare API token. Create one at **Cloudflare Dashboard** → **My Profile** → **API Tokens** with **Zone:DNS:Edit** permissions. + +**For other DNS providers:** + +Check the [acme.sh DNS API documentation](https://github.com/acmesh-official/acme.sh/wiki/dnsapi) for provider-specific configuration. + +## Apply SSL configuration + +Restart Plane to apply the SSL settings: + +```bash +sudo prime-cli restart +``` + +Prime CLI will: + +1. Stop all Plane services +2. Request a new SSL certificate from Let's Encrypt +3. Configure the built-in proxy to use HTTPS +4. Restart all services with SSL enabled + +This process typically takes 30-60 seconds. + +## Verify SSL is working + +Check that your Plane instance is accessible via HTTPS: + +```bash +curl -I https://plane.yourcompany.com +``` + +You should see a response with `HTTP/2 200` or `HTTP/1.1 200` and SSL-related headers. + +Visit your Plane instance in a browser at `https://plane.yourcompany.com`. You should see a secure connection (padlock icon) without certificate warnings. + +## Using custom SSL certificates + +Custom SSL certificates (from a corporate CA or purchased certificates) are not currently supported in Plane's deployment. diff --git a/apps/developer-docs/docs/self-hosting/govern/custom-domain.md b/apps/developer-docs/docs/self-hosting/govern/custom-domain.md new file mode 100644 index 00000000..9deeff46 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/custom-domain.md @@ -0,0 +1,142 @@ +--- +title: Configure custom domain +description: Configure custom domain for self-hosted Plane. Setup your own domain name for your Plane instance. +keywords: plane custom domain, domain setup, dns configuration, self-hosting, plane domain name, custom url +--- + +# Configure custom domain + +During installation, you configure a domain for your instance. If you need to change that domain later, whether you're moving to a production domain, switching to a different hostname, or updating your DNS configuration, this guide walks you through the process. + +:::info +**Prime CLI is for Docker installations only.** These commands only work on Plane instances originally installed using `prime-cli`. + +If you're running Kubernetes or another deployment method, the environment variable names are the same, but the configuration method differs based on your setup. +::: + +:::warning +**Plan for downtime** +Changing domains requires restarting Plane services. Your instance will be unavailable for a few minutes during the restart. Plan accordingly or notify your users. +::: + +## Check current domain configuration + +First, see which environment variables currently reference your old domain. This helps you identify exactly what needs updating. + +```bash +cat /opt/plane/plane.env | grep +``` + +**Example output:** + +```ini +DOMAIN_NAME=localhost +SITE_ADDRESS=http://localhost +WEB_URL=http://localhost +CORS_ALLOWED_ORIGINS=http://localhost,https://localhost +``` + +This shows you all the variables that contain your current domain. You'll update each of these in the next step. + +## Update domain in environment file + +1. Open the Plane environment configuration file: + + ```bash + vim /opt/plane/plane.env + ``` + +2. Find and update these environment variables with your new domain: + - **DOMAIN_NAME** + + Set this to your bare domain name without protocol: + + ```ini + DOMAIN_NAME=plane.company.com + ``` + + Don't include `http://` or `https://` here, just the hostname. + - **SITE_ADDRESS** + + Set this to your full domain URL: + + ```ini + SITE_ADDRESS=https://plane.company.com + ``` + + Include the protocol (`https://` for SSL, `http://` if you haven't set up SSL yet). + - **WEB_URL** + + This should match your SITE_ADDRESS: + + ```ini + WEB_URL=https://plane.company.com + ``` + + Again, include the full protocol. + + **CORS_ALLOWED_ORIGINS** + + List all domains that should be allowed to make cross-origin requests to your Plane instance. This typically includes both HTTP and HTTPS versions of your domain: + + ```ini + CORS_ALLOWED_ORIGINS=https://plane.company.com,http://plane.company.com + ``` + + Separate multiple entries with commas, no spaces. If you have multiple domains or subdomains that need access, add them all here. + +## Restart Plane services + +Apply your configuration changes by restarting Plane: + +```bash +sudo prime-cli restart +``` + +This process typically takes a few minutes. You'll see output indicating the status of each service as it restarts. + +::: details Community Edition + +Our steps differ slightly depending on whether you are hosting on a public IP or a private/internal IP. Follow the steps listed below. + +#### Update configuration in .env file + +Open your project's `.env` file in a text editor. This file contains configuration settings for your application. Locate the following lines: + +``` +WEB_URL= +CORS_ALLOWED_ORIGINS= +``` + +Replace `` with your actual domain name, including the protocol (http:// or https://). For example: + +``` +WEB_URL=https://example.com +CORS_ALLOWED_ORIGINS=https://example.com +``` + +If you are hosting Plane on a public IP, then follow the steps here. However, if you are hosting Plane on an internal IP then follow these steps. + +#### Set DNS A record (for public IP) + +If your server has a public IP address, you need to configure the DNS A record to point to this IP address. This allows users to access your application using your custom domain name. Here’s how to do it: + +- Log in to your domain registrar's website or DNS hosting provider. +- Navigate to the DNS management section. +- Find the option to edit your domain's DNS records. +- Add a new A record with the hostname set to `@` (or your subdomain if applicable) and the IP address set to your server's public IP address. +- Save the changes. It may take some time for the DNS changes to propagate. + +#### Configure reverse proxy (for internal IP) + +If your server is behind a firewall or router and has an internal IP address, you'll need to set up a reverse proxy to route requests from your custom domain to your server. Follow these steps: + +- Configure a CNAME record in your domain's DNS settings that points to your reverse proxy server's hostname. This allows your domain to resolve to the reverse proxy server. + +- Set up reverse proxy redirection on your reverse proxy server to forward incoming requests to your server's internal IP address and port. + +- Depending on the reverse proxy software you're using (e.g., Nginx, Apache, etc.), the configuration process may vary. Refer to the documentation for your specific reverse proxy server for detailed instructions on setting up reverse proxy redirection. + +- Once the reverse proxy is properly configured, ensure that your firewall/router allows incoming traffic on the necessary ports to reach your server. + +By following these steps, you will be able to access your self-hosted instance of Plane using your custom domain name, whether your server has a public IP address or is behind a firewall with an internal IP address. diff --git a/apps/developer-docs/docs/self-hosting/govern/database-and-storage.md b/apps/developer-docs/docs/self-hosting/govern/database-and-storage.md new file mode 100644 index 00000000..1257d54b --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/database-and-storage.md @@ -0,0 +1,142 @@ +--- +title: Configure external services +description: Configure external database and storage for Plane. Setup PostgreSQL, Redis, and S3-compatible storage services. +keywords: plane external database, postgresql setup, redis configuration, s3 storage, minio, self-hosting, plane storage +--- + +# Configure external services + +The Prime CLI lets you easily configure your Commercial Edition instance, providing options to customize the PostgreSQL database, Redis, external storage, and other advanced settings. + +::: warning +**Prime CLI is for Docker installations only.** These commands only work on Plane instances originally installed using `prime-cli`. +::: + +1. Run the Prime CLI with ↓: + +`sudo prime-cli` + +2. Once the CLI is running, enter `configure`, which will guide you through a step-by-step form where you can specify the following: + +- `Listening port` + Define the port for the built-in reverse proxy. + _Default_: `80` + +- `Max file-upload size` + Set the maximum file size (in MB) that members can upload. + _Default_: `5 MB` + +- `External Postgres URL` + Provide the URL of your external PostgreSQL instance if you want to switch from the default Plane configuration. + _Default_: `Postgres 15.5` in the Docker container. + +::: warning +Don’t use a database on your local machine. If you use `localhost` in the URL, it won’t work. Make sure to use a database hosted on a network-accessible server. + +Avoid using special characters in your PostgreSQL password. +::: + +- `External Redis URL` + Specify the URL of your external Redis instance to override the default Redis configuration. + _Default_: `Redis 7.2.4` + +- `External storage` + Plane currently supports only S3 compatible storages. + _Default_: `MinIO` + 1. Ensure your IAM user has the following permissions on your S3 bucket. + - **s3:GetObject** + To access the objects. + - **s3:PutObject** + To upload new assets using the presigned url. + 2. Configure the CORS policy on your bucket to enable presigned uploads. Use the example policy below, making sure to replace `` with your actual domain. + + ``` + [ + { + "AllowedHeaders": [ + "*" + ], + "AllowedMethods": [ + "GET", + "POST", + "PUT", + "DELETE", + "HEAD" + ], + "AllowedOrigins": [ + "", + ], + "ExposeHeaders": [ + "ETag", + "x-amz-server-side-encryption", + "x-amz-request-id", + "x-amz-id-2" + ], + "MaxAgeSeconds": 3000 + } + ] + ``` + + 3. Switch to your external storage by providing the following values: + - S3 access key ID  + - S3 secret access key + - S3 bucket name + - S3 region  + - S3 endpoint URL + +3. After confirming your choices, your instance will automatically restart with the updated configuration. + +::: details Community Edition + +To configure external Postgres, Redis, and S3 storage for the Plane Community Edition, you’ll need to adjust several environment variables in the plane.env file. Follow this guide to set up each component using the correct values for your external services. + +1. Open the `plane.env` file on your server where Plane is installed. + +2. In the **DB SETTINGS** section, update the variables to connect to your external Postgres instance: + + ```bash + # DB SETTINGS + PGHOST=your-external-postgres-host # Replace with the hostname or IP address of your Postgres server. + PGDATABASE= # Leave blank when using external database. + POSTGRES_USER=your-postgres-username # The username to access Postgres. + POSTGRES_PASSWORD=your-postgres-password # Password for the Postgres user. + POSTGRES_DB=your-database-name # The name of the database Plane should connect to. + POSTGRES_PORT=5432 # Port where Postgres is accessible (usually 5432). + PGDATA=/var/lib/postgresql/data # No need to change this for external Postgres. + DATABASE_URL= # Leave this empty if you're providing values for the variables above. If you choose to use the DATABASE_URL, you can leave all the other database-related variables empty. + ``` + + ::: warning + Don’t use a database on your local machine. If you use `localhost` in the URL, it won’t work. Make sure to use a database hosted on a network-accessible server. + + Avoid using special characters in your PostgreSQL password. + ::: + +3. In the **REDIS SETTINGS** section, update the variables to connect to your external Redis instance: + + ```bash + # REDIS SETTINGS + REDIS_HOST=your-external-redis-host # Hostname or IP of the Redis server. + REDIS_PORT=6379 # Port where Redis is accessible (default is 6379). + REDIS_URL= # Leave this empty if you're providing values for the variables above. If you choose to use the REDIS_URL, you can leave all the other redis-related variables empty. + ``` + +4. In the **DATA STORE SETTINGS** section, update the variables for any S3-compatible storage: + ```bash + # DATA STORE SETTINGS + USE_MINIO=0 # Set to 0 if using an external S3, 1 if using MinIO (default). + AWS_REGION=your-s3-region # For AWS, set the region, e.g., "us-west-1". + AWS_ACCESS_KEY_ID=your-s3-access-key # Access key for S3. + AWS_SECRET_ACCESS_KEY=your-s3-secret-key # Secret key for S3. + AWS_S3_ENDPOINT_URL=https://your-s3-endpoint # URL for S3 API endpoint (e.g., "https://s3.amazonaws.com" for AWS). + AWS_S3_BUCKET_NAME=your-s3-bucket-name # Name of the S3 bucket for storing Plane data. + MINIO_ROOT_USER= # Leave blank when using external S3. + MINIO_ROOT_PASSWORD= # Leave blank when using external S3. + BUCKET_NAME= # Leave blank when using external S3. + FILE_SIZE_LIMIT=5242880 # Set maximum file upload size in bytes (5MB here). + ``` +5. Save your changes to the `plane.env` file. + +6. Restart Plane services to apply the new settings using the `setup.sh` script. + +::: diff --git a/apps/developer-docs/docs/self-hosting/govern/environment-variables.md b/apps/developer-docs/docs/self-hosting/govern/environment-variables.md new file mode 100644 index 00000000..9c9bf3d5 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/environment-variables.md @@ -0,0 +1,424 @@ +--- +title: Environment variables reference +description: Configure environment variables for Plane. Complete reference of all configuration options and settings. +keywords: plane environment variables, configuration reference, env settings, plane config, self-hosting settings, plane env +--- + +# Environment variables reference + +This guide provides a comprehensive overview of all environment variables used in the Commercial Edition. These variables allow you to customize your Plane instance to best fit your organization's needs. + +## Where to find the .env file + +The environment file for Plane Commercial Edition is located at: + +```bash +/opt/plane/plane.env +``` + +This is where you'll make all configuration changes. Remember to restart the instance after making changes to ensure they take effect. + +## Environment variables + +### General settings + +| Variable | Description | Default Value | +| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| **INSTALL_DIR** | Directory where Plane is installed. | /opt/plane | +| **DOMAIN_NAME** | Primary domain name for your Plane instance. This determines how users will access your installation. | localhost | +| **APP_RELEASE_VERSION** | The version of Plane Commercial Edition you're running. This helps with troubleshooting and ensures compatibility. | _Current release version_ | +| **WEB_URL** | The complete base URL for the web application including protocol (e.g., `https://plane.example.com`). | http://localhost | +| **CORS_ALLOWED_ORIGINS** | Comma-separated list of origins allowed to make cross-origin requests to your API. Usually, this should include your WEB_URL. | http://localhost | +| **DEBUG** | Toggles debug mode for more verbose logging and debugging information. | 0 (disabled) | + +### Scaling and performance + +| Variable | Description | Default Value | +| -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------- | +| **WEB_REPLICAS** | Number of web server replicas for load balancing. | 1 | +| **SPACE_REPLICAS** | Number of space service replicas for workspaces. | 1 | +| **ADMIN_REPLICAS** | Number of admin service replicas. | 1 | +| **API_REPLICAS** | Number of API service replicas. | 1 | +| **WORKER_REPLICAS** | Number of worker service replicas for background tasks. | 1 | +| **BEAT_WORKER_REPLICAS** | Number of beat worker replicas for scheduled tasks. | 1 | +| **LIVE_REPLICAS** | Number of live service replicas for real-time updates. | 1 | +| **GUNICORN_WORKERS** | Number of Gunicorn workers for handling web requests. Increase for better performance on high-traffic instances. | 2 | +| **SILO_REPLICAS** | Number of Silo (integration) service replicas. | 1 | +| **IFRAMELY_REPLICAS** | Number of Iframely service replicas for link previews and embeds. | 1 | +| **EMAIL_REPLICAS** | Number of email service replicas. Set to `1` to enable the built-in email intake service. | 0 | +| **AUTOMATION_CONSUMER_REPLICAS** | Number of automation consumer replicas for processing automation events. | 1 | +| **OUTBOX_POLLER_REPLICAS** | Number of outbox poller replicas for event processing. | 1 | +| **PI_API_REPLICAS** | Number of Plane Intelligence API replicas. Set to `1` to enable AI features. | 0 | +| **PI_BEAT_REPLICAS** | Number of Plane Intelligence beat worker replicas for scheduled AI tasks. | 0 | +| **PI_WORKER_REPLICAS** | Number of Plane Intelligence worker replicas for background AI processing. | 0 | +| **PI_MIGRATOR_REPLICAS** | Number of Plane Intelligence migrator replicas. Set to `1` to run PI database migrations. | 0 | + +### Networking and security + +| Variable | Description | Default Value | +| ------------------------ | ----------------------------------------------------------------------------------------------------------------- | ------------- | +| **LISTEN_HTTP_PORT** | Port for HTTP traffic. | 80 | +| **LISTEN_HTTPS_PORT** | Port for HTTPS traffic. | 443 | +| **APP_PROTOCOL** | Protocol to be used, either `http` or `https`. | http | +| **TRUSTED_PROXIES** | CIDR notation of trusted proxies for request forwarding. Important when behind load balancers or reverse proxies. | 0.0.0.0/0 | +| **SSL_VERIFY** | Whether to verify SSL certificates for outgoing connections. Set to `0` only in development environments. | 1 | +| **LISTEN_SMTP_PORT_25** | Port for SMTP traffic on port 25. | 25 | +| **LISTEN_SMTP_PORT_465** | Port for SMTPS traffic on port 465. | 465 | +| **LISTEN_SMTP_PORT_587** | Port for SMTP submission traffic on port 587. | 587 | + +### SSL and certificates + +| Variable | Description | Default Value | +| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------- | +| **CERT_EMAIL** | Email used for SSL certificate registration with Let's Encrypt or other ACME providers. | admin@example.com | +| **CERT_ACME_CA** | ACME Certificate Authority URL for SSL certificate issuance. | https://acme-v02.api.letsencrypt.org/directory | +| **CERT_ACME_DNS** | DNS provider configuration for SSL certificate domain validation. Format varies by provider. | | +| **SITE_ADDRESS** | The domain name and port required by Caddy for serving your Plane instance. This determines how Caddy will handle incoming requests. | localhost:80 | + +### Database settings + +| Variable | Description | Default Value | +| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| **PGHOST** | Hostname or IP address of your PostgreSQL server. | plane-db | +| **PGDATABASE** | Name of the PostgreSQL database Plane will use. | plane | +| **POSTGRES_USER** | Username for PostgreSQL authentication. | plane | +| **POSTGRES_PASSWORD** | Password for PostgreSQL authentication. **Critical:** Use a strong, unique password here. | plane | +| **POSTGRES_DB** | Same as PGDATABASE - the name of the PostgreSQL database. | plane | +| **POSTGRES_PORT** | TCP port your PostgreSQL server is listening on. | 5432 | +| **PGDATA** | Directory path where PostgreSQL data is stored. Only relevant if you're managing PostgreSQL within the same container/system. | /var/lib/postgresql/data | +| **DATABASE_URL** | Full connection string for PostgreSQL. If provided, this takes precedence over individual connection parameters. Format: `postgresql://username:password@host:port/dbname` | | + +### Redis settings + +| Variable | Description | Default Value | +| -------------- | -------------------------------------------- | ------------- | +| **REDIS_HOST** | Hostname or IP address of your Redis server. | plane-redis | +| **REDIS_PORT** | TCP port your Redis server is listening on. | 6379 | +| **REDIS_URL** | Full connection string for Redis. | | + +### RabbitMQ settings + +| Variable | Description | Default Value | +| -------------------------- | --------------------------------------------------------------------------------------- | ------------- | +| **RABBITMQ_HOST** | Hostname or IP address of your RabbitMQ server. | plane-mq | +| **RABBITMQ_PORT** | TCP port your RabbitMQ server is listening on. | 5672 | +| **RABBITMQ_DEFAULT_USER** | Username for RabbitMQ authentication. | plane | +| **RABBITMQ_DEFAULT_PASS** | Password for RabbitMQ authentication. | plane | +| **RABBITMQ_DEFAULT_VHOST** | Virtual host for RabbitMQ, providing logical separation of resources. | plane | +| **RABBITMQ_VHOST** | Virtual host name for RabbitMQ used by application services. | plane | +| **AMQP_URL** | Full connection string for RabbitMQ. Format: `amqp://username:password@host:port/vhost` | | + +### Authentication and security + +| Variable | Description | Default Value | +| -------------------------- | ------------------------------------------------------------------------------------------------------- | ------------- | +| **SECRET_KEY** | Secret key used for various cryptographic operations, including JWT token signing. | | +| **MACHINE_SIGNATURE** | Unique identifier for your instance, used for licensing and authentication. | | +| **LIVE_SERVER_SECRET_KEY** | Secret key for communication between the API and live (real-time) service. Must match on both services. | | +| **PI_INTERNAL_SECRET** | Secret key for internal communication with the Plane Intelligence service. | | +| **SILO_HMAC_SECRET_KEY** | HMAC secret key for authenticating requests between the API and Silo (integration) service. | | +| **AES_SECRET_KEY** | AES encryption key used for encrypting sensitive integration credentials at rest. | | + +### File Storage (MinIO / S3) + +| Variable | Description | Default Value | +| ------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ----------------------- | +| **USE_MINIO** | Determines whether to use MinIO for object storage. Set to `1` to enable MinIO, `0` to use configured S3 or local storage. | 1 | +| **USE_STORAGE_PROXY** | Whether to proxy file storage requests through the application server. Set to `1` to enable. | 0 | +| **AWS_REGION** | AWS region for S3 storage services. | | +| **AWS_ACCESS_KEY_ID** | Access key for MinIO or AWS S3 authentication. | | +| **AWS_SECRET_ACCESS_KEY** | Secret key for MinIO or AWS S3 authentication. | | +| **AWS_S3_ENDPOINT_URL** | Custom endpoint URL for MinIO or S3-compatible storage. | http://plane-minio:9000 | +| **AWS_S3_BUCKET_NAME** | S3 bucket name for file storage. | uploads | +| **MINIO_ROOT_USER** | Username for MinIO authentication. This is effectively your MinIO admin account. | access-key | +| **MINIO_ROOT_PASSWORD** | Password for MinIO root user authentication. Keep this secure as it provides full access to your storage. | secret-key | +| **BUCKET_NAME** | S3 bucket name where all file uploads will be stored. This bucket will be automatically created if it doesn't exist. | uploads | +| **FILE_SIZE_LIMIT** | Maximum file upload size in bytes. | 5242880 (5MB) | +| **MINIO_ENDPOINT_SSL** | Force HTTPS for MinIO when dealing with SSL termination. Set to `1` to enable. | 0 | + +### GitHub integration + +| Variable | Description | Default Value | +| ------------------------ | ------------------------------------------------ | ------------- | +| **GITHUB_CLIENT_ID** | OAuth client ID for GitHub integration. | | +| **GITHUB_CLIENT_SECRET** | OAuth client secret for GitHub integration. | | +| **GITHUB_APP_NAME** | GitHub App name for enhanced GitHub integration. | | +| **GITHUB_APP_ID** | GitHub App ID for enhanced GitHub integration. | | +| **GITHUB_PRIVATE_KEY** | Private key for GitHub App authentication. | | + +### Slack integration + +| Variable | Description | Default Value | +| ----------------------- | ------------------------------------------ | ------------- | +| **SLACK_CLIENT_ID** | OAuth client ID for Slack integration. | | +| **SLACK_CLIENT_SECRET** | OAuth client secret for Slack integration. | | + +### GitLab integration + +| Variable | Description | Default Value | +| ------------------------ | ------------------------------------------- | ------------- | +| **GITLAB_CLIENT_ID** | OAuth client ID for GitLab integration. | | +| **GITLAB_CLIENT_SECRET** | OAuth client secret for GitLab integration. | | + +### OpenSearch + +| Variable | Description | Default Value | +| --------------------------- | ----------------------------------------------------------- | ------------------------------------ | +| **OPENSEARCH_ENABLED** | Enable OpenSearch integration | 1 | +| **OPENSEARCH_URL** | OpenSearch endpoint URL | https://opensearch.example.com:9200/ | +| **OPENSEARCH_USERNAME** | Authentication username | admin | +| **OPENSEARCH_PASSWORD** | Authentication password | your-secure-password | +| **OPENSEARCH_INDEX_PREFIX** | Prefix for all index names (useful for multi-tenant setups) | (empty) | + +### Plane AI + +#### Plane AI replicas + +To start Plane AI services, set each replica count to `1`: + +| Variable | Description | Required | +| ------------------------ | ---------------------------------- | -------- | +| **PI_API_REPLICAS** | Plane AI API replica count | Yes | +| **PI_BEAT_REPLICAS** | Plane AI Beat Worker replica count | Yes | +| **PI_WORKER_REPLICAS** | Plane AI Worker replica count | Yes | +| **PI_MIGRATOR_REPLICAS** | Plane AI Migrator replica count | Yes | + +#### Database settings + +::: info Plane AI database +Plane AI uses a separate PostgreSQL database. Create a new database (e.g. `plane_pi`) on your PostgreSQL server, then set **PLANE_PI_DATABASE_URL** to its connection string. Example: `postgresql://user:password@host:5432/plane_pi` +::: + +| Variable | Description | Default Value | +| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | +| **PLANE_PI_DATABASE_URL** | Connection string for the Plane AI database. A separate database used by the PI service. | postgresql://plane:plane@plane-db/plane_pi | +| **FOLLOWER_POSTGRES_URI** | Connection string for a Plane PostgreSQL DB read replica. Used for read-heavy operations to reduce load on the primary database. | Same as DATABASE_URL | + +#### LLM provider API keys + +Plane AI supports multiple LLM providers. Configure one or more by adding their API keys. + +| Variable | Description | Required | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | +| **OPENAI_API_KEY** | API key for OpenAI models | Optional | +| **CLAUDE_API_KEY** | API key for Anthropic models | Optional | +| **GROQ_API_KEY** | API key for speech-to-text features | Optional | +| **CUSTOM_LLM_ENABLED** | Set to `true` to enable a custom LLM. Supports OpenAI-compatible endpoints and AWS Bedrock. | Optional | +| **CUSTOM_LLM_PROVIDER** | Backend provider for the custom model. Accepted values: `openai` (default), `bedrock`. | Optional | +| **CUSTOM_LLM_MODEL_KEY** | Identifier key for the custom model (e.g. a model ID or name). | Optional | +| **CUSTOM_LLM_BASE_URL** | Base URL of the custom model's OpenAI-compatible endpoint. Required when `CUSTOM_LLM_PROVIDER=openai`. | Optional | +| **CUSTOM_LLM_API_KEY** | API key for authenticating with the custom endpoint. Required for `openai` provider; used as the AWS access key ID when `CUSTOM_LLM_PROVIDER=bedrock`. | Optional | +| **CUSTOM_LLM_AWS_REGION** | AWS region for the Bedrock model (e.g. `us-east-1`). Required when `CUSTOM_LLM_PROVIDER=bedrock`. | Optional | +| **CUSTOM_LLM_NAME** | Display name for the custom model shown in the UI. Defaults to `Custom LLM`. | Optional | +| **CUSTOM_LLM_MAX_TOKENS** | Maximum token limit for the custom model. Defaults to `64000`. | Optional | + +#### Provider base URLs + +Use these when routing requests through self-hosted gateways, proxies, or compatible third-party endpoints. + +| Variable | Description | Default | +| ------------------- | ------------------------------------------ | --------- | +| **OPENAI_BASE_URL** | Custom base URL for OpenAI-compatible APIs | OpenAI | +| **CLAUDE_BASE_URL** | Custom base URL for Claude-compatible APIs | Anthropic | +| **COHERE_BASE_URL** | Custom base URL for Cohere APIs | Cohere | +| **GROQ_BASE_URL** | Custom base URL for Groq APIs | Groq | + +#### Embedding model configuration + +These settings are required for semantic search and Plane AI Chat. Configure one of the following options. + +| Variable | Description | Required | +| ---------------------------------- | ---------------------------------------------------------------------------------------- | ----------- | +| **OPENSEARCH_ML_MODEL_ID** | ID of an existing embedding model deployed in OpenSearch. | Conditional | +| **EMBEDDING_MODEL** | Model used for generating embeddings and query construction (e.g., `cohere/embed-v4.0`). | Required | +| **OPENSEARCH_EMBEDDING_DIMENSION** | The dimension of the embedding model (e.g., `1536`). | Required | +| **COHERE_API_KEY** | API key for Cohere embedding models | Conditional | +| **BR_AWS_ACCESS_KEY_ID** | AWS access key ID for Bedrock Titan embedding | Conditional | +| **BR_AWS_SECRET_ACCESS_KEY** | AWS secret access key for Bedrock Titan embedding | Conditional | +| **BR_AWS_REGION** | AWS region for Bedrock Titan embedding | Conditional | + +For setup instructions, supported models, and IAM permissions, see [Configure Plane AI](/self-hosting/govern/plane-ai/configure-plane-ai). + +### API settings + +| Variable | Description | Default Value | +| ---------------------- | ----------------------------------------------------------------------- | ------------- | +| **API_KEY_RATE_LIMIT** | Rate limit for API requests to prevent abuse. Format: `number/timeunit` | 60/minute | + +### Email settings + +| Variable | Description | Default Value | +| ----------------------- | ----------------------------------------------------------------------------------------------- | ------------- | +| **SMTP_DOMAIN** | Domain used for the built-in SMTP email intake service. | 0.0.0.0 | +| **TLS_CERT_PATH** | File path to the TLS certificate for email service encryption. | | +| **TLS_PRIV_KEY_PATH** | File path to the TLS private key for email service encryption. | | +| **INTAKE_EMAIL_DOMAIN** | Domain name for intake email addresses (e.g., `example.com` for `issue+id@example.com` format). | example.com | + +### Integration (Silo) settings + +| Variable | Description | Default Value | +| --------------------------------- | ---------------------------------------------------------------------- | ------------- | +| **SILO_BASE_PATH** | Base path for the Silo integration service. | /silo | +| **WEBHOOK_SECRET** | Secret key for verifying webhook payloads from external integrations. | plane-silo | +| **BATCH_SIZE** | Number of events to process in a single batch for integration syncs. | 60 | +| **DEDUP_INTERVAL** | Interval (in seconds) for deduplication of integration events. | 3 | +| **MQ_PREFETCH_COUNT** | Number of messages the Silo service prefetches from the message queue. | 10 | +| **INTEGRATION_CALLBACK_BASE_URL** | Base URL for integration callbacks. Defaults to `WEB_URL` if not set. | | + +### Outbox poller settings + +| Variable | Description | Default Value | +| -------------------------------------------- | --------------------------------------------------------------------- | ------------- | +| **OUTBOX_POLLER_MEMORY_LIMIT_MB** | Maximum memory usage (in MB) before the outbox poller restarts. | 512 | +| **OUTBOX_POLLER_INTERVAL_MIN** | Minimum polling interval (in seconds) for the outbox poller. | 0.25 | +| **OUTBOX_POLLER_INTERVAL_MAX** | Maximum polling interval (in seconds) for the outbox poller. | 2 | +| **OUTBOX_POLLER_BATCH_SIZE** | Number of outbox events to process per polling cycle. | 250 | +| **OUTBOX_POLLER_MEMORY_CHECK_INTERVAL** | Interval (in seconds) between memory usage checks. | 30 | +| **OUTBOX_POLLER_POOL_SIZE** | Default connection pool size for the outbox poller. | 4 | +| **OUTBOX_POLLER_POOL_MIN_SIZE** | Minimum number of connections in the pool. | 2 | +| **OUTBOX_POLLER_POOL_MAX_SIZE** | Maximum number of connections in the pool. | 10 | +| **OUTBOX_POLLER_POOL_TIMEOUT** | Timeout (in seconds) for acquiring a connection from the pool. | 30.0 | +| **OUTBOX_POLLER_POOL_MAX_IDLE** | Maximum idle time (in seconds) for a connection before it is closed. | 300.0 | +| **OUTBOX_POLLER_POOL_MAX_LIFETIME** | Maximum lifetime (in seconds) for a connection before it is recycled. | 3600 | +| **OUTBOX_POLLER_POOL_RECONNECT_TIMEOUT** | Timeout (in seconds) for reconnecting a dropped connection. | 5.0 | +| **OUTBOX_POLLER_POOL_HEALTH_CHECK_INTERVAL** | Interval (in seconds) between connection health checks. | 30 | + +### Automation consumer settings + +| Variable | Description | Default Value | +| -------------------------------------- | --------------------------------------------------------------- | ------------------------------ | +| **AUTOMATION_EVENT_STREAM_QUEUE_NAME** | RabbitMQ queue name for automation event processing. | plane.event_stream.automations | +| **AUTOMATION_EVENT_STREAM_PREFETCH** | Number of messages to prefetch for automation event processing. | 10 | +| **AUTOMATION_EXCHANGE_NAME** | RabbitMQ exchange name for automation events. | plane.event_stream | + +### Plane Intelligence (PI) settings + +| Variable | Description | Default Value | +| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | +| **OPENAI_API_KEY** | API key for OpenAI services used by Plane Intelligence. | | +| **OPENAI_BASE_URL** | Custom base URL for OpenAI-compatible API endpoints. | | +| **CLAUDE_API_KEY** | API key for Anthropic Claude services used by Plane Intelligence. | | +| **CLAUDE_BASE_URL** | Custom base URL for Claude API endpoints. | | +| **GROQ_API_KEY** | API key for Groq services used by Plane Intelligence. | | +| **GROQ_BASE_URL** | Custom base URL for Groq API endpoints. | | +| **COHERE_API_KEY** | API key for Cohere services used by Plane Intelligence. | | +| **COHERE_BASE_URL** | Custom base URL for Cohere API endpoints. | | +| **CUSTOM_LLM_ENABLED** | Enable a custom OpenAI-compatible LLM provider. Set to `true` to enable. | false | +| **CUSTOM_LLM_MODEL_KEY** | Model key identifier for the custom LLM. | gpt-oss-120b | +| **CUSTOM_LLM_BASE_URL** | Base URL for the custom LLM API endpoint. | | +| **CUSTOM_LLM_API_KEY** | API key for the custom LLM provider. | | +| **CUSTOM_LLM_NAME** | Display name for the custom LLM in the Plane UI. | GPT-OSS-120B | +| **CUSTOM_LLM_DESCRIPTION** | Description of the custom LLM shown in the Plane UI. | A self-hosted OpenAI-compatible model | +| **CUSTOM_LLM_MAX_TOKENS** | Maximum token limit for the custom LLM. | 128000 | +| **EMBEDDING_MODEL** | Model key for generating embeddings (e.g. `cohere/embed-v4.0`). Required for PI API startup when Plane AI is enabled. | | +| **OPENSEARCH_ML_MODEL_ID** | OpenSearch ML model ID for the deployed embedding model. | | +| **OPENSEARCH_EMBEDDING_DIMENSION** | Vector dimension for `knn_vector` fields; must match the embedding model and stay aligned with the API service. See [Configure embedding model](/self-hosting/govern/plane-ai/configure-embedding-model). | 1536 | +| **BR_AWS_ACCESS_KEY_ID** | AWS access key for Amazon Bedrock integration. | | +| **BR_AWS_SECRET_ACCESS_KEY** | AWS secret key for Amazon Bedrock integration. | | +| **BR_AWS_SESSION_TOKEN** | AWS session token for Amazon Bedrock integration (for temporary credentials). | | +| **FASTAPI_APP_WORKERS** | Number of FastAPI workers for the PI service. | 1 | +| **PLANE_OAUTH_STATE_EXPIRY_SECONDS** | Expiry time (in seconds) for PI OAuth state tokens. | 82800 | +| **CELERY_VECTOR_SYNC_ENABLED** | Enable periodic vector synchronization for AI-powered search. | 0 | +| **CELERY_VECTOR_SYNC_INTERVAL** | Interval (in seconds) for vector synchronization. | 3 | +| **CELERY_WORKSPACE_PLAN_SYNC_ENABLED** | Enable periodic workspace plan synchronization. | 0 | +| **CELERY_WORKSPACE_PLAN_SYNC_INTERVAL** | Interval (in seconds) for workspace plan synchronization. | 86400 | +| **CELERY_DOCS_SYNC_ENABLED** | Enable periodic documents synchronization for AI indexing. | 0 | +| **CELERY_DOCS_SYNC_INTERVAL** | Interval (in seconds) for documents synchronization. | 86400 | + +::: details Community Edition + +This guide provides a comprehensive overview of all environment variables available for configuring your self-hosted Plane Community Edition. Use these variables to customize your instance to fit your deployment needs. + +## Where to find the environment file + +The environment configuration file is located at: + +```bash +plane-selfhost/plane-app/plane.env +``` + +## Environment Variables + +### General settings + +| Variable | Description | Default Value | +| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | +| **APP_DOMAIN** | Domain name for your Plane instance. This determines how users will access your installation. | localhost | +| **APP_RELEASE** | Release version of Plane. Helps with compatibility and troubleshooting. | stable | +| **WEB_URL** | The complete base URL for the web application including protocol. Essential for email links and integrations. | http://${APP_DOMAIN} | +| **CORS_ALLOWED_ORIGINS** | Comma-separated list of origins allowed to make cross-origin requests to your API. | http://${APP_DOMAIN} | +| **DEBUG** | Toggles debug mode for verbose logging. Set to `1` to enable, `0` to disable. Not recommended in production as it may expose sensitive information. | 0 | +| **LISTEN_HTTP_PORT** | Port for HTTP traffic. The primary port your users will connect to. | 80 | +| **LISTEN_HTTPS_PORT** | Port for HTTPS traffic. The primary port your users will connect to. | 443 | + +### Scaling and performance + +| Variable | Description | Default Value | +| ------------------------ | ------------------------------------------------------------------------------------------------- | ------------- | +| **WEB_REPLICAS** | Number of web server replicas for serving the frontend UI. Increase for better load distribution. | 1 | +| **SPACE_REPLICAS** | Number of space service replicas handling workspace-related operations. | 1 | +| **ADMIN_REPLICAS** | Number of admin service replicas for administrative functions. | 1 | +| **API_REPLICAS** | Number of API service replicas processing API requests. | 1 | +| **WORKER_REPLICAS** | Number of worker service replicas handling background tasks. | 1 | +| **BEAT_WORKER_REPLICAS** | Number of beat worker replicas for scheduled/periodic tasks. | 1 | +| **LIVE_REPLICAS** | Number of live service replicas for real-time updates and WebSocket connections. | 1 | +| **GUNICORN_WORKERS** | Number of Gunicorn workers per API instance. Increase for better request handling capacity. | 1 | + +### API settings + +| Variable | Description | Default Value | +| ---------------------- | ----------------------------------------------------------------------- | ------------- | +| **API_KEY_RATE_LIMIT** | Rate limit for API requests to prevent abuse. Format: `number/timeunit` | 60/minute | + +### Database settings + +| Variable | Description | Default Value | +| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| **PGHOST** | Hostname or IP address of your PostgreSQL server. | plane-db | +| **PGDATABASE** | Name of the PostgreSQL database Plane will use. | plane | +| **POSTGRES_USER** | Username for PostgreSQL authentication. | plane | +| **POSTGRES_PASSWORD** | Password for PostgreSQL authentication. Use a strong, unique password. | plane | +| **POSTGRES_DB** | Same as PGDATABASE - the name of the PostgreSQL database. | plane | +| **POSTGRES_PORT** | TCP port your PostgreSQL server is listening on. | 5432 | +| **PGDATA** | Directory path where PostgreSQL data is stored. Only relevant if you're managing PostgreSQL directly. | /var/lib/postgresql/data | +| **DATABASE_URL** | Full connection string for PostgreSQL. If provided, overrides individual settings. Format: `postgresql://username:password@host:port/dbname` | | + +### Redis settings + +| Variable | Description | Default Value | +| -------------- | ------------------------------------------------------------------------------- | ------------- | +| **REDIS_HOST** | Hostname or IP address of your Redis server. | plane-redis | +| **REDIS_PORT** | TCP port your Redis server is listening on. | 6379 | +| **REDIS_URL** | Full connection string for Redis. Format: `redis://username:password@host:port` | | + +### RabbitMQ settings + +| Variable | Description | Default Value | +| --------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------- | +| **RABBITMQ_HOST** | Hostname or IP address of your RabbitMQ server. | plane-mq | +| **RABBITMQ_PORT** | TCP port your RabbitMQ server is listening on. | 5672 | +| **RABBITMQ_USER** | Username for RabbitMQ authentication. | plane | +| **RABBITMQ_PASSWORD** | Password for RabbitMQ authentication. Use a strong, unique password. | plane | +| **RABBITMQ_VHOST** | Virtual host for RabbitMQ, providing logical separation of resources. | plane | +| **AMQP_URL** | Full connection string for RabbitMQ. If not provided, it's constructed from individual settings. | amqp://plane:plane@plane-mq:5672/plane | + +### File Storage (MinIO / S3) + +| Variable | Description | Default Value | +| ------------------------- | --------------------------------------------------------------------------------------------------- | ------------- | +| **USE_MINIO** | Whether to use MinIO for object storage. Set to `1` to enable, `0` to use other configured storage. | 1 | +| **MINIO_ENDPOINT_SSL** | Force HTTPS for MinIO when handling SSL termination. Set to `1` to enable. | 0 | +| **AWS_REGION** | AWS region for S3 storage services. Applies when using S3 or MinIO. | | +| **AWS_ACCESS_KEY_ID** | Access key for MinIO or AWS S3 authentication. | access-key | +| **AWS_SECRET_ACCESS_KEY** | Secret key for MinIO or AWS S3 authentication. | secret-key | +| **AWS_S3_ENDPOINT_URL** | Endpoint URL for MinIO or S3-compatible storage. | | +| **AWS_S3_BUCKET_NAME** | S3 bucket name for file storage. All uploads will be stored in this bucket. | uploads | +| **FILE_SIZE_LIMIT** | Maximum file upload size in bytes. | 5242880 (5MB) | + +### Security settings + +| Variable | Description | Default Value | +| -------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------- | +| **SECRET_KEY** | Secret key used for cryptographic operations like session handling and token generation. Should be a long, random string. | | + +::: diff --git a/apps/developer-docs/docs/self-hosting/govern/external-secrets.md b/apps/developer-docs/docs/self-hosting/govern/external-secrets.md new file mode 100644 index 00000000..fdea8891 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/external-secrets.md @@ -0,0 +1,198 @@ +--- +title: Configure external secrets for Kubernetes deployments +description: Use external secrets operators with Plane on Kubernetes. Integrate AWS Secrets Manager, HashiCorp Vault, or Azure Key Vault for secure configuration. +keywords: plane external secrets, kubernetes secrets, vault integration, aws secrets manager, secret management, self-hosting +--- + +# Configure external secrets for Kubernetes deployments + +This guide explains how to integrate Plane with external secret management solutions, enabling secure and centralized management of sensitive configuration data. The examples provided cover AWS Secrets Manager and HashiCorp Vault integrations, but you can adapt these patterns to your preferred secret management solution. + +## AWS Secrets Manager + +1. Create a dedicated IAM user (e.g., `external-secret-access-user`). You can uncheck **Console Access Required**. +2. Generate `ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` and keep them handy. +3. Note the user's ARN for later use (format: `arn:aws:iam:::user/`). + +4. Create IAM policy (e.g., `external-secret-access-policy`) with the following JSON: + + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "secretsmanager:GetResourcePolicy", + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds" + ], + "Resource": ["arn:aws:secretsmanager:::secret:*"] + } + ] + } + ``` + + Replace `` and `` with your AWS region and account ID. + +5. Create IAM role (e.g., external-secret-access-role) with the following trust relationship: + + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": "" + }, + "Action": "sts:AssumeRole" + } + ] + } + ``` + + Replace `` with the ARN of the user created in step 1. + +6. Attach the AWS IAM policy created in step 4 to the IAM role. + +7. Create secrets in AWS Secrets Manager with your Plane configuration values. For example, store RabbitMQ credentials with a name like `prod/secrets/rabbitmq`. + + | Key | Value | + | --------------------- | -------- | + | RABBITMQ_DEFAULT_USER | plane | + | RABBITMQ_DEFAULT_PASS | plane123 | + + Follow this pattern to manage all the [environment variables](/self-hosting/methods/kubernetes#external-secrets-config) in AWS Secrets Manager. + +8. Create a Kubernetes secret containing AWS credentials in your application namespace: + + ```sh + kubectl create secret generic aws-creds-secret \ + --from-literal=access-key= \ + --from-literal=secret-access-key= \ + -n + ``` + +9. Apply the following YAML to create a ClusterSecretStore resource: + + ```yaml + apiVersion: external-secrets.io/v1 + kind: ClusterSecretStore + metadata: + name: cluster-aws-secretsmanager + namespace: + spec: + provider: + aws: + service: SecretsManager + role: arn:aws:iam:::role/ + region: eu-west-1 + auth: + accessKeyIDSecretRef: + name: aws-creds-secret + key: access-key + secretAccessKeySecretRef: + name: aws-creds-secret + key: secret-access-key + ``` + + Replace `` and `` with your AWS account ID and the role name created in Step 5. + +10. Create an ExternalSecret resource to fetch secrets from AWS and create a corresponding Kubernetes secret: + ```yaml + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: rabbitmq-external-secrets + namespace: + spec: + refreshInterval: 1m + secretStoreRef: + name: cluster-aws-secretsmanager # ClusterSecretStore name + kind: ClusterSecretStore + target: + name: rabbitmq-secret # Target Kubernetes secret name + creationPolicy: Owner + data: + - secretKey: RABBITMQ_DEFAULT_USER + remoteRef: + key: prod/secrets/rabbitmq + property: RABBITMQ_DEFAULT_USER + - secretKey: RABBITMQ_DEFAULT_PASS + remoteRef: + key: prod/secrets/rabbitmq + ``` + +Make sure to set all [environment variables](/self-hosting/methods/kubernetes#external-secrets-config) in the AWS Secrets Manager, and then access them via ExternalSecret resources in your Kubernetes cluster. + +## HashiCorp Vault + +1. Access the Vault UI at `https:///`. + +2. Set up a KV secrets engine if not already configured. + +3. Create a secret with your Plane configuration values (e.g., `secrets/rabbitmq_secrets`). For this example, we're setting up RabbitMQ credentials: + + | Key | Value | + | --------------------- | -------- | + | RABBITMQ_DEFAULT_USER | plane | + | RABBITMQ_DEFAULT_PASS | plane123 | + + Follow this pattern to manage all the other [environment variables](/self-hosting/methods/kubernetes#external-secrets-config) in the Vault. + +4. Create a Kubernetes secret containing your Vault token in your application namespace: + + ```sh + kubectl create secret generic vault-token -n --from-literal=token= + ``` + +5. Apply the following YAML to create a ClusterSecretStore resource: + + ```yaml + apiVersion: external-secrets.io/v1 + kind: ClusterSecretStore + metadata: + name: vault-backend + namespace: + spec: + provider: + vault: + server: "https://" # the address of your vault instance + path: "secrets" # path for accessing the secrets + version: "v2" # Vault API version + auth: + tokenSecretRef: + name: "vault-token" # Use a k8s secret called vault-token + key: "token" # Use this key to access the vault token + ``` + + Replace `` with your Vault server address. + +6. Create an ExternalSecret resource to fetch secrets from Vault and create a corresponding Kubernetes secret: + ```yaml + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: rabbitmq-external-secrets + namespace: # application-namespace + spec: + refreshInterval: "1m" + secretStoreRef: + name: vault-backend # ClusterSecretStore name + kind: ClusterSecretStore + target: + name: rabbitmq-secret # Target Kubernetes secret name + creationPolicy: Owner + data: + - secretKey: RABBITMQ_DEFAULT_USER + remoteRef: + key: secrets/data/rabbitmq_secrets + property: RABBITMQ_DEFAULT_USER + - secretKey: RABBITMQ_DEFAULT_PASS + remoteRef: + key: secrets/data/rabbitmq_secrets + ``` + +Follow this pattern to manage all the environment variables in the Vault, then access them via ExternalSecret resources in your Kubernetes cluster. diff --git a/apps/developer-docs/docs/self-hosting/govern/github-oauth.md b/apps/developer-docs/docs/self-hosting/govern/github-oauth.md new file mode 100644 index 00000000..16eebdf7 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/github-oauth.md @@ -0,0 +1,35 @@ +--- +title: Github OAuth +description: Configure GitHub OAuth authentication for Plane. Enable GitHub sign-in for your self-hosted Plane instance. +keywords: plane github oauth, github sign-in, github authentication, github login, oauth provider, self-hosting, plane sso +--- + +# Github OAuth + +Plane supports GitHub OAuth so your users can sign-in with GitHub instead. + +## Configure Plane as an OAuth app on GitHub + +1. Log in to your [GitHub account](https://github.com/). +2. Click your profile's avatar and navigate to **Settings.** +3. Click **Developer Settings** and then **OAuth Apps**. + ![](/images/authentication/github/github-auth-1.png) +4. Click **Register a new application**. +5. Configure the following OAuth credentials for your Plane app. + 1. **Homepage URL**\ + The domain, with HTTPS, on which you host Plane, e.g., `https://app.plane.so` + 2. **Authorization Callback URL**\ + Append the path that users should be redirected to after they have authenticated with GitHub. e.g., `https:////auth/github/callback/` and `https:///auth/mobile/github/callback/` where `` is your self-hosted instance's domain. +6. Click `Register application` to save it. + ![](/images/authentication/github/github-auth-2.png) +7. Find the app you just registered and click through to find the client ID and the client secret. You will need this for the next steps. + +## Configure Plane + +![GitHub Oauth Configuration](/images/custom-sso/github-oauth.png) + +1. Go to `GitHub` on the Authentication screen of `/god mode`. +2. Add the client ID + the client secret from the GitHub app you just registered. +3. Click `Save `. + +Your Plane instance should now work with GitHub sign-in. diff --git a/apps/developer-docs/docs/self-hosting/govern/god-mode/session-expiry.md b/apps/developer-docs/docs/self-hosting/govern/god-mode/session-expiry.md new file mode 100644 index 00000000..dd9c6b92 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/god-mode/session-expiry.md @@ -0,0 +1,55 @@ +--- +title: Configure session expiry +sidebar_label: Session expiry +description: Set how long user and admin sessions stay active before requiring re-authentication on a self-hosted Plane instance. +--- + +# Configure session expiry + +Instance administrators can control how long a signed-in session stays valid before the user has to sign in again. This is set from the instance admin panel and applies to everyone on the instance. + +## Where to configure it + +1. Open the **instance admin panel** (God mode). +2. Go to **System Configurations**. +3. Find the **Session settings** section. + +## Settings + +There are two independent settings. + +| Setting | Applies to | Unit | Default | Minimum | +| ------------------------ | ---------------------------------------------------- | ----- | ------- | ------- | +| **Session expiry** | Everyone signed in to the Plane app (all workspaces) | Days | 7 days | 1 day | +| **Admin session expiry** | The instance admin panel (God mode) session | Hours | 1 hour | 1 hour | + +**Session expiry (days).** How long a user's session to the main Plane app stays valid. This is shared across the whole instance; it is not set per workspace or per user. + +**Admin session expiry (hours).** How long a session to the instance admin panel stays valid. This is deliberately shorter than the app session because the admin panel is more sensitive. It applies only to the God-mode admin panel, not to workspace admins or members. + +Enter a whole number in each field and save. Values below the minimum are rejected. + +## How expiry behaves + +**Main app sessions are inactivity-based.** A user's session stays alive as long as they keep using Plane. Each active session's expiry is extended in the background (at most once per day) so active users are not signed out mid-work. A session only lapses after the configured number of days of inactivity. + +**Admin panel sessions are an absolute limit.** The admin session is not extended by activity. It expires the configured number of hours after sign-in, regardless of use, and the administrator must sign in again. + +In both cases, when a session expires the user is signed out and must re-authenticate to continue. + +## Setting it with environment variables + +The same two values can also be set with environment variables, which is useful for scripted or reproducible deployments. These are specified in **seconds**. + +| Variable | Meaning | Default (seconds) | +| -------------------------- | -------------------------- | ----------------- | +| `SESSION_COOKIE_AGE` | Main app session length | `604800` (7 days) | +| `ADMIN_SESSION_COOKIE_AGE` | Admin panel session length | `3600` (1 hour) | + +A value set in the admin panel is stored as the instance configuration and takes precedence over the environment default. + +## Notes + +- Session expiry is **instance-wide**. Every workspace and user on the instance shares the same **Session expiry** value; there is no per-workspace or per-user override. +- "Admin session" refers to the **instance admin panel** (God mode), not to workspace Admins or Owners. Workspace admins use the main app session like everyone else. +- Changing these values does not sign out existing sessions immediately; the new length applies as sessions are created or refreshed. diff --git a/apps/developer-docs/docs/self-hosting/govern/google-oauth.md b/apps/developer-docs/docs/self-hosting/govern/google-oauth.md new file mode 100644 index 00000000..92f486fd --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/google-oauth.md @@ -0,0 +1,43 @@ +--- +title: Google OAuth +description: Setup Google OAuth authentication for Plane. Step-by-step guide to enable Google sign-in for your self-hosted instance. +keywords: plane google oauth, google sign-in, google authentication, google login, oauth provider, self-hosting, plane sso +--- + +# Google OAuth + +Plane already ships with out-of-the-box support for Google OAuth. This is the easiest option to configure for Google Workspace users. + +## Configure Plane as an app on Google API Console + +First, you will need to identify Plane as an approved OAuth app to Google. + +1. Go to the [Google API console](https://console.cloud.google.com/apis) and create a new project. +2. Navigate to the **OAuth consent screen** under **APIs & Services**. Choose how you want to configure and register the Plane app, including your target users, and click **Create**. + ![](/images/authentication/google/google-auth-1.png) +3. Configure the OAuth consent screen with information about the app. + ![](/images/authentication/google/google-auth-2.png) +4. Navigate to the **Credentials** screen, click **Create Credentials**, and select **OAuth client ID** from the options given. + ![](/images/authentication/google/google-auth-3.png) +5. Select **Web application** under the **Application type** dropdown list. Update the following fields. + 1. **Authorized JavaScript origins**\ + The HTTP origins that host your web application, e.g., `https://app.plane.so` + 2. **Authorized redirect URIs**\ + Append the path that users should be redirected to after they have authenticated with Google. `https:///auth/google/callback` and `https:///auth/mobile/google/callback/` where `` is your self-hosted instance's domain. + 3. Click **Create**. + 4. Get the Client ID and Client secret under **OAuth 2.0 Client IDs** on the **Credentials** screen. + ![](/images/authentication/google/google-auth-4.png) + +## Configure Plane + +![Google Oauth Configuration](/images/custom-sso/google-oauth.png) + +1. Go to `Google` on the Authentication screen of `/god mode`. +2. Add the client ID + the client secret from Google API Console. +3. Click `Save `. + +Your Plane instance should now work with `Sign in with Google`. + +::: info +We don't restrict domains in with Google OAuth yet. It's on our roadmap. +::: diff --git a/apps/developer-docs/docs/self-hosting/govern/high-availability.md b/apps/developer-docs/docs/self-hosting/govern/high-availability.md new file mode 100644 index 00000000..744cd470 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/high-availability.md @@ -0,0 +1,706 @@ +--- +title: High Availability Deployment +description: How to deploy Plane Commercial Edition on Kubernetes with high availability using the plane-enterprise Helm chart. +keywords: plane high availability, kubernetes ha, multi-az deployment, plane-enterprise helm chart, karpenter, pod disruption budget, hpa, self-hosting, plane kubernetes +--- + +# High Availability on Kubernetes + +This guide covers what high availability means, how the `plane-enterprise` Helm chart workloads behave under failure, and exactly what to configure so your deployment survives the loss of a single availability zone or node without manual recovery. The setup is cloud-agnostic. If you're deploying on AWS with Karpenter, there's a dedicated section for you. + +Read this alongside the chart's [README](https://github.com/makeplane/helm-charts/blob/master/charts/plane-enterprise/README.md) and [values.yaml](https://github.com/makeplane/helm-charts/blob/master/charts/plane-enterprise/values.yaml). + +## What HA means here + +Plane Commercial Edition is a single-region application. There's one primary Postgres, one Redis, one message queue, one search cluster. High availability here means Plane keeps serving traffic when **one AZ or one node disappears**, not that you can run two independent active-active regions. + +That's an important distinction for how you plan your infrastructure. You're engineering for node and AZ fault tolerance, not geographic redundancy. The playbook: run stateless workloads with multiple replicas spread across AZs, and replace every in-chart stateful service with a managed, multi-AZ equivalent. + +## Workload tiers + +Every workload in the chart falls into one of three tiers. The tier determines how you scale it, how it recovers from failure, and what HA configuration it needs. + +### Tier 1 - Stateless, scale horizontally + +These run as `Deployment`s with no local state. Scale them freely across nodes and AZs. + +`api`, `web`, `space`, `admin`, `live`, `worker`, `silo`, `email_service`, `outbox_poller`, `automation_consumer`, `pi`, `pi_worker`, `runner`, `iframely` + +Run at least `replicas: 2` per service. Use `replicas >= 2` for `api`, `worker`, `web`, and `live` - they carry the most traffic. + +### Tier 2 - Singletons (replicas: 1 only) + +These do scheduled or coordinator work. **Do not scale any of them past `replicas: 1`** - running two copies doubles job execution. + +| Workload | Kind | Why it stays at 1 | +| ---------------- | ----------- | -------------------------------------------- | +| `monitor` | StatefulSet | Coordinator role; owns a `ReadWriteOnce` PVC | +| `beatworker` | Deployment | Celery beat - schedules periodic Plane jobs | +| `pi_beat_worker` | Deployment | PI beat - schedules periodic PI jobs | +| `migrator` | Job | DB migration; runs once per release | +| `pi-migrator` | Job | PI DB migration; runs once per release | + +The stateless singletons (`beatworker`, `pi_beat_worker`) reschedule onto a healthy node within seconds when their node fails. + +`monitor` is different: it owns an AZ-bound `ReadWriteOnce` PVC. On AZ failure, Kubernetes has to reschedule it onto a node in a live AZ and reattach the volume - expect a **60–120 second** recovery window. That's acceptable because `monitor` is an internal component, not user-facing. + +`migrator` and `pi-migrator` are run-once-per-release Jobs. They aren't long-running, but they still must not run in parallel. + +### Tier 3 - Local stateful (not HA) + +The chart ships optional in-cluster StatefulSets for development and small deployments: + +`postgres`, `redis`, `rabbitmq`, `opensearch`, `minio` + +These use single-replica `ReadWriteOnce` PVCs. They're **not HA.** Their data is pinned to one disk in one AZ, and the chart doesn't configure replication, failover, or quorum. + +**For every HA deployment, set `local_setup: false` for every Tier-3 service** and point Plane at managed, multi-AZ equivalents. The [External managed services](#external-managed-services) section has the exact value keys. + +## Cluster prerequisites + +Your cluster needs the following before installing in HA mode. + +**1. Worker nodes in at least three AZs.** Three is the minimum for any quorum service (etcd, Postgres synchronous replicas, OpenSearch master quorum). Two AZs survive single-AZ loss for stateless workloads but can't maintain quorum. + +**2. A default `StorageClass` with `volumeBindingMode: WaitForFirstConsumer`.** This is non-negotiable when Tier-2 singletons run on nodes provisioned just-in-time (Karpenter, Cluster Autoscaler). Without it, a PVC can bind to a zone before the pod schedules, leaving the pod unable to find a matching node. + +Example for AWS EBS gp2: + +```yaml +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: gp2 +parameters: + type: gp2 + fsType: ext4 +provisioner: ebs.csi.aws.com +volumeBindingMode: WaitForFirstConsumer +reclaimPolicy: Retain +allowVolumeExpansion: true +``` + +Then set this in `values.yaml`: + +```yaml +env: + storageClass: gp2 +``` + +**3. A cross-zone load balancer.** Traffic must reach pods in any AZ. + +| Cloud | Recommendation | +| ------- | ------------------------------------------------- | +| AWS | NLB or ALB with cross-zone load balancing enabled | +| GCP | Default global LB | +| Azure | Standard Load Balancer with zones `[1,2,3]` | +| On-prem | MetalLB in BGP mode, or an external LB | + +**4. A working `IngressClass`.** The chart supports `traefik` (default) or `nginx`. Deploy the ingress controller with `replicas >= 2` spread across AZs. + +**5. AZ-aware node labels.** Kubernetes uses `topology.kubernetes.io/zone` for AZ awareness. Managed clusters populate this automatically. Verify your nodes carry this label if you're on a self-managed cluster. + +## Recommended topology + +```text + ┌──────────────────────────┐ + │ External Load Balancer │ + │ (cross-zone enabled) │ + └────────────┬─────────────┘ + │ + ┌───────────────────┼───────────────────┐ + │ │ │ + ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ + │ AZ-a │ │ AZ-b │ │ AZ-c │ + │ │ │ │ │ │ + │ ingress │ │ ingress │ │ ingress │ + │ api x N │ │ api x N │ │ api x N │ + │ web x N │ │ web x N │ │ web x N │ + │ worker │ │ worker │ │ worker │ + │ … │ │ … │ │ … │ + └─────────┘ └─────────┘ └─────────┘ + │ + ┌─────────────────┼─────────────────┐ + │ │ │ + ┌───────▼──────┐ ┌───────▼──────┐ ┌───────▼──────┐ + │ Managed │ │ Managed │ │ Object │ + │ Postgres │ │ Redis │ │ Storage │ + │ (multi-AZ) │ │ (multi-AZ) │ │ (S3-class) │ + └──────────────┘ └──────────────┘ └──────────────┘ + ┌──────────────┐ ┌──────────────┐ + │ Managed │ │ Managed │ + │ RabbitMQ │ │ OpenSearch │ + │ (cluster) │ │ (multi-AZ) │ + └──────────────┘ └──────────────┘ +``` + +Tier-1 pods spread across AZs. All Tier-3 state lives in managed services that handle their own replication and failover. + +## External managed services + +### Value keys + +The chart supports pointing each stateful component at a remote managed service. Use these value keys. + +| Component | Disable local | External URL / credentials | +| ------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Postgres | `services.postgres.local_setup: false` | `env.pgdb_remote_url`, `env.pg_pi_db_remote_url`; optional read replica via `services.postgres.read_replica.enabled` + `services.postgres.read_replica.remote_url` | +| Redis | `services.redis.local_setup: false` | `env.remote_redis_url` | +| RabbitMQ | `services.rabbitmq.local_setup: false` | `services.rabbitmq.external_rabbitmq_url` | +| OpenSearch | `services.opensearch.local_setup: false` | `env.opensearch_remote_url`, `env.opensearch_remote_username`, `env.opensearch_remote_password`; optional `env.opensearch_index_prefix` for multi-tenant clusters | +| Object store | `services.minio.local_setup: false` | `env.aws_access_key`, `env.aws_secret_access_key`, `env.aws_region`, `env.aws_s3_endpoint_url`, `env.docstore_bucket` | + +### What HA looks like for each service + +Setting `local_setup: false` doesn't make your data tier HA on its own. The managed service you point Plane at must also be HA. Here's what each one needs. + +- **Postgres** - Multi-AZ primary with synchronous replication and automated failover. Use RDS Multi-AZ, Cloud SQL HA, Azure Flexible Server zone-redundant, or self-managed Patroni. + +- **Redis** - A replica group with automatic failover. Use ElastiCache Multi-AZ, Memorystore HA, or Redis Sentinel/Cluster. Redis failover drops in-flight connections; Plane reconnects automatically. + +- **RabbitMQ** - A true cluster with quorum queues across ≥3 nodes in ≥3 AZs. CloudAMQP and Amazon MQ for RabbitMQ in cluster mode both work. A single-node managed RabbitMQ is **not** HA. + +- **OpenSearch** - ≥3 master-eligible nodes across 3 AZs, plus data nodes spread across AZs. + +- **Object storage** - S3, GCS, and Azure Blob are multi-AZ by design. + +## Spreading pods across availability zones + +### How the chart exposes scheduling controls + +The chart exposes `nodeSelector`, `tolerations`, and `affinity` on every service (see `templates/_helpers.tpl` → `plane.podScheduling`). Use these to spread Tier-1 pods across AZs. + +:::info +The chart doesn't natively support `topologySpreadConstraints` - that's on the roadmap. Use `podAntiAffinity` in the meantime. It's functionally equivalent for AZ spreading. +::: + +### Recommended pattern: soft AZ anti-affinity + hard node anti-affinity + +Use a hard rule to prevent two replicas landing on the same node, and a soft rule to prefer spreading across AZs. The soft AZ rule means the scheduler can still place pods if one AZ is under pressure. + +```yaml +services: + api: + replicas: 3 + affinity: + podAntiAffinity: + # Hard: never put two api pods on the same node + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app.name + operator: In + values: + - --api + topologyKey: kubernetes.io/hostname + # Soft: prefer spreading api pods across AZs + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: app.name + operator: In + values: + - --api + topologyKey: topology.kubernetes.io/zone +``` + +The chart labels every workload with `app.name` set to {{ .Release.Namespace }}-{{ .Release.Name }}-<svc>. For a release named `plane` in namespace `plane`, that's `plane-plane-api` for the API. + +:::warning +**Watch for this** +The hard hostname anti-affinity rule requires at least as many schedulable nodes as the workload's replica count. Three `api` replicas need three nodes available, or pods sit `Pending`. If you can't guarantee that (small cluster, dedicated taints), relax the hostname rule to `preferredDuringSchedulingIgnoredDuringExecution`. +::: + +Apply this pattern to every Tier-1 service: `web`, `space`, `admin`, `live`, `worker`, `silo`, `email_service`, `outbox_poller`, `automation_consumer`, `pi`, `pi_worker`, `runner`, `iframely`. + +### Pinning workloads to specific node pools + +Use `nodeSelector` and `tolerations` to route a workload to a specific pool - for example, spot instances for batch workers: + +```yaml +services: + worker: + replicas: 6 + nodeSelector: + workload-class: batch + tolerations: + - key: workload-class + operator: Equal + value: batch + effect: NoSchedule +``` + +## PodDisruptionBudgets + +:::info +Native PDB rendering is planned for a future release. Apply the manifests below yourself until then. +::: + +PDBs protect Tier-1 deployments from voluntary disruption - a node drain or cluster upgrade - taking a service down entirely. Without them, Kubernetes can evict all pods of a deployment simultaneously. + +Apply this manifest in the same namespace as your release. Replace `RELEASE` and `NAMESPACE` with your values. + +```yaml +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: plane-api-pdb + namespace: NAMESPACE +spec: + minAvailable: 1 + selector: + matchLabels: + app.name: NAMESPACE-RELEASE-api +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: plane-web-pdb + namespace: NAMESPACE +spec: + minAvailable: 1 + selector: + matchLabels: + app.name: NAMESPACE-RELEASE-web +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: plane-space-pdb + namespace: NAMESPACE +spec: + minAvailable: 1 + selector: + matchLabels: + app.name: NAMESPACE-RELEASE-space +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: plane-admin-pdb + namespace: NAMESPACE +spec: + minAvailable: 1 + selector: + matchLabels: + app.name: NAMESPACE-RELEASE-admin +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: plane-live-pdb + namespace: NAMESPACE +spec: + minAvailable: 1 + selector: + matchLabels: + app.name: NAMESPACE-RELEASE-live +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: plane-worker-pdb + namespace: NAMESPACE +spec: + minAvailable: 1 + selector: + matchLabels: + app.name: NAMESPACE-RELEASE-worker +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: plane-silo-pdb + namespace: NAMESPACE +spec: + minAvailable: 1 + selector: + matchLabels: + app.name: NAMESPACE-RELEASE-silo +``` + +Add similar PDBs for `pi`, `pi_worker`, `outbox_poller`, `automation_consumer`, `email_service`, `runner`, and `iframely` if you have enabled them. + +:::warning +**Don't create PDBs for Tier-2 singletons** (`beatworker`, `pi_beat_worker`, `monitor`, `migrator`). A `minAvailable: 1` PDB on a `replicas: 1` workload blocks node drains entirely. +::: + +## HorizontalPodAutoscalers + +:::info +Native HPA rendering is planned for a future release. Apply the manifests below yourself until then. +::: + +HPAs scale Tier-1 services automatically under load. The thresholds below match the default resource requests in `values.yaml`. Tune `averageUtilization` and `maxReplicas` based on observed production load. + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: plane-api-hpa + namespace: NAMESPACE +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: RELEASE-api-wl + minReplicas: 3 + maxReplicas: 12 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: plane-worker-hpa + namespace: NAMESPACE +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: RELEASE-worker-wl + minReplicas: 3 + maxReplicas: 20 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: plane-web-hpa + namespace: NAMESPACE +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: RELEASE-web-wl + minReplicas: 2 + maxReplicas: 8 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 +``` + +:::warning +**Never create an HPA for `beatworker`, `pi_beat_worker`, `monitor`, or any migration Job.** Scheduled jobs would fire multiple times. +::: + +## Karpenter on AWS + +If you're on EKS, Karpenter is the recommended node provisioner for Plane Commercial Edition. It's AZ-aware, provisions nodes in seconds, and lets you mix on-demand and spot capacity per workload type. + +### Minimum versions + +- Karpenter ≥ v1.0 +- Kubernetes ≥ 1.29 +- AWS Load Balancer Controller ≥ v2.7 +- AWS EBS CSI driver installed + +### EC2NodeClass + +One `EC2NodeClass` covers most installs. Use AL2023, IMDSv2-only, and gp2 root volumes. + +```yaml +apiVersion: karpenter.k8s.aws/v1 +kind: EC2NodeClass +metadata: + name: plane-default +spec: + amiFamily: AL2023 + amiSelectorTerms: + - alias: al2023@latest + role: KarpenterNodeRole-CLUSTER_NAME + subnetSelectorTerms: + - tags: + karpenter.sh/discovery: CLUSTER_NAME + securityGroupSelectorTerms: + - tags: + karpenter.sh/discovery: CLUSTER_NAME + blockDeviceMappings: + - deviceName: /dev/xvda + ebs: + volumeType: gp2 + volumeSize: 100Gi + encrypted: true + deleteOnTermination: true + metadataOptions: + httpEndpoint: enabled + httpTokens: required + httpPutResponseHopLimit: 1 +``` + +### NodePools + +Two NodePools cover most deployments: an on-demand pool for general Tier-1 workloads, and a spot pool for batch workers (`worker`, `pi_worker`, `runner`, `outbox_poller`, `automation_consumer`). + +```yaml +apiVersion: karpenter.sh/v1 +kind: NodePool +metadata: + name: plane-general +spec: + template: + spec: + nodeClassRef: + group: karpenter.k8s.aws + kind: EC2NodeClass + name: plane-default + requirements: + - key: kubernetes.io/arch + operator: In + values: [amd64] + - key: karpenter.sh/capacity-type + operator: In + values: [on-demand] + - key: karpenter.k8s.aws/instance-category + operator: In + values: [c, m] + - key: karpenter.k8s.aws/instance-generation + operator: Gt + values: ["5"] + - key: topology.kubernetes.io/zone + operator: In + values: [REGION-a, REGION-b, REGION-c] + expireAfter: 720h + limits: + cpu: "200" + memory: 400Gi + disruption: + consolidationPolicy: WhenEmptyOrUnderutilized + consolidateAfter: 1m +--- +apiVersion: karpenter.sh/v1 +kind: NodePool +metadata: + name: plane-spot +spec: + template: + spec: + nodeClassRef: + group: karpenter.k8s.aws + kind: EC2NodeClass + name: plane-default + taints: + - key: workload-class + value: batch + effect: NoSchedule + requirements: + - key: kubernetes.io/arch + operator: In + values: [amd64] + - key: karpenter.sh/capacity-type + operator: In + values: [spot] + - key: karpenter.k8s.aws/instance-category + operator: In + values: [c, m, r] + - key: karpenter.k8s.aws/instance-generation + operator: Gt + values: ["5"] + - key: topology.kubernetes.io/zone + operator: In + values: [REGION-a, REGION-b, REGION-c] + expireAfter: 24h + limits: + cpu: "400" + memory: 800Gi + disruption: + consolidationPolicy: WhenEmptyOrUnderutilized + consolidateAfter: 5m +``` + +Match the spot NodePool taint with tolerations in your values: + +```yaml +services: + worker: + tolerations: + - key: workload-class + operator: Equal + value: batch + effect: NoSchedule + nodeSelector: + karpenter.sh/nodepool: plane-spot +``` + +### How Karpenter interacts with AZ spread + +- Karpenter respects `podAntiAffinity` when deciding which AZ to provision a node in. The affinity patterns from the previous section are sufficient to drive Karpenter's AZ distribution - no extra configuration needed. + +- Don't add `karpenter.sh/do-not-disrupt: "true"` to Tier-1 pods. They're stateless. Let Karpenter consolidate them freely. + +- Do add it to Tier-2 singletons (`beatworker`, `pi_beat_worker`, `monitor`) and to in-flight long-running Jobs (`migrator`). They tolerate rescheduling, but you don't want Karpenter bouncing them during a deployment: + +```yaml +services: + beatworker: + annotations: + karpenter.sh/do-not-disrupt: "true" +``` + +- `consolidateAfter: 1m` on the on-demand pool keeps the cluster cost-efficient. Raise it to `5m` or `10m` if you see churn during normal scaling. The spot pool's `expireAfter: 24h` forces daily node recycling, spreading the impact of spot interruptions across time rather than concentrating them. + +## Ingress and load balancer + +- Deploy the ingress controller (`traefik` or `nginx`) with `replicas >= 2` spread across AZs using the same `podAntiAffinity` pattern. + +- Enable cross-zone load balancing on the cloud LB. On AWS: + + ```yaml + service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true" + ``` + +- The `live` service uses WebSockets. Make sure your ingress controller and LB don't have idle-timeout values that drop long-lived connections. The default AWS NLB idle timeout is 350s - that's usually fine. ALB defaults to 60s and needs raising for WebSocket connections. + +- The chart configures request-body size limits via `ingress.traefik.maxRequestBodyBytes` (Traefik) and `nginx.ingress.kubernetes.io/proxy-body-size` (nginx). Tune these to your expected file upload size. + +## Backup and disaster recovery + +HA protects against AZ and node failure. Backups protect against logical corruption, accidental deletion, and ransomware. You need both. + +| Component | Backup mechanism | Recommended retention | +| ------------------ | --------------------------------------------------------------------------------------------------------------------------- | ---------------------- | +| Postgres | Managed-service automated backups + PITR | 30 days, PITR ≥ 7 days | +| Object storage | Bucket versioning + lifecycle to a different bucket/region | 90 days | +| OpenSearch | Snapshots to object storage | 7 days | +| Redis | Optional; treat as cache + queue. Document what your team loses on a full Redis failure (sessions, in-flight Celery tasks). | - | +| RabbitMQ | Definitions export (users, queues, bindings) on a schedule; messages are transient | - | +| Kubernetes objects | Velero, namespace-scoped, daily | 30 days | + +**Run a restore drill** before go-live and at least once per quarter. A backup that's never been restored is an assumption, not a guarantee. + +## Pre-go-live checklist + +Work through every item before sending real traffic. + +- [ ] Cluster has worker nodes in ≥3 AZs +- [ ] Default `StorageClass` is `WaitForFirstConsumer` +- [ ] `env.storageClass` is set to that class +- [ ] All Tier-3 `local_setup` flags are `false` +- [ ] Managed Postgres is multi-AZ with synchronous replica +- [ ] Managed Redis has replica + auto-failover +- [ ] Managed RabbitMQ is a true cluster across ≥3 AZs +- [ ] Managed OpenSearch has ≥3 masters across ≥3 AZs +- [ ] Object storage is multi-AZ (S3/GCS/Blob) with versioning enabled +- [ ] Every Tier-1 service has `replicas >= 2` (3 for `api`, `worker`, `web`) +- [ ] Every Tier-1 service has a `podAntiAffinity` block (hostname + zone) +- [ ] Every Tier-1 service has a PDB +- [ ] HPAs applied for `api`, `worker`, `web` at minimum +- [ ] No HPA or PDB on `beatworker`, `pi_beat_worker`, `monitor`, `migrator` +- [ ] Ingress controller runs with `replicas >= 2` spread across AZs +- [ ] LB has cross-zone load balancing enabled +- [ ] Backups configured and a restore drill has succeeded +- [ ] Failure drill: cordon and drain every node in one AZ; Plane stays up +- [ ] Failure drill: kill the active Postgres node; Plane recovers + +## Known chart gaps + +The following capabilities aren't natively provided by the chart and need to be applied separately. + +| Gap | Workaround | +| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| No native `topologySpreadConstraints` in `plane.podScheduling` | Use `podAntiAffinity` as shown in the spreading section - functionally equivalent for AZ spread | +| No PDBs rendered by the chart | Apply the PDB manifests from the PodDisruptionBudgets section | +| No HPAs rendered by the chart | Apply the HPA manifests from the HorizontalPodAutoscalers section | +| In-chart Tier-3 StatefulSets are single-replica, RWO | Set `local_setup: false` and use managed services | +| `monitor` is a singleton StatefulSet | Accept the 60–120s reschedule window on AZ failure - it's internal and non-user-facing | + +## Reference values.yaml for HA + +A minimal example that disables every local stateful service and gives each Tier-1 workload three replicas with AZ anti-affinity. Adapt names to your release. + +```yaml +planeVersion: v2.6.3 + +license: + licenseServer: https://prime.plane.so + licenseDomain: plane.example.com + +ingress: + enabled: true + ingressClass: traefik + +env: + storageClass: gp2 + pgdb_remote_url: "postgres://plane:***@pg-primary.example.internal:5432/plane?sslmode=require" + pg_pi_db_remote_url: "postgres://plane:***@pg-primary.example.internal:5432/plane_pi?sslmode=require" + remote_redis_url: "redis://:***@redis.example.internal:6379/0" + opensearch_remote_url: "https://opensearch.example.internal:9200" + opensearch_remote_username: plane + opensearch_remote_password: "***" + aws_access_key: "***" + aws_secret_access_key: "***" + aws_region: us-east-1 + aws_s3_endpoint_url: https://s3.us-east-1.amazonaws.com + docstore_bucket: plane-uploads-prod + web_url: https://plane.example.com + instance_admin_email: admin@example.com + cors_allowed_origins: https://plane.example.com + +services: + postgres: + local_setup: false + read_replica: + enabled: true + remote_url: "postgres://plane:***@pg-reader.example.internal:5432/plane?sslmode=require" + redis: + local_setup: false + rabbitmq: + local_setup: false + external_rabbitmq_url: "amqps://plane:***@rabbitmq.example.internal:5671/plane" + opensearch: + local_setup: false + minio: + local_setup: false + + api: + replicas: 3 + affinity: &spread-api + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - { key: app.name, operator: In, values: [plane-plane-api] } + topologyKey: kubernetes.io/hostname + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - { key: app.name, operator: In, values: [plane-plane-api] } + topologyKey: topology.kubernetes.io/zone + + web: { replicas: 3 } + space: { replicas: 2 } + admin: { replicas: 2 } + live: { replicas: 3 } + worker: { replicas: 4 } + silo: { enabled: true, replicas: 2 } + + beatworker: { replicas: 1 } # singleton - do not scale + pi_beat_worker: { replicas: 1 } # singleton - do not scale +``` + +Repeat the `affinity` block (varying the pod label) for every Tier-1 service. YAML anchors (`&spread-api` / `*spread-api`) help avoid repetition. diff --git a/apps/developer-docs/docs/self-hosting/govern/instance-admin.md b/apps/developer-docs/docs/self-hosting/govern/instance-admin.md new file mode 100644 index 00000000..44cf7dee --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/instance-admin.md @@ -0,0 +1,150 @@ +--- +title: Instance admin and God mode +description: Configure Plane instance admin settings. Learn about God mode and administrative controls for self-hosted Plane. +keywords: plane instance admin, god mode, admin panel, plane administration, instance settings, self-hosting admin +--- + +# Instance admin and God mode + +An instance is a single self-managed installation of Plane on a private cloud or server that the `Instance admin` controls and administers. A single instance can house multiple workspaces. + +::: info +There may also be cases where a user IRL is running multiple instances, e.g., when using Plane for several clients. An `Instance admin` role will have to be declared for each of those instances, but it is okay to use the same email address for all of them. +::: + +This role lets instance admins access `/god-mode`, a route for features that help them administer and govern their Plane instance better for all users of that instance. + +::: tip +New instances allow skipping going to God Mode and setting up your workspace instead. Whatever you choose after secure instance set-up, we highly recommend coming quickly to /god-mode to set up at least your SMTP server so your users can start getting invite emails to projects. +::: + +## Settings + +God Mode features a few screens as shown below. + +### General + +The General settings page allows you to view or configure core instance details and telemetry preferences. +Here’s what you can manage: + +- **Name of instance** + Customize the name of your instance. + +- **Email** + Displays the instance admin email address. + +- **Instance ID** + Displays a unique identifier for your instance. + +- **Chat with us** + Enable or disable in-app chat support for users. Disabling telemetry automatically turns this off. + +- **Let Plane collect anonymous usage data** + Plane collects anonymized usage data (no PII) to help improve features and overall experience. You can turn this off anytime. See [Telemetry](/self-hosting/telemetry) for more info. + +![](/images/instance-admin/god-mode-general.webp#hero) + +### Email + +Set up your SMTP server here so you can send essential emails—password resets, exports, changes to your instance—and Plane-enabled emails—onboarding, tips and tricks, new features— to all your users. [Learn more here](/self-hosting/govern/communication). + +![](/images/instance-admin/god-mode-email.webp#hero) + +### Authentication + +Control what SSO and OAuth services your users can use to sign up and log in to your Plane instance. You can also toggle unique code and password logins on and off from here. [Learn more here](/self-hosting/govern/authentication). + +- **Allow anyone to sign up without an invite** + Toggle this setting off if you want your users to join the instance only if they receive an invite. + +Once SSO is configured, instance admins can also use SSO to log in to God Mode. + +::: info +This is where you will see new SSO services and custom OAuth configs in the future. +::: + +![](/images/instance-admin/god-mode-authentication.webp#hero) + +### Workspaces + +The Workspaces section allows you to manage all workspaces within your Plane instance. + +- **View all Workspaces** + Access a complete list of workspaces on your instance. + +- **Create Workspaces** + You can create new workspaces directly from this section. If workspace creation is restricted, only the instance admin will have this ability. + +- **Restrict Workspace creation** + Toggle the **Prevent anyone from creating a workspace** option to prevent anyone else from creating workspaces. Once enabled, only you (the instance admin) can create new workspaces. + +To add users to a workspace, you will need to [invite them](https://docs.plane.so/core-concepts/workspaces/members#add-member) after creating it. + +::: info +Workspace deletion is currently not supported. +::: + +![](/images/instance-admin/god-mode-workspaces.webp#hero) + +### User management + +View and manage all users across the instance, invite instance admins, and control access to God Mode. + +![User management](/images/instance-admin/user-management.webp#hero) + +- View all users with their account type, status, and joining date +- Invite new instance admins +- Grant or remove admin access for existing users +- Remove users from the instance + +See [User management](/self-hosting/manage/manage-instance-users) for details. + +### Images in Plane + +You can use your own third-party libraries to update images in project settings. Configure your Unsplash key here. When we add more image libraries, they will show up here. + +![](/images/instance-admin/god-mode-images.webp#hero) + +## FAQs + +::: details How do you know who an Instance admin is? +Whoever spins up the instance or upgrades to v0.14, we assume, is the instance admin. When you see Let's secure your instance, enter your email-password combo. If you are already using Plane with those credentials, you will be logged in and will see /god-mode features. If not, we will create a new user on your local instance and you will see /god-mode. + +Our shrewd guess right now is users are technical enough to upgrade to or bring up a new instance with v0.14 are instance admins. If there’s a case where this isn’t true, please reach out to us before you upgrade or set up your fresh instance. +::: + +::: details What if I don’t complete secure instance set-up at the time of the upgrade? +We strongly recommend completing set-up at upgrade so your regular users can access Plane without trouble. Because we are introducing several sensitive admin features in `God Mode`, we will show an instance-not-set-up screen to your regular users until such a time that you can complete the setup. +![success-on-setup-existing-instances-self-hosted](/images/faq-2.png) +::: + +::: details What has changed with how existing regular users of my instance log in? +All existing users will log in with their usual email address-password combos if they are already doing it. If they haven’t been using a password when not OAuthing into Plane, they will now need to. If OAuth is enabled, users can continue using your OAuth methods. New users will need to choose a password or OAuth into Plane. +::: + +::: details What will happen to the default captain@plane.so account that you shipped so far? +For all new instances, there won’t be a `captain@plane.so` account. Instance set-up will allow you to set up a workspace and set workspace and project admins. + +For existing instances, the instance admin’s email will be added to each project with the same permissions as `captain@plane.so’s` so you can remove that email completely from your workspaces and projects. +::: + +::: details This is unreal, but I have an instance that has a /god-mode path already. I can’t access my Plane instance. Help! +That is unreal! Please reach out to us immediately on [support](https://discord.com/login?redirect_to=%2Fchannels%2F1031547764020084846%2F1094927053867995176) or on our [Discord](https://discord.com/invite/A92xrEGCge) and mark your message urgent. We will help you get your instance back pronto. + +::: + +::: details How will emails for password resets and onboarding be sent to users of my instance(s)? +We have always let you configure your own SMTP server to send emails from within your instance. It’s also why we are being deliberate about leading the instance admin of an existing instance to `/god-mode` first. After completing secure instance set-up now, you can configure your SMTP server on the UI instead of via `.env` variables. We strongly recommend you do that to avoid password-reset failures and failures in email delivery. + +Please [reach out](https://discord.com/login?redirect_to=%2Fchannels%2F1031547764020084846%2F1094927053867995176) to us on [Discord](https://discord.com/invite/A92xrEGCge) if you haven’t set up SMTP and are facing troubles with your users logging in. +::: + +::: details Why are you introducing passwords for app.plane.so users? What’s happening with unique links to sign up and sign in? +Unique links are secure and relatively easier, but we have heard from enough of our Cloud users that they would like to log in using a more permanent and easier method. Should you want to continue using unique codes, you are covered. We will keep that option alive for good. + +While using Google or GitHub are good options already, not all of you would want to use them. For those that prefer a password and would like to do away with codes, we want to make that option available. +::: + +::: details Is there a God Mode for Cloud admins, too? +Not now, but soon enough, there will be a `God Mode` for Cloud admins. +::: diff --git a/apps/developer-docs/docs/self-hosting/govern/integrations/bitbucket.md b/apps/developer-docs/docs/self-hosting/govern/integrations/bitbucket.md new file mode 100644 index 00000000..3b0676e5 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/integrations/bitbucket.md @@ -0,0 +1,125 @@ +--- +title: Configure Bitbucket for Plane integration +description: Connect Bitbucket to your self-hosted Plane instance. Sync pull requests and commits with Plane work items for development workflow tracking. +keywords: plane bitbucket integration, bitbucket cloud, bitbucket data center, bitbucket sync, pull request tracking +--- + +# Configure Bitbucket for Plane integration + +This guide walks you through setting up a Bitbucket application to enable Bitbucket integration for your Plane workspace on a self-hosted instance. Since self-hosted environments don't come pre-configured for Bitbucket, you'll need to create an OAuth consumer or application link, configure authentication, and set the necessary permissions to ensure seamless integration. + +This guide covers configuration for both: + +- **[Bitbucket Cloud](/self-hosting/govern/integrations/bitbucket#bitbucket-cloud)** + The standard cloud-hosted Bitbucket service at bitbucket.org + +- **[Bitbucket Data Center](/self-hosting/govern/integrations/bitbucket#bitbucket-data-center)** + Self-hosted Bitbucket instances for organizations with specific compliance or security requirements + +In this guide, you'll: + +1. [Create and configure a Bitbucket application](/self-hosting/govern/integrations/bitbucket#create-bitbucket-application) +2. [Configure your Plane instance](/self-hosting/govern/integrations/bitbucket#configure-plane-instance) + +::: warning +**Activate Bitbucket integration** + +After creating and configuring the Bitbucket application and configuring the instance as detailed on this page, you'll need to [setup the Bitbucket integration](https://docs.plane.so/integrations/bitbucket) within Plane. +::: + +## Create Bitbucket Application + +:::tabs key:bitbucket-edition + +== Bitbucket Cloud {#bitbucket-cloud} + +Follow these steps to register an OAuth consumer in your Bitbucket workspace, set the callback URL and scopes, and then configure your Plane instance so it can sync pull requests and commits. + +#### Bitbucket Cloud + +1. Log in to Bitbucket Cloud and navigate to your workspace. + +2. Go to **Workspace Settings → Apps & Features → OAuth Consumers**. + +3. Click **Add consumer** to begin the setup. + +4. Provide a **Name** for your OAuth consumer. + +5. Enter the following **Callback URL**, replacing `[YOUR_DOMAIN]` with your actual domain: + + ```bash + https://[YOUR_DOMAIN]/silo/api/oauth/bitbucket/auth/callback + ``` + +6. Set permissions by selecting the required **Scopes**. The table below explains each scope: + + | Category | Permission | Explanation | + | ------------- | ---------- | ---------------------------------------------------------------------- | + | Account | `email` | Read the user's primary email address. | + | Account | `read` | Read the user's account information and workspace memberships. | + | Repositories | `read` | Read access to repositories, including source code and metadata. | + | Repositories | `write` | Write access to repositories, required for creating and updating refs. | + | Pull requests | `read` | Read pull requests, comments, and activity on repositories. | + | Pull requests | `write` | Create and update pull requests and post comments. | + | Projects | `read` | Read project metadata and repository associations. | + | Issues | `read` | Read issues and their comments on repositories. | + | Issues | `write` | Create and update issues and post comments. | + | Webhooks | `read` | Read webhook subscriptions on repositories and workspaces. | + | Webhooks | `write` | Create and manage webhook subscriptions. | + +7. Click **Save** to finalize the setup. + +== Bitbucket Data Center {#bitbucket-data-center} + +These instructions cover registering an application link on your self-hosted Bitbucket Data Center instance, setting the redirect URL, and assigning the required permissions for Plane to access your repositories. + +#### Bitbucket Data Center + +1. Log in to your Bitbucket Data Center instance as an administrator. + +2. Go to **Settings → Application Links**. + +3. Click **Create link** to begin configuring a new application link. + +4. Enter the URL of your Plane instance and click **Continue**. + +5. Enter the following **Redirect URL**, replacing `[YOUR_DOMAIN]` with your actual domain: + + ```bash + https://[YOUR_DOMAIN]/silo/api/oauth/bitbucket-dc/auth/callback + ``` + +6. Set the required **Application Permissions**: + + | Resource | Permission Level | Explanation | + | ------------ | ---------------- | ------------------------------------------------------------------- | + | Projects | `Admin` | Required to read project metadata and manage webhook subscriptions. | + | Repositories | `Read` | Read access to repository metadata, branches, and commits. | + | Repositories | `Write` | Write access to create refs and update repository content. | + | Repositories | `Admin` | Required to manage repository-level webhooks. | + +7. Click **Save** to create the application link. + +::: + +## Configure Plane instance + +:::tabs key:bitbucket-edition + +== Bitbucket Cloud {#bitbucket-cloud} + +1. Copy the **Key** and **Secret** from the newly created OAuth consumer. + +2. Add these environment variables with the values to your Plane instance's `.env` file. + + ```bash + BITBUCKET_CLIENT_ID= + BITBUCKET_CLIENT_SECRET= + BITBUCKET_WEBHOOK_SECRET= + ``` + +3. Save the file and restart the instance. + +4. Once you've completed the instance configuration, [activate the Bitbucket integration in Plane](https://docs.plane.so/integrations/bitbucket?edition=bitbucket-cloud). + +::: diff --git a/apps/developer-docs/docs/self-hosting/govern/integrations/github.md b/apps/developer-docs/docs/self-hosting/govern/integrations/github.md new file mode 100644 index 00000000..11de074f --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/integrations/github.md @@ -0,0 +1,345 @@ +--- +title: Configure GitHub for Plane integration +description: Connect GitHub to your self-hosted Plane instance. Sync pull requests, commits, and branches with Plane work items for seamless development tracking. +keywords: plane github integration, github cloud, github enterprise, github sync, pull request tracking, commit linking, github app, +--- + +# Configure GitHub for Plane integration + +This guide walks you through setting up a GitHub App to enable GitHub integration for your Plane workspace on a self-hosted instance. Since self-hosted environments don’t come pre-configured for GitHub, you’ll need to set up the necessary authentication, permissions, and webhooks to ensure smooth integration. + +This guide covers configuration for both: + +- **[GitHub Cloud](/self-hosting/govern/integrations/github#github-cloud)** (github.com) + The standard GitHub service, available at github.com. Covers all plans + +- **[GitHub Enterprise Cloud](/self-hosting/govern/integrations/github#github-enterprise-server)** (ghe.com) + GitHub's managed enterprise service where your organization gets a dedicated subdomain + +- **[GitHub Enterprise Server](/self-hosting/govern/integrations/github#github-enterprise-server)** (self-hosted) + A self-hosted GitHub instance deployed on your own infrastructure + +In this guide, you’ll: + +1. [Create and configure a GitHub App](/self-hosting/govern/integrations/github#create-github-app) +2. [Set up permissions and events](/self-hosting/govern/integrations/github#set-up-permissions-and-events) +3. [Configure your Plane instance](/self-hosting/govern/integrations/github#configure-plane-instance) + +::: warning +**Activate GitHub integration** + +After creating and configuring the GitHub app and configuring the instance as detailed on this page, you'll need to [setup the GitHub integration](https://docs.plane.so/integrations/github) within Plane. +::: + +## Create GitHub App + +To configure GitHub integration, you'll need to create a GitHub App within your organization. + +:::tabs key:github-edition + +== GitHub Cloud {#github-cloud} + +Follow these steps to create a GitHub App, set callback URLs, and configure webhooks so Plane can sync PRs and commits from GitHub Cloud. + +#### GitHub Cloud + +1. Go to **Settings \> Developer Settings \> GitHub Apps** in your GitHub organization. + +2. Click **New GitHub App**. + + ![Create GitHub App](/images/integrations/github/create-github-app.webp#hero) + +3. In the **Register new GitHub App** page, provide a **GitHub App name** and **Homepage URL**. + + ![App name and homepage URL](/images/integrations/github/app-name-homepage-url.webp#hero) + +4. In the **Identifying and authorizing users** section, add the following **Callback URLS**. + + ```bash + https:///silo/api/github/auth/callback + https:///silo/api/github/auth/user/callback + ``` + + These URLs allow Plane to verify and enable workspace connection with the Github App. + + ![Add Callback URL](/images/integrations/github/add-callback-url.webp#hero) + + :::warning + Make sure to opt out of **Expire user authorization tokens** feature. + ::: + +5. In the **Post installation** section, add the below **Setup URL**. + + ```bash + https:///silo/api/github/auth/callback + ``` + + Redirects users to this URL after GitHub app installation. + + ![Add setup URL](/images/integrations/github/add-setup-url.webp#hero) + +6. Turn on **Redirect on update**. + +7. In the **Webhook** section, add the below **Webhook URL**. + + ```bash + https:///silo/api/github/github-webhook + ``` + + This allows Plane to receive updates from GitHub repositories. + + ![Add Webhook URL](/images/integrations/github/add-webhook-url.webp#hero) + +== GitHub Enterprise Server {#github-enterprise-server} +These steps cover hostname, callback URLs, and private key differences for on‑prem GitHub deployments. + +> [!CAUTION] GitHub Enterprise Cloud (GHE.com) +> On GHE.com enterprise, go to `https://.ghe.com/enterprises//settings/apps` and create a new GitHub App. + +#### GitHub Enterprise Server + +1. Go to **Settings \> Developer Settings \> GitHub Apps** in your GitHub organization. + +2. Click **New GitHub App**. + + ![Create GitHub App](/images/integrations/github/create-github-app.webp#hero) + +3. In the **Register new GitHub App** page, provide a **GitHub App name** and **Homepage URL**. + + ![App name and homepage URL](/images/integrations/github/app-name-homepage-url.webp#hero) + +4. In the **Identifying and authorizing users** section, add the following **Callback URLS**. + + **For Plane cloud instance** + + ```bash + https://silo.plane.so/api/oauth/github-enterprise/auth/callback + https://silo.plane.so/api/oauth/github-enterprise/auth/user/callback + ``` + + **For Plane self-hosted instance** + + ```bash + https:///silo/api/oauth/github-enterprise/auth/callback + https:///silo/api/oauth/github-enterprise/auth/user/callback + ``` + + These URLs allow Plane to verify and enable workspace connection with the Github App. + ![Add Callback URL](/images/integrations/github/add-callback-url.webp#hero) + :::warning + Make sure to opt out of **Expire user authorization tokens** feature. + ::: + +5. In the **Post installation** section, add the below **Setup URL**. + + **For Plane cloud instance** + + ```bash + https://silo.plane.so/api/oauth/github-enterprise/auth/callback + ``` + + **For Plane self-hosted instance** + + ```bash + https:///silo/api/oauth/github-enterprise/auth/callback + ``` + + Redirects users to this URL after GitHub app installation. + ![Add setup URL](/images/integrations/github/add-setup-url.webp#hero) + +6. Turn on **Redirect on update**. + +7. In the **Webhook** section, add the below **Webhook URL**. + + **For Plane cloud instance** + + ```bash + https://silo.plane.so/api/github-enterprise/github-webhook + ``` + + **For Plane self-hosted instance** + + ```bash + https:///silo/api/github-enterprise/github-webhook + ``` + + This allows Plane to receive updates from GitHub repositories. + + ![Add Webhook URL](/images/integrations/github/add-webhook-url.webp#hero) + +::: + +### Set up permissions and events + +1. Add repository and account permissions by setting the **Access** dropdown next to each permission, as shown in the tables below. + + ![Setup permissions](/images/integrations/github/setup-permissions.webp#hero) + + **Repository permissions** + + | Permission            | Access level     | Purpose | + | ---------------------------------------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------ | + | Issues | Read and write | Enables reading, creating, updating, closing, and commenting on issues within the repository. | + | Metadata | Read-only | Provides read-only access to repository metadata, such as its name, description, and visibility. | + | Pull requests | Read and write | Allows reading, creating, updating, merging, and commenting on pull requests. | + + **Account permissions** + + | Permission           | Access level     | Purpose | + | ---------------------------------------------------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------- | + | Email addresses | Read-only | Grants access to users' email addresses, typically for notifications or communication. | + | Profile | Read and write | Enables access to user profile details like name, username, and avatar. | + +2. In the **Subscribe to events** section, turn on all the required events below. + + ![Subscribe to events](/images/integrations/github/subscribe-to-events.webp#hero) + + | Event                                             | Purpose | + | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | Installation target | This is where the repositories or organizations where your GitHub App is installed. This determines which repositories Plane can sync with. | + | Meta | Includes metadata about the app's configuration and setup. This is essential for maintaining integration stability. | + | Issue comment | Triggers when a comment is added, edited, or deleted on an issue. Useful for keeping comments synced between Plane and GitHub. | + | Issues | Triggers when an issue is created, updated, closed, reopened, assigned, labeled, or transferred. Ensures issue status and details remain consistent between Plane and GitHub. | + | Pull request | Fires when a pull request is opened, closed, merged, edited, or labeled. Essential for tracking development progress. | + | Pull request review | Activates when a review is submitted, edited, or dismissed. Keeps review activities aligned between Plane and GitHub. | + | Pull request review comment | Fires when a review comment is added, modified, or removed. Ensures feedback is reflected across both platforms. | + | Pull request review thread | Triggers when a review discussion thread is resolved or reopened. Helps maintain visibility on code review discussions. | + | Push | Activates when new commits are pushed to a repository. Useful for tracking code updates and changes. | + | Repository sub issues | Tracks issues within a repository that are linked to or managed by another issue. Ensures accurate synchronization of related issues. | + +3. Click the **Create GitHub App** button at the bottom of the page. + +## Configure Plane instance + +:::tabs key:github-edition + +== GitHub Cloud {#github-cloud} + +1. Go back to **Settings \> Developer Settings \> GitHub Apps**. + +2. Click **Edit** on the GitHub you created. + +3. In the **General** tab, under the **Client secrets** section, click **Generate a new client secret**. + + ![General tab](/images/integrations/github/general-tab.webp#hero) + +4. Scroll down to the **Private keys** section. + + ![Private keys](/images/integrations/github/private-keys.webp#hero) + +5. Click **Genereate a private key**. + +6. Retrieve the following details from the **General** tab: + - App ID + - Client ID + - Client secret + - GitHub App name + - Private key + +7. Before adding the Private key as an environment variable, you'll need to convert it to base64. Since private keys are typically multi-line, they can cause parsing errors or issues when setting environment variables. To avoid this, run the following command to convert the key to base64: + + ```bash + cat private_key.pem | base64 -w 0 + ``` + +8. Add these environment variables with the values to your Plane instance's `.env` file. + + ```bash + GITHUB_CLIENT_ID= + GITHUB_CLIENT_SECRET= + GITHUB_APP_NAME= + GITHUB_APP_ID= + GITHUB_PRIVATE_KEY= + ``` + +9. Save the file and restart the instance. + +10. Once you've completed the instance configuration, [activate the GitHub integration in Plane](https://docs.plane.so/integrations/github). + +== GitHub Enterprise Server {#github-enterprise-server} + +1. Go back to **Settings \> Developer Settings \> GitHub Apps**. + +2. Click **Edit** on the GitHub you created. + +3. In the **General** tab, under the **Client secrets** section, click **Generate a new client secret**. + + ![General tab](/images/integrations/github/general-tab.webp#hero) + +4. Scroll down to the **Private keys** section. + + ![Private keys](/images/integrations/github/private-keys.webp#hero) + +5. Click **Generate a private key**. + +6. Retrieve the following details from the **General** tab: + - App ID + - App Slug (You can find this in browser url) + - Client ID + - Client secret + - Private key + +7. Convert the Private key to convert it to base64. Since private keys are typically multi-line, they can cause parsing errors or issues when setting environment variables. To avoid this, run the following command to convert the key to base64: + + ```bash + cat private_key.pem | base64 -w 0 + ``` + +8. Once you've created the app, [activate the GitHub Enterprise integration in Plane](https://docs.plane.so/integrations/github?edition=github-enterprise#connect-github-organization). + +::: + +## Troubleshooting + +### Invalid private key + +
+ Error: Failed to create GitHub connection: Invalid keyData +
+ +This error usually occurs when the private key is not correctly generated. To fix this, follow the below steps. + +1. Generate a new private key. +2. Convert the private key to base64. + +```bash + cat private_key.pem | base64 -w 0 +``` + +3. Add the private key to the `.env` file. + +```bash + GITHUB_PRIVATE_KEY= +``` + +4. Save the file and restart the instance. + +### Unable to connect GitHub organization account or personal account + +
+ Error: Invalid request callback URL. +
+ +This error usually occurs when the callback URL is not correctly configured or the GitHub App is not marked public. To fix this, follow the below steps. + +1. Check if the callback URL is correctly configured. +2. Check if your GitHub App is marked public. + +### Application secret value not found + +
+ Error: Application secret value not found for key: x-github-id +
+ +This error usually occurs when the application secret is not correctly configured. To fix this, follow the below steps. + +1. Delete the `plane_app_details_github` key from redis cache. `del plane_app_details_github`. +2. Set the `SILO_BASE_URL` in env with plane self hosted url and restart the api server. `export SILO_BASE_URL=https://` +3. Run this command in api server shell `python manage.py reset_marketplace_app_secrets` to reset the application secrets. +4. Try to connect again to the organization account to Plane. + +### Github integration suddenly stopped working after a while + +This error usually occurs when the GitHub integration is not correctly configured. To fix this, follow the below steps. + +1. Make sure you've `opted out` of Server Token expiration and reconnect once again to the organization account to Plane. Check in Github App Settings > Optional Features diff --git a/apps/developer-docs/docs/self-hosting/govern/integrations/gitlab.md b/apps/developer-docs/docs/self-hosting/govern/integrations/gitlab.md new file mode 100644 index 00000000..fdf73a1d --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/integrations/gitlab.md @@ -0,0 +1,147 @@ +--- +title: Configure GitLab for Plane integration +description: Connect GitLab to your self-hosted Plane instance. Sync merge requests and commits with Plane work items for development workflow tracking. +keywords: plane gitlab integration, gitlab cloud, gitlab self-managed, gitlab sync, merge request tracking +--- + +# Configure GitLab for Plane integration + +This guide walks you through setting up a GitLab application to enable GitLab integration for your Plane workspace on a self-hosted instance. Since self-hosted environments don’t come pre-configured for GitLab, you’ll need to create an application, configure authentication, and set the necessary permissions to ensure seamless integration. + +This guide covers configuration for both: + +- **[GitLab.com](/self-hosting/govern/integrations/gitlab#gitlab-cloud)** + The standard cloud-hosted GitLab service + +- **[GitLab Self-managed](/self-hosting/govern/integrations/gitlab#gitlab-self-managed)** + Self-hosted GitLab instances for organizations with specific compliance or security requirements + +In this guide, you’ll: + +1. [Create and configure a GitLab Application](/self-hosting/govern/integrations/gitlab#create-gitlab-application) +2. [Configure your Plane instance](/self-hosting/govern/integrations/gitlab#configure-plane-instance) + +::: warning +**Activate GitLab integration** + +After creating and configuring the GitLab application and configuring the instance as detailed on this page, you'll need to [setup the GitLab integration](https://docs.plane.so/integrations/gitlab) within Plane. +::: + +## Create GitLab Application + +:::tabs key:gitlab-edition + +== GitLab Cloud {#gitlab-cloud} + +Follow these steps to register an application on the public GitLab service, set the redirect URI and scopes, +and then configure your Plane instance so it can sync merge requests and commits. + +#### GitLab Cloud + +1. On the left sidebar in GitLab, select your avatar. + +2. Select **Preferences** tab. + +3. Navigate to the **Applications** tab. + +4. Click on **Add new application** to begin the setup. + ![Add GitLab application](/images/integrations/gitlab/add-gitlab-application.webp#hero) + +5. Provide a **Name** for your application. + +6. Enter the following **Redirect URI**, replacing [YOUR_DOMAIN] with your actual domain: + ```bash + https://[YOUR_DOMAIN]/silo/api/gitlab/auth/callback + ``` +7. Check the **Confidential** box. + + ![Add app details](/images/integrations/gitlab/add-app-details.webp#hero) + +8. Set permissions by selecting the required **Scopes**. The table below explains each scope: + + | Permission | Explanation | + | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | `api` | Grants full read/write access to the API, including all groups, projects, container registry, dependency proxy, and package registry. Required for API requests. | + | `read_api` | Allows read-only access to all groups, projects, container registry, and package registry. | + | `read_user` | Grants read-only access to user profiles via the /user API endpoint, including username, public email, and full name. Also provides access to /users endpoints. | + | `read_repository` | Enables read-only access to repositories in private projects via Git-over-HTTP or the Repository Files API. | + | `profile` | Grants read-only access to the user's profile data using OpenID Connect. | + | `email` | Provides read-only access to the user's primary email address using OpenID Connect. | + +9. Click **Save Application** to finalize the setup. + +== GitLab Self-managed {#gitlab-self-managed} + +These instructions cover registering an OAuth app on your private GitLab server, using the correct callback URLs, and assigning the required scopes for Plane to access your repos and users. + +#### GitLab Self-managed + +1. Log in to your GitLab instance. +2. Click on your profile icon in the top-right corner. +3. From the dropdown menu that appears, select **Edit profile**. +4. Look for and select the **Applications** option within this menu. +5. On the Applications page, click **Add new application** to begin configuring your OAuth application. + +Fill in the application details with the following configuration: + +- **Name** + Enter a descriptive name for your application (e.g., `Plane Local Dev` or `Plane Integration`). + +- **Redirect URI** + The redirect URI depends on your Plane deployment: + + **For Plane Cloud:** + + `https://silo.plane.so/api/oauth/gitlab-enterprise/auth/callback` + + **For Plane Self-Hosted:** + + `https:///silo/api/oauth/gitlab-enterprise/auth/callback` + +Replace `` with your actual Plane instance domain. + +- **Confidential** + Keep the **Confidential** checkbox enabled. This ensures the application uses a client secret for secure authentication. + +- **Scopes** + Select the following scopes to grant Plane the necessary permissions: + +- **api** - Grants complete read/write access to the API, including all groups and projects +- **read_api** - Grants read access to the API, including all groups and projects +- **read_user** - Grants read-only access to your profile information +- **read_repository** - Grants read-only access to repositories on private projects +- **profile** - Grants read-only access to the user's profile data using OpenID Connect +- **email** - Grants read-only access to the user's primary email address using OpenID Connect + +6. Click **Save application** to create the OAuth application. + +::: + +## Configure Plane instance + +:::tabs key:gitlab-edition + +== GitLab Cloud {#gitlab-cloud} + +1. Copy the **Application ID** and **Secret** from the newly created application. + ![Copy credentials](/images/integrations/gitlab/copy-credentials.webp#hero) + +2. Add these environment variables with the values to your Plane instance's `.env` file. + + ```bash + GITLAB_CLIENT_ID= + GITLAB_CLIENT_SECRET= + ``` + +3. Save the file and restart the instance. + +4. Once you've completed the instance configuration, [activate the GitLab integration in Plane](https://docs.plane.so/integrations/gitlab?edition=gitlab-cloud). + +== GitLab Self-managed {#gitlab-self-managed} + +1. Copy the **Application ID** and **Secret** from the newly created application. + ![Copy credentials](/images/integrations/gitlab/copy-credentials.webp#hero) + +2. Once you've created the application, [activate the GitLab Self-managed integration in Plane](https://docs.plane.so/integrations/gitlab?edition=gitlab-self-managed). + +::: diff --git a/apps/developer-docs/docs/self-hosting/govern/integrations/sentry.md b/apps/developer-docs/docs/self-hosting/govern/integrations/sentry.md new file mode 100644 index 00000000..d39c4d98 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/integrations/sentry.md @@ -0,0 +1,356 @@ +--- +title: Configure Sentry for Plane integration +description: Connect Sentry error monitoring to your self-hosted Plane instance. Automatically create work items from Sentry alerts and track error resolution. +keywords: plane sentry integration, error tracking, sentry alerts, bug tracking, error monitoring, self-hosting, plane devops +--- + +# Configure Sentry for Plane integration + +This guide shows you how to set up Sentry integration for your self-hosted Plane instance. Unlike Plane Cloud where Sentry comes pre-configured, self-hosted instances require you to create a custom integration in Sentry and configure your Plane deployment with the necessary credentials. + +::: info +**What you'll accomplish:** + +1. Create a Sentry custom integration with proper permissions and webhooks +2. Configure your Plane instance with Sentry credentials +3. Enable error tracking and automatic issue creation from Sentry alerts + ::: + +## Before you begin + +You'll need: + +- Administrator access to your Sentry organization +- Access to your Plane instance configuration files +- Your Plane instance domain (e.g., `plane.yourcompany.com`) + +## Create Sentry custom integration + +A custom integration (also called a public integration) connects your Sentry organization to Plane, enabling bidirectional communication for issue tracking and alert handling. + +1. Log in to your Sentry organization. +2. Go to **Settings** → **Developer Settings** → **Custom Integrations**. +3. Click **Create New Integration**. +4. Select **Public Integration**. +5. Fill in these fields on the integration creation screen: + + | Field | Value | + | ----------------------- | ----------------------------------------------------------- | + | **Name** | `Plane` (or any name you prefer) | + | **Author** | Your organization name | + | **Webhook URL** | `https://[YOUR_DOMAIN]/silo/api/sentry/sentry-webhook/` | + | **Redirect URL** | `https://[YOUR_DOMAIN]/silo/api/oauth/sentry/auth/callback` | + | **Verify Installation** | Disabled (recommended) | + | **Alert Rule Action** | Enabled | + +::: tip +Replace `[YOUR_DOMAIN]` with your actual Plane instance domain. For example, if your Plane instance is at `plane.company.com`, your Webhook URL would be `https://plane.company.com/silo/api/sentry/sentry-webhook/` +::: + + + +**Field explanations:** + +**Webhook URL** +Sentry sends event notifications to this endpoint. Plane processes these webhooks to sync Sentry issues with Plane work items. + +**Redirect URL** +After OAuth authorization, Sentry redirects users back to this URL to complete the connection. + +**Alert Rule Action** +Enables automatic Plane work item creation when Sentry alert rules trigger. + +### Configure integration schema + +The schema defines how Sentry and Plane interact—what fields appear when creating issues and how alert rules behave. + +Paste this schema into the **Schema** field: + +```json +{ + "elements": [ + { + "link": { + "uri": "/api/sentry/issues/link", + "required_fields": [ + { + "uri": "/api/sentry/issues", + "name": "identifier", + "type": "select", + "label": "Issue", + "skip_load_on_open": true + } + ] + }, + "type": "issue-link", + "create": { + "uri": "/api/sentry/issues/create", + "optional_fields": [ + { + "uri": "/api/sentry/users", + "name": "assignee_ids", + "type": "select", + "async": false, + "label": "Assignees", + "multiple": true, + "depends_on": ["project_id"] + }, + { + "uri": "/api/sentry/priorities", + "name": "priorities", + "type": "select", + "async": false, + "label": "Priorities", + "depends_on": ["project_id"] + }, + { + "uri": "/api/sentry/labels", + "name": "labels", + "type": "select", + "async": false, + "label": "Labels", + "multiple": true, + "depends_on": ["project_id"] + }, + { + "uri": "/api/sentry/states", + "name": "state", + "type": "select", + "async": false, + "label": "State", + "depends_on": ["project_id"] + }, + { + "uri": "/api/sentry/modules", + "name": "module", + "type": "select", + "async": false, + "label": "Module", + "depends_on": ["project_id"] + }, + { + "uri": "/api/sentry/cycles", + "name": "cycle", + "type": "select", + "async": false, + "label": "Cycle", + "depends_on": ["project_id"] + } + ], + "required_fields": [ + { + "name": "title", + "type": "text", + "label": "Title", + "default": "issue.title" + }, + { + "name": "description", + "type": "textarea", + "label": "Description", + "default": "issue.description" + }, + { + "uri": "/api/sentry/projects", + "name": "project_id", + "type": "select", + "async": false, + "label": "Project" + } + ] + } + }, + { + "type": "alert-rule-action", + "title": "Create Plane Work Item or Intake Issue", + "settings": { + "uri": "/api/sentry/alert-rule", + "type": "alert-rule-settings", + "description": "Create a Plane Work Item or Intake Issue when an alert is triggered", + "optional_fields": [ + { + "uri": "/api/sentry/users", + "name": "assignee_ids", + "type": "select", + "async": false, + "label": "Assignees", + "multiple": true, + "depends_on": ["project_id"] + }, + { + "uri": "/api/sentry/states", + "name": "state", + "type": "select", + "async": false, + "label": "State", + "depends_on": ["project_id"] + }, + { + "uri": "/api/sentry/labels", + "name": "labels", + "type": "select", + "async": false, + "label": "Labels", + "multiple": true, + "depends_on": ["project_id"] + } + ], + "required_fields": [ + { + "name": "type", + "type": "select", + "label": "Type", + "options": [ + ["intake", "Intake"], + ["work_item", "Work Item"] + ] + }, + { + "uri": "/api/sentry/projects", + "name": "project_id", + "type": "select", + "async": false, + "label": "Project", + "depends_on": ["type"] + } + ] + } + } + ] +} +``` + +**What this schema enables** + +**Work item linking** +The first element defines how users create new Plane work items from Sentry or link existing ones. Required fields (title, description, project) ensure every work item has essential information. Optional fields (assignees, priority, labels, state, module, cycle) provide flexibility for detailed work item tracking. + +**Alert rule actions** +The second element enables automatic work item creation when Sentry alerts fire. You can configure whether alerts create regular work items or intake work items for triage, and set default assignees, states, and labels. + +### Set permissions + +Configure these permissions to allow Sentry to interact with Plane appropriately: + +| Permission | Access Level | Why This Matters | +| ----------------- | ------------ | ------------------------------------------------------------ | +| **Project** | Read | Access project details, tags, and debug files from Sentry | +| **Team** | Read | Retrieve team member lists for assignee dropdowns | +| **Release** | No Access | Not required for Plane integration | +| **Distribution** | No Access | Not required for Plane integration | +| **Issue & Event** | Read & Write | Create and link issues, sync status updates bidirectionally | +| **Organization** | Read | Resolve organization IDs and retrieve repository information | +| **Member** | Read | Access member details for assignee functionality | +| **Alerts** | Read | Enable alert rule actions for automatic issue creation | + + + +### Enable webhooks + +Webhooks keep Plane and Sentry synchronized. When issues change in Sentry, Plane receives notifications and updates accordingly. + +Enable the **issue** webhook with these events: + +| Event | Why It's Needed | +| -------------- | -------------------------------------------------------------- | +| **created** | Notify Plane when new Sentry issues are detected | +| **resolved** | Update linked Plane work items when Sentry issues are resolved | +| **assigned** | Sync assignee changes from Sentry to Plane | +| **archived** | Reflect archived status in Plane | +| **unresolved** | Update Plane when resolved issues reopen | + + + +### Save and retrieve credentials + +After saving your integration, Sentry generates OAuth credentials: + +- **Client ID** - A public identifier for your integration +- **Client Secret** - A private key used to authenticate API requests + +::: warning +**Important** +The Client Secret is only displayed once immediately after creating the integration. Copy it now and store it securely. If you lose it, you'll need to regenerate the integration. +::: + +Copy both the Client ID and Client Secret. You'll need these in the next step. + +## Configure your Plane instance + +Add Sentry credentials to your Plane instance so it can communicate with Sentry's API. + +### Locate your configuration file + +**For Docker deployments:** + +- Edit `plane.env` in your Plane installation directory + +**For Kubernetes deployments:** + +- Edit your `custom-values.yaml` file or ConfigMap containing environment variables + +### Add environment variables + +Add these variables to your Plane configuration: + +**For Docker (`plane.env`):** + +```bash +# Sentry Integration +SENTRY_BASE_URL=https://sentry.io +SENTRY_CLIENT_ID= +SENTRY_CLIENT_SECRET= +SENTRY_INTEGRATION_SLUG=plane +``` + +**For Kubernetes (`custom-values.yaml`):** + +```yaml +env: + silo_envs: + sentry_base_url: "https://sentry.io" + sentry_client_id: "" + sentry_client_secret: "" + sentry_integration_slug: "plane" +``` + +Replace `` and `` with the credentials from Step 1. + +### Environment variable reference + +| Variable | Required | Default | Description | +| ------------------------- | -------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `SENTRY_BASE_URL` | No | `https://sentry.io` | Base URL of your Sentry instance. For self-hosted Sentry, use your Sentry domain (e.g., `https://sentry.company.com`) | +| `SENTRY_CLIENT_ID` | Yes | - | Client ID from your Sentry custom integration | +| `SENTRY_CLIENT_SECRET` | Yes | - | Client Secret from your Sentry custom integration (only shown once during creation) | +| `SENTRY_INTEGRATION_SLUG` | No | - | The slug identifier for your integration. Find this in your integration's URL: `https://org.sentry.io/settings/developer-settings/plane-local` (here `plane-local` is the slug) | + +::: info +**Using self-hosted Sentry?** +If you're running your own Sentry instance, change `SENTRY_BASE_URL` to your Sentry domain. All other configuration remains the same. +::: + +### Restart Plane + +Apply the configuration changes: + +**For Docker:** + +```bash +docker compose down +docker compose up -d +``` + +**For Kubernetes:** + +```bash +helm upgrade plane-app plane-enterprise.tgz \ + --namespace plane \ + -f custom-values.yaml +``` + +## Activate integration in your workspace + +Once you’ve completed the instance configuration, [activate the Sentry integration](https://docs.plane.so/integrations/sentry#set-up-sentry-integration) in Plane. + +For questions about Sentry integration, contact [support@plane.so](mailto:support@plane.so). diff --git a/apps/developer-docs/docs/self-hosting/govern/integrations/slack.md b/apps/developer-docs/docs/self-hosting/govern/integrations/slack.md new file mode 100644 index 00000000..7909c601 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/integrations/slack.md @@ -0,0 +1,296 @@ +--- +title: Configure Slack for Plane integration +description: Connect Slack to your self-hosted Plane instance. Get notifications for work item updates and interact with Plane from Slack channels. +keywords: plane slack integration, slack notifications, slack bot, workspace notifications, slack webhook, self-hosting, plane communication +--- + +# Configure Slack for Plane integration + +This guide walks you through setting up a Slack App to enable Slack integration for your Plane workspace on a self-hosted instance. Since self-hosted environments don’t come pre-configured for Slack, you’ll need to set up the necessary authentication, permissions, and event subscriptions to ensure seamless communication between Plane and Slack. + +In this guide, you’ll: + +1. [Create and configure a Slack App](/self-hosting/govern/integrations/slack#create-slack-app) +2. [Configure your Plane instance](/self-hosting/govern/integrations/slack#configure-plane-instance) + +::: warning +**Activate Slack integration** + +After creating and configuring the Slack app and configuring the instance as detailed on this page, you'll need to [set up the Slack integration](https://docs.plane.so/integrations/slack) within Plane. +::: + +## Create Slack App + +To configure Slack integration, you'll need to create a Slack App within your organization. Follow these steps: + +1. Go to [Your Apps](https://api.slack.com/apps) on Slack. + +2. Click **Create an App**. + ![Create Slack App](/images/integrations/slack/create-slack-app.webp#hero) + +3. Choose **From a manifest**. + ![Choose Manifest](/images/integrations/slack/choose-from-manifest.webp#hero) + +4. Select the workspace where you want the app installed. + +5. Remove the default manifest and paste the one below, making sure to update the placeholders with your actual values. + ![Manifest](/images/integrations/slack/app-from-manifest.webp#hero) + +:::tabs key:manifest-file + +== JSON {json} + +```json +{ + "display_information": { + "name": "[YOUR_APP_NAME]", + "description": "[YOUR_APP_DESCRIPTION]", + "background_color": "#224dab" + }, + "features": { + "bot_user": { + "display_name": "[YOUR_APP_NAME]", + "always_online": false + }, + "shortcuts": [ + { + "name": "Create new issue", + "type": "message", + "callback_id": "issue_shortcut", + "description": "Create a new issue in plane" + }, + { + "name": "Link Work Item", + "type": "message", + "callback_id": "link_work_item", + "description": "Links thread with an existing work item" + } + ], + "slash_commands": [ + { + "command": "/plane", + "url": "https://[YOUR_DOMAIN]/silo/api/slack/command/", + "description": "Create issue in Plane", + "should_escape": false + } + ], + "unfurl_domains": ["[YOUR_DOMAIN]"] + }, + "oauth_config": { + "redirect_urls": [ + "https://[YOUR_DOMAIN]/silo/api/slack/team/auth/callback/", + "https://[YOUR_DOMAIN]/silo/api/slack/user/auth/callback/" + ], + "scopes": { + "user": ["chat:write", "identify", "im:read", "im:write", "links:write", "links:read"], + "bot": [ + "channels:join", + "channels:read", + "users:read", + "users:read.email", + "chat:write", + "chat:write.customize", + "channels:history", + "groups:history", + "mpim:history", + "im:history", + "links:read", + "links:write", + "groups:read", + "im:read", + "mpim:read", + "reactions:read", + "reactions:write", + "files:read", + "files:write", + "im:write", + "commands" + ] + } + }, + "settings": { + "event_subscriptions": { + "request_url": "https://[YOUR_DOMAIN]/silo/api/slack/events", + "bot_events": ["link_shared", "message.channels", "message.im"] + }, + "interactivity": { + "is_enabled": true, + "request_url": "https://[YOUR_DOMAIN]/silo/api/slack/action/", + "message_menu_options_url": "https://[YOUR_DOMAIN]/silo/api/slack/options/" + }, + "org_deploy_enabled": false, + "socket_mode_enabled": false, + "token_rotation_enabled": true + } +} +``` + +== YAML {yaml} + +```yaml +display_information: +name: [YOUR_APP_NAME] +description: [YOUR_APP_DESCRIPTION] +background_color: "#224dab" +features: +bot_user: + display_name: [YOUR_APP_NAME] + always_online: false +shortcuts: + - name: Create new issue + type: message + callback_id: issue_shortcut + description: Create a new issue in plane + - name: Link Work Item + type: message + callback_id: link_work_item + description: Links thread with an existing work item +slash_commands: + - command: /plane + url: https://[YOUR_DOMAIN]/silo/api/slack/command/ + description: Create issue in Plane + should_escape: false +unfurl_domains: + - [YOUR_DOMAIN] +oauth_config: +redirect_urls: + - https://[YOUR_DOMAIN]/silo/api/slack/team/auth/callback/ + - https://[YOUR_DOMAIN]/silo/api/slack/user/auth/callback/ +scopes: + user: + - chat:write + - identify + - im:read + - im:write + - links:write + - links:read + bot: + - channels:join + - channels:read + - users:read + - users:read.email + - chat:write + - chat:write.customize + - channels:history + - groups:history + - mpim:history + - im:history + - links:read + - links:write + - groups:read + - im:read + - mpim:read + - reactions:read + - reactions:write + - files:read + - files:write + - im:write + - commands +settings: +event_subscriptions: + request_url: https://[YOUR_DOMAIN]/silo/api/slack/events + bot_events: + - link_shared + - message.channels + - message.im + interactivity: + is_enabled: true + request_url: https://[YOUR_DOMAIN]/silo/api/slack/action/ + message_menu_options_url: https://[YOUR_DOMAIN]/silo/api/slack/options/ + org_deploy_enabled: false + socket_mode_enabled: false + token_rotation_enabled: true +``` + +::: + +6. Review the permissions and click **Create**. + ![Review summary](/images/integrations/slack/review-summary.webp#hero) + +### Manifest reference + +The manifest file defines the configuration for integrating Plane with Slack. It requests access to several features, enabling Plane to interact with Slack efficiently. + +#### Features + +| Feature | Explanation | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| `bot_user` | Required to send thread messages while syncing issues or sending Plane notifications to Slack. | +| `slack_commands` | A Slack command (`/plane`) allows users to create issues directly from Slack using a slash command. | +| `shortcuts` | After activation, users can create issues from messages inside Slack. | +| `unfurl_domain` | Specifies the domain where Plane is hosted. When an issue, cycle, or module link is pasted in Slack, it generates a preview of the entity. | + +#### Variables + +| Variable | Explanation | +| ---------------------- | ----------------------------------------------------------------------------------------------------------- | +| `YOUR_DOMAIN` | The domain where Plane is hosted. This is required for sending webhook events and authentication callbacks. | +| `YOUR_APP_NAME` | The name you want to give your Slack app. "Plane" is a good default option. | +| `YOUR_APP_DESCRIPTION` | A short description of your Slack app’s purpose. | + +#### Event subscription + +For thread sync and link unfurling to work, event subscriptions must be enabled. These events send relevant activity to Plane. + +| Bot event | Explanation | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | +| `link_shared` | When a link is shared in Slack and its hostname matches `unfurl_domain`, Plane receives the event and generates a preview of the entity. | +| `message_channels` | When a message is posted in a channel, an event is triggered in Plane to support thread sync. | +| `message_im` | When a direct message (DM) is posted, an event is triggered in Plane to support thread sync. | + +#### User permissions + +| Permission | Explanation | +| ------------- | -------------------------------------------------------------------------------- | +| `chat:write` | Allows the bot to send messages in channels and conversations it is a member of. | +| `identify` | Allows the bot to verify its own identity and retrieve basic information. | +| `im:read` | Enables the bot to view direct messages (DMs) where it has been added. | +| `im:write` | Allows the bot to send direct messages (DMs) to users. | +| `links:write` | Permits the bot to add, edit, and remove link unfurls. | +| `links:read` | Allows the bot to view link unfurls and associated metadata. | + +#### Bot permissions + +| Permission | Explanation | +| ---------------------- | -------------------------------------------------------------------------- | +| `channels:join` | Allows the bot to join public channels. | +| `channels:read` | Permits viewing public channel information and members. | +| `users:read` | Allows viewing user information and presence status. | +| `users:read.email` | Enables access to users' email addresses. | +| `chat:write` | Allows sending messages in channels and conversations. | +| `chat:write.customize` | Enables customization of the bot's name and profile when sending messages. | +| `channels:history` | Allows viewing message history in public channels. | +| `groups:history` | Permits viewing message history in private channels. | +| `mpim:history` | Enables access to message history in multi-person direct messages. | +| `im:history` | Allows viewing message history in direct messages. | +| `links:read` | Permits viewing link unfurls and associated metadata. | +| `links:write` | Allows adding, editing, and removing link unfurls. | +| `groups:read` | Enables viewing private channel information and members. | +| `im:read` | Allows viewing direct messages where the bot is added. | +| `mpim:read` | Permits viewing multi-person direct messages. | +| `reactions:read` | Enables viewing emoji reactions on messages. | +| `reactions:write` | Allows adding and removing emoji reactions. | +| `files:read` | Permits viewing and downloading files. | +| `files:write` | Enables uploading, editing, and deleting files. | +| `im:write` | Allows sending direct messages to users. | +| `commands` | Enables the bot to add and respond to slash commands. | + +## Configure Plane instance + +After creating your Slack app, follow these steps: + +1. Go to the **Event Subscriptions** tab. + +2. Click **Retry** to verify your event subscription URL. + ![Event subscriptions](/images/integrations/slack/event-subscriptions.webp#hero) + +3. Navigate to the **Basic Information** tab on Slack to find your `client_id` and `client_secret`. + +4. Add these environment variables with the values to your Plane instance's `.env` file. + ```bash + SLACK_CLIENT_ID= + SLACK_CLIENT_SECRET= + ``` +5. Save the file and restart the instance. + +6. Once you've completed the instance configuration, [activate the Slack integration in Plane](https://docs.plane.so/integrations/slack). diff --git a/apps/developer-docs/docs/self-hosting/govern/ldap.md b/apps/developer-docs/docs/self-hosting/govern/ldap.md new file mode 100644 index 00000000..1be2fa1f --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/ldap.md @@ -0,0 +1,184 @@ +--- +title: LDAP authentication +description: Setup LDAP authentication for Plane. Configure Lightweight Directory Access Protocol for directory-based authentication. +keywords: plane ldap, ldap authentication, active directory, directory service, ldap configuration, enterprise authentication, self-hosting +--- + +# LDAP authentication + +LDAP (Lightweight Directory Access Protocol) authentication lets your team sign in to Plane using their existing corporate credentials. Instead of creating separate Plane passwords, users authenticate through your organization's directory service. + +## Before you begin + +You'll need: + +- Plane Commercial Edition with an active Enterprise Grid license. + - Don't have an Enterprise license? Contact Sales at [sales@plane.so](mailto:sales@plane.so) to get started. + - Already have a license? See how to [activate your Enterprise license](/self-hosting/manage/manage-licenses/activate-enterprise). +- Connection details for your LDAP server. +- A service account on your LDAP server with read-only access to the user directory. + +## Configure LDAP authentication + +1. Sign in to your Plane instance in [God Mode](/self-hosting/govern/instance-admin). + ![Turn on LDAP](/images/ldap/enable-ldap.webp#hero) +2. Select **Authentication** from the left pane. +3. Click **Configure** next to **LDAP** at the bottom of the page. +4. Enter your LDAP server details. + ![LDAP configuration](/images/ldap/ldap-configuration.webp#hero) + - **Server URI (required)** + This is the address of your LDAP server. Include the protocol and port number. + + **Format:** + - For unencrypted connections: `ldap://hostname:389` + - For encrypted connections (recommended): `ldaps://hostname:636` + + **Examples:** + + ``` + ldap://ldap.company.com:389 + ldaps://ad.company.com:636 + ldap://192.168.1.100:389 + ``` + + - **Bind DN (required)** + + This is the username of the service account that Plane will use to search your directory. Think of it as Plane's "read-only" account on your LDAP server. + + The format varies depending on your directory service: + + **Active Directory examples:** + + ``` + cn=PlaneService,ou=Service Accounts,dc=company,dc=com + plane-svc@company.com + ``` + + **OpenLDAP examples:** + + ``` + cn=admin,dc=example,dc=com + cn=readonly,ou=services,dc=example,dc=com + ``` + + - **Bind Password (required)** + + Enter the password for your service account (Bind DN). Plane encrypts and stores this securely in its database. + + - **User Search Base (required)** + + This defines where in your directory Plane should look for users. Think of it as the "starting folder" for user searches. + + Use the most specific path possible for better performance. + + **Examples:** + + ``` + ou=users,dc=example,dc=com + ou=employees,ou=people,dc=company,dc=com + cn=users,dc=company,dc=local + ``` + + - **User Search Filter (optional)** + + This tells Plane how to find users when they try to sign in. Use `{username}` as a placeholder - Plane replaces it with whatever the user types in the login field. + + **Common filters by directory type:** + + | Directory Type | Filter | What it does | + | ---------------- | -------------------------------- | -------------------------------- | + | OpenLDAP | `(uid={username})` | Searches by user ID | + | Active Directory | `(sAMAccountName={username})` | Searches by Windows login name | + | Active Directory | `(userPrincipalName={username})` | Searches by email-style username | + | Any | `(mail={username})` | Searches by email address | + + **Default:** If you don't specify a filter, Plane uses `(uid={username})`. + + **Combined filter example:**\ + If you want users to sign in with either their username OR email: + + ``` + (|(uid={username})(mail={username})) + ``` + + - **User Attributes (optional)** + + List the LDAP attributes Plane should retrieve to create user profiles. Plane uses these to populate the user's display name and email in Plane. + + **How Plane maps attributes:** + + | Plane needs | LDAP provides (in order of preference) | + | ------------- | ------------------------------------------------------------ | + | Email address | `mail`, `userPrincipalName` | + | First name | `givenName`, or first part of `cn` if `givenName` is missing | + | Last name | `sn`, or last part of `cn` if `sn` is missing | + + **Recommended setting:** + + ``` + mail,cn,givenName,sn,userPrincipalName,displayName + ``` + + **Default:** If you don't specify attributes, Plane uses `mail,cn,givenName,sn`. + + - **Provider Name (optional)** + + This is the label that appears on Plane's login button. Choose something your team will recognize. + + **Examples:** + - `Corporate Directory` + - `Company SSO` + - `Active Directory` + + **Default:** If you don't specify a name, Plane shows `LDAP`. + + The login button will display as: **"Sign in with [Provider Name]"** + +5. Click **Save changes** to apply your LDAP settings. Plane will validate the connection to your LDAP server. + +6. Users will see **Sign in with LDAP** on Plane's login page and can use their directory credentials to sign in. + ![Sign in using LDAP](/images/ldap/sign-in-ldap.webp#hero) + +## How LDAP authentication works + +LDAP authentication in Plane works through a two-phase process. First, Plane locates the user in your directory, then it verifies their credentials. This separation is fundamental to how LDAP works and explains why you need both a service account (Bind DN) and the user's own credentials. + +### The service account pattern + +Unlike simpler authentication systems where you might directly check a username and password against a database, LDAP uses what's called a "bind" operation. Plane needs to authenticate twice: once as itself (using the Bind DN) to search your directory, and once as the user to verify their password. + +This is why you configure a Bind DN and password - it's Plane's identity on your LDAP server. Think of it as Plane introducing itself before asking about your users. The Bind DN only needs read access because Plane is just looking up information, never modifying your directory. + +### The authentication flow + +When a user tries to sign in, here's what happens behind the scenes: + +**Connection and service authentication** +Plane connects to your LDAP server using the Server URI, then authenticates using the Bind DN credentials. If this fails, no users can sign in; the service account must work first. + +**User search** +Now authenticated, Plane searches for the user starting from the User Search Base and applying the User Search Filter. For example, if a user enters "jsmith" and your filter is `(uid={username})`, Plane searches for `(uid=jsmith)`. The search returns the user's distinguished name (DN), their full path in the directory. + +**User authentication** +Plane now attempts to bind again using the user's DN and their entered password. If the bind succeeds, the password is correct. This is why LDAP authentication is secure. Plane never stores user passwords; they go straight to your LDAP server for verification. + +**Profile creation** +Once authentication succeeds, Plane retrieves the User Attributes you configured (like email, first name, last name) from the user's LDAP record. If this is the user's first time signing in, Plane creates their profile using this information. If they've signed in before, Plane updates their profile with any changes from LDAP. + +**Session establishment** +Finally, Plane creates a session for the user and redirects them into the workspace. From this point on, the user's session works identically to any other Plane session. The LDAP interaction is complete. + +### Why search filters matter + +The User Search Filter determines sign-in flexibility. A simple filter like `(uid={username})` requires exact usernames, but you can make it more flexible: + +`(mail={username})` lets users sign in with email +`(|(uid={username})(mail={username}))` allows either username or email + +Filters can also restrict access: `(&(uid={username})(memberOf=cn=plane-users,ou=groups,dc=company,dc=com))` only permits specific group members. + +### The role of user attributes + +User Attributes tell Plane which LDAP fields to retrieve after authentication. This is separate from the search filter. The filter finds the user, the attributes populate their profile. + +Plane specifically looks for email addresses (required) and names (optional). If your LDAP server uses different attribute names, you need to include them. diff --git a/apps/developer-docs/docs/self-hosting/govern/oidc-sso.md b/apps/developer-docs/docs/self-hosting/govern/oidc-sso.md new file mode 100644 index 00000000..924d9881 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/oidc-sso.md @@ -0,0 +1,63 @@ +--- +title: OIDC SSO +description: Setup OIDC SSO authentication for Plane. Configure OpenID Connect single sign-on for enterprise authentication. +keywords: plane oidc, openid connect, sso configuration, single sign-on, oidc provider, enterprise sso, self-hosting +--- + +# OIDC SSO + +Plane enables custom SSO via any identity provider with an official and supported implementation of OIDC standards. This page cites examples from Okta, but we will soon publish provider-specific instructions in phases. + +## OIDC + +You will need to configure values on your IdP first and then on Plane later. + +### On your preferred IdP + +Create a Plane client or application per your IdP's documentation and configure ↓. + +::: tip +`domain.tld` is the domain that you have hosted your Plane app on. +::: + +| **Config** | **Key** | +| ------------ | ------------------------------------------ | +| Origin URL | `http(s)://domain.tld/auth/oidc/` | +| Callback URL | `http(s)://domain.tld/auth/oidc/callback/` | +| Logout URL | `http(s)://domain.tld/auth/oidc/logout/` | + +### On Plane + +Go to `/god-mode/authentication/oidc` on your Plane app and find the configs ↓. + +::: tip +Your IdP will generate some of the following configs for you. Others, you will specify yourself. Just copy them over to each field. +::: + +![OIDC Configuration](/images/custom-sso/oidc-oauth.png) + +- Copy the `CLIENT_ID` for the Plane client or app you just created over from your IdP and paste it in the field for it. + + With providers like Keycloak, you have to choose a unique ID per app your configure. With providers like Okta and Auth0, you copy over the generated ID over to Plane. Typically, you will find it on the Plane application Home or Settings page on your IdP. + +- Copy the `CLIENT_SECRET` for the Plane client or app you created over from your IdP and paste it in the field for it. + + The secret is usually auto-generated and you just need to copy it over from the Plane app or client's Home or Settings page. + +- Copy the `TOKEN URL` from your IdP and paste it into the field for it on `/god-mode/authentication/oidc/`.\ + Typically used to maintain user authentication and to persist it with refreshes, this URL lives in the `.well-known/` directory for the Plane app or client on your IdP. + +- Copy the `User info URL` from your IdP and paste it into the field for it on `/god-mode/authentication/oidc/`. + + Used to get an authenticating user's information from the IdP. Plane requires the `email` field for user authentication. The `first_name` and `last_name` fields are optional but recommended for a complete user profile. This URL can be copied from the `.well-known/` directory. + +- Copy the `Authorize URL` over from the `.well-known/` directory and paste it into the field for it on Plane's `/god-mode/authentication/oidc/`.\ + This is the URL that Plane's login screen redirects to when your users click `Sign up with ` or `Login with `. + + ![Login with IdP](/images/custom-sso/plane-login.png) + + To test if this URL is right, see if clicking the `Login with ` button brings up your IdP's authentication screen. + + ![Login with Okta](/images/custom-sso/okta-signin.webp#hero) + +- Finally, choose a name for your IdP on Plane so you can recognize this set of configs. diff --git a/apps/developer-docs/docs/self-hosting/govern/plane-ai/aws-opensearch-embedding.md b/apps/developer-docs/docs/self-hosting/govern/plane-ai/aws-opensearch-embedding.md new file mode 100644 index 00000000..39189e1c --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/plane-ai/aws-opensearch-embedding.md @@ -0,0 +1,239 @@ +--- +title: AWS OpenSearch embedding +description: Step-by-step guide to deploying a Cohere embedding model on AWS OpenSearch for use with Plane AI semantic search. +keywords: aws opensearch, embedding model, cohere, plane ai, semantic search, ml commons, opensearch connector +--- + +# Deploy an embedding model on AWS OpenSearch + +This guide walks you through deploying a Cohere embedding model on AWS OpenSearch (managed) for Plane AI semantic search. + +For other connector blueprints and embedding model configurations, see the [OpenSearch ML Commons remote inference blueprints](https://github.com/opensearch-project/ml-commons/tree/2.x/docs/remote_inference_blueprints). + +## Before you begin + +Make sure you have: + +- An AWS OpenSearch domain with **fine-grained access control** enabled. +- Admin access to OpenSearch Dashboards. +- AWS CLI configured locally. +- An IAM user with permissions to create roles, policies, and access Secrets Manager. +- A Cohere API key. + +## Create an IAM policy + +1. Go to **IAM → Policies → Create Policy**. +2. Select **JSON** and paste the following: + + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "PassRoleAccess", + "Effect": "Allow", + "Action": "iam:PassRole", + "Resource": "arn:aws:iam:::role/plane-opensearch-access-role" + }, + { + "Sid": "SecretManagerAccess", + "Effect": "Allow", + "Action": [ + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecrets" + ], + "Resource": "*" + } + ] + } + ``` + +3. Click **Next**, name the policy `plane-opensearch-access-policy`, and click **Create Policy**. + +## Create an IAM role + +Create an IAM role that OpenSearch can assume to access Secrets Manager. + +1. Go to **IAM → Roles → Create Role**. +2. Name the role `plane-opensearch-access-role`. +3. Set this **Trust Relationship**: + + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + }, + "Action": "sts:AssumeRole" + }, + { + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam:::user/" + }, + "Action": "sts:AssumeRole" + } + ] + } + ``` + +4. Attach the `plane-opensearch-access-policy` you created in Step 1. +5. Click **Create Role** and note the role ARN. + +## Grant ML permissions to the IAM role + +1. Open **OpenSearch Dashboards**. +2. Go to **Security → Roles → `ml_full_access`**. +3. Open the **Mapped users** tab and click **Map users**. +4. Under **Backend roles**, add the role ARN: + + ``` + arn:aws:iam:::role/plane-opensearch-access-role + ``` + +## Assume the role locally + +Run this command to get temporary credentials: + +```bash +aws sts assume-role \ + --role-arn arn:aws:iam:::role/plane-opensearch-access-role \ + --role-session-name session +``` + +Export the credentials from the response: + +```bash +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_SESSION_TOKEN= +``` + +## Store the Cohere API key in Secrets Manager + +1. Go to **Secrets Manager → Store a new secret**. +2. Select **Other type of secret**. +3. Set the key to `azure_ai_foundry_key_cohere` and the value to your Cohere API key. +4. Click **Next**, name the secret `plane-ai/cohere`, and click **Store**. +5. Note the **Secret ARN** — you'll need it in the next step. + +## Create a Cohere connector in OpenSearch + +Using the temporary credentials from Step 4, send a `POST` request to your OpenSearch cluster. + +**Endpoint:** `POST https:///_plugins/_ml/connectors/_create` + +**Request body:** + +```json +{ + "name": "Cohere", + "description": "Cohere embedding connector", + "version": "1", + "protocol": "http", + "parameters": { + "endpoint": "https://azureaitrials-resource.services.ai.azure.com/models", + "model": "embed-v-4-0", + "api_version": "2024-05-01-preview", + "input_type": "search_document", + "truncate": "END" + }, + "credential": { + "secretArn": "", + "roleArn": "arn:aws:iam:::role/plane-opensearch-access-role" + }, + "actions": [ + { + "action_type": "predict", + "method": "POST", + "url": "${parameters.endpoint}/embeddings?api-version=${parameters.api_version}", + "headers": { + "api-key": "${credential.secretArn.azure_ai_foundry_key_cohere}", + "x-ms-model-mesh-model-name": "embed-v-4-0" + }, + "request_body": "{ \"texts\": ${parameters.texts}, \"truncate\": \"${parameters.truncate}\", \"model\": \"${parameters.model}\", \"input_type\": \"${parameters.input_type}\" }", + "pre_process_function": "connector.pre_process.cohere.embedding", + "post_process_function": "connector.post_process.cohere.embedding" + } + ] +} +``` + +Save the `connector_id` from the response. + +## Configure the OpenSearch cluster + +Run these commands in **Dev Tools** in OpenSearch Dashboards. + +### Allow the connector's external endpoints + +```json +PUT /_cluster/settings +{ + "persistent": { + "plugins.ml_commons.trusted_connector_endpoints_regex": [ + "^https://api\\.cohere\\.ai(/.*)?$", + "^https://azureaitrials-resource\\.services\\.ai\\.azure\\.com(/.*)?$" + ] + } +} +``` + +### Register the embedding model + +```json +POST /_plugins/_ml/models/_register +{ + "name": "cohere_4_0_embed", + "function_name": "remote", + "connector_id": "", + "description": "Cohere Embedding Model" +} +``` + +Save the `model_id` from the response. + +### Deploy the model + +``` +POST /_plugins/_ml/models//_deploy +``` + +### Verify deployment status + +``` +GET /_plugins/_ml/models/ +``` + +Wait until the response shows: + +```json +"model_state": "DEPLOYED" +``` + +### Test inference (optional) + +```json +POST /_plugins/_ml/models//_predict +{ + "parameters": { + "inputs": ["hello world"] + } +} +``` + +## Configure Plane + +Add the deployed model ID and configuration to `/opt/plane/plane.env`: + +```bash +OPENSEARCH_ML_MODEL_ID= +EMBEDDING_MODEL=cohere/embed-v4.0 +OPENSEARCH_EMBEDDING_DIMENSION=1536 +``` + +Restart Plane and complete the remaining steps in [Configure embedding model](/self-hosting/govern/plane-ai/configure-embedding-model). diff --git a/apps/developer-docs/docs/self-hosting/govern/plane-ai/configure-embedding-model.md b/apps/developer-docs/docs/self-hosting/govern/plane-ai/configure-embedding-model.md new file mode 100644 index 00000000..bcad62a2 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/plane-ai/configure-embedding-model.md @@ -0,0 +1,151 @@ +--- +title: Configure embedding model for semantic search +description: Configure an embedding model for Plane AI semantic search, duplicate detection, and vector retrieval over work items and pages. +keywords: plane ai semantic search, opensearch, embedding model, vector search, cohere, openai embeddings, bedrock embeddings +--- + +# Configure embedding model for semantic search + +Configuring the embedding model is optional. Without it, Plane AI uses BM25 keyword search. With it, you get vector similarity search, duplicate issue detection, and semantic retrieval over work items and pages. + +This builds on the OpenSearch connection configured in [Configure Plane AI](/self-hosting/govern/plane-ai/configure-plane-ai). Make sure `OPENSEARCH_URL`, `OPENSEARCH_USERNAME`, and `OPENSEARCH_PASSWORD` are already set before continuing. + +## Supported embedding models + +| Provider | Model | Dimension | +| --------------- | -------------------------------------- | --------- | +| **Cohere** | `cohere/embed-v4.0` | 1536 | +| | `cohere/embed-english-v3.0` | 1024 | +| | `cohere/embed-english-v2.0` | 4096 | +| **OpenAI** | `openai/text-embedding-ada-002` | 1536 | +| | `openai/text-embedding-3-small` | 1536 | +| | `openai/text-embedding-3-large` | 3072 | +| **AWS Bedrock** | `bedrock/amazon.titan-embed-text-v1` | 1536 | +| | `bedrock/amazon.titan-embed-text-v2` | 1024 | +| | `bedrock/cohere.embed-english-v3` | 1024 | +| | `bedrock/cohere.embed-multilingual-v3` | 1024 | + +## Configure the embedding model + +Two options depending on your OpenSearch setup. + +### Option A: Use an existing OpenSearch model ID + +Use this if you've already deployed an embedding model in OpenSearch - either via the [AWS OpenSearch embedding guide](/self-hosting/govern/plane-ai/aws-opensearch-embedding) or manually on self-hosted OpenSearch. + +```bash +EMBEDDING_MODEL=cohere/embed-v4.0 # must match the deployed model +OPENSEARCH_ML_MODEL_ID= # model ID returned by OpenSearch on deploy +# OPENSEARCH_EMBEDDING_DIMENSION=1024 # only if not using default 1536 +``` + +:::tip +`OPENSEARCH_EMBEDDING_DIMENSION` must match the model's actual output dimension (see table above). It defaults to `1536` - only set it explicitly if your model uses a different value. A mismatch between the configured dimension and the model's real output breaks indexing. +::: + +### Option B: Automatic deployment (self-hosted OpenSearch only) + +Plane AI can create and deploy the embedding model automatically when the migrator starts. Provide the model name and provider credentials - no manual OpenSearch setup needed. + +**Cohere:** + +```bash +EMBEDDING_MODEL=cohere/embed-v4.0 +COHERE_API_KEY=your-cohere-api-key +``` + +**OpenAI:** + +```bash +EMBEDDING_MODEL=openai/text-embedding-3-small +OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx +``` + +**AWS Bedrock:** + +```bash +EMBEDDING_MODEL=bedrock/amazon.titan-embed-text-v1 +BR_AWS_ACCESS_KEY_ID=your-access-key +BR_AWS_SECRET_ACCESS_KEY=your-secret-key +BR_AWS_REGION=us-east-1 +``` + +:::warning IAM permission required for Bedrock +The IAM user for `BR_AWS_ACCESS_KEY_ID` and `BR_AWS_SECRET_ACCESS_KEY` needs `bedrock:InvokeModel` permission on the Titan foundation model. Without it, embedding requests fail with a 403 error. + +Attach this policy to the IAM user: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "bedrock:InvokeModel", + "Resource": "arn:aws:bedrock:::foundation-model/amazon.titan-embed-text-v1" + } + ] +} +``` + +Replace `` with your `BR_AWS_REGION` value. +::: + +:::info AWS managed OpenSearch +Auto-deploy requires direct ML access to OpenSearch. AWS managed OpenSearch restricts this, so deploy the model manually using the [AWS OpenSearch embedding guide](/self-hosting/govern/plane-ai/aws-opensearch-embedding), then use the model ID option above. +::: + +## Restart Plane + +After updating `/opt/plane/plane.env`, restart Plane. + +The Plane AI migrator runs automatically on start and handles embedding model deployment, index creation, and pipeline setup. + +## Vectorize existing data + +New content is indexed automatically. For existing work items and pages, run this in the API container: + +Generate embeddings for your existing content by running this command in the API container. + +**Docker:** + +```bash +docker exec -it plane-api-1 sh +python manage.py manage_search_index --background --vectorize document index --force +``` + +**Kubernetes:** + +```bash +API_POD=$(kubectl get pods -n plane --no-headers | grep api | head -1 | awk '{print $1}') +kubectl exec -n plane $API_POD -- python manage.py manage_search_index --background --vectorize document index --force +``` + +The `--background` flag processes vectorization through Celery workers. This is recommended for instances with large amounts of existing content. + +## Changing the embedding model + +If you update the model or manually override the dimension size by setting `OPENSEARCH_EMBEDDING_DIMENSION`, you must recreate your search indices so they adopt the new dimension size, then reindex and revectorize your workspace. Ensure that the model associated with your `OPENSEARCH_ML_MODEL_ID` and your `EMBEDDING_MODEL` configuration share this same dimension size. + +### Model only (same dimension) + +Update `EMBEDDING_MODEL` and provider credentials in `/opt/plane/plane.env`, restart Plane, then revectorize: + +```bash +docker exec -it plane-api-1 sh +python manage.py manage_search_index --background --vectorize document index --force +``` + +### Dimension change + +Update `EMBEDDING_MODEL`, `OPENSEARCH_EMBEDDING_DIMENSION`, and provider credentials in `/opt/plane/plane.env`, restart Plane, then rebuild and revectorize: + +```bash +# Rebuild indices with the new dimension +python manage.py manage_search_index index rebuild --force + +# Reindex and revectorize all existing documents +python manage.py manage_search_index --background --vectorize document index --force +``` + +`OPENSEARCH_EMBEDDING_DIMENSION` must match the actual output dimension of the model in `EMBEDDING_MODEL`. A mismatch breaks indexing. diff --git a/apps/developer-docs/docs/self-hosting/govern/plane-ai/configure-plane-ai.md b/apps/developer-docs/docs/self-hosting/govern/plane-ai/configure-plane-ai.md new file mode 100644 index 00000000..fb413027 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/plane-ai/configure-plane-ai.md @@ -0,0 +1,268 @@ +--- +title: Configure Plane AI +description: Enable Plane AI on your self-hosted instance. Set up a dedicated database, configure OpenSearch, add an LLM API key, and connect PI to your Plane deployment. +keywords: plane ai setup, self-hosted ai, llm api key, openai, anthropic, opensearch, enable plane ai +--- + +# Configure Plane AI + +Plane AI brings AI-powered features to your workspace, including natural language chat, duplicate detection, and search across work items, pages, and projects. This guide walks you through configuring Plane AI on your self-hosted instance. + +For an overview of what Plane AI can do, see [Plane AI](https://docs.plane.so/ai/pi-chat). + +## Prerequisites + +Plane AI requires four things to work: + +1. **An LLM API key** (OpenAI or Anthropic) - powers AI responses. +2. **OpenSearch** - An OpenSearch instance running version 2.19 or later (self-hosted or AWS OpenSearch) configured for [advanced search](/self-hosting/govern/advanced-search). Search over your workspace data (work items, pages, cycles) runs through OpenSearch indices. +3. **A dedicated database** - Plane AI must not share the main Plane application database. +4. **Read access to the main Plane database** - PI reads workspace data directly from the main Plane DB. + +## Supported LLM providers + +### OpenAI + +- GPT-5.4 +- GPT-5.2 + +### Anthropic + +- Claude Sonnet 4.5 +- Claude Sonnet 4.6 + +You can provide API keys for both OpenAI and Anthropic, making all models available to users. If you provide only one key, users will only have access to that provider's models. + +:::tip Custom or self-hosted models +To use Ollama, Groq, LiteLLM, AWS Bedrock, or any OpenAI-compatible endpoint, see [Custom LLM models](#custom-llm-models). +::: + +## Set up databases + +Plane AI needs two database connections. + +```bash +PLANE_PI_DATABASE_URL=postgresql://user:password@host:5432/plane-pi +FOLLOWER_POSTGRES_URI=postgresql://user:password@host:5432/plane +``` + +- **`PLANE_PI_DATABASE_URL`** - PI's own dedicated database. Must not be shared with the main Plane application database. +- **`FOLLOWER_POSTGRES_URI`** - Read connection to the main Plane database. PI reads workspace data (issues, pages, projects) directly from here. Can be a read replica. + +Both are checked at startup - PI will not start if either is unreachable. + +## Configure OpenSearch + +Add to `/opt/plane/plane.env`: + +```bash +OPENSEARCH_URL=https://your-opensearch-instance:9200/ +OPENSEARCH_USERNAME=admin +OPENSEARCH_PASSWORD=your-secure-password +``` + +If you haven't set up OpenSearch yet, see [OpenSearch for advanced search](/self-hosting/govern/advanced-search) first. + +## Configure an LLM provider + +Add at least one to `/opt/plane/plane.env`: + +```bash +OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx +CLAUDE_API_KEY=xxxxxxxxxxxxxxxx +``` + +### Custom LLM models + +:::warning +The custom model should have at least 1 trillion parameters for all Plane AI features to work reliably. Larger, more capable models yield better results. +::: + +Plane AI supports one custom LLM alongside OpenAI and Anthropic. + +- OpenAI-compatible - any model exposed via an OpenAI Chat Completions API, including models served by Ollama, Groq, Cerebras, and similar runtimes. +- AWS Bedrock - models accessed directly through Amazon Bedrock using your AWS credentials. + One custom model can be configured alongside your public provider keys. + +:::tip No OpenAI-compatible API? +Proxy any model through - it exposes any LLM behind the OpenAI API. Then use the OpenAI-compatible setup below. + +If you need to use an LLM that isn't from OpenAI or Anthropic - for example, an open-source model or a regional provider for compliance reasons - you can proxy it through [LiteLLM](https://docs.litellm.ai).Then use the OpenAI-compatible setup below. +::: + +#### OpenAI-compatible + +Add to `/opt/plane/plane.env`: + +```bash +CUSTOM_LLM_ENABLED=true +CUSTOM_LLM_PROVIDER=openai +CUSTOM_LLM_MODEL_KEY=your-model-id # model ID as the endpoint expects it +CUSTOM_LLM_BASE_URL=https://your-endpoint/v1 +CUSTOM_LLM_API_KEY=your-api-key # use any non-empty string if no key is required +CUSTOM_LLM_NAME=Your Model Name # display name shown to users +CUSTOM_LLM_MAX_TOKENS=64000 # optional; max output tokens per response +``` + +**Examples:** + +```bash +# Groq +CUSTOM_LLM_MODEL_KEY=llama-3.3-70b-versatile +CUSTOM_LLM_BASE_URL=https://api.groq.com/openai/v1 +CUSTOM_LLM_API_KEY=gsk_xxxxxxxxxxxx + +# Ollama (local) +CUSTOM_LLM_MODEL_KEY=llama3 +CUSTOM_LLM_BASE_URL=http://localhost:11434/v1 +CUSTOM_LLM_API_KEY=ollama + +# LiteLLM proxy +CUSTOM_LLM_MODEL_KEY=your-litellm-model +CUSTOM_LLM_BASE_URL=http://litellm:4000/v1 +CUSTOM_LLM_API_KEY=your-litellm-master-key +``` + +#### AWS Bedrock + +##### Standard credentials + +Use for IAM user access with an explicit access key and secret. + +```bash +CUSTOM_LLM_ENABLED=true +CUSTOM_LLM_PROVIDER=bedrock +CUSTOM_LLM_MODEL_KEY=anthropic.claude-3-5-sonnet-20241022-v2:0 # Bedrock model ID +CUSTOM_LLM_API_KEY=your-aws-secret-access-key +CUSTOM_LLM_AWS_REGION=us-east-1 +AWS_ACCESS_KEY_ID=your-aws-access-key-id # standard AWS env var, picked up by boto3 +CUSTOM_LLM_NAME=Claude via Bedrock +CUSTOM_LLM_MAX_TOKENS=64000 # optional; max output tokens per response +``` + +:::warning IAM permission required +The IAM user must have `bedrock:InvokeModel` permission on the target model. +::: + +#### Inference profile (IRSA / EKS Pod Identity) + +Use for Kubernetes deployments where the pod has an ambient IAM role. No static credentials needed. + +```bash +CUSTOM_LLM_ENABLED=true +CUSTOM_LLM_PROVIDER=bedrock +CUSTOM_LLM_MODEL_KEY=claude-sonnet-4-6 +CUSTOM_LLM_AWS_REGION=us-east-1 +BEDROCK_INFERENCE_PROFILE_ARN=arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/xxxx +# or use BEDROCK_INFERENCE_PROFILE_ID=global.anthropic.claude-sonnet-4-6 +CUSTOM_LLM_NAME=Claude via Inference Profile +CUSTOM_LLM_MAX_TOKENS=64000 # optional; max output tokens per response +``` + +Plane AI activates inference profile mode automatically when a profile ARN or ID is set and ambient AWS credentials are present (`AWS_ROLE_ARN`, `AWS_WEB_IDENTITY_TOKEN_FILE`, `AWS_CONTAINER_CREDENTIALS_FULL_URI`, or `AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE`). + +## Connect Plane AI to your Plane deployment + +Plane AI runs as a separate service and must be wired to your Plane deployment. Two things matter here. + +**In the Plane API env** (`/opt/plane/plane.env`), tell Plane where PI is reachable: + +```bash +PI_BASE_URL=https://plane.example.com +``` + +Plane builds the PI URL as `PI_BASE_URL + PI_BASE_PATH` - with the default `PI_BASE_PATH=/pi` this becomes `https://plane.example.com/pi`. + +**In the PI env** (`/opt/plane/plane.env`), set the OAuth redirect URI to match: + +```bash +PLANE_OAUTH_REDIRECT_URI=https://plane.example.com/pi/api/v1/oauth/callback/ +``` + +## Enable Plane AI services + +:::tip Other deployment methods +For Coolify, Portainer, Docker Swarm, and Podman Quadlets, use the same environment variables as Docker Compose - only the replica variables differ. +::: + +:::tabs key:deployment-method + +== Docker Compose {#docker-compose} + +In `/opt/plane/plane.env`, set replica counts to `1`: + +```bash +PI_API_REPLICAS=1 +PI_BEAT_REPLICAS=1 +PI_WORKER_REPLICAS=1 +PI_MIGRATOR_REPLICAS=1 +``` + +== Kubernetes {#kubernetes} + +In `values.yaml`, enable the Plane AI service: + +```yaml +services: + pi: + enabled: true +``` + +This activates the Plane AI API, worker, beat-worker, and migrator workloads. Configure replicas and resource limits through the [Plane AI values block](/self-hosting/methods/kubernetes#plane-ai-deployment). +::: + +## Optional configuration + +### Voice input + +Enables speech-to-text in Plane AI chat. Get a key at [console.groq.com](https://console.groq.com). + +```bash +GROQ_API_KEY=your-groq-api-key +``` + +### File uploads + +Enables file attachments in Plane AI. + +```bash +AWS_S3_BUCKET_NAME=your-bucket-name +AWS_S3_REGION=us-east-1 +AWS_ACCESS_KEY_ID=your-access-key +AWS_SECRET_ACCESS_KEY=your-secret-key +``` + +For MinIO or S3-compatible storage, also add: + +```bash +AWS_S3_ENDPOINT_URL=http://your-minio-host:9000 +USE_MINIO=1 +``` + +## Restart Plane + +:::tabs key:deployment-method + +== Docker Compose {#docker-compose} + +```bash +prime-cli restart +``` + +Or directly: + +```bash +docker compose down +docker compose up -d +``` + +== Kubernetes {#kubernetes} + +```bash +helm upgrade --install plane-app plane/plane-enterprise \ + --namespace plane \ + -f values.yaml \ + --wait +``` + +::: diff --git a/apps/developer-docs/docs/self-hosting/govern/private-bucket.md b/apps/developer-docs/docs/self-hosting/govern/private-bucket.md new file mode 100644 index 00000000..2298f9f6 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/private-bucket.md @@ -0,0 +1,131 @@ +--- +title: Switch from public to private buckets • Commercial Edition +description: Switch Plane storage from public to private S3 buckets. Secure file uploads with signed URLs and access control for commercial editions. +keywords: plane private bucket, s3 private storage, signed urls, secure file uploads, storage migration, self-hosting, plane storage security +--- + +# Switch from public to private buckets • Commercial Edition + +::: warning +Starting with v1.4.0 of the Commercial edition Plane will use private storage buckets for any file uploaded to your Plane instance. +::: + +::: info +New installations with default storage, which is MiniO, don't need to change anything. For S3 or S3-compatible storage, please see [this](https://developers.plane.so/self-hosting/govern/database-and-storage). +::: + +While you can use the current public storage paradigm that Plane has followed so far, we highly recommend you migrate to private storage buckets which ensure greater security and give you more control over how files are accessed. + +::: info +To keep public storage on external S3 compatible services, you still have to update your CORS policy. +::: + +See the instructions to switch to private storage by the provider you use below. + +## For default MinIO storage + +Simply run the command ↓. + +```bash +docker exec -it python manage.py update_bucket +``` + +A successful run keeps any public files you already have accessible while moving you to private storage. + +## For external storage • S3 or S3 compatible + +There are two parts to this—updating your CORS policy and then switching to private storage. + +### Update bucket's CORS policy + +::: warning +This step is critical if you are using external storage to ensure continued functionality. +::: + +Here’s a sample CORS policy for your reference. Just replace `` with your actual domain and apply the policy to your bucket. + +```bash +[ + { + "AllowedHeaders": [ + "*" + ], + "AllowedMethods": [ + "GET", + "POST", + "PUT", + "DELETE", + "HEAD" + ], + "AllowedOrigins": [ + "", + ], + "ExposeHeaders": [ + "ETag", + "x-amz-server-side-encryption", + "x-amz-request-id", + "x-amz-id-2" + ], + "MaxAgeSeconds": 3000 + } +] +``` + +### Switch to private storage + +::: warning +Don't start from here if you haven't updated your CORS policy. +::: + +To migrate from public to private bucket storage, follow the instructions below: + +1. First, make sure you have the following permissions on your S3 bucket. If you don't, make changes to get those permissions on your bucket first. + - **s3:GetObject** + So you can access your public files so far To access existing objects publicly + + - **s3:ListBucket** + So you can apply policies to your bucket for public access + + - **s3:PutObject** + So you can create new files + + - **s3:PutBucketPolicy** + So you can update your buckets' policy + +2. Now, run the command ↓. + + ```bash + docker exec -it python manage.py update_bucket + ``` + + ::: tip + 1. If the command finds the necessary permissions missing, it will generate a `permissions.json` file which you can use to update your bucket policy manually. Here’s how the `permissions.json` file should look. + + ```bash + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": [ + "arn:aws:s3:::/", + "arn:aws:s3:::/" + ] + } + ] + } + ``` + + 2. To copy the `permissions.json` file to the local machine, run the command ↓. + + ```bash + docker cp :/code/permissions.json . + ``` + + ::: + +## Troubleshoot + +- [Bucket policy exceeds size limit](/self-hosting/troubleshoot/storage-errors#bucket-policy-exceeds-size-limit) diff --git a/apps/developer-docs/docs/self-hosting/govern/reset-password.md b/apps/developer-docs/docs/self-hosting/govern/reset-password.md new file mode 100644 index 00000000..fa1c45ad --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/reset-password.md @@ -0,0 +1,31 @@ +--- +title: Reset password +description: Reset user and admin passwords for self-hosted Plane. Recover access to your instance using CLI commands or database operations. +keywords: plane password reset, admin password recovery, plane account recovery, self-hosting, plane login issues +--- + +# Reset password + +Users can reset their password through the terminal of the Plane application. You need to login to backend docker container and run the below command for resetting a user’s password. + +1. Get the container id for **plane-api**. + +```bash +docker ps +``` + +2. Log in to the container. + +```bash +docker exec -it /bin/sh +``` + +3. Run the reset password command. + +```bash + python manage.py reset_password +``` + +::: tip +The email should be of an already existing user on the Plane application. If the email is not attached to any user the command will throw an error. +::: diff --git a/apps/developer-docs/docs/self-hosting/govern/reverse-proxy.md b/apps/developer-docs/docs/self-hosting/govern/reverse-proxy.md new file mode 100644 index 00000000..fbc4046d --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/reverse-proxy.md @@ -0,0 +1,202 @@ +--- +title: Configure external reverse proxy +description: Configure Nginx, Caddy, or Traefik as a reverse proxy for self-hosted Plane. Setup upstream proxying, headers, and WebSocket support. +keywords: plane reverse proxy, nginx proxy, traefik, caddy, upstream proxy, proxy configuration, self-hosting +--- + +# Configure external reverse proxy + +This page provides configuration for setting up an external reverse proxy with Plane. + +## Plane environment setup + +Make sure to update the following environment variables in your plane.env file. + +1. Assign free ports for Plane to listen on. Update the following variables with two different unsused ports: + + ```bash + LISTEN_HTTP_PORT= + LISTEN_HTTPS_PORT= + ``` + +2. Update the SITE_ADDRESS variable to `:80` + + ```bash + SITE_ADDRESS=:80 + ``` + + This is required so that generated links and redirects work correctly behind the proxy: + +3. After editing plane.env, restart your instance so the changes take effect: + ```bash + sudo prime-cli restart + ``` + +::: warning +**Prime CLI is for Docker installations only.** These commands only work on Plane instances originally installed using `prime-cli`. +::: + +## Proxy setup + +1. Choose the appropriate [configuration template](#configuration-templates) for your reverse proxy. + +2. Replace the following placeholders: + - `` + Your Plane application's domain name. + - `` + The IP address where Plane is hosted. + - `` + The port Plane listens on. +3. For Traefik, also update `your-email@example.com` with your email. + +Ensure that your reverse proxy setup follows the template provided, and that the forwarded headers and ports are correctly set to match the environment variable configuration. + +## Configuration templates + +All configurations include: + +- Automatic HTTPS redirection +- WebSocket support +- Standard proxy headers +- SSL/TLS certificate management + - NGINX: Uses Certbot + - Caddy: Handles certificates automatically + - Traefik: Uses Let’s Encrypt + +::: details NGINX configuration + +```bash +server { + server_name ; + + location / { + proxy_pass http://:/; + + # Set headers for proxied request + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; + proxy_http_version 1.1; + } + + client_max_body_size 10M; + + listen 443 ssl; # managed by Certbot + ssl_certificate /etc/letsencrypt/live//fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live//privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; +} + +server { + if ($host = ) { + return 301 https://$host$request_uri; + } + + listen 80; + server_name ; + return 404; +} +``` + +::: + +::: details Caddy configuration + +```bash + { + tls { + # Caddy will automatically handle certificates + } + + redir / https://{host}{uri} permanent + + reverse_proxy : { + header_up X-Forwarded-Proto {scheme} + header_up X-Forwarded-Host {host} + header_up X-Real-IP {remote_host} + header_up X-Forwarded-For {remote_host} + header_up Host {http.request.host} + + header_up Upgrade {http.request.header.Upgrade} + header_up Connection {http.request.header.Connection} + + transport http { + tls_insecure_skip_verify + read_buffer 4096 + write_buffer 4096 + } + } + + request_body { + max_size 10MB + } +} +``` + +::: + +::: details Traefik configuration + +```bash +entryPoints: + web: + address: ":80" + http: + redirections: + entryPoint: + to: websecure + scheme: https + permanent: true + + websecure: + address: ":443" + +certificatesResolvers: + letsencrypt: + acme: + email: your-email@example.com # Replace with your email + storage: acme.json + httpChallenge: + entryPoint: web + +providers: + http: + routers: + plane-router: + rule: "Host(``)" + service: plane-service + entryPoints: + - websecure + tls: + certResolver: letsencrypt + + services: + plane-service: + loadBalancer: + servers: + - url: "http://:" + passHostHeader: true + responseForwarding: + flushInterval: "100ms" + serversTransport: + maxIdleConnsPerHost: 100 + forwardingTimeouts: + dialTimeout: 30s + responseHeaderTimeout: 30s + idleConnTimeout: 90s + + middlewares: + headers: + headers: + customRequestHeaders: + X-Forwarded-Proto: "https" + X-Real-IP: "{{ .RemoteAddr }}" +``` + +::: diff --git a/apps/developer-docs/docs/self-hosting/govern/saml-sso.md b/apps/developer-docs/docs/self-hosting/govern/saml-sso.md new file mode 100644 index 00000000..e599ed91 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/govern/saml-sso.md @@ -0,0 +1,79 @@ +--- +title: SAML SSO +description: Configure SAML SSO authentication for Plane. Setup Security Assertion Markup Language for enterprise SSO. +keywords: plane saml, saml sso, enterprise sso, saml configuration, identity provider, okta, azure ad, self-hosting +--- + +# SAML SSO + +Plane enables custom SSO via any identity provider with an official and supported implementation of SAML standards. This page cites examples from Okta, but we will soon publish provider-specific instructions in phases. + +## SAML + +You will need to configure values on your IdP first and then on Plane later. +::: tip +`domain.tld` is the domain that you have hosted your Plane app on. +::: + +### On your preferred IdP + +Create a Plane client or application per your IdP's documentation and configure ↓. + +| **Config** | **Value** | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- | +| Entity ID

Metadata that identifies Plane as an authorized service on your IdP | `http(s)://domain.tld/auth/saml/` | +| ACS URL

Assertion Consumer service that your IdP will redirect to after successful authentication by a user

This is roughly the counterpart of the `Callback URL` in SAML set-ups. | `http(s)://domain.tld/auth/saml/callback/`

Plane supports HTTP-POST bindings. | +| SLS URL

Single Logout Service that your IdP will recognize to end a Plane session when a user logs out

This is roughly the counterpart of the `Logout URL` in SAML set-ups. | `http(s)://domain.tld/auth/saml/logout/` | + +::: tip +When setting these values up on the IdP, it’s important to remember Plane does not need to provide a signing certificate like other service providers. +::: + +### Let your IdP identify your users on Plane. + +| **Config** | **Value** | +| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Name ID format | emailAddress

By default, your IdP should send back a username, but Plane recognizes email addresses as the username. Set the value to the above so Plane recognizes the user correctly. | + +### Set additional attribute values. + +By default, your IdP will send the value listed under `Property`. You have to map it to the SAML attribute Plane recognizes. + +| **Default property value** | **Plane SAML attribute** | +| -------------------------- | ------------------------ | +| user.firstName | first_name | +| user.lastName | last_name | +| user.email | email | + +::: info +**first_name** and **last_name** are optional but recommended for complete user profiles. If these are not provided, Plane will create the user account with just the email address. + +::: + +::: tip +Depending on your IdP, you will have to find both the `Name ID format` and the three other user identification properties on different screens. Please refer to your IdP's documentation when configuring these up on your IdP. Additionally, you may have to configure the IdP to sign assertions. Irrespective of that, you have to copy the signing certificate from the IdP. +::: + +### On Plane + +![SAML Configuration](/images/custom-sso/saml-oauth.png) + +::: tip +You will find all of the values for the fields below in the `/metadata` endpoint your IdP generates for the Plane app or client. +::: + +- Copy the `ENTITY_ID` for the Plane client or app you just created over from your IdP and paste it in the field for it. + +- Copy the `SSO URL` for the Plane client or app from your IdP and paste it in the field for it. + + This will bring up the IdP's authentication screen for your users. + + ![SSO URL](/images/custom-sso/okta-signin.webp#hero) + +- Copy the `SLS URL` for the Plane client or app from your IdP and paste it in the `Logout URL` field on Plane's `/god-mode/authentication/saml/`. + +- Add the name of the IdP that you want to show on your Plane instance's log-in or sign-up screens. + + ![Log-in Screen](/images/custom-sso/instance-login.png) + +- Finally, paste the signing certificate from your IdP that you got in the last step of setting up your Plane client or app on your IdP above and paste it in the field for it. diff --git a/apps/developer-docs/docs/self-hosting/manage/backup-restore.md b/apps/developer-docs/docs/self-hosting/manage/backup-restore.md new file mode 100644 index 00000000..c0d1e3b4 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/backup-restore.md @@ -0,0 +1,174 @@ +--- +title: Backup and restore data +description: Backup and restore Plane data. Complete guide for backing up database, storage, and configuration files. +keywords: plane backup, plane restore, database backup, postgresql backup, data recovery, plane data export, self-hosting +--- + +# Backup and restore data + +Backing up your data regularly helps prevent data loss and allows you to restore your system quickly if necessary. Follow these instructions to back up and restore your data using Plane’s command-line interface. + +## For Docker Compose + +### Backup data + +::: warning +**Prime CLI is for Docker installations only.** These commands only work on Plane instances originally installed using `prime-cli`. +::: + +Create a backup of your Plane data with ↓: + +```bash +sudo prime-cli backup +``` + +This command initiates a full backup of all critical data, storing it in the default backup location at: + +```bash +/opt/plane/backups +``` + +Each backup file will be timestamped to ensure you can easily identify the latest or a specific backup if needed. + +### Backup plane.env + +If you need to back up only the `plane.env` file, you'll need to do it manually. Here’s how: + +1. Navigate to the `/opt/plane` folder on your machine or server where Plane is installed.. +2. Locate the `plane.env` file. +3. Copy this file to a different location as a backup, so you can restore it if needed. + +### Restore data + +You can restore your data from a previous backup with ↓: + +```bash +sudo prime-cli restore +``` + +This command prompts the restoration process, which will overwrite the current data with the data from the most recent backup file. Ensure you have selected the correct backup before running this command, as restoring will replace your current data. + +::: details Community Edition + +#### Backup data + +To create a backup, start by running the setup script: + +```bash +./setup.sh +``` + +You’ll see a menu of options—just type 7 to select "Backup Data." + +``` +Select an Action you want to perform: + 1) Install (x86_64) + 2) Start + 3) Stop + 4) Restart + 5) Upgrade + 6) View Logs + 7) Backup Data + 8) Exit + +Action [2]: 7 +``` + +The system will start backing up the PostgreSQL, Redis, and upload data: + +``` +Backing Up plane-app_pgdata +Backing Up plane-app_redisdata +Backing Up plane-app_uploads + +Backup completed successfully. Backup files are stored in /....../plane-app/backup/20240502-1120 +``` + +The backup files are stored locally, so you can copy them to an external storage service if needed for extra security. + +#### Backup plane.env + +If you need to back up only the `plane.env` file, you'll need to do it manually. Here’s how: + +1. Navigate to the folder on your machine or server where Plane is installed.. +2. Locate the `plane.env` file. +3. Copy this file to a different location as a backup, so you can restore it if needed. + +--- + +#### Restore data + +Follow these steps to restore data from a backup: + +1. Make sure Plane-CE is installed and started, then stop it. This ensures the necessary Docker volumes are ready. + +2. Use the command ↓ to download the restore script. It’s easiest to save it in the same directory as `setup.sh`. + + ```bash + curl -fsSL -o restore.sh https://raw.githubusercontent.com/makeplane/plane/refs/heads/preview/deployments/cli/community/restore.sh + chmod +x restore.sh + ``` + +3. Now, run the command ↓ to restore your data, specifying the path to your backup folder (the folder with the `*.tar.gz` files): + + ```bash + ./restore.sh + ``` + + Here’s an example output for restoring from /opt/plane-selfhost/plane-app/backup/20240722-0914: + + ```bash + -------------------------------------------- + ____ _ ///////// + | _ \| | __ _ _ __ ___ ///////// + | |_) | |/ _` | '_ \ / _ \ ///// ///// + | __/| | (_| | | | | __/ ///// ///// + |_| |_|\__,_|_| |_|\___| //// + //// + -------------------------------------------- + Project management tool from the future + -------------------------------------------- + Found /opt/plane-selfhost/plane-app/backup/20240722-0914/pgdata.tar.gz + .....Restoring plane-app_pgdata + .....Successfully restored volume plane-app_pgdata from pgdata.tar.gz + + Found /opt/plane-selfhost/plane-app/backup/20240722-0914/redisdata.tar.gz + .....Restoring plane-app_redisdata + .....Successfully restored volume plane-app_redisdata from redisdata.tar.gz + + Found /opt/plane-selfhost/plane-app/backup/20240722-0914/uploads.tar.gz + .....Restoring plane-app_uploads + .....Successfully restored volume plane-app_uploads from uploads.tar.gz + + + Restore completed successfully. + ``` + +4. Start your Plane instance again with ↓: + ```bash + ./setup.sh start + ``` + +That’s it! You’re back up and running with your restored data. + +::: + +## Other deployment methods + +For Kubernetes, or other deployment methods, use your platform's native backup tools. Plane stores data in two places that need to be backed up: + +| Component | What it contains | +| ----------------------- | ---------------------------------------------------------------------------- | +| **PostgreSQL database** | All Plane data — workspaces, projects, work items, users, comments, settings | +| **Object storage** | Attachments, uploaded images, files (MinIO, S3, or S3-compatible storage) | + +### Configuration files + +Also back up your environment configuration — this includes database connection strings, storage credentials, and other settings. + +- **Kubernetes:** Helm values file, ConfigMaps, and Secrets +- **Other platforms:** Environment variables or configuration files specific to your setup + +:::tip +Store backups in a separate location from your Plane installation — ideally offsite or in a different cloud region. +::: diff --git a/apps/developer-docs/docs/self-hosting/manage/community-to-airgapped.md b/apps/developer-docs/docs/self-hosting/manage/community-to-airgapped.md new file mode 100644 index 00000000..b7dece65 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/community-to-airgapped.md @@ -0,0 +1,78 @@ +--- +title: Upgrade from Community to Airgapped Edition +description: Deploy Plane in airgapped environment without internet access. Complete guide for offline Plane installation. +keywords: plane community to airgapped, edition upgrade, airgapped migration, offline deployment, air-gapped plane, self-hosting +--- + +# Upgrade from Community to Airgapped Edition + +This guide walks you through migrating your existing Plane Community Edition data to an air-gapped environment. You'll backup your current installation, transfer the data, and restore it in your air-gapped setup. + +::: warning +**Important** +Make sure you already have Commercial Airgapped Edition installed on a fresh machine before starting this migration. If you haven't installed it yet, follow our [airgapped installation guide](/self-hosting/methods/airgapped-edition) first. +::: + +## Prerequisites + +- Install the [Commercial Airgapped Edition](/self-hosting/methods/airgapped-edition) on a fresh machine, not the one running the Community Edition. +- Be sure to log in as the root user or as a user with sudo access. The `/opt` folder requires sudo or root privileges. + +## Backup data on Community instance + +1. Download the latest version of `setup.sh`. + +```bash +curl -fsSL https://github.com/makeplane/plane/releases/latest/download/setup.sh -o setup.sh +``` + +2. Run the setup.sh backup script to take the backup of the Community Edition instance. + +```bash +./setup.sh backup +``` + +This will create a backup of the plane community instance in the `backup/` folder with the timestamp as the folder name. + +```bash +backup/ +└── 20250605-0938 + ├── pgdata.tar.gz + ├── rabbitmq_data.tar.gz + ├── redisdata.tar.gz + └── uploads.tar.gz +``` + +## Restore data on Airgapped instance + +1. Download the latest version of `restore-airgapped.sh` + + ```bash + curl -fsSL https://github.com/makeplane/plane/releases/latest/download/restore-airgapped.sh -o restore-airgapped.sh + chmod +x restore-airgapped.sh + ``` + + This allows you to restore the Community Edition data to the Commercial Airgapped instance. + +2. Copy the `restore-airgapped.sh` script into your backup folder. + +3. Move your entire backup folder to the server running the Commercial Airgapped Edition. + +4. Open terminal, and execute the following command: + + ```bash + sudo bash restore-airgapped.sh ./20250605-0938 + ``` + + This will prompt you to enter the Commercial Airgapped Edition installation folder using whatever secure method works in your environment. + +5. After the data restore is finished, start the instance. + + ```bash + cd + sudo docker compose -f docker-compose.yml --env-file plane.env up -d + ``` + + You can now access the Commercial Airgapped instance at `http://` + +Once your migration is complete, verify that all your projects, issues, and team data have been successfully transferred to your air-gapped environment. diff --git a/apps/developer-docs/docs/self-hosting/manage/health-checks.md b/apps/developer-docs/docs/self-hosting/manage/health-checks.md new file mode 100644 index 00000000..29ede0ed --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/health-checks.md @@ -0,0 +1,544 @@ +--- +title: Health checks +description: Liveness, readiness, and health endpoints for self-hosted Plane services, and how to wire them into uptime monitors, load balancers, and Kubernetes or Docker Compose probes. +keywords: plane health check, liveness probe, readiness probe, self-hosting monitoring, kubernetes probe, docker compose healthcheck, uptime monitor, /api/ready, /api/live, /api/health +--- + +# Health checks + +Self-hosted Plane services expose HTTP health endpoints so you can wire up uptime monitoring, load-balancer health checks, and container or orchestrator probes. This page documents every endpoint that ships with Plane, exactly what each one checks, and how to consume them from Kubernetes, Docker Compose, and external monitors. + +::: info Edition availability +The dedicated liveness, readiness, and detailed health probes documented here — `/api/live/`, `/api/ready/`, `/api/health/`, and the per-service endpoints — ship with **Commercial Edition** (Pro, Business, and Enterprise) deployments. **Community Edition** exposes only the basic root health check at `/` that returns `{ "status": "OK" }` (see [A note on the root endpoint](#a-note-on-the-root-endpoint)). +::: + +## Liveness vs. readiness vs. health + +Plane follows the standard three-tier probe model. Knowing which one to point a given tool at matters: + +- **Liveness** — "is the process up?" A liveness probe answers a single question: is the service running and able to respond to HTTP at all. It performs no dependency checks. If a liveness probe fails, your orchestrator should **restart** the container. Liveness endpoints in Plane never return a failure body — if the process is alive, you get `200`; if it isn't, the request simply fails to connect. +- **Readiness** — "should this instance receive traffic right now?" A readiness probe verifies that the service's critical dependencies (database, cache, Redis) are reachable. If a readiness probe fails, your orchestrator or load balancer should **stop routing traffic** to that instance until it recovers — but it should not restart it, since the process itself is fine. Readiness endpoints return `200` when ready and `503` when a dependency is unavailable. +- **Health / detailed** — "what is the current state of this instance?" A detailed health endpoint returns the same up/down signal as readiness but with structured diagnostic detail (timestamps, uptime, per-dependency connection status). Use this for dashboards and debugging rather than as the gate for an orchestrator. + +::: tip Why the distinction matters for self-hosting +If you point a liveness probe at an endpoint that checks the database, a transient database blip will cause your orchestrator to **kill and restart healthy pods**, turning a small dependency hiccup into a restart storm. Always point liveness at the liveness endpoint and readiness at the readiness endpoint. +::: + +## Primary API probes + +The Plane Django API is the main REST API and web application server. Its probes are the ones most operators need, and they are reachable externally through the reverse proxy under `/api/`. All three are unauthenticated `GET` requests, and all Django routes use a **trailing slash**. + +| Endpoint | Type | Checks | Healthy | Unhealthy | +| -------------- | ----------------- | ------------------------------------- | ------- | ------------------ | +| `/api/live/` | Liveness | Process running | `200` | (connection fails) | +| `/api/ready/` | Readiness | Database + cache connectivity | `200` | `503` | +| `/api/health/` | Health (detailed) | Database + cache connectivity, uptime | `200` | `503` | + +### `/api/live/` + +Returns `200` as long as the process can serve HTTP. No dependency checks are performed. + +```bash +curl -i https://your-plane-domain.com/api/live/ +``` + +```json +{ "alive": true } +``` + +### `/api/ready/` + +Returns `200` when the database and cache are both reachable, and `503` if either is down. The failure response includes a sanitized `reason` field. + +```bash +curl -i https://your-plane-domain.com/api/ready/ +``` + +Healthy (`200`): + +```json +{ "ready": true } +``` + +Not ready (`503`): + +```json +{ "ready": false, "reason": "" } +``` + +The database check runs a `SELECT 1` query; the cache check sets and verifies a probe key. Failure reasons are **sanitized** in the response (the full exception is logged server-side only) so probes do not leak internal details. + +### `/api/health/` + +Returns a detailed status payload. Use this for dashboards and diagnostics. It performs the same database and cache checks as readiness, but reports each dependency individually along with a timestamp and uptime. + +```bash +curl -i https://your-plane-domain.com/api/health/ +``` + +Healthy (`200`): + +```json +{ + "status": "ok", + "timestamp": 1717346400000, + "uptime": 12345, + "database": { "connected": true }, + "cache": { "connected": true } +} +``` + +Degraded (`503`): + +```json +{ + "status": "degraded", + "timestamp": 1717346400000, + "uptime": 12345, + "database": { "connected": false }, + "cache": { "connected": false } +} +``` + +::: info 5-second result caching +The `/api/ready/` and `/api/health/` endpoints cache their dependency-check results for **5 seconds per process** (`_RESULT_TTL_SECONDS=5`). This caps real database and cache hits at one per worker process per 5-second window, even under a flood of probe requests, so aggressive monitoring will not thrash your database or cache. The same 5-second per-worker caching applies to the Plane AI (pi) readiness check. +::: + +### A note on the root endpoint + +The Django API also serves a minimal health indicator at the root path: + +| Endpoint | Type | Healthy body | +| ------------- | ---------------- | ------------------------------- | +| `/` | Health (minimal) | `{ "status": "OK" }` | +| `/robots.txt` | Other | `User-agent: *` / `Disallow: /` | + +The root `/` and `/robots.txt` endpoints are mounted at the Django root level, while the detailed probes (`/api/live/`, `/api/ready/`, `/api/health/`) are mounted under `/api/`. This minimal root check is the only health endpoint present in **Community Edition** — the `/api/*` probes and the per-service endpoints below are Commercial Edition only. + +::: warning Root path routing through the proxy +In the reverse proxy (Caddyfile), `/` is a catch-all that routes to the web frontend (`web:3000`), not to the Django API. A request to `/` through the proxy will hit the frontend rather than the API's root health indicator. **Use the `/api/` probes for monitoring** — they route to the API service explicitly. See [Reverse proxy](/self-hosting/govern/reverse-proxy) for routing details. +::: + +## All services and probe endpoints + +The table below lists every health-related endpoint across all Plane services. The **External path** column shows how the endpoint is reached through the reverse proxy; endpoints marked **internal only** are not exposed through the proxy and must not be made publicly reachable. + +| Service | Type | External path | Internal route | Checks | Success | Reachable | +| ------------- | ------------------ | ------------------------- | -------------------- | ----------------------------- | -------------------- | ----------------------- | +| API (Django) | Liveness | `/api/live/` | `/api/live/` | process running | `200` | External | +| API (Django) | Readiness | `/api/ready/` | `/api/ready/` | database, cache | `200` (`503` fail) | External | +| API (Django) | Health | `/api/health/` | `/api/health/` | database, cache, uptime | `200` (`503` fail) | External | +| API (Django) | Health (min) | `/` | `/` | none | `200` | Via proxy hits frontend | +| Plane AI (pi) | Liveness | `/pi/live/` | `/live/` | process running | `200` | External | +| Plane AI (pi) | Readiness | `/pi/ready/` | `/ready/` | database | `200` (`503` fail) | External | +| Plane AI (pi) | Health | `/pi/health/` | `/health/` | database, uptime | `200` (`503` fail) | External | +| Plane AI (pi) | Other (deprecated) | `/pi/api/v1/health/` | `/api/v1/health/` | none | `200` | External | +| Plane AI (pi) | Other (deprecated) | `/pi/api/v2/health/` | `/api/v2/health/` | none | `200` | External | +| live | Health | `/live/health/` | `/health/` | process running, server agent | `200` | External | +| live | Other (metrics) | `/live/health/memory` | `/health/memory` | memory, agent metrics | `200` (`401` no key) | External (secret-key) | +| silo | Liveness | `/silo/health/` | `/health/` | process running | `201` (`500` fail) | External | +| silo | Other | `/silo/health/check-hmac` | `/health/check-hmac` | API connectivity | `201` (`500` fail) | External | +| silo | Readiness | `/silo/health/check-db` | `/health/check-db` | database | `200` (`500` fail) | External | +| flux | Health | internal only | `/health` | redis, process running | `200` (`503` fail) | Internal | +| flux | Readiness | internal only | `/ready` | redis | `200` (`503` fail) | Internal | +| flux | Liveness | internal only | `/live` | process running | `200` | Internal | +| node-runner | Health | internal only | `/health` | process running | `200` | Internal | +| monitor | (prober) | internal only | — | watches other services | — | Internal | + +::: warning Internal-only endpoints +The **flux**, **node-runner**, and **monitor** services are not exposed through the reverse proxy. Their endpoints are reachable only on the internal Docker/Kubernetes network. Do not expose them publicly. The `live` memory-metrics endpoint (`/live/health/memory`) is reachable through the proxy but is secret-key protected — keep it that way. +::: + +## Per-service details + +### live (real-time collaboration) + +The `live` service is the real-time collaboration server for document editing (WebSocket via Hocuspocus), running on internal port `3000` and exposed under the `/live` proxy prefix. + +**Public health endpoint** — unauthenticated, suitable for probes: + +```bash +curl -i https://your-plane-domain.com/live/health/ +``` + +```json +{ "status": "OK", "timestamp": "2026-06-02T00:00:00.000Z", "version": "1.0.0" } +``` + +This endpoint always returns `200` when the process is up; it has no failure state. Point readiness/liveness probes here. + +**Memory metrics endpoint** — for internal monitoring only, protected by a secret key: + +```bash +curl -i \ + -H "live-server-secret-key: $LIVE_SERVER_SECRET_KEY" \ + https://your-plane-domain.com/live/health/memory +``` + +```json +{ + "status": "ok", + "timestamp": "2026-06-02T00:00:00.000Z", + "memory": { + "heapUsed": "120MB", + "heapTotal": "256MB", + "heapPercent": "47%", + "rss": "300MB", + "external": "5MB", + "arrayBuffers": "2MB" + }, + "serverAgent": { "connections": 42, "recentActivity": [] }, + "hocuspocus": {}, + "uptime": "3600s" +} +``` + +The `live-server-secret-key` header is checked against the `LIVE_SERVER_SECRET_KEY` environment variable; a missing or invalid key returns `401 Unauthorized`. Memory values are human-readable strings (e.g. `"120MB"`), `serverAgent.recentActivity` is truncated to the last 10 connections, and `uptime` is a string in seconds. + +::: info Status-string inconsistency +The `/live/health/` endpoint returns `"status": "OK"` (uppercase) while `/live/health/memory` returns `"status": "ok"` (lowercase). If you parse these programmatically, account for the case difference. +::: + +### silo (integrations engine) + +The `silo` service is the integrations engine (ETL, OAuth, webhooks, and sync for GitHub, Jira, Linear, Asana, Slack, etc.). It runs on internal port `3000` and is exposed under the `/silo` proxy prefix. The base path is configurable via the `SILO_BASE_PATH` environment variable (default `/silo`). + +```bash +# Liveness — process running +curl -i https://your-plane-domain.com/silo/health/ + +# API connectivity check (despite the name, this validates API connectivity, not HMAC) +curl -i https://your-plane-domain.com/silo/health/check-hmac + +# Readiness — database (SELECT 1) +curl -i https://your-plane-domain.com/silo/health/check-db +``` + +Success bodies: + +```json +{ "message": "Welcome to Silo health check" } +``` + +```json +{ "message": "Welcome to Silo API health check" } +``` + +```json +{ "message": "Silo DB is up and running" } +``` + +A database failure on `check-db` returns `500`: + +```json +{ + "status": 500, + "message": "Internal Server Error", + "errors": { "message": "Database is not running" } +} +``` + +::: warning Non-standard status codes +Silo's status codes are inconsistent with the other services. `/silo/health/` and `/silo/health/check-hmac` return **`201`** on success (not `200`), while `/silo/health/check-db` returns `200`. All three return `500` on failure. If you configure an external monitor that expects `2xx`, this works; if you expect exactly `200`, treat `201` as healthy. The `check-hmac` name is historical — it actually validates API connectivity to the Plane API, not HMAC. +::: + +### Plane AI (pi) + +The Plane AI service (`pi`) is a FastAPI service for AI features (chat, embeddings, transcription, LLM proxy). It runs on internal port `8000` and is exposed under the `/pi` proxy prefix. Health routes are mounted at the app root; the prefix is controlled by `PI_BASE_PATH` (default empty), and the proxy adds the `/pi` prefix without stripping it. + +```bash +# Liveness — process running +curl -i https://your-plane-domain.com/pi/live/ + +# Readiness — database connectivity +curl -i https://your-plane-domain.com/pi/ready/ + +# Health (detailed) — database connectivity + uptime +curl -i https://your-plane-domain.com/pi/health/ +``` + +Liveness (`200`): + +```json +{ "alive": true } +``` + +Readiness — healthy (`200`) / not ready (`503`): + +```json +{ "ready": true } +``` + +```json +{ "ready": false, "reason": "database unavailable" } +``` + +Health — healthy (`200`) / degraded (`503`): + +```json +{ + "status": "ok", + "timestamp": 1717346400000, + "uptime": 12345, + "database": { "connected": true } +} +``` + +```json +{ + "status": "degraded", + "timestamp": 1717346400000, + "uptime": 12345, + "database": { "connected": false } +} +``` + +The database readiness check caches results for **5 seconds per worker** to avoid connection-pool exhaustion under probe floods. + +::: info Deprecated pi endpoints +`/pi/api/v1/health/` and `/pi/api/v2/health/` are **deprecated** in favor of the root-level probes above. They return a legacy payload `{ "status": "alive" }` and emit deprecation headers (per RFC 8594). Use `/pi/live/`, `/pi/ready/`, and `/pi/health/` instead. +::: + +### flux (real-time event server) + +The `flux` service is a WebSocket server for real-time collaborative events, backed by Redis. It runs internally (default port `3004`, configurable via `PORT`) and is **internal-only** — it is not exposed through the reverse proxy. Its routes sit under the base path set by `FLUX_BASE_PATH` (default `/flux`), so they are reachable as `/flux/health`, `/flux/ready`, and `/flux/live` on the internal network: + +```bash +# From within the internal network / cluster (paths are relative to FLUX_BASE_PATH) +curl -i http://flux:3004/flux/live # liveness — process running +curl -i http://flux:3004/flux/ready # readiness — Redis connectivity +curl -i http://flux:3004/flux/health # health (detailed) — Redis + connection counts +``` + +Liveness (`200`): + +```json +{ "alive": true } +``` + +Readiness — healthy (`200`) / not ready (`503`): + +```json +{ "ready": true } +``` + +```json +{ "ready": false, "reason": "Redis not connected" } +``` + +Health — healthy (`200`) / degraded (`503`): + +```json +{ + "status": "ok", + "timestamp": 1717346400000, + "uptime": 12345, + "connections": { "total": 5, "channels": 10 }, + "redis": { "connected": true } +} +``` + +```json +{ + "status": "degraded", + "timestamp": 1717346400000, + "uptime": 12345, + "connections": { "total": 5, "channels": 10 }, + "redis": { "connected": false } +} +``` + +Liveness always returns `200`; readiness and health both gate on Redis connectivity. Note the degraded status string is `"degraded"`, not `"unhealthy"`. + +### node-runner (automation execution) + +The `node-runner` service executes automation scripts (build, validate, sandboxed execution). It runs on internal port `3000` and is **internal-only** — not reachable through the reverse proxy. + +Its own health endpoint is reachable only on the internal network: + +```bash +curl -i http://node-runner:3000/health +``` + +```json +{ "status": "ok" } +``` + +Runner health is also surfaced through the Django API at `/api/workspaces/{slug}/runnerctl/health/` (a `GET` that requires session authentication and workspace-admin permission). That endpoint checks the node-runner service and returns `{ "is_available": }` with status `200`. Use the Django-exposed endpoint if you need to observe runner health without internal network access. + +### monitor (internal prober) + +The `monitor` service is a Go-based **prober** — it is not a service you probe. It runs internally on port `8080` (not exposed through the reverse proxy) and **does not expose any liveness/readiness/health endpoint of its own**. Instead, it periodically checks the other Plane services and reports their status. + +What it does: + +- **Health-check cron job** (default every 5 minutes, configurable via `--health-check-interval`): runs HTTP or TCP probes against the services you define via `SERVICE_*` environment variables. HTTP probes (`HTTP_TEST_METHOD`) issue `GET` requests and treat `200`–`399` as healthy; TCP probes (`TCP_TEST_METHOD`) attempt a raw connection. Each service is probed with `maxRetries=5`, `confirmTries=3`, a 5s timeout, and a 2s retry interval. Results are posted to the monitoring API at `HOST/api/service-status/`. +- **Flag/license resync cron job** (default every 300 minutes, configurable via `--resync-flags-interval`): refreshes feature flags and licenses from the monitoring server. + +Services are declared as environment variables in the form `SERVICE__=hostname:port/path`. Examples: + +```bash +SERVICE_HTTP_WEB=web:3000 +SERVICE_HTTP_API=api:8000 +SERVICE_HTTP_LIVE=live:3000/live/health +SERVICE_HTTP_PROXY=proxy:80 +SERVICE_HTTP_MINIO=plane-minio:9090 +SERVICE_TCP_REDIS=plane-redis:6379 +SERVICE_TCP_POSTGRES=plane-db:5432 +``` + +For an unreachable service, monitor posts status code `500`; for a non-`2xx`/`3xx` HTTP response, it posts the actual status code. + +## Using these in your infrastructure + +### Kubernetes liveness and readiness probes + +Point `livenessProbe` at the liveness endpoint and `readinessProbe` at the readiness endpoint. The following mirrors the probe configuration Plane's own Helm charts use for the API service: + +```yaml +# api deployment +livenessProbe: + httpGet: + path: /api/live/ + port: 8000 + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 +readinessProbe: + httpGet: + path: /api/ready/ + port: 8000 + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 +``` + +The Plane AI (`pi-api`) service uses the same pattern against its own paths: + +```yaml +# pi-api deployment +livenessProbe: + httpGet: + path: /pi/live/ # or {PI_BASE_PATH}/live/ + port: 8000 + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 +readinessProbe: + httpGet: + path: /pi/ready/ # or {PI_BASE_PATH}/ready/ + port: 8000 + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 +``` + +The `live` and `silo` services use a readiness-only probe (no liveness) against their `/health` path, with a lenient `failureThreshold` to allow for slow startups: + +```yaml +# live deployment (silo is identical against {SILO_BASE_PATH}/health) +readinessProbe: + httpGet: + path: /live/health # {LIVE_BASE_PATH}/health + port: 3000 + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 1 + failureThreshold: 30 + successThreshold: 1 +``` + +::: info Timing implications +With `initialDelaySeconds: 30` plus the start period, the API and pi-api pods take roughly 90 seconds before they are considered ready — this is intentional, giving migrations and warm-up time to complete. The `live`/`silo` readiness probe with `failureThreshold: 30` at a 10s period tolerates up to ~305 seconds of startup before marking the pod unhealthy. +::: + +### Docker Compose healthcheck + +Plane's Compose deployments probe the API by calling its readiness endpoint from inside the container with Python (no extra tooling needed): + +```yaml +# api service +healthcheck: + test: + [ + "CMD", + "python", + "-c", + "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/ready/', timeout=5)", + ] + interval: 30s + timeout: 10s + retries: 5 + start_period: 60s +``` + +The `pi-api` service follows the same approach, honoring its configurable base path: + +```yaml +# pi-api service +healthcheck: + test: + [ + "CMD", + "python", + "-c", + 'import os,urllib.request; urllib.request.urlopen(f"http://localhost:8000{os.environ.get(''PI_BASE_PATH'','''')}/ready/", timeout=5)', + ] + interval: 30s + timeout: 10s + retries: 5 + start_period: 60s +``` + +Stateful dependencies are probed with their native CLIs rather than HTTP: + +```yaml +# plane-db (PostgreSQL) +healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + +# plane-mq (RabbitMQ) +healthcheck: + test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"] + interval: 10s + timeout: 10s + retries: 5 +``` + +Other services then declare `depends_on` with `condition: service_healthy` so they start only after their dependencies are healthy. + +### External uptime monitors and load balancers + +For an external uptime monitor (UptimeRobot, Better Stack, Pingdom, etc.) or a load balancer health check, target the reverse-proxy URL of the **readiness** endpoint over HTTPS: + +```bash +# Load balancer / uptime monitor target +GET https://your-plane-domain.com/api/ready/ +# Healthy: HTTP 200 Unhealthy: HTTP 503 +``` + +Guidelines: + +- Use **`/api/ready/`** as the load-balancer health check so traffic is only routed to instances whose database and cache are reachable. Treat `200` as healthy and `503` as unhealthy. +- Use **`/api/health/`** for richer dashboards where you want to display per-dependency status, timestamps, and uptime. +- All API probes are **unauthenticated**, so no credentials are needed. The 5-second per-worker result cache means a frequent polling interval (e.g. every 15–30 seconds) will not stress your database or cache. +- Mind the **trailing slash** — Django routes require it (`/api/ready/`, not `/api/ready`). +- Do not point external monitors at internal-only services (`flux`, `node-runner`, `monitor`); they are not reachable through the proxy by design. + +## Troubleshooting + +- **`503` from `/api/ready/` or `/api/health/`** — the database or cache is unreachable. Check that PostgreSQL and Redis/Valkey are running and reachable from the API container. The `reason` field on `/api/ready/` and the per-dependency `connected` flags on `/api/health/` tell you which dependency failed. Remember the full exception is logged server-side (the probe response is sanitized), so check the API logs for details. +- **`503` from `/pi/ready/`** — the Plane AI service cannot reach its database (`"reason": "database unavailable"`). Verify database connectivity from the `pi` container. +- **`503` from flux `/ready` or `/health` (`"status": "degraded"`)** — Redis is not connected (`"reason": "Redis not connected"`). Check Redis availability on the internal network. +- **`401` from `/live/health/memory`** — the `live-server-secret-key` header is missing or does not match `LIVE_SERVER_SECRET_KEY`. Confirm the env var is set and the header value matches. +- **`500` from `/silo/health/check-db`** — Silo cannot reach its database (`"message": "Database is not running"`). Note that silo's other health endpoints return `201` on success, not `200`. +- **A pod restart loop in Kubernetes** — confirm the `livenessProbe` points at a _liveness_ path (`/api/live/`, `/pi/live/`), not a readiness or health path. A liveness probe that checks the database will restart healthy pods during a transient dependency outage. +- **Probes failing right after deploy** — the API and pi-api need ~90 seconds before they report ready (30s initial delay plus start period). Make sure your probe `initialDelaySeconds` and `start_period` allow for startup and migrations before marking the service down. diff --git a/apps/developer-docs/docs/self-hosting/manage/manage-instance-users.md b/apps/developer-docs/docs/self-hosting/manage/manage-instance-users.md new file mode 100644 index 00000000..e292bff1 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/manage-instance-users.md @@ -0,0 +1,61 @@ +--- +title: User management +description: Manage instance users, invite instance admins, and control access to God Mode. +--- + +# Manage instance users + +User management lets instance admins view all users across the instance and manage their access. This is separate from workspace-level member management. + +## View users + +Go to **God Mode → User Management** to see all users in the instance. + +![User management](/images/instance-admin/user-management.webp#hero) + +The table displays: + +| Column | Description | +| ------------ | ---------------------- | +| Full Name | User's full name | +| Display Name | User's display name | +| Email | User's email address | +| Account Type | User or Instance Admin | +| Status | Active or Suspended | +| Joining Date | When the user joined | + +Use the search bar to find specific users. + +## Invite an instance admin + +Instance admins have access to God Mode but are not automatically added to any workspace. + +1. Click **Invite members**. +2. Enter the user's email and password. +3. Optionally enable: + - **Generate random password** — auto-create a password + - **Prompt user to change password after onboarding** — require password reset on first login +4. Click **Invite**. + +![Invite instance admin](/images/instance-admin/invite-instance-admin.webp#hero) + +:::warning +No invitation email is sent. You must share the credentials with the user manually. +::: + +## Manage user access + +Click **…** next to any user to: + +- **Grant admin access** — promote a user to Instance Admin. +- **Remove admin access** — downgrade an Instance Admin to a regular user (loses God Mode access). +- **Remove** — remove the user from the instance entirely. + +![User actions](/images/instance-admin/user-actions.webp#hero) + +## User status + +| Status | Description | +| --------- | -------------------------------------------------- | +| Active | User can access the instance | +| Suspended | User account exists but cannot access the instance | diff --git a/apps/developer-docs/docs/self-hosting/manage/manage-licenses/activate-airgapped-enterprise.md b/apps/developer-docs/docs/self-hosting/manage/manage-licenses/activate-airgapped-enterprise.md new file mode 100644 index 00000000..923b3a94 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/manage-licenses/activate-airgapped-enterprise.md @@ -0,0 +1,19 @@ +--- +title: Activate Enterprise Grid on Airgapped Edition +description: Offline Enterprise Grid license activation for airgapped environments without internet access. +keywords: plane airgapped license, offline activation, air-gapped deployment, plane license key, self-hosting +--- + +# Activate Enterprise Grid on Airgapped Edition + +Once your air-gapped installation is running, you'll need to activate your workspace with the license file. + +1. Login to the [Prime portal](https://prime.plane.so/licenses) with the same email address you used to purchase the paid plan. +2. Go to [Manage licenses](https://prime.plane.so/licenses). +3. Click **Download license** to download the license file for your Plane version. + ![Download license file](/images/activate-license/download-license.webp#hero) +4. Sign in to your Plane instance in [God Mode](/self-hosting/govern/instance-admin). +5. Select **Billing** from the left pane. +6. Upload the license file to activate your instance. + ![Upload license file](/images/activate-license/upload-airgapped-enterprise.webp#hero) +7. Click **Activate**. diff --git a/apps/developer-docs/docs/self-hosting/manage/manage-licenses/activate-airgapped.md b/apps/developer-docs/docs/self-hosting/manage/manage-licenses/activate-airgapped.md new file mode 100644 index 00000000..485eb52c --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/manage-licenses/activate-airgapped.md @@ -0,0 +1,21 @@ +--- +title: Activate Airgapped Edition license +description: Activate and configure your Plane Airgapped Edition license. Offline license activation for air-gapped environments without internet access. +keywords: plane airgapped license, offline activation, air-gapped deployment, plane license key, self-hosting +--- + +# Activate Pro or Business on Airgapped Edition + +Once your air-gapped installation is running, you'll need to activate your workspace with the license file. + +1. Login to the [Prime portal](https://prime.plane.so/licenses) with the same email address you used to purchase the paid plan. +2. Go to [Manage licenses](https://prime.plane.so/licenses). +3. Click **Download license** to download the license file for your Plane version. + ![Download license file](/images/activate-license/download-license.webp#hero) +4. Navigate to the [Workspace Settings](https://docs.plane.so/core-concepts/workspaces/overview#workspace-settings) in the Plane application. +5. Select **Billing and plans** on the right pane. +6. Click the **Activate this workspace** button. + ![Upload license file](/images/activate-license/upload-airgapped-license-file.webp#hero) +7. Upload the license file to activate your workspace. + +You now have Plane running in your air-gapped environment. If you run into any issues, check the logs, or reach out to our support team for assistance. diff --git a/apps/developer-docs/docs/self-hosting/manage/manage-licenses/activate-enterprise.md b/apps/developer-docs/docs/self-hosting/manage/manage-licenses/activate-enterprise.md new file mode 100644 index 00000000..111ae281 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/manage-licenses/activate-enterprise.md @@ -0,0 +1,23 @@ +--- +title: Activate Enterprise Grid license +description: Activate your Enterprise Grid license on the Commercial Edition. +keywords: plane enterprise license, license activation, enterprise edition, plane enterprise features, self-hosting +--- + +# Activate Enterprise Grid on Commercial Edition + +Enterprise Grid licenses are activated at the instance level through God Mode, not through individual workspace settings. This gives instance administrators centralized control over Enterprise features across all workspaces. + +## Activate license key + +1. Sign in to the [Prime portal](https://prime.plane.so/licenses) with the email address you used to purchase your Enterprise Grid. +2. Navigate to **Manage licenses**. +3. Copy the license key for your Enterprise Grid. +4. Sign in to your Plane instance in [God Mode](/self-hosting/govern/instance-admin). +5. Select **Billing** from the left pane. +6. Paste your license key in the **Activate Enterprise license** field. +7. Click **Activate**. + + ![Activate Enterprise license](/images/activate-license/activate-enterprise-plan.webp#hero) + +Once activated, Enterprise features become available across all workspaces on your instance. diff --git a/apps/developer-docs/docs/self-hosting/manage/manage-licenses/activate-pro-and-business.md b/apps/developer-docs/docs/self-hosting/manage/manage-licenses/activate-pro-and-business.md new file mode 100644 index 00000000..83ee710a --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/manage-licenses/activate-pro-and-business.md @@ -0,0 +1,64 @@ +--- +title: Activate Pro and Business licenses +description: Activate your Plane Pro or Business Edition license. Upgrade from Community Edition to unlock advanced project management features. +keywords: plane pro license, plane business license, license activation, commercial edition, plane upgrade, self-hosting +--- + +# Activate Pro or Business on Commercial Edition + +Pro and Business plan licenses are activated at the workspace level as each license is tied to a specific workspace. + +## Activate license key + +Activate a paid plan license on your self-hosted Plane instance using a license key from the Prime portal. + +1. Login to the [Prime portal](https://prime.plane.so/licenses) with the same email address you used to purchase one of our paid plans. + +2. Go to [Manage licenses](https://prime.plane.so/licenses). Copy the value of the **License key** for the license you want to activate. + + ![Manage licenses](https://media.docs.plane.so/activate-license/copy-license-key.webp#hero) + + > [!TIP] + > Click the **Get more licenses** button as in the image above on the [Prime portal](https://prime.plane.so/licenses/plans) to buy more licenses. + +3. On the Plane app, navigate to **Workspace Settings > Billing and plans** +4. Click the **Activate this Workspace** button. + + ![Activate workspace](https://media.docs.plane.so/activate-license/enter-license-key-selfhosted.webp#hero-tr) + +5. Paste the license key in the **Enter license key** box. +6. Click **Activate**. You will see a confirmation. +7. That's it. To check your plan at any time and find additional details, just go to the **Billing and plans** tab in **Workspace Settings**. + + ![Manage subscription](https://media.docs.plane.so/activate-license/pro-activated-cloud.webp#hero-tr) + +## Sync plan + +If you've made changes to your subscription, like renewing your license, upgrading your plan, or adjusting seats, use **Sync plan** to pull those updates into your workspace. + +Syncing refreshes your workspace with the latest subscription information from the Prime server, including plan type, seat count, expiration dates, and feature access. + +**To sync your plan:** + +1. Navigate to **Workspace Settings > Billing and plans**. +2. Click **Sync plan**. +3. Wait for the sync to complete. You'll see a confirmation once your workspace is updated. + +Use this whenever you see a mismatch between what's in the Prime portal and what appears in your workspace, or after making any subscription changes externally. + +## Delink license key + +Your license key is linked to both a workspace and an instance, meaning it can only be used on one workspace on one machine at a time. If you switch machines or reinstall the Commercial edition, you’ll need to reactivate your workspace. This helps prevent any misuse of the license on multiple machines or workspaces. + +To make it easier for you to move between machines or workspaces, we've added a new Delink feature. This lets you free up your license from its current workspace, so you can reuse it on a new machine or workspace. + +Here’s how to delink your license key from a workspace: + +1. Head over to the **Billing and Plans** screen of the workspace that's currently using the license. +2. Click **Delink license key**. This will release the license key, making it available for use on another machine or workspace. + + ![Delink license key](https://media.docs.plane.so/activate-license/delink-license-key.webp#hero-tl) + +3. Restart the instance using `prime-cli restart`. +4. If you’re switching machines or reinstalling the Commercial edition, see [Move Plane instance to another server](https://developers.plane.so/self-hosting/manage/migrate-plane). +5. Ensure you are connected to the internet and reactivate the new workspace using the license key you delinked earlier. diff --git a/apps/developer-docs/docs/self-hosting/manage/migrate-plane.md b/apps/developer-docs/docs/self-hosting/manage/migrate-plane.md new file mode 100644 index 00000000..8e6f2ed0 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/migrate-plane.md @@ -0,0 +1,59 @@ +--- +title: Move Plane instance to a new server +description: Migrate Plane between servers or environments. Guide for moving your Plane installation to new infrastructure. +keywords: plane migration, server migration, plane data transfer, infrastructure move, plane backup restore, self-hosting +--- + +# Move Plane instance to a new server + +Switching to another machine is straightforward on the Commercial Edition. + +## Prerequisites + +Before we dive in, ensure: + +- You’re running Plane's Commercial Edition. +- You have a different machine with our standard config to migrate to. +- You understand the same domain will be used to host the app as the current machine. + +::: warning +If you need to change your domain during migration, contact our support team for assistance. +::: + +## Steps + +1. **Delink licenses** + Log in to Plane on your current server. Head to each paid workspace like Pro or Business and [delink the licenses](/self-hosting/manage/manage-licenses/activate-pro-and-business#delink-license-key). This will free up the licenses for activation on your new server. Ideally, you have just one paid workspace. + +2. **Backup data** + Create a backup of your Plane instance with ↓: + +```bash +prime-cli backup +``` + +This command will generate a backup file in the path: `/opt/plane/backups`. + +::: warning +**Prime CLI is for Docker installations only.** These commands only work on Plane instances originally installed using `prime-cli`. +::: + +3. **Set up Plane on the new server** + Follow the [installation guide](/self-hosting/methods/docker-compose#install-plane) to deploy Plane on the new instance. + +4. **Transfer backup files** + Copy the `backups` folder from the old server, created in step 2, to the new server. Place the backup in the folder `/opt/plane`. + +5. **Restore data** + On the new server, restore your data with ↓: + +```bash +prime-cli restore +``` + +Follow the prompts during the restore process to make sure everything is set up correctly. + +6. **Reactivate license** + Finally, [reactivate your license keys](/self-hosting/manage/manage-licenses/activate-pro-and-business#activate-your-license) on the new instance. + +This should get your Plane instance up and running on the new server. diff --git a/apps/developer-docs/docs/self-hosting/manage/migration/migrate-data-to-external-services.md b/apps/developer-docs/docs/self-hosting/manage/migration/migrate-data-to-external-services.md new file mode 100644 index 00000000..fb39182b --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/migration/migrate-data-to-external-services.md @@ -0,0 +1,201 @@ +--- +title: Migrate to external services +description: Move your Plane data from the default local Postgres and MinIO containers to managed cloud services for a production ready deployment. +keywords: Plane migration, external Postgres, cloud storage, MinIO migration, self-hosted Plane, production deployment, pg_dump, pg_restore, S3-compatible storage +--- + +# Migrate to external Postgres and storage + +The default Plane installation includes a local PostgreSQL container and a local MinIO container for storage. These are convenient for getting started, but not recommended for production. For production deployments, use a managed database service and an S3-compatible object store. + +This guide walks through moving your existing data from the local containers to your cloud-managed services. Follow these steps during a maintenance window as the migration requires Plane to be offline. + +## Before you begin + +You need: + +- Docker and Docker Compose installed on the host running Plane +- The PostgreSQL client `psql` and `pg_restore` installed on your local machine +- Your cloud Postgres connection details: host, username, database name, and password +- Your cloud storage connection details: endpoint URL, access key, secret key, and bucket name +- Your cloud PostgreSQL version should ideally match your source PostgreSQL version + +## Stop Plane + +Take Plane offline before starting. This prevents new writes during the migration. + +```bash +docker compose down +``` + +Do not stop the database and MinIO containers yet, you still need them running to export data. + +Start only the database and MinIO: + +```bash +docker compose up -d plane-db plane-minio +``` + +## Export the database + +Run `pg_dump` inside the running database container. This creates a compressed binary dump file in your current directory. + +```bash +docker exec \ + -e PGPASSWORD=plane \ + -e PGUSER=plane \ + -e PGDATABASE=plane \ + plane-app-plane-db-1 \ + pg_dump -Fc > plane-backup.dump +``` + +Verify the file was created and is not empty: + +```bash +ls -lh plane-backup.dump +``` + +## Restore the database to your cloud Postgres + +Before restoring, clear the existing schema in your cloud database. + +```bash +psql \ + -h "your-cloud-host.com" \ + -U "cloud-username" \ + -d "cloud-database-name" \ + -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;" +``` + +Restore the dump using `pg_restore`. + +The `--no-owner` flag prevents ownership errors when the source database roles do not exist in your cloud database. + +The `--no-privileges` flag skips grants for roles that may not exist in the target database. + +```bash +pg_restore \ + --no-owner \ + --no-privileges \ + --verbose \ + -h "your-cloud-host.com" \ + -U "cloud-username" \ + -d "cloud-database-name" \ + plane-backup.dump +``` + +You will be prompted for your cloud database password. + +### PostgreSQL version compatibility + +Use a `pg_restore` version that matches your target PostgreSQL version when possible. + +For example, if your cloud database is PostgreSQL 15, use PostgreSQL 15 client tools. + +If you use a newer `pg_restore` version, you may see: + +```text +ERROR: unrecognized configuration parameter "transaction_timeout" +Command was: SET transaction_timeout = 0; +``` + +This warning can be ignored if the restore continues successfully. Using matching PostgreSQL client tools avoids this warning. + +## Sync storage to your cloud bucket + +MinIO ships with the `mc` client. Use it from inside the MinIO container to sync your local uploads to your cloud storage. + +### 1. Create an alias for your local MinIO + +Your MinIO credentials are in `plane.env` as `MINIO_ROOT_USER` and `MINIO_ROOT_PASSWORD`. + +```bash +docker compose --env-file plane.env exec plane-minio \ + mc alias set localminio http://localhost:9000 \ + +``` + +### 2. Create an alias for your cloud storage + +For AWS S3, use the S3 endpoint. + +Default AWS endpoint: + +```bash +docker compose --env-file plane.env exec plane-minio \ + mc alias set cloudminio https://s3.amazonaws.com \ + +``` + +Regional AWS S3 endpoint: + +```bash +docker compose --env-file plane.env exec plane-minio \ + mc alias set cloudminio https://s3..amazonaws.com \ + +``` + +For other S3-compatible providers, replace the endpoint with your provider's S3 endpoint. + +Verify the connection: + +```bash +docker compose --env-file plane.env exec plane-minio \ + mc ls cloudminio +``` + +### 3. Mirror local data to your cloud bucket + +```bash +docker compose --env-file plane.env exec plane-minio \ + mc mirror localminio/uploads cloudminio/ --overwrite +``` + +Example: + +```bash +docker compose --env-file plane.env exec plane-minio \ + mc mirror localminio/uploads cloudminio/my-plane-bucket --overwrite +``` + +This copies all files from the local uploads bucket to your cloud bucket. The `--overwrite` flag replaces existing files with the same name. + +Wait for the sync to complete before continuing. + +## Update your environment configuration + +Open `plane.env` and update the database and storage settings to point to your cloud services. + +### Database + +```bash +DATABASE_URL=postgresql://cloud-username:cloud-password@your-cloud-host.com:5432/cloud-database-name +``` + +### Storage + +For AWS S3: + +```bash +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_S3_ENDPOINT_URL=https://s3.amazonaws.com +AWS_S3_BUCKET_NAME= +AWS_REGION= +``` + +For other S3-compatible providers, replace `AWS_S3_ENDPOINT_URL` with your provider endpoint. + +## Restart Plane + +Bring all services back up: + +```bash +docker compose up -d +``` + +Open Plane in a browser and verify that your data, attachments, and pages are intact. + +## After migration + +Keep `plane-backup.dump` in a safe location as a point-in-time backup. diff --git a/apps/developer-docs/docs/self-hosting/manage/prime-cli.md b/apps/developer-docs/docs/self-hosting/manage/prime-cli.md new file mode 100644 index 00000000..1db2f140 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/prime-cli.md @@ -0,0 +1,157 @@ +--- +title: Command line tools +description: Use Plane Prime CLI for managing your self-hosted instance. Commands for setup, configuration, upgrades, and troubleshooting from the terminal. +keywords: plane cli, prime cli, command line tools, plane management, plane setup commands, self-hosting, plane terminal +--- + +# Command line tools + +Our command-line tool is here to make managing your Plane instance simple. You can handle installs, upgrades, and general management without needing to be a Docker expert. + +## Prime CLI + +The Prime CLI provides commands for common tasks like configuring services, monitoring health, managing backups, and upgrading your Plane instance. + +::: warning +**Prime CLI is for Docker installations only.** These commands only work on Plane instances originally installed using `prime-cli`. +::: + +Bring up the Prime CLI with `sudo prime-cli` from any directory on your machine. + +- The three operators you will use the most are: + - `start` + You will use this to start a service in the Docker network with the name of the service. + + - `stop` + You will use this to stop a service in the Docker network with the name of the service. + + - `restart` + You will use this to restart a service in the Docker network with the name of the service as a `{param or flag}`. + +- Often, you will want to monitor the health of your instance and see if some services are up or down. Use `monitor` to do that. + +- `healthcheck` is another useful utility that lets you see the status and errors, if any, of all running services + +- `repair` automatically diagnoses and fixes common errors in your Plane instance. This command also resets all configuration values in the plane.env file to their defaults. + +- `update-cli` downloads and installs the latest version of Prime CLI. + ::: tip + It is highly recommend to run this first before you download any Plane updates. The latest version of the CLI ensures your Plane upgrades happen smoothly. + ::: + +For more advanced admins that want greater control over their instance, the list of additional commands available on Prime CLI follow. + +- `configure` + Brings up a step form to let you specify the following. + +::: details Steps to configure your instance + +- `Listening port` + Specify the port that the built-in reverse proxy will use + + Default value: `80` + +- `Max file-upload size` + Specify a size in MBs for how big each file uploaded to your Plane app can be + + Default value: `5 MB` + +- `External Postgres URL` + Specify the URL of your own hosted Postgres if you would like to change the database your Plane app uses. + +Default database: Postgres 15.5 in the Docker container + +- `External Redis URL` + Specify the URL of your own hosted REdis if you would like to change the default Redis Plane ships with. + +Default Redis: Redis 7.2.4 + +- `External storage` + Specify your AWS S3 bucket's credentials in the format below to change storage from the default Plane ships with. +- AWS Access Key ID +- AWS Secret Access Key +- AWS S3 Bucket Name + +Default storage: MinIO + +- Confirm your choices on the screen ↓. + This restarts your instance with the new configs. + ::: + +- `upgrade` + +checks your instance for available version upgrades and asks you for a confirmation before downloading the latest available version. + +1. Typing `YES` lets the CLI automatically download's the latest version and installs it. Then it restarts the instance to load the latest app. +2. Typing `NO` cancels the upgrade. + +- `uninstall` + +uninstalls Plane. Before it goes through, it asks you for a confirmation. + +1. Typing `YES` lets the CLI clean up the `/opt/plane` folder, leaving behind the `/opt/plane/data` and `/opt/plane/logs` folders. +2. Typing `NO` cancels the uninstall. + +::: details Setup.sh script • Community Edition + +The setup script `setup.sh` provides a menu-driven interface to help you install and manage your Plane instance. + +#### Usage + +To run the setup.sh script, use the following command in your terminal from the directory where the script is located: + +```bash +./setup.sh +``` + +This will launch an interactive menu with options to manage various aspects of your Plane instance. + +```bash +Select a Action you want to perform: + 1) Install + 2) Start + 3) Stop + 4) Restart + 5) Upgrade + 6) View Logs + 7) Backup Data + 8) Exit +``` + +#### Actions + +- **Install** + Installs the Plane Community Edition on your machine. Choose this option if you are setting up Plane for the first time. + +- **Start** + Starts the Plane server and all related services. + +- **Stop** + Stops the Plane server and all services currently running on the machine. + +- **Restart** + Restarts the Plane server and all associated services. + +- **Upgrade** + Upgrades Plane to the latest available version. This will stop all services, update the necessary files, and then restart Plane with the latest configuration. See [Update Plane](/self-hosting/manage/upgrade-plane#prerequisites) for more info. + + > [!WARNING] + > It's recommended to create a backup before upgrading your instance. See [Backup and restore](/self-hosting/manage/backup-restore#backup-data). + +- **View Logs** + Displays real-time logs of specific Plane services. See [View logs](/self-hosting/manage/view-logs) for more info. + + > [!TIP] + > Use **View Logs** to monitor service performance or troubleshoot issues. Press `CTRL+C` to exit the log view and return to the main menu. + +- **Backup Data** + Creates a backup of your current Plane installation, including all data. See [Backup and restore data](/self-hosting/manage/backup-restore#backup-data) for more info. + +- **Exit** + Closes the setup script and returns you to the command line. + +::: + +## Troubleshoot + +- [Failed to update Prime CLI](/self-hosting/troubleshoot/cli-errors#failed-to-update-prime-cli) diff --git a/apps/developer-docs/docs/self-hosting/manage/update-plane/airgapped-edition/update-airgapped-docker.md b/apps/developer-docs/docs/self-hosting/manage/update-plane/airgapped-edition/update-airgapped-docker.md new file mode 100644 index 00000000..44f4f83f --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/update-plane/airgapped-edition/update-airgapped-docker.md @@ -0,0 +1,47 @@ +--- +title: Upgrade Airgapped Edition (Docker) +description: Upgrade your airgapped Plane instance running on Docker by cloning images, replacing configuration files, and uploading a new license. +keywords: plane airgapped upgrade, air-gapped docker upgrade, plane offline update +--- + +# Update Airgapped Edition on Docker + +Since airgapped instances can't pull updates from the internet, updating the version requires manually transferring the latest Docker images and configuration files from a machine with internet access. + +## Prerequisites + +- A machine with internet access to download images and files. +- Access to your airgapped Docker registry. +- Your current `plane.env` file backed up. + +## Update Plane + +1. On a machine with internet access, pull the latest Plane images and push them to your airgapped Docker registry. Follow the guide for [cloning and pushing Plane Docker images](https://developers.plane.so/self-hosting/methods/clone-docker-images). + + Once complete, the latest Plane images are available in your internal registry. + +2. On the same machine with internet access, download the updated `docker-compose.yml` and environment template for your target version. + + ```bash + # Download docker-compose.yml + curl -fsSL https://prime.plane.so/releases//docker-compose-airgapped.yml -o docker-compose.yml + + # Download environment template + curl -fsSL https://prime.plane.so/releases//variables-airgapped.env -o plane.env + ``` + + Transfer both files to your airgapped instance and replace the existing ones. Before replacing your existing `plane.env`, compare it with the new template. Copy over any custom values from your old plane.env into the new template. The new template may include additional variables required by the latest version, so always use the new file as the base and bring your existing values into it. + + :::info + Replace `` with the version you're upgrading to (e.g., v2.6.3). Check the [release notes](https://plane.so/changelog?category=self-hosted) for the latest available release version. + ::: + +3. Download the latest license file for the new version from [prime.plane.so](https://prime.plane.so). Follow [this guide](https://developers.plane.so/self-hosting/manage/manage-licenses/activate-airgapped) to activate license. + +4. Restart the instance to bring the instance back up with the new configuration. + + ```bash + docker compose up -d + ``` + +Verify the upgrade by checking the version in your Plane application. diff --git a/apps/developer-docs/docs/self-hosting/manage/update-plane/airgapped-edition/update-airgapped-kubernetes.md b/apps/developer-docs/docs/self-hosting/manage/update-plane/airgapped-edition/update-airgapped-kubernetes.md new file mode 100644 index 00000000..0b6941d9 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/update-plane/airgapped-edition/update-airgapped-kubernetes.md @@ -0,0 +1,53 @@ +--- +title: Upgrade Airgapped Edition on Kubernetes +description: Upgrade your airgapped Plane instance running on Kubernetes by cloning images, updating the Helm chart, and redeploying. +keywords: plane airgapped kubernetes upgrade, air-gapped helm chart upgrade, plane offline k8s update +--- + +# Update Airgapped Edition on Kubernetes + +Since airgapped clusters can't pull updates from the internet, upgrading requires manually transferring Docker images to your private registry and updating the Helm chart. + +## Prerequisites + +- A machine with internet access to download images and the Helm chart. +- Access to your airgapped Docker registry used by the cluster. +- Your current Helm `values.yaml` file backed up. + +## Update Plane + +1. On a machine with internet access, pull the latest Plane images and push them to your air-gapped Docker registry. Follow the guide for [cloning and pushing Plane Docker images](https://developers.plane.so/self-hosting/methods/clone-docker-images). + + Once complete, the latest Plane images are available in your internal registry. + +2. Download the latest Plane Enterprise Helm chart. You can check the most recent version on [Artifact Hub](https://artifacthub.io/packages/helm/makeplane/plane-enterprise). + + ```bash + # Using wget + wget https://github.com/makeplane/helm-charts/releases/download/plane-enterprise-/plane-enterprise-.tgz + + # Using curl + curl -L -O https://github.com/makeplane/helm-charts/releases/download/plane-enterprise-/plane-enterprise-.tgz + ``` + + Transfer the `.tgz` file to a machine that can access the cluster. + + :::info + Replace with the latest Helm chart version (e.g., 2.2.4). You can check the most recent version on [Artifact Hub](https://artifacthub.io/packages/helm/makeplane/plane-enterprise). + ::: + + Before replacing your existing `values.yaml`, compare it with the new Helm chart's default values. Copy over any custom configuration from your old `values.yaml` into the new template. The new chart version may include additional or renamed fields, so always use the new default values as the base and bring your existing configuration into it. + +3. In your `values.yaml`, update `planeVersion` to match the version of Plane images you pushed to the registry. + + ```yaml + planeVersion: + ``` + + :::info + Replace `` with the version you're upgrading to (e.g., v2.6.3). Check the [release notes](https://plane.so/changelog?category=self-hosted) for the latest available release version. + ::: + +4. Once the Helm chart and `values.yaml` file are updated, redeploy the Helm release in your Kubernetes cluster to complete the update. + +Verify the upgrade by checking the version in your Plane application. diff --git a/apps/developer-docs/docs/self-hosting/manage/upgrade-from-0.13.2-0.14.0.md b/apps/developer-docs/docs/self-hosting/manage/upgrade-from-0.13.2-0.14.0.md new file mode 100644 index 00000000..845b8f2d --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/upgrade-from-0.13.2-0.14.0.md @@ -0,0 +1,122 @@ +--- +title: Mandatory checkpoint at v0.14.0 +description: Upgrade self-hosted Plane to the latest version. Step-by-step guide for updating your Plane installation safely. +keywords: plane v0.14 upgrade, mandatory checkpoint, plane migration 0.13, breaking changes, version upgrade, self-hosting update +--- + +# Mandatory checkpoint at v0.14.0 + +If you’re upgrading from `v0.13.2` or below, there are some additional migration steps due to significant changes in the self-hosting setup. Follow these instructions to migrate your data to the new volume structure in `v0.14.0`. + +1. First, stop the running `v0.13-2` (or older) instance of Plane. If it's still running, you might hit a "ports not available" error, which will prevent the `v0.14-0` containers from starting up correctly. + + ```bash + docker compose down + ``` + +2. Create a new folder for `v0.14-0` to ensure a clean installation. + + ```bash + mkdir plane-selfhost + cd plane-selfhost + ``` + +3. Set up the environment variable for the `RELEASE` variable, then download and prepare the installation script: + + ```bash + export RELEASE=v0.14-dev + curl -fsSL https://raw.githubusercontent.com/makeplane/plane/master/deploy/selfhost/install.sh | sed -e 's@BRANCH=${BRANCH:-master}@BRANCH='"$RELEASE"'@' -e 's@APP_RELEASE="stable"@APP_RELEASE='"$RELEASE"'@' > setup.sh + chmod +x setup.sh + ``` + +4. Execute the script to install Plane: + + ```bash + ./setup.sh install + ``` + +5. Start up your new v0.14-0 Plane instance: + + ```bash + ./setup.sh start + ``` + +6. Now stop the instance to initialize the new Docker volumes: + + ```bash + ./setup.sh stop + ``` + +7. Download the migration script: + + ```bash + curl -fsSL -o migrate.sh https://raw.githubusercontent.com/makeplane/plane/master/deploy/selfhost/migration-0.13-0.14.sh + chmod +x migrate.sh + ``` + +8. Run the migration script: + + ```bash + ./migrate.sh + ``` + + You’ll see the following instructions: + + ``` + ****************************************************************** + + This script is solely for the migration purpose only. + This is a 1 time migration of volume data from v0.13.2 => v0.14.x + + Assumption: + 1. Postgres data volume name ends with _pgdata + 2. Minio data volume name ends with _uploads + 3. Redis data volume name ends with _redisdata + + Any changes to this script can break the migration. + + Before you proceed, make sure you run the below command + to know the docker volumes + + docker volume ls -q | grep -i "_pgdata" + docker volume ls -q | grep -i "_uploads" + docker volume ls -q | grep -i "_redisdata" + + ******************************************************* + + Given below list of REDIS volumes, identify the prefix of source and destination volumes leaving "_redisdata" + --------------------- + plane-app_redisdata + v0132_redisdata + + Provide the Source Volume Prefix : + ``` + +9. Open a second terminal and run the commands shown above to identify your source and destination volume prefixes. For example, if you run `docker volume ls -q | grep -i "_pgdata"`, you might see something like: + + ![](/images/update-plane/docker-volumes.png) + + In this example, `plane-013-dev` is the prefix for `v0.13.2`, and `plane-app` is the prefix for `v0.14.0`. + +10. Return to the original terminal, enter the source volume prefix `plane-013-dev` and destination volume prefix `plane-app`, and press ENTER: + + ```bash + Provide the Source Volume Prefix : plane-013-dev + Provide the Destination Volume Prefix : plane-app + ``` + + If there are any issues, an error will appear. For a successful migration, there will be no error, and the process will exit quietly. + +11. Restart the upgraded v0.14.0 instance with: + + ```bash + ./setup.sh restart + ``` + +12. Login as instance admin by appending `/god-mode` to your domain. + +13. Once logged in, just click **Save Changes** to finalize your setup. + +14. You’re all set! Log in to your updated `v0.14-0` instance to check if all of your data has migrated successfully. + +15. Now, [update to the latest version](/self-hosting/manage/upgrade-plane#update-version). diff --git a/apps/developer-docs/docs/self-hosting/manage/upgrade-plane.md b/apps/developer-docs/docs/self-hosting/manage/upgrade-plane.md new file mode 100644 index 00000000..b74f0c26 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/upgrade-plane.md @@ -0,0 +1,97 @@ +--- +title: Update Plane version +description: Upgrade self-hosted Plane to the latest version. Step-by-step guide for updating your Plane installation safely. +keywords: plane version upgrade, update plane, plane latest version, upgrade guide, self-hosting update, plane migration +--- + +# Update Plane version + +Keeping Plane up to date ensures you’re using the latest features, improvements, and security fixes. Here’s how to upgrade your Plane installation with a single command. + +::: info +The upgrade process may involve a brief downtime as services are updated and restarted. +::: + +## Prerequisites + +We recommend creating a backup of your data before any version updates. See [Backup data](/self-hosting/manage/backup-restore). + +## Check version + +You can quickly check your Plane version by clicking the **?** icon on the sidebar. + +![Check version number](https://media.docs.plane.so/product/check-version.webp#hero) + +## Update version + +::: warning +For Commercial Edition v1.13.0, ensure you're using the **latest version of Docker Compose**. Check your Docker Compose version with `docker-compose --version` and update if needed. + +**Prime CLI is for Docker installations only.** These commands only work on Plane instances originally installed using `prime-cli`. +::: + +1. Update your Prime CLI with the command ↓: + + ```bash + sudo prime-cli update-cli + ``` + + The latest version of the CLI ensures your Plane upgrades happen smoothly. + +2. To update Plane to the latest version, run: + ```bash + sudo prime-cli upgrade + ``` + This command checks for the latest version of Plane and applies the upgrade if a new version is available. + +::: details Community Edition + +> [!WARNING] +> This guide covers how to upgrade from version 0.14.0 and above. If you’re running version 0.13.2 or below, first follow the guide to [upgrade to version v0.14.0](/self-hosting/manage/upgrade-from-0.13.2-0.14.0) before continuing with these steps. + +#### Prerequisites + +Before starting, make a backup of your Plane instance. For detailed steps, see the [Backup data](/self-hosting/manage/backup-restore#backup-data) section. This is strongly recommended to ensure you have a safe restore point. + +#### Update version + +1. Download the latest stable release with ↓: + + ```bash + curl -fsSL -o setup.sh https://github.com/makeplane/plane/releases/latest/download/setup.sh + ``` + +2. Execute the setup script with ↓: + + ```bash + ./setup.sh + ``` + + This will bring up a menu with several options. Select option `5` to upgrade: + + ```bash + Select a Action you want to perform: + 1) Install (x86_64) + 2) Start + 3) Stop + 4) Restart + 5) Upgrade + 6) View Logs + 7) Backup Data + 8) Exit + + Action [2]: 5 + ``` + + Choosing this option stops all services and downloads the latest `docker-compose.yaml` and `variables-upgrade.env` files. The `plane.env` file won’t be overwritten, so your existing environment settings are safe. + + You’ll see a message indicating the services have been stopped. + ![Stopped Docker services](/images/docker-compose/stopped-docker.png) + +3. After the update completes, select `6` to exit the prompt. + +4. After the upgrade, open `variables-upgrade.env` and compare it with your `plane.env` file. Copy any new variables from `variables-upgrade.env` to your `plane.env` file and set the correct values. This step is essential to ensure that all configuration changes are in place for the latest version. + +5. Once your `plane.env` file is updated, start your Plane instance again by selecting option `2`. + +::: diff --git a/apps/developer-docs/docs/self-hosting/manage/view-logs.md b/apps/developer-docs/docs/self-hosting/manage/view-logs.md new file mode 100644 index 00000000..2a53c9ac --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/manage/view-logs.md @@ -0,0 +1,119 @@ +--- +title: View container logs +description: View and debug container logs for self-hosted Plane. Monitor Docker and Kubernetes service logs to troubleshoot issues. +keywords: plane logs, container logs, docker logs, kubernetes logs, plane debugging, plane troubleshooting, self-hosting +--- + +# View container logs + +If you need to check the logs for troubleshooting or to monitor what’s happening in specific Plane services like the API or Worker, you can access them directly from the command line. + +To view logs, start by running the command ↓: + +```bash +sudo prime-cli monitor +``` + +This brings up a table where you can select which container logs you want to view. + +![Container logs](/images/view-logs/container-logs.webp#hero) + +::: warning +**Prime CLI is for Docker installations only.** These commands only work on Plane instances originally installed using `prime-cli`. +::: + +::: details Community Edition + +Here’s how to view logs for any service in Plane Community Edition, whether it’s the API, Worker, Redis, or others. This can be really helpful when troubleshooting or just getting insights into how each service is running. + +1. Start by running the setup script: + + ```bash + ./setup.sh + ``` + + This will bring up the main menu with options. Select `6` to view logs. + + ``` + Select a Action you want to perform: + 1) Install (x86_64) + 2) Start + 3) Stop + 4) Restart + 5) Upgrade + 6) View Logs + 7) Backup Data + 8) Exit + + Action [2]: 6 + ``` + +2. After choosing `6`, you’ll see a sub-menu listing all available services: + + ``` + Select a Service you want to view the logs for: + 1) Web + 2) Space + 3) API + 4) Worker + 5) Beat-Worker + 6) Migrator + 7) Proxy + 8) Redis + 9) Postgres + 10) Minio + 0) Back to Main Menu + + Service: + ``` + +3. Pick the service whose logs you’d like to check. For example, if you want to view the **API logs**, type `3`. + + After selecting a service, you’ll see the logs in real-time. Here’s an example of what API logs might look like: + + ``` + api-1 | Waiting for database... + api-1 | Database available! + api-1 | Waiting for database migrations to complete... + api-1 | Waiting for database migrations to complete... + api-1 | Waiting for database migrations to complete... + api-1 | Waiting for database migrations to complete... + api-1 | Waiting for database migrations to complete... + api-1 | Waiting for database migrations to complete... + api-1 | Waiting for database migrations to complete... + api-1 | No migrations Pending. Starting processes ... + api-1 | Instance registered + api-1 | ENABLE_SIGNUP loaded with value from environment variable. + api-1 | ENABLE_EMAIL_PASSWORD loaded with value from environment variable. + api-1 | ENABLE_MAGIC_LINK_LOGIN loaded with value from environment variable. + api-1 | GOOGLE_CLIENT_ID loaded with value from environment variable. + api-1 | GITHUB_CLIENT_ID loaded with value from environment variable. + api-1 | GITHUB_CLIENT_SECRET loaded with value from environment variable. + api-1 | EMAIL_HOST loaded with value from environment variable. + api-1 | EMAIL_HOST_USER loaded with value from environment variable. + api-1 | EMAIL_HOST_PASSWORD loaded with value from environment variable. + api-1 | EMAIL_PORT loaded with value from environment variable. + api-1 | EMAIL_FROM loaded with value from environment variable. + api-1 | EMAIL_USE_TLS loaded with value from environment variable. + api-1 | EMAIL_USE_SSL loaded with value from environment variable. + api-1 | OPENAI_API_KEY loaded with value from environment variable. + api-1 | GPT_ENGINE loaded with value from environment variable. + api-1 | UNSPLASH_ACCESS_KEY loaded with value from environment variable. + api-1 | Checking bucket... + api-1 | Bucket 'uploads' does not exist. Creating bucket... + api-1 | Bucket 'uploads' created successfully. + api-1 | Public read access policy set for bucket 'uploads'. + api-1 | Cache Cleared + api-1 | [2024-05-02 03:56:01 +0000] [1] [INFO] Starting gunicorn 21.2.0 + api-1 | [2024-05-02 03:56:01 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1) + api-1 | [2024-05-02 03:56:01 +0000] [1] [INFO] Using worker: uvicorn.workers.UvicornWorker + api-1 | [2024-05-02 03:56:01 +0000] [25] [INFO] Booting worker with pid: 25 + api-1 | [2024-05-02 03:56:03 +0000] [25] [INFO] Started server process [25] + api-1 | [2024-05-02 03:56:03 +0000] [25] [INFO] Waiting for application startup. + api-1 | [2024-05-02 03:56:03 +0000] [25] [INFO] ASGI 'lifespan' protocol appears unsupported. + api-1 | [2024-05-02 03:56:03 +0000] [25] [INFO] Application startup complete. + ``` + +4. To exit the logs, use `CTRL+C`. This will take you back to the main menu where you can select another action or view logs from a different service. + +::: diff --git a/apps/developer-docs/docs/self-hosting/methods/airgapped-edition-kubernetes.md b/apps/developer-docs/docs/self-hosting/methods/airgapped-edition-kubernetes.md new file mode 100644 index 00000000..baac2328 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/airgapped-edition-kubernetes.md @@ -0,0 +1,292 @@ +--- +title: Deploy Plane with airgapped Kubernetes +description: Deploy Plane on Kubernetes using Helm charts. Complete guide for production-ready Kubernetes deployment with scaling and management. +keywords: plane airgapped kubernetes, offline k8s deployment, air-gapped helm, kubernetes offline, plane helm airgapped, self-hosting +--- + +# Deploy Plane Airgapped on Kubernetes + +::: info +Airgapped deployments are available exclusively for Enterprise Grid customers with a minimum commitment of 100 seats. Contact our [Sales team](mailto:sales@plane.so) for trials, exceptions to the seat cut-off, tailored pricing, and licensing info. +::: + +This guide walks you through deploying Plane Commercial in an airgapped Kubernetes environment using Helm charts and pre-packaged Docker images. + +## What you'll need + +Before starting, ensure you have: + +- Kubernetes cluster (v1.31 - v1.33) +- Helm 3.x installed +- `kubectl` configured to access your cluster +- `cert-manager` available in the cluster +- A valid and working ingress controller (nginx, traefik, etc) +- Required ports opened to access the application (80, 443) +- SMTP ports opened if using email intake (25, 465, 587) + +::: warning +While Kubernetes can run stateful services with persistent volumes, and Plane's Helm chart supports deploying PostgreSQL, MinIO, RabbitMQ, and Redis, we strongly recommend using external managed services for better reliability in backup/restore operations and disaster recovery. + +Consider these alternatives: + +- **MinIO**: Replace with AWS S3, Google Cloud Storage, or any S3-compatible service +- **Redis**: Replace with Valkey or a managed Redis service +- **PostgreSQL**: Use a managed PostgreSQL service +- **RabbitMQ**: Use a managed message queue service +- **OpenSearch**: Use a managed OpenSearch service + ::: + +## Install Plane + +1. **Download Plane Enterprise Helm chart** + + Get the Plane Enterprise Helm chart from the official release. Check for the latest version at [Artifact Hub](https://artifacthub.io/packages/helm/makeplane/plane-enterprise). + + ```bash + # Using wget + wget https://github.com/makeplane/helm-charts/releases/download/plane-enterprise-1.6.4/plane-enterprise-1.6.4.tgz + + # Using curl + curl -L -O https://github.com/makeplane/helm-charts/releases/download/plane-enterprise-1.6.4/plane-enterprise-1.6.4.tgz + ``` + +2. **Prepare Docker images for airgapped environment** + + Refer to [this document](/self-hosting/methods/clone-docker-images) to download the Docker images from the public repository to your internal repository. + + ::: info + This process will NOT download or clone these infrastructure images: + - `valkey:7.2.5-alpine` + - `postgres:15.7-alpine` + - `rabbitmq:3.13.6-management-alpine` + - `minio/minio:latest` + - `minio/mc:latest` + - `opensearchproject/opensearch:3.3.2` + + If you're using `local_setup: true` for any of these services, you'll need to pull and transfer these images separately. + ::: + +3. **Configure custom values file** + + a. Extract the default values from the Helm chart. + + ```bash + helm show values plane-enterprise-1.6.4.tgz > custom-values.yaml + ``` + + b. Update Docker image references + + Edit the `custom-values.yaml` file to point to your local or private registry images and configure important settings. + + **Basic configuration:** + + ```yaml + # Specify the Plane version + planeVersion: + + # Enable airgapped mode (REQUIRED) + airgapped: + enabled: true # Must be TRUE for airgapped installations + # If using custom root CA for S3 storage + s3Secrets: + - name: plane-s3-ca + key: s3-custom-ca.crt + - name: plane-s3-ca-2 + key: s3-custom-ca-2.crt + ``` + + **Service images:** + + ```yaml + services: + web: + image: /web-commercial + + api: + image: /backend-commercial + + space: + image: /space-commercial + + admin: + image: /admin-commercial + + live: + image: /live-commercial + + monitor: + image: /monitor-commercial + + email_service: + enabled: true + image: /email-commercial + + silo: + enabled: true + image: /silo-commercial + + iframely: + enabled: true + image: /iframely:v1.2.0 + ``` + + **Infrastructure services:** + + Configure whether to use local (in-cluster) or external services: + + ```yaml + services: + # Database and infrastructure images + redis: + local_setup: true # Set to false if using external service + image: valkey/valkey:7.2.11-alpine + + postgres: + local_setup: true # Set to false if using external service + image: postgres:15.7-alpine + + rabbitmq: + local_setup: true # Set to false if using external service + image: rabbitmq:3.13.6-management-alpine + external_rabbitmq_url: "" # Required only if using remote RabbitMQ + + minio: + local_setup: true # Set to false if using external service + image: minio/minio:latest + image_mc: minio/mc:latest + ``` + + **Environment variables:** + + ```yaml + env: + storageClass: "" + remote_redis_url: "" # Required only if using remote Redis + pgdb_remote_url: "" # Required only if using remote PostgreSQL + # Required if MinIO local_setup is false + aws_access_key: "" + aws_secret_access_key: "" + aws_region: "" + aws_s3_endpoint_url: "" + ``` + + c. **Configure integrations and importers** + + To set up integrations with external systems like Slack, GitHub, and GitLab, configure these values in `custom-values.yaml`: + + ```yaml + services: + silo: + enabled: true + connectors: + slack: + enabled: false + client_id: "" + client_secret: "" + github: + enabled: false + client_id: "" + client_secret: "" + app_name: "" + app_id: "" + private_key: "" + gitlab: + enabled: false + client_id: "" + client_secret: "" + + env: + silo_envs: + batch_size: 100 + mq_prefetch_count: 1 + request_interval: 400 + hmac_secret_key: "" + aes_secret_key: "dsOdt7YrvxsTIFJ37pOaEVvLxN8KGBCr" + ``` + + d. **Configure intake email** + + The email intake feature in Plane lets you capture incoming emails. Before or after setting up the application, configure DNS settings following [this guide](https://developers.plane.so/self-hosting/govern/configure-dns-email-service). + + Add these required values to `custom-values.yaml`: + + ```yaml + ingress: + enabled: true + ingressClass: 'nginx' # Or as per your cluster + ingress_annotations: {} + + ssl: + tls_secret_name: '' # If you have a custom TLS secret name + # If you want to use Let's Encrypt, set createIssuer and generateCerts to true + createIssuer: false + issuer: http # Allowed: cloudflare, digitalocean, http + token: '' # Not required for http + server: https://acme-v02.api.letsencrypt.org/directory + email: plane@example.com # A valid email address + generateCerts: true + + services: + email_service: + enabled: true + replicas: 1 + memoryLimit: 1000Mi + cpuLimit: 500m + memoryRequest: 50Mi + cpuRequest: 50m + image: /email-commercial: + pullPolicy: Always + nodeSelector: {} + tolerations: [] + affinity: {} + labels: {} + annotations: {} + + env: + email_service_envs: + smtp_domain: '' + ``` + +4. **Install or upgrade with custom values** + + Install Plane Enterprise using your customized values file: + + ```bash + helm upgrade plane-app plane-enterprise-1.6.4.tgz \ + --install \ + --create-namespace \ + --namespace plane \ + -f custom-values.yaml \ + --timeout 10m \ + --wait \ + --wait-for-jobs + ``` + +5. **Verify installation** + + Check that all components are running: + + ```bash + # Check all pods + kubectl get pods -n plane + + # Check services + kubectl get services -n plane + + # Check ingress + kubectl get ingress -n plane + + # Check persistent volumes + kubectl get pv,pvc -n plane + + # Get the ingress URL + kubectl get ingress -n plane -o wide + ``` + + You now have Plane running in your air-gapped environment. If you run into any issues, check the logs using the commands above, or reach out to our support team for assistance. + +6. [Activate your license key](/self-hosting/manage/manage-licenses/activate-airgapped). + +## Additional configuration + +For more advanced Plane configuration options, refer to the [Kubernetes documentation](https://developers.plane.so/self-hosting/methods/kubernetes#configuration-settings). diff --git a/apps/developer-docs/docs/self-hosting/methods/airgapped-edition.md b/apps/developer-docs/docs/self-hosting/methods/airgapped-edition.md new file mode 100644 index 00000000..2a06b19d --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/airgapped-edition.md @@ -0,0 +1,196 @@ +--- +title: Deploy Plane with Airgapped Docker +description: Deploy Plane in airgapped environment without internet access. Complete guide for offline Plane installation. +keywords: plane airgapped, offline deployment, air-gapped docker, plane offline install, disconnected environment, self-hosting +--- + +# Deploy Plane Airgapped on Docker + +:::info +Airgapped deployments are available exclusively for Enterprise Grid customers with a minimum commitment of 100 seats. Contact our [Sales team](mailto:sales@plane.so) for trials, exceptions to the seat cut-off, tailored pricing, and licensing info. +::: + +This guide walks you through deploying Plane Commercial in an airgapped Docker environment using Docker Compose and pre-configured images from your private registry. + +## Prerequisites + +Before starting, ensure you have: + +- Docker (version 24 or later) installed and running +- Docker Compose Plugin installed (you should be able to run `docker compose` or `docker-compose`) +- Access to a private Docker registry containing Plane images +- Required ports opened to access the application (80, 443) + +:::warning +While Docker can run stateful services with persistent volumes, we strongly recommend using external managed services for better reliability in backup/restore operations and disaster recovery. + +Consider these alternatives: + +- **MinIO**: Replace with AWS S3, Google Cloud Storage, or any S3-compatible service +- **Redis**: Replace with Valkey or a managed Redis service +- **PostgreSQL**: Use a managed PostgreSQL service +- **RabbitMQ**: Use a managed message queue service +- **OpenSearch**: Use a managed OpenSearch service + ::: + +## Install Plane + +1. **Prepare Docker images for airgapped environment** + + Refer to [this document](/self-hosting/methods/clone-docker-images) to download the Docker images from the Plane artifact registry to your internal registry. + + :::info + This process will NOT download or clone these infrastructure images: + - `valkey/valkey:7.2.11-alpine` + - `postgres:15.7-alpine` + - `rabbitmq:3.13.6-management-alpine` + - `minio/minio:latest` + - `minio/mc:latest` + - `opensearchproject/opensearch:3.3.2` + + If you're using local infrastructure services, you'll need to pull and transfer these images separately. + ::: + +2. **Download Docker Compose configuration** + + ```bash + # Download docker-compose.yml + curl -fsSL https://prime.plane.so/releases//docker-compose-airgapped.yml -o docker-compose.yml + + # Download environment template + curl -fsSL https://prime.plane.so/releases//variables-airgapped.env -o plane.env + ``` + +3. **Configure environment variables** + + Edit the `plane.env` file to configure your deployment: + + ```bash + # Generate a unique machine signature + export MACHINE_SIGNATURE=$(uuidgen) + + # Set your domain + export DOMAIN_NAME=plane.yourcompany.com + export WEB_URL=https://plane.yourcompany.com + export CORS_ALLOWED_ORIGINS=https://plane.yourcompany.com + ``` + + **Update image references** in `docker-compose.yml` to point to your private registry: + + ```yaml + services: + web: + image: your-registry.io/plane/web-commercial:${APP_RELEASE_VERSION} + + api: + image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} + + worker: + image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} + + beat-worker: + image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} + + migrator: + image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} + + importer-worker: + image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} + + automation-consumer: + image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} + + webhook-consumer: + image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} + + outbox-poller: + image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} + + space: + image: your-registry.io/plane/space-commercial:${APP_RELEASE_VERSION} + + admin: + image: your-registry.io/plane/admin-commercial:${APP_RELEASE_VERSION} + + live: + image: your-registry.io/plane/live-commercial:${APP_RELEASE_VERSION} + + live-exporter: + image: your-registry.io/plane/live-commercial:${APP_RELEASE_VERSION} + + monitor: + image: your-registry.io/plane/monitor-commercial:${APP_RELEASE_VERSION} + + silo: + image: your-registry.io/plane/silo-commercial:${APP_RELEASE_VERSION} + + email: + image: your-registry.io/plane/email-commercial:${APP_RELEASE_VERSION} + + pi-api: + image: your-registry.io/plane/plane-pi-commercial:${APP_RELEASE_VERSION} + + pi-beat: + image: your-registry.io/plane/plane-pi-commercial:${APP_RELEASE_VERSION} + + pi-worker: + image: your-registry.io/plane/plane-pi-commercial:${APP_RELEASE_VERSION} + + pi-migrator: + image: your-registry.io/plane/plane-pi-commercial:${APP_RELEASE_VERSION} + + runner: + image: your-registry.io/plane/node-runner-commercial:${APP_RELEASE_VERSION} + + iframely: + image: your-registry.io/plane/iframely:v2.5.3 + + proxy: + image: your-registry.io/plane/proxy-commercial:${APP_RELEASE_VERSION} + ``` + + **Infrastructure services** (if using local setup): + + ```yaml + services: + redis: + image: valkey/valkey:7.2.11-alpine + + postgres: + image: postgres:15.7-alpine + + rabbitmq: + image: rabbitmq:3.13.6-management-alpine + + minio: + image: minio/minio:latest + ``` + +## Start Plane + +1. Start the services: + + ```bash + docker compose --env-file plane.env up -d + ``` + +2. Watch the logs to make sure everything starts properly. + - To monitor the database migration process: + + ```bash + docker compose logs -f migrator + ``` + + - To monitor the API service startup: + + ```bash + docker compose logs -f api + ``` + + The API is healthy when you see: `api-1 listening at` + + Once all services are running smoothly, you can access Plane by opening your browser and going to the domain you configured. + + You now have Plane running in your air-gapped environment. If you run into any issues, check the logs using the commands above, or reach out to our support team for assistance. + +3. [Activate your license key](/self-hosting/manage/manage-licenses/activate-airgapped) diff --git a/apps/developer-docs/docs/self-hosting/methods/airgapped-requirements.md b/apps/developer-docs/docs/self-hosting/methods/airgapped-requirements.md new file mode 100644 index 00000000..b50bbdc3 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/airgapped-requirements.md @@ -0,0 +1,122 @@ +--- +title: Airgapped deployment architecture +description: System requirements and architecture overview for Plane airgapped deployments. Hardware specs, network topology, and prerequisites for offline installations. +keywords: plane airgapped requirements, air-gapped architecture, offline prerequisites, system requirements, airgapped planning, self-hosting +--- + +# Airgapped deployment architecture + +::: info +Airgapped deployments are available exclusively for Enterprise Grid customers with a minimum commitment of 100 seats. Contact our [Sales team](mailto:sales@plane.so) for trials, exceptions to the seat cut-off, tailored pricing, and licensing info. +::: + +This document explains Plane's architecture and specific requirements for airgapped deployments. Review this before beginning your airgapped installation on [Docker](/self-hosting/methods/airgapped-edition) or [Kubernetes](/self-hosting/methods/airgapped-edition-kubernetes). + +## What is an airgapped deployment? + +An airgapped deployment operates in a completely isolated network environment with no external internet connectivity. This isolation is common in highly regulated industries, government facilities, and organizations with strict security requirements. + +Plane supports fully airgapped deployments where all components - application services, databases, storage, and integrations - operate entirely within your isolated network perimeter. + +## Deployment methods + +Plane's Airgapped Edition can be deployed using Docker or Kubernetes. Choose the method that best fits your infrastructure. + + + + Deploy on a single machine using Docker. + + + Deploy on a Kubernetes cluster using Helm charts. + + + +## Airgapped cluster architecture + +Here's how Plane operates in an airgapped environment with internal enterprise applications: + +![Airgapped cluster architecture](/images/airgapped/airgapped-cluster.webp#hero) + +This diagram illustrates a critical principle: **all OAuth flows and API communication remain internal to the airgapped cluster**. When integrating with self-hosted GitHub Enterprise, GitLab, or other internal services, the entire authentication and data exchange happens within your isolated network — no internet access required. + +For a detailed breakdown of Plane's services and infrastructure dependencies, see [Plane self-hosted architecture](/self-hosting/plane-architecture). + +**Critical guarantees for airgapped environments** + +- **No telemetry** + Plane does not send application data, usage metrics, or telemetry outside the cluster. No analytics, crash reports, or usage statistics leave your network. + +- **Offline licensing** + License validation happens through uploaded license files downloaded from the Prime portal. No internet connection required after initial license file transfer. + +- **Zero external dependencies** + After initial image import, no external network connectivity is required for Plane to operate. All features work entirely within your isolated environment. + +- **Internal-only communication** + All service-to-service communication stays within your cluster. Services never attempt to reach external APIs, CDNs, or third-party services. + +### How integrations stay internal + +The airgapped cluster diagram above shows the complete data flow. Key points: + +- **OAuth providers** - Your internal GitHub Enterprise or GitLab instance acts as the OAuth provider +- **Authorization endpoints** - All OAuth URLs point to internal systems, never external SaaS services +- **API communication** - Plane makes API calls only to your internal instances +- **Webhook delivery** - Internal systems send webhooks to Plane's internal endpoints +- **No SaaS fallback** - Plane never attempts to reach github.com, gitlab.com, or slack.com APIs + +This architecture ensures complete network isolation while maintaining full integration functionality. + +--- + +## Kubernetes-specific requirements + +### Base environment + +Deploying airgapped Plane via Kubernetes requires preparing all dependencies to operate without any external network access. + +#### Container images and artifacts + +- Maintain an internal OCI or container registry to host all Plane service images +- Prepare a controlled process to pull, verify, and mirror Plane container images and Helm charts from an online staging environment into the airgapped registry + +#### Kubernetes environment + +**Supported versions:** Kubernetes 1.31 – 1.33 + +**Required components:** + +- IngressClass configured +- StorageClass available +- cert-manager configured with an internal CA + +**Node requirements:** + +- Ensure node OS dependencies and container runtime packages are available from mirrored package repositories like apt, yum, or offline bundles + +### Scaling + +Horizontal scaling is handled via replica counts configurable in `values.yaml`. + +Plane avoids using StatefulSets where possible due to the complexity of scaling stateful workloads in Kubernetes. The `monitor` service uses a StatefulSet. + +**For airgapped clusters:** + +- Ensure metrics-server images are mirrored if using HPA +- If using node autoscaling, ensure node images are pre-loaded and registries accessible on bootstrap + +### Secrets management + +Plane supports using existing external secret stores, provided they are reachable within the airgapped environment: + +- AWS Secrets Manager for private VPC with no internet +- HashiCorp Vault +- Self-hosted Bitwarden +- Kubernetes Secrets +- SOPS, sealed-secrets, if preferred + +### Additional considerations + +- Ensure all secret providers can function without external network access +- cert-manager must use an internal certificate authority +- Keys and secret rotation policies should be part of the airgap operational procedures diff --git a/apps/developer-docs/docs/self-hosting/methods/clone-docker-images.md b/apps/developer-docs/docs/self-hosting/methods/clone-docker-images.md new file mode 100644 index 00000000..f08901cf --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/clone-docker-images.md @@ -0,0 +1,323 @@ +--- +title: Clone Docker images to your private registry +description: Mirror Plane Docker images to your private container registry. Pull, tag, and push images for airgapped or restricted network deployments. +keywords: plane docker images, private registry, container mirroring, docker pull, image cloning, airgapped docker, self-hosting +--- + +# Clone Docker images to your private registry + +::: info +**Part of airgapped deployment** +This guide is part of the airgapped deployment process. If you're setting up Plane in an airgapped environment, return to that guide after copying your images. +::: + +This guide shows you how to copy Docker images from the Plane artifact registry to your destination registry using the `crane` tool. + +## Prerequisites + +### Install crane + +Crane is a tool for interacting with remote container images and registries. Install it on a machine with internet access. + +**macOS:** + +```bash +brew install crane +``` + +**Linux:** + +```bash +# Download the latest release +VERSION=$(curl -s https://api.github.com/repos/google/go-containerregistry/releases/latest | grep '"tag_name"' | cut -d'"' -f4) +curl -sL "https://github.com/google/go-containerregistry/releases/download/${VERSION}/go-containerregistry_Linux_x86_64.tar.gz" | tar xz crane +sudo mv crane /usr/local/bin/ + +# Verify installation +crane version +``` + +**Windows (using WSL or Git Bash):** + +```bash +# Download and extract +curl -sL "https://github.com/google/go-containerregistry/releases/latest/download/go-containerregistry_Windows_x86_64.tar.gz" | tar xz crane.exe +``` + +## Plane images to copy + +The following Plane Commercial images need to be transferred to your private registry: + +``` +makeplane/admin-commercial:${APP_RELEASE_VERSION} +makeplane/web-commercial:${APP_RELEASE_VERSION} +makeplane/space-commercial:${APP_RELEASE_VERSION} +makeplane/live-commercial:${APP_RELEASE_VERSION} +makeplane/monitor-commercial:${APP_RELEASE_VERSION} +makeplane/backend-commercial:${APP_RELEASE_VERSION} +makeplane/iframely:v2.5.3 +makeplane/silo-commercial:${APP_RELEASE_VERSION} +makeplane/email-commercial:${APP_RELEASE_VERSION} +makeplane/plane-pi-commercial:${APP_RELEASE_VERSION} +makeplane/node-runner-commercial:${APP_RELEASE_VERSION} +makeplane/proxy-commercial:${APP_RELEASE_VERSION} +``` + +::: warning +**Infrastructure images not included:** The Plane artifact registry does not include infrastructure images. If you're using `local_setup: true` for any infrastructure services, you'll need to pull these separately from public registries: + +- `valkey/valkey:7.2.11-alpine` +- `postgres:15.7-alpine` +- `rabbitmq:3.13.6-management-alpine` +- `minio/minio:latest` +- `minio/mc:latest` + ::: + +## Configure environment variables + +Set your version and destination registry before copying images. + +```bash +# Set your Plane version +export APP_RELEASE_VERSION="v3.1.0" # Replace with your desired version + +# Set your destination registry +export DESTINATION_REGISTRY="your-registry.io/your-namespace" +``` + +**Example destination registry values:** + +```bash +# Docker Hub +export DESTINATION_REGISTRY="docker.io/yourcompany" + +# Google Container Registry +export DESTINATION_REGISTRY="gcr.io/your-project" + +# Private registry +export DESTINATION_REGISTRY="your-private-registry.com/plane" + +# AWS ECR +export DESTINATION_REGISTRY="123456789012.dkr.ecr.us-east-1.amazonaws.com/plane" + +# Azure Container Registry +export DESTINATION_REGISTRY="yourregistry.azurecr.io/plane" +``` + +## Authenticate to your destination registry + +Before copying images, authenticate crane to your destination registry. + +**Docker Hub:** + +```bash +crane auth login docker.io -u YOUR_USERNAME -p YOUR_PASSWORD +``` + +**Google Container Registry:** + +```bash +gcloud auth configure-docker +``` + +**AWS ECR:** + +```bash +aws ecr get-login-password --region REGION | crane auth login --username AWS --password-stdin AWS_ACCOUNT_ID.dkr.ecr.REGION.amazonaws.com +``` + +**Azure Container Registry:** + +```bash +az acr login --name YOUR_REGISTRY_NAME +``` + +**Harbor or other private registries:** + +```bash +crane auth login your-registry.com -u YOUR_USERNAME -p YOUR_PASSWORD +``` + +## Copy images to your registry + +You can copy images individually or use the provided script to copy all images at once. + +### Option 1: Copy individual images + +**Basic image copy:** + +```bash +crane copy \ + makeplane/backend-commercial:${APP_RELEASE_VERSION} \ + ${DESTINATION_REGISTRY}/backend-commercial:${APP_RELEASE_VERSION} +``` + +**Copy with specific platform (architecture):** + +```bash +crane copy \ + --platform linux/amd64 \ + makeplane/backend-commercial:${APP_RELEASE_VERSION} \ + ${DESTINATION_REGISTRY}/backend-commercial:${APP_RELEASE_VERSION} +``` + +**Verify source image before copying:** + +```bash +# Check if source image exists +crane manifest makeplane/backend-commercial:${APP_RELEASE_VERSION} + +# List all available tags +crane ls makeplane/backend-commercial +``` + +**Verify image after copying:** + +```bash +# Get image digest +crane digest ${DESTINATION_REGISTRY}/backend-commercial:${APP_RELEASE_VERSION} + +# Verify manifest +crane manifest ${DESTINATION_REGISTRY}/backend-commercial:${APP_RELEASE_VERSION} +``` + +### Option 2: Copy all images with a script + +Create a file named `copy-plane-images.sh`: + +```bash +#!/bin/bash + +set -e + +# Configuration +APP_RELEASE_VERSION="${APP_RELEASE_VERSION:-v3.1.0}" +DESTINATION_REGISTRY="${DESTINATION_REGISTRY}" + +if [ -z "$DESTINATION_REGISTRY" ]; then + echo "Error: DESTINATION_REGISTRY environment variable is not set" + echo "Example: export DESTINATION_REGISTRY='docker.io/yourcompany'" + exit 1 +fi + +# Source registry +SOURCE_REGISTRY="makeplane" + +# Image list +declare -a IMAGES=( + "admin-commercial:${APP_RELEASE_VERSION}" + "web-commercial:${APP_RELEASE_VERSION}" + "space-commercial:${APP_RELEASE_VERSION}" + "live-commercial:${APP_RELEASE_VERSION}" + "monitor-commercial:${APP_RELEASE_VERSION}" + "backend-commercial:${APP_RELEASE_VERSION}" + "iframely:v2.5.3" + "silo-commercial:${APP_RELEASE_VERSION}" + "email-commercial:${APP_RELEASE_VERSION}" + "plane-pi-commercial:${APP_RELEASE_VERSION}" + "node-runner-commercial:${APP_RELEASE_VERSION}" + "proxy-commercial:${APP_RELEASE_VERSION}" +) + +echo "Starting image copy process..." +echo "Source: ${SOURCE_REGISTRY}" +echo "Destination: ${DESTINATION_REGISTRY}" +echo "Version: ${APP_RELEASE_VERSION}" +echo "" + +# Copy each image +for IMAGE in "${IMAGES[@]}"; do + SOURCE="${SOURCE_REGISTRY}/${IMAGE}" + DESTINATION="${DESTINATION_REGISTRY}/${IMAGE}" + + echo "Copying: ${SOURCE} -> ${DESTINATION}" + crane copy "${SOURCE}" "${DESTINATION}" + + if [ $? -eq 0 ]; then + echo "✓ Successfully copied ${IMAGE}" + else + echo "✗ Failed to copy ${IMAGE}" + exit 1 + fi + echo "" +done + +echo "All images copied successfully!" +``` + +Make the script executable and run it: + +```bash +chmod +x copy-plane-images.sh +./copy-plane-images.sh +``` + +The script will copy all Plane images to your destination registry. Each image copy is verified, and the script exits if any copy fails. + +## Troubleshooting + +### Authentication issues + +**Error:** `unauthorized: authentication required` + +**Solution:** Re-authenticate to your destination registry: + +```bash +crane auth login your-destination-registry.com +``` + +Verify your credentials are correct and that you have push permissions to the registry. + +### Network timeouts + +**Error:** Large images timing out during transfer + +**Solution:** Crane handles retries automatically, but you can also: + +- Check your network connection stability +- Try copying during off-peak hours +- Use a machine with better network connectivity +- Copy images individually rather than using the batch script + +### Permission denied + +**Error:** `denied: requested access to the resource is denied` + +**Solution:** + +- Ensure you have push permissions to the destination registry +- Verify your authentication credentials are correct +- Check that the destination repository exists, or that you have permission to create it +- For organizational registries, confirm your account has the necessary roles + +### Image not found + +**Error:** `MANIFEST_UNKNOWN: manifest unknown` + +**Solution:** + +- Verify the source image exists: + +```bash + crane ls makeplane/backend-commercial +``` + +- Check that you're using the correct version tag +- Ensure `APP_RELEASE_VERSION` is set correctly +- Verify the image name is spelled correctly + +### Rate limiting + +**Error:** `429 Too Many Requests` + +**Solution:** + +- Wait a few minutes and retry +- Authenticate to increase rate limits +- For Docker Hub, ensure you're using an authenticated account (free accounts have higher limits than anonymous) +- Spread out image copies over time if hitting limits repeatedly + +## Additional resources + +- [Crane documentation](https://github.com/google/go-containerregistry/blob/main/cmd/crane/doc/crane.md) +- [Crane GitHub repository](https://github.com/google/go-containerregistry) diff --git a/apps/developer-docs/docs/self-hosting/methods/coolify.md b/apps/developer-docs/docs/self-hosting/methods/coolify.md new file mode 100644 index 00000000..19027cb4 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/coolify.md @@ -0,0 +1,52 @@ +--- +title: Deploy Plane with Coolify +description: Deploy Plane using Coolify, an open-source PaaS. One-click deployment with automatic SSL, domain configuration, and container management. +keywords: plane coolify, coolify deployment, plane paas, one-click deployment, plane hosting platform, self-hosting +--- + +# Deploy Plane with Coolify + +This guide shows you the steps to deploy a self-hosted instance of Plane using Coolify. + +## Install Plane + +### Prerequisites + +- Before you get started, make sure you have a Coolify environment set up and ready to go. +- Your setup should support either amd64 or arm64 architectures. + +### Procedure + +1. **Download the required deployment files** + +`coolify-compose.yml` – Defines Plane's services and dependencies. + +```bash +curl -fsSL https://prime.plane.so/releases//coolify-compose.yml -o coolify-compose.yml +``` + +::: warning +The `` value should be v1.8.2 or higher. +::: + +2. Create a new project in Coolify. + +3. Add a new resource. + +4. Select **Docker Compose Empty** as the deployment method. + +5. Copy and paste the contents of the `coolify-compose.yml` file into the editor. + +6. Configure external DB, Redis, RabbitMQ and any other required environment variables in the UI. + ::: warning + When self-hosting Plane for production use, it is strongly recommended to configure external database and storage. This ensures that your data remains secure and accessible even if the local machine crashes or encounters hardware issues. Relying solely on local storage for these components increases the risk of data loss and service disruption. + ::: + +- `DATABASE_URL` – Connection string for your external database. +- `REDIS_URL` – Connection string for your external Redis instance. +- `AMQP_URL` – Connection string for your external RabbitMQ server. + +7. Deploy to launch your Plane instance. + Once the deployment is complete, your Plane instance should be accessible on the configured domain. + +8. If you've purchased a paid plan, [activate your license key](/self-hosting/manage/manage-licenses/activate-pro-and-business#activate-your-license) to unlock premium features. diff --git a/apps/developer-docs/docs/self-hosting/methods/docker-aio.md b/apps/developer-docs/docs/self-hosting/methods/docker-aio.md new file mode 100644 index 00000000..d99debfb --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/docker-aio.md @@ -0,0 +1,213 @@ +--- +title: Docker AIO (All-in-One) +description: Deploy Plane with Docker All-in-One (AIO) setup. Quick installation guide for running Plane in a single Docker container. +keywords: plane docker aio, all-in-one container, single container deployment, quick install, plane docker setup, self-hosting +--- + +# Docker AIO (All-in-One) + +The Plane Commercial All-in-One (AIO) Docker image packages all Plane services into a single container, making it the fastest way to get Plane running. + +## What's included + +Your single AIO container includes all these services running together: + +- **Web App** - The main Plane web interface you'll use +- **Space** - Public project spaces for external collaboration +- **Admin** - Administrative interface +- **API Server** - Backend API +- **Live Server** - Real-time collaboration features +- **Silo** - Integration services +- **Monitor** - Feature flags and payments +- **Email Server** - SMTP server for notifications +- **Proxy** (Port 80, 20025, 20465, 20587) - Caddy reverse proxy +- **Worker and Beat Worker** - Background task processing + +### Port Mapping + +The following ports are exposed: + +- `80`: Main web interface (HTTP) +- `443`: HTTPS (if SSL configured) +- `20025`: SMTP port 25 +- `20465`: SMTP port 465 (SSL/TLS) +- `20587`: SMTP port 587 (STARTTLS) + +## Prerequisites + +- [Docker](https://docs.docker.com/engine/) +- Set up these external services: + - _PostgreSQL_ + For data storage + - _Redis_ + For caching and session management + - _RabbitMQ_ + For message queuing + - _S3-compatible storage_ + For file uploads (AWS S3 or MinIO) + +## Install Plane + +1. Download the image with: + + ```bash + docker pull makeplane/plane-aio-commercial:stable + ``` + +2. Run the following command to deploy the Plane AIO container. Make sure to replace all placeholder values (e.g., `your-domain.com`, `user:pass`) with your actual configuration. + + ::: warning + All environment variables are required for the container to function correctly. + ::: + + ```bash + docker run --name plane-aio --rm -it \ + -p 80:80 \ + -p 20025:20025 \ + -p 20465:20465 \ + -p 20587:20587 \ + -e DOMAIN_NAME=your-domain.com \ + -e DATABASE_URL=postgresql://user:pass@host:port/database \ + -e REDIS_URL=redis://host:port \ + -e AMQP_URL=amqp://user:pass@host:port/vhost \ + -e AWS_REGION=us-east-1 \ + -e AWS_ACCESS_KEY_ID=your-access-key \ + -e AWS_SECRET_ACCESS_KEY=your-secret-key \ + -e AWS_S3_BUCKET_NAME=your-bucket \ + makeplane/plane-aio-commercial:stable + ``` + + If you're running on an IP address, use this example: + + ```bash + MYIP=192.168.68.169 + docker run --name myaio --rm -it \ + -p 80:80 \ + -p 20025:20025 \ + -p 20465:20465 \ + -p 20587:20587 \ + -e DOMAIN_NAME=${MYIP} \ + -e DATABASE_URL=postgresql://plane:plane@${MYIP}:15432/plane \ + -e REDIS_URL=redis://${MYIP}:16379 \ + -e AMQP_URL=amqp://plane:plane@${MYIP}:15673/plane \ + -e AWS_REGION=us-east-1 \ + -e AWS_ACCESS_KEY_ID= \ + -e AWS_SECRET_ACCESS_KEY= \ + -e AWS_S3_BUCKET_NAME=plane-app \ + -e AWS_S3_ENDPOINT_URL=http://${MYIP}:19000 \ + -e FILE_SIZE_LIMIT=10485760 \ + makeplane/plane-aio-commercial:stable + ``` + +3. Once it's running, you can access the Plane application on the domain you provided during the deployment. + +4. If you've purchased a paid plan, [activate your license key](/self-hosting/manage/manage-licenses/activate-pro-and-business#activate-your-license) to unlock premium features. + +## Volume mounts + +### Recommended persistent volumes + +```bash +-v /path/to/logs:/app/logs \ +-v /path/to/data:/app/data +``` + +### Workspace license DB + +```bash +-v /path/to/monitordb:/app/monitor +``` + +### SSL certificate support + +For HTTPS support, mount certificates: + +```bash +-v /path/to/certs:/app/email/tls +``` + +## Environment variables (optional) + +### Network and Protocol + +- `SITE_ADDRESS`: Server bind address (default: `:80`) +- `APP_PROTOCOL`: Protocol to use (`http` or `https`, default: `http`) + +### Email configuration + +- `INTAKE_EMAIL_DOMAIN`: Domain for intake emails (default: `intake.`) +- `LISTEN_SMTP_PORT_25`: SMTP port 25 mapping (default: `20025`) +- `LISTEN_SMTP_PORT_465`: SMTP port 465 mapping (default: `20465`) +- `LISTEN_SMTP_PORT_587`: SMTP port 587 mapping (default: `20587`) +- `SMTP_DOMAIN`: SMTP server domain (default: `0.0.0.0`) +- `TLS_CERT_PATH`: Path to TLS certificate file (optional) +- `TLS_PRIV_KEY_PATH`: Path to TLS private key file (optional) + +### Security and secrets + +- `MACHINE_SIGNATURE`: Unique machine identifier (auto-generated if not provided) +- `SECRET_KEY`: Django secret key (default provided) +- `SILO_HMAC_SECRET_KEY`: Silo HMAC secret (default provided) +- `AES_SECRET_KEY`: AES encryption key (default provided) +- `LIVE_SERVER_SECRET_KEY`: Live server secret (default provided) + +### File handling + +- `FILE_SIZE_LIMIT`: Maximum file upload size in bytes (default: `5242880` = 5MB) + +### Integration callbacks + +- `INTEGRATION_CALLBACK_BASE_URL`: Base URL for OAuth callbacks + +### API configuration + +- `API_KEY_RATE_LIMIT`: API key rate limit (default: `60/minute`) + +### Third-party integrations + +- `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`: GitHub integration +- `GITHUB_APP_NAME`, `GITHUB_APP_ID`, `GITHUB_PRIVATE_KEY`: GitHub App integration +- `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET`: Slack integration +- `GITLAB_CLIENT_ID`, `GITLAB_CLIENT_SECRET`: GitLab integration + +## Build the image + +To build the AIO image yourself: + +```bash +cd deploy/aio/commercial +./build.sh --release=v1.11.1 +``` + +Available build options: + +- `--release`: Plane version to build (required) +- `--image-name`: Custom image name (default: `plane-aio-commercial`) + +## Troubleshoot + +The container will validate required environment variables on startup and display helpful error messages if any are missing. + +### Logs + +All service logs are available in `/app/logs/`: + +- Access logs: `/app/logs/access/` +- Error logs: `/app/logs/error/` + +### Health checks + +The container runs multiple services managed by Supervisor. Check service status: + +```bash +docker exec -it supervisorctl status +``` + +## Production considerations + +- Use proper SSL certificates for HTTPS +- Configure proper backup strategies for data +- Monitor resource usage and scale accordingly +- Use external load balancer for high availability +- Regularly update to latest versions +- Secure your environment variables and secrets diff --git a/apps/developer-docs/docs/self-hosting/methods/docker-compose.md b/apps/developer-docs/docs/self-hosting/methods/docker-compose.md new file mode 100644 index 00000000..766a3b5a --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/docker-compose.md @@ -0,0 +1,143 @@ +--- +title: Docker Compose +description: Install Plane using Docker Compose. Step-by-step guide for deploying Plane with Docker on your server with all required services. +keywords: plane docker compose, docker deployment, container setup, docker install plane, plane containers, self-hosting +--- + +# Docker Compose + +This guide shows you the steps to deploy a self-hosted instance of Plane using Docker. + +::: tip +If you want to upgrade from Community to the Commercial edition, see [Upgrade to Commercial Edition](/self-hosting/upgrade-from-community). +::: + +## Install Plane + +Plane Pro and Plane Business are enabled on this edition, so the Free plan on this edition is easier to trial our paid plans from. + +### Prerequisites + +- **CPU:** 2 cores (x64/AMD64 or AArch64/ARM64) +- **RAM:** 4GB (8GB recommended for production) +- **OS:** Ubuntu, Debian, CentOS, Amazon Linux 2 or 2023, macOS, Windows with WSL2 + +::: info +Ensure you're using the **latest version of Docker Compose**. Check your Docker Compose version with `docker-compose --version` and update if needed. +::: + +### Procedure + +1. `ssh` into your machine as the root user (or user with sudo access) per the norms of your hosting provider. +2. Run the command below: + ```bash + curl -fsSL https://prime.plane.so/install/ | sh - + ``` +3. Follow the instructions on the terminal. Hit `Enter` or `Return` to continue. +4. Enter the domain name where you will access the Plane app in the format `domain.tld` or `subdomain.domain.tld`. +5. Choose one of the options below: + - **Express**: Plane installs with the default configurations. + - **Advanced**: You can customize the database, Redis, storage and other settings. + ::: warning + When self-hosting Plane for production use, it is strongly recommended to configure [external database and storage](/self-hosting/govern/database-and-storage). This ensures that your data remains secure and accessible even if the local machine crashes or encounters hardware issues. Relying solely on local storage for these components increases the risk of data loss and service disruption. + ::: +6. The installation will take a few minutes to complete and you will see the message **Plane has successfully installed**. You can access the Plane application on the domain you provided during the installation. +7. If you've purchased a paid plan, [activate your license key](/self-hosting/manage/manage-licenses/activate-pro-and-business#activate-your-license) to unlock premium features. + +::: details Install Community Edition +The Commercial edition comes with a free plan and the flexibility to upgrade to a paid plan at any point. If you still want to install the Community edition, follow the steps below: + +#### Prerequisites + +- Docker installed and running. Choose one of the following options: + - **Option 1** + Create an EC2 machine on AWS. It must of minimum **t3.medium/t3a.medium**. Run the below command to install docker engine. + ```bash + curl -fsSL https://get.docker.com | sh - + ``` + - **Option 2** + Install [Docker Desktop](https://www.docker.com/products/docker-desktop/). +- OS with bash scripting enabled (Ubuntu, Linux AMI, macOS). Windows systems need to have [gitbash](https://git-scm.com/download/win). +- User context used must have access to docker services. In most cases, use `sudo su` to switch as root user. +- Use the terminal (or gitbash) window to run all the future steps. + +#### Installation + +1. Create a folder named `plane-selfhost` on your machine for deployment and data storage. + ```bash + mkdir plane-selfhost + ``` +2. Navigate to this folder using the cd command. + ```bash + cd plane-selfhost + ``` +3. Download the latest stable release. + ```bash + curl -fsSL -o setup.sh https://github.com/makeplane/plane/releases/latest/download/setup.sh + ``` +4. Make the file executable. + ```bash + chmod +x setup.sh + ``` +5. Run the following command: + ```bash + ./setup.sh + ``` + This will prompt you with the below options. + ```bash + Select a Action you want to perform: + 1) Install (arm64) + 2) Start + 3) Stop + 4) Restart + 5) Upgrade + 6) View Logs + 7) Backup Data + 8) Exit + Action [2]: 1 + ``` +6. Enter `1` as input. + This will create a folder `plane-app` or `plane-app-preview` (in case of preview deployment) and will download the `docker-compose.yaml` and `plane.env` files. +7. Enter `8` to exit. +8. Set up the environment variables. You can use any text editor to edit this file. Below are the most importants keys you must refer to: + - `LISTEN_HTTP_PORT`: This is set to `80` by default. Make sure the port you choose to use is not preoccupied. For example, `LISTEN_HTTP_PORT=8080` + - `LISTEN_HTTPS_PORT`: This is set to `443` by default. Make sure the port you choose to use is not preoccupied. For example, `LISTEN_HTTPS_PORT=4430` + - `WEB_URL`: This is set to `http://localhost` by default. Change this to the FQDN you plan to use along with LISTEN_HTTP_PORT. For example, `https://plane.example.com:8080` or `http://[IP-ADDRESS]:8080`. + - `CORS_ALLOWED_ORIGINS`: This is set to `http://localhost` by default. Change this to the FQDN you plan to use along with LISTEN_HTTP_PORT. For example, `https://plane.example.com:8080` or `http://[IP-ADDRESS]:8080`. +9. Run the following command to continue with the setup. + ```bash + ./setup.sh + ``` +10. Enter `2` as input to start the services. + You will something like this: + ![Downloading docker images](/images/docker-compose/download-docker.png) + Be patient as it might take some time based on your download speed and system configuration. If all goes well, you must see something like this: + ![Downloading completed](/images/docker-compose/download-complete.png) + This is the confirmation that all images were downloaded and the services are up and running. + +You have successfully self-hosted the Plane instance. Access the application by going to IP or domain you have configured it on. For example, `https://plane.example.com:8080` or `http://[IP-ADDRESS]:8080`. + +##### Stop server + +In case you want to make changes to the environment variables in the `plane.env` file, we recommend that you stop the services before doing that. + +Run the `./setup.sh` command. Enter `3` to stop the services. + +If all goes well, you will see something like this: + +![Stop Services](/images/docker-compose/stopped-docker.png) + +##### Restart server + +In case you want to make changes to `plane.env` variables without stopping the server or noticed some abnormalities in services, you can restart the services. + +Run the `./setup.sh` command. Enter `4` to restart the services. + +If all goes well, you will see something like this: +![Restart Services](/images/docker-compose/restart-docker.png) +::: + +## Troubleshoot + +- [Error during Docker Compose execution](/self-hosting/troubleshoot/installation-errors#error-during-docker-compose-execution) +- [Migrator container exited](/self-hosting/troubleshoot/installation-errors#migrator-container-exited) diff --git a/apps/developer-docs/docs/self-hosting/methods/docker-swarm.md b/apps/developer-docs/docs/self-hosting/methods/docker-swarm.md new file mode 100644 index 00000000..46b24d2a --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/docker-swarm.md @@ -0,0 +1,69 @@ +--- +title: Deploy Plane with Docker Swarm +description: Deploy Plane on Docker Swarm cluster. Guide for running Plane in a distributed Docker Swarm environment with high availability. +keywords: plane docker swarm, swarm deployment, distributed containers, high availability, plane cluster, docker orchestration, self-hosting +--- + +# Deploy Plane with Docker Swarm + +This guide shows you the steps to deploy a self-hosted instance of the Plane Commercial Edition using Docker Swarm. + +## Install Plane + +### Prerequisites + +- Before you get started, make sure you have a Docker Swarm environment set up and ready to go. +- Your setup should support either amd64 or arm64 architectures. + +### Procedure + +1. **Download the required deployment files** + - `swarm-compose.yml` – Defines Plane's services and dependencies. + + ```bash + curl -fsSL https://prime.plane.so/releases//swarm-compose.yml -o swarm-compose.yml + ``` + + - `variables.env` – Stores environment variables for your deployment. + + ```bash + curl -fsSL https://prime.plane.so/releases//variables.env -o plane.env + ``` + + ::: warning + The `` value should be v1.8.3 or higher. + ::: + +2. **Configure environment variables** + Before deploying, edit the `variables.env` file in your preferred text editor and update the following values: + - `DOMAIN_NAME` – (required) Your application's domain name. + - `SITE_ADDRESS` – (required) The full domain name (FQDN) of your instance. + - `MACHINE_SIGNATURE` – (required) A unique identifier for your machine. You can generate this by running below code in terminal: + ```sh + sed -i 's/MACHINE_SIGNATURE=.*/MACHINE_SIGNATURE='$(openssl rand -hex 16)'/' plane.env + ``` + - `CERT_EMAIL` – (optional) Email address for SSL certificate generation (only needed if you're setting up HTTPS). + +3. **Configure external DB, Redis, and RabbitMQ** + ::: warning + When self-hosting Plane for production use, it is strongly recommended to configure external database and storage. This ensures that your data remains secure and accessible even if the local machine crashes or encounters hardware issues. Relying solely on local storage for these components increases the risk of data loss and service disruption. + ::: + - `DATABASE_URL` – Connection string for your external database. + - `REDIS_URL` – Connection string for your external Redis instance. + - `AMQP_URL` – Connection string for your external RabbitMQ server. + +4. **Load the environment variables** + + ```bash + set -o allexport; source ; set +o allexport; + ``` + +5. **Deploy the stack** + + ```bash + docker stack deploy -c plane + ``` + + That's it! This will deploy Plane as a Swarm stack, and your instance should be accessible on your configured domain. + +6. If you've purchased a paid plan, [activate your license key](/self-hosting/manage/manage-licenses/activate-pro-and-business#activate-your-license) to unlock premium features. diff --git a/apps/developer-docs/docs/self-hosting/methods/download-config.md b/apps/developer-docs/docs/self-hosting/methods/download-config.md new file mode 100644 index 00000000..55e610e1 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/download-config.md @@ -0,0 +1,73 @@ +--- +title: Download Docker config files +description: Download docker-compose.yml and variables.env files for a specific Plane release as a zip archive. +keywords: plane, self-hosting, setup, docker compose, config download, version config, airgapped, variables.env +--- + +# Download Docker config files + +If you're running a custom Docker setup and don't use `prime-cli`, you can download the `docker-compose.yml` and `variables.env` files for any Plane release directly. + +## Endpoint + +```bash +curl "https://prime.plane.so/api/v2/setup/?version=&airgapped=&platform=" -o plane.zip +``` + +**Authentication:** None required (public endpoint) + +## Parameters + +| Parameter | Required | Default | Description | +| ----------- | -------- | ------- | ---------------------------------------------------------------------------- | +| `version` | Yes | — | Release tag (e.g., `v2.6.3`) | +| `airgapped` | No | `false` | Set to `true` for airgapped compose files | +| `platform` | No | `amd64` | Target architecture: `amd64` or `arm64`. Only applies when `airgapped=true`. | + +## What's in the zip + +**Standard download** + +- `docker-compose.yml` +- `variables.env` + +**Airgapped download** + +- `airgapped-docker-compose-{platform}.yml` +- `variables.env` + +## Quick download + +**Standard setup** + +```bash +curl "https://prime.plane.so/api/v2/setup/?version=v2.6.3" -o plane.zip +unzip plane.zip +``` + +**Airgapped setup (AMD64)** + +```bash +curl "https://prime.plane.so/api/v2/setup/?version=v2.6.3&airgapped=true" -o plane.zip +unzip plane.zip +``` + +**Airgapped setup (ARM64)** + +```bash +curl "https://prime.plane.so/api/v2/setup/?version=v2.6.3&airgapped=true&platform=arm64" -o plane.zip +unzip plane.zip +``` + +Replace `v2.6.3` with the version you need. See the [releases page](https://plane.so/changelog?category=self-hosted) for available versions. + +### Error responses + +| Status | Cause | Response | +| ------ | ----------------------------------- | ------------------------------------------------------- | +| 400 | Missing `version` parameter | `{"error": "version query parameter is required"}` | +| 400 | Invalid `platform` value | `{"error": "platform must be amd64 or arm64"}` | +| 400 | Server missing GitHub configuration | `{"error": "missing required settings"}` | +| 404 | Release tag not found | `{"error": "release not found"}` | +| 404 | Config files missing from release | `{"error": "assets not found in release: "}` | +| 500 | GitHub API failure | `{"error": "Failed to fetch release information"}` | diff --git a/apps/developer-docs/docs/self-hosting/methods/fips-deployment.md b/apps/developer-docs/docs/self-hosting/methods/fips-deployment.md new file mode 100644 index 00000000..f374714d --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/fips-deployment.md @@ -0,0 +1,212 @@ +--- +title: FIPS deployment +description: Deploy the FIPS variant of Plane Enterprise on a FIPS-enforcing host, including prerequisites, image list, and verification. +keywords: plane fips, fips 140-3 deployment, plane commercial fips, govcloud plane, federal self-hosting, fips enabled containers +head: + - - meta + - name: robots + content: noindex, nofollow +--- + +# FIPS deployment + +Plane publishes a FIPS variant of every application image alongside the standard set. +These images are built on Red Hat UBI 10, apply the system-wide FIPS cryptographic policy, and run +their cryptography against FIPS-validated modules (Red Hat's OpenSSL FIPS provider for the Python +and static services; the Go FIPS 140-3 module for the Go services). They are intended for +deployments that must meet FIPS 140-3 expectations, such as US Federal or GovCloud environments. + +::: warning **The single most important prerequisite** +FIPS mode is a property of the **host**, not of the image. A FIPS image on a non-FIPS host starts cleanly and looks identical from the inside while providing none of the guarantees. Read [Host prerequisite](#host-prerequisite) first. +::: + +## Images + +The FIPS images use the same names as the standard `-commercial` images with a `-fips` suffix, in the `makeplane` Docker Hub organization: + +| Service | Image | +| ------------- | --------------------------------------- | +| Backend / API | `makeplane/backend-commercial-fips` | +| Web | `makeplane/web-commercial-fips` | +| Admin | `makeplane/admin-commercial-fips` | +| Space | `makeplane/space-commercial-fips` | +| Live | `makeplane/live-commercial-fips` | +| Silo | `makeplane/silo-commercial-fips` | +| Monitor | `makeplane/monitor-commercial-fips` | +| Email | `makeplane/email-commercial-fips` | +| Plane AI | `makeplane/plane-pi-commercial-fips` | +| Proxy | `makeplane/proxy-commercial-fips` | +| Flux | `makeplane/flux-commercial-fips` | +| Node runner | `makeplane/node-runner-commercial-fips` | + +Pin a specific release tag for any accredited deployment rather than tracking `latest` - a known, +fixed image version is part of the audit trail. + +:::info +There is no FIPS All-in-One (AIO) image. The AIO image is built on an Alpine base, which has no FIPS-validated cryptography, so a FIPS deployment uses the multi-container stack, not the AIO image. +::: + +## Host prerequisite + +The host kernel must be booted in FIPS mode. The container inherits this through +`/proc/sys/crypto/fips_enabled` and **cannot set it itself**. Verify before deploying: + +```bash +cat /proc/sys/crypto/fips_enabled # must print 1 +``` + +How you put the host into FIPS mode depends on the distribution and version: + +**Amazon Linux 2023, RHEL 8/9 (and Rocky, Alma)** - enable in place, then reboot: + +```bash +sudo dnf install -y crypto-policies-scripts +sudo fips-mode-setup --enable +sudo reboot +``` + +**RHEL 10** - `fips-mode-setup` has been removed and post-install switching is not supported: enable FIPS **at install time** with `fips=1` on the kernel command line. + +**Other** - boot a vendor FIPS image (a RHEL FIPS AMI, Ubuntu Pro FIPS), or install OpenShift with FIPS enabled. + +As a safeguard, run the FIPS images with `PLANE_REQUIRE_FIPS=1`: the containers then **refuse to +start** if the host is not in FIPS mode. Without it, a FIPS image on a non-FIPS host logs a startup +warning but runs. + +## Deploy on Kubernetes + +Use the same `plane-enterprise` Helm chart as a [standard Kubernetes install](/self-hosting/methods/kubernetes) - +FIPS is a values overlay, not a different chart. Three things change: + +1. **Nodes** - provision a node pool whose machine image boots in FIPS mode (see + [Host prerequisite](#host-prerequisite)). Label it (e.g. `fips: enabled`) and taint it (e.g. + `fips=true:NoSchedule`) so only FIPS workloads land there. +2. **Images** - override every service image to its `-fips` variant. +3. **Scheduling** - every service must carry the matching `nodeSelector` and `toleration`. A pod + that misses them schedules onto a stock node and **silently loses FIPS**. + +```yaml +# values-fips.yaml +planeVersion: + +# Non-root with group 0, matching the FIPS images' group-0-writable directories +# (see the non-root section below). +securityContext: + enabled: true + podSecurityContext: + runAsGroup: 0 + fsGroup: 0 + +_fips_sched: &fips + nodeSelector: + fips: enabled + tolerations: + - key: fips + operator: Equal + value: "true" + effect: NoSchedule + +services: + api: + image: makeplane/backend-commercial-fips + <<: *fips + web: + image: makeplane/web-commercial-fips + <<: *fips + space: + image: makeplane/space-commercial-fips + <<: *fips + admin: + image: makeplane/admin-commercial-fips + <<: *fips + live: + image: makeplane/live-commercial-fips + <<: *fips + silo: + image: makeplane/silo-commercial-fips + <<: *fips + monitor: + image: makeplane/monitor-commercial-fips + <<: *fips + worker: + <<: *fips + beatworker: + <<: *fips + # Every additional service you enable needs the same <<: *fips block - + # e.g. Plane AI also takes image: makeplane/plane-pi-commercial-fips, and + # its pi_worker / pi_beat_worker need the block too. + postgres: + <<: *fips + redis: + <<: *fips + rabbitmq: + <<: *fips + minio: + <<: *fips +``` + +```bash +helm repo add plane https://helm.plane.so/ +helm upgrade --install plane-app plane/plane-enterprise \ + --namespace plane --create-namespace \ + -f values-fips.yaml +``` + +On OpenShift, drop the `securityContext` override and see +[Running under a non-root or arbitrary UID](#running-under-a-non-root-or-arbitrary-uid-openshift). + +## Verify + +Each container logs its posture on startup: + +```text +plane: FIPS mode ACTIVE (host kernel reports fips_enabled=1) +``` + +The Go services (monitor, email, proxy) log a corresponding line, for example +`Go FIPS 140-3 module ACTIVE`. + +To check a running container directly: + +```bash +# Kernel flag inherited from the host - must print 1 +docker exec plane-api cat /proc/sys/crypto/fips_enabled + +# The FIPS-validated OpenSSL provider must be loaded and "active" +docker exec plane-api openssl list -providers + +# A non-approved digest must be refused - this must FAIL +docker exec plane-api sh -c 'echo x | openssl md5' + +# Node services must report FIPS - must print 1 +docker exec plane-live node -p "require('crypto').getFips()" +``` + +On Kubernetes, run the same checks with `kubectl exec` against any application pod, e.g. +`kubectl -n plane exec deploy/plane-app-api-wl -- cat /proc/sys/crypto/fips_enabled`. + +## Configuration defaults specific to FIPS images + +The FIPS images default to a stricter security posture than the standard images. A fresh FIPS +install needs none of these changed; they matter mainly when moving an existing standard +deployment onto the FIPS images. + +| Setting | FIPS default | Standard default | Notes | +| ---------------------------------- | ------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------- | +| `LDAP_TLS_REQUIRE_CERT` | `demand` | `never` | Validates the LDAP server's TLS certificate. Set to `never` to restore the previous behavior. | +| `SAML_REJECT_DEPRECATED_ALGORITHM` | on | off | Rejects assertions signed with RSA-SHA1. The IdP must sign with SHA-256. | +| `SECRET_ENCRYPTION_V2` | on | off | Writes at-rest secrets as AES-256-GCM instead of the legacy format. Both formats are always readable. | +| `USAGE_ID_DIGEST` | `sha256` (required) | `md5` | Digest for Plane AI usage-ledger keys. `md5` is incompatible with a FIPS-mode Postgres, so `sha256` is required. | + +## Running under a non-root or arbitrary UID (OpenShift) + +The FIPS application images run non-root, and FIPS mode itself requires no privilege. + +**Plain Kubernetes** - set `runAsUser: 1000` (the images' built-in user). For any other UID, add +`runAsGroup: 0` and `fsGroup: 0`. + +**OpenShift (`restricted-v2`)** - works out of the box. Don't set `runAsUser`/`runAsGroup`/`fsGroup` +yourself; the SCC assigns an arbitrary UID in group `0`, and the images' writable directories are +group-`0` writable by design. One exception: the bundled proxy binds ports 80/443, which +`restricted-v2` forbids - front it with an OpenShift Route instead. Ingress-based deployments don't +use the bundled proxy. diff --git a/apps/developer-docs/docs/self-hosting/methods/install-methods-commercial/docker-compose.md b/apps/developer-docs/docs/self-hosting/methods/install-methods-commercial/docker-compose.md new file mode 100644 index 00000000..4accc9df --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/install-methods-commercial/docker-compose.md @@ -0,0 +1,51 @@ +--- +title: Docker Compose +description: Install Plane using Docker Compose. Step-by-step guide for deploying Plane with Docker on your server with all required services. +keywords: plane commercial docker, commercial edition docker compose, plane pro docker, self-hosting commercial +search: false +sidebar: false +head: + - - meta + - name: robots + content: noindex, nofollow +--- + +# Docker Compose + +This guide shows you the steps to deploy a self-hosted instance of Plane using Docker. + +## Install Plane + +Plane Pro and Plane Business are enabled on this edition, so the Free plan on this edition is easier to trial our paid plans from. + +### Prerequisites + +- A virtual or on-prem machine with at least 2 vCPUs and 4 GB RAM (8 GB RAM recommended) +- `x64` AKA `AMD 64` or `AArch 64` AKA `ARM 64` CPUs +- Supported operating systems: + - Ubuntu + - Debian + - CentOS + - Amazon Linux 2 or Linux 2023 + +::: info +Ensure you're using the **latest version of Docker Compose**. Check your Docker Compose version with `docker-compose --version` and update if needed. +::: + +### Procedure + +1. `ssh` into your machine as the root user (or user with sudo access) per the norms of your hosting provider. +2. Run the command below: + ``` + curl -fsSL https://prime.plane.so/install/ | sh - + ``` +3. Follow the instructions on the terminal. Hit `Enter` or `Return` to continue. +4. Enter the domain name where you will access the Plane app in the format `domain.tld` or `subdomain.domain.tld`. +5. Choose one of the options below: + - **Express**: Plane installs with the default configurations. + - **Advanced**: You can customize the database, Redis, storage and other settings. + ::: warning + When self-hosting Plane for production use, it is strongly recommended to configure [external database and storage](/self-hosting/govern/database-and-storage). This ensures that your data remains secure and accessible even if the local machine crashes or encounters hardware issues. Relying solely on local storage for these components increases the risk of data loss and service disruption. + ::: +6. The installation will take a few minutes to complete and you will see the message **Plane has successfully installed**. You can access the Plane application on the domain you provided during the installation. +7. If you've purchased a paid plan, [activate your license key](/self-hosting/manage/manage-licenses/activate-pro-and-business#activate-your-license) to unlock premium features. diff --git a/apps/developer-docs/docs/self-hosting/methods/install-methods-commercial/kubernetes.md b/apps/developer-docs/docs/self-hosting/methods/install-methods-commercial/kubernetes.md new file mode 100644 index 00000000..6c4b4054 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/install-methods-commercial/kubernetes.md @@ -0,0 +1,726 @@ +--- +title: Kubernetes +description: Deploy Plane on Kubernetes using Helm charts. Complete guide for production-ready Kubernetes deployment with scaling and management. +keywords: plane commercial kubernetes, commercial edition helm, plane pro kubernetes, self-hosting commercial +search: false +sidebar: false +head: + - - meta + - name: robots + content: noindex, nofollow +--- + +# Kubernetes + +This guide shows you the steps to deploy a self-hosted instance of Plane using Kubernetes. + +## Install Plane + +Plane Pro and Plane Business are enabled on this edition, so the Free plan on this edition is easier to trial our paid plans from. + +### Prerequisites + +- A working Kubernetes cluster +- `kubectl` and `helm` on the client system that you will use to install our Helm charts + +::: info +Ensure you use use the latest Helm chart version. +::: + +### Procedure + +1. Open terminal or any other command-line app that has access to Kubernetes tools on your local system. +2. Set the following environment variables: + +```bash +PLANE_VERSION=v2.6.3 +``` + +```bash +DOMAIN_NAME= +``` + +::: warning +When configuring the PLANE_VERSION environment variable, **do not** set it to `stable`. Always specify the latest version number (e.g., `2.4.0`). Using `stable` can lead to unexpected issues. +::: + +3. Add the Plane helm chart repo. + +```bash +helm repo add plane https://helm.plane.so/ +``` + +4. Use one of the following ways to deploy Plane: + - **Quick setup**: + This is the fastest way to deploy Plane with the default settings. This will create stateful deployments for Postgres, Redis/Valkey, and Minio with a persistent volume claim using the `longhorn` storage class. This also sets up the Ingress routes for you using `nginx` ingress class. To customize these settings, see the [Custom ingress routes](#custom-ingress-routes). + + Run the following command to deploy Plane: + + ``` + helm upgrade --install plane-app plane/plane-enterprise \ + --create-namespace \ + --namespace plane \ + --set license.licenseDomain=${DOMAIN_NAME} \ + --set license.licenseServer=https://prime.plane.so \ + --set planeVersion=${PLANE_VERSION} \ + --set ingress.enabled=true \ + --set ingress.ingressClass=nginx \ + --set env.storageClass=longhorn \ + --timeout 10m \ + --wait \ + --wait-for-jobs + ``` + + ::: info + This is the minimum required to set up Plane Commercial edition. You can change the default namespace from `plane`, the default app name from `plane-app`, the default storage class from `longhorn`, and the default ingress class from `nginx` to whatever you would like to.

+ To use a custom StorageClass, add `--set env.storageClass=` to the command above.

+ You can also pass other settings referring to the **Configuration Settings** toggle section below. + ::: + + - **Advanced setup**: + + ::: warning + When self-hosting Plane for production use, it is strongly recommended to configure [external database and storage](/self-hosting/methods/kubernetes#configuration-settings). This ensures that your data remains secure and accessible even if the local machine crashes or encounters hardware issues. Relying solely on local storage for these components increases the risk of data loss and service disruption. + ::: + + For more control over your setup, follow the steps below: + + i. Run the script below to download the `values.yaml` file and edit using any editor like Vim or Nano. + + Make sure you set the required environment variables listed below: + - `planeVersion: v2.6.3` + - `license.licenseDomain: ` + - `license.licenseServer: https://prime.plane.so` + - `ingress.enabled: ` + - `ingress.ingressClass: ` + - `env.storageClass: ` + + See the **Configuration settings** toggle section for more details. + + ```bash + helm upgrade --install plane-app plane/plane-enterprise \ + --create-namespace \ + --namespace plane \ + -f values.yaml \ + --timeout 10m \ + --wait \ + --wait-for-jobs + ``` + + ii. If you've purchased a paid plan, [activate your license key](/self-hosting/manage/manage-licenses/activate-pro-and-business#activate-your-license) to unlock premium features. + +## Configuration settings + +#### License + +| Setting | Default | Required | Description | +| --------------------- | :-----------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| planeVersion | v2.6.3 | Yes | Specifies the version of Plane to be deployed. Copy this from prime.plane.so. | +| license.licenseDomain | 'plane.example.com' | Yes | The fully-qualified domain name (FQDN) in the format `sudomain.domain.tld` or `domain.tld` that the license is bound to. It is also attached to your `ingress` host to access Plane. | + +### Airgapped Settings + +| Setting | Default | Required | Description | +| ---------------------- | :-----: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| airgapped.enabled | false | No | Enable airgapped mode for the Plane API. | +| airgapped.s3Secrets | [] | No | List of Kubernetes Secrets containing CA certificates to install. Each entry requires `name` (Secret name) and `key` (filename in the Secret). Example: `kubectl -n plane create secret generic plane-s3-ca --from-file=s3-custom-ca.crt=/path/to/ca.crt`. Supports multiple certs (e.g. S3 + internal CA). Available in v2.6.3 and later. | +| airgapped.s3SecretName | "" | No | **Deprecated**
Name of a single Kubernetes Secret containing the S3 CA cert. Used only when `s3Secrets` is empty. Use `s3Secrets` instead. | +| airgapped.s3SecretKey | "" | No | **Deprecated**
Key (filename) of the cert file inside the Secret. Used only when `s3Secrets` is empty. Set together with `airgapped.s3SecretName`. Use `s3Secrets` instead. | + +#### CA certificate configuration (For airgapped deployments only) + +Plane supports custom CA certificates for connecting to S3-compatible storage and other internal services in airgapped environments. + +- **New deployments:** Use `airgapped.s3Secrets` as shown in the table above. +- **Existing deployments using `s3SecretName` and `s3SecretKey`:** Your configuration still works. Migrate only if you need to use multiple CA certificates. + +#### Migrating to the new configuration + +:::warning +Requires Plane v2.6.3 or later. +::: + +The new `s3Secrets` configuration supports multiple CA certificates, useful if you need to trust certificates from different sources (e.g., S3 endpoint CA and internal PKI). If you only need a single certificate, migration is optional. + +To migrate: + +1. Add your existing secret to the `s3Secrets` list: + +```yaml +airgapped: + enabled: true + s3Secrets: + - name: plane-s3-ca # your existing s3SecretName value + key: s3-custom-ca.crt # your existing s3SecretKey value + + + # s3SecretName and s3SecretKey can be removed after migration +``` + +2. Remove `s3SecretName` and `s3SecretKey` from your values file. + +3. Upgrade your Helm release. + +#### Docker Registry + +| Setting | Default | Required | Description | +| ----------------------------- | :-----------------: | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| dockerRegistry.enabled | false | No | Enable to configure image pull secrets for pulling images from a private docker registry. When enabled, you can either provide credentials to create a new secret or use an existing Kubernetes secret. | +| dockerRegistry.existingSecret | | No | Name of an existing Kubernetes secret containing docker registry credentials. When specified, the chart will use this secret for `imagePullSecrets` instead of creating a new one. The secret should be of type `kubernetes.io/dockerconfigjson`. If left empty, credentials below will be used to create a new secret. | +| dockerRegistry.registry | index.docker.io/v1/ | No | Docker registry URL. Only used when `dockerRegistry.existingSecret` is empty. | +| dockerRegistry.loginid | | No | Login ID / Username for the docker registry. Only used when `dockerRegistry.existingSecret` is empty. | +| dockerRegistry.password | | No | Password or Token for the docker registry. Only used when `dockerRegistry.existingSecret` is empty. | + +#### Postgres + +| Setting | Default | Required | Description | +| ----------------------------------- | :--------------------: | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.postgres.local_setup | true | | Plane uses `postgres` as the primary database to store all the transactional data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to `true` when you choose to setup stateful deployment of `postgres`. Mark it as `false` when using a remotely hosted database | +| services.postgres.image | `postgres:15.7-alpine` | | Using this key, user must provide the docker image name to setup the stateful deployment of `postgres`. (must be set when `services.postgres.local_setup=true`) | +| services.postgres.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of postgres. (must be set when `services.postgres.local_setup=true`) | +| services.postgres.servicePort | 5432 | | This key sets the default port number to be used while setting up stateful deployment of `postgres`. | +| services.postgres.volumeSize | 2Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| env.pgdb_username | plane | | Database credentials are requried to access the hosted stateful deployment of `postgres`. Use this key to set the username for the stateful deployment. | +| env.pgdb_password | plane | | Database credentials are requried to access the hosted stateful deployment of `postgres`. Use this key to set the password for the stateful deployment. | +| env.pgdb_name | plane | | Database name to be used while setting up stateful deployment of `Postgres` | +| services.postgres.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.postgres.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of postgres. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.postgres.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of postgres. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.postgres.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of postgres. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.postgres.labels | {} | | This key allows you to set custom labels for the stateful deployment of postgres. This is useful for organizing and selecting resources in your Kubernetes cluster. | +| services.postgres.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of postgres. This is useful for adding metadata or configuration hints to your resources. | +| env.pgdb_remote_url | | | Users can also decide to use the remote hosted database and link to Plane deployment. Ignoring all the above keys, set `services.postgres.local_setup` to `false` and set this key with remote connection url. | + +#### Redis/Valkey Setup + +| Setting | Default | Required | Description | +| -------------------------------- | :---------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.redis.local_setup | true | | Plane uses `redis` to cache the session authentication and other static data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to `true` when you choose to setup stateful deployment of `redis`. Mark it as `false` when using a remotely hosted database | +| services.redis.image | `valkey/valkey:7.2.11-alpine` | | Using this key, user must provide the docker image name to setup the stateful deployment of `redis`. (must be set when `services.redis.local_setup=true`) | +| services.redis.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of redis. (must be set when services.redis.local_setup=true) | +| services.redis.servicePort | 6379 | | This key sets the default port number to be used while setting up stateful deployment of `redis`. | +| services.redis.volumeSize | 500Mi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| services.redis.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.redis.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of redis. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.redis.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of redis. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.redis.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of redis. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.redis.labels | {} | | This key allows you to set custom labels for the stateful deployment of redis. This is useful for organizing and selecting resources in your Kubernetes cluster. | +| services.redis.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of redis. This is useful for adding metadata or configuration hints to your resources. | +| env.remote_redis_url | | | Users can also decide to use the remote hosted database and link to Plane deployment. Ignoring all the above keys, set `services.redis.local_setup` to `false` and set this key with remote connection url. | + +#### RabbitMQ Setup + +| Setting | Default | Required | Description | +| --------------------------------------- | :---------------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.rabbitmq.local_setup | true | | Plane uses `rabbitmq` as message queuing system. This can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws mq or similar services). Set this to `true` when you choose to setup stateful deployment of `rabbitmq`. Mark it as `false` when using a remotely hosted service | +| services.rabbitmq.image | `rabbitmq:3.13.6-management-alpine` | | Using this key, user must provide the docker image name to setup the stateful deployment of `rabbitmq`. (must be set when `services.rabbitmq.local_setup=true`) | +| services.rabbitmq.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of `rabbitmq`. (must be set when `services.rabbitmq.local_setup=true`) | +| services.rabbitmq.servicePort | 5672 | | This key sets the default port number to be used while setting up stateful deployment of `rabbitmq`. | +| services.rabbitmq.managementPort | 15672 | | This key sets the default management port number to be used while setting up stateful deployment of `rabbitmq`. | +| services.rabbitmq.volumeSize | 100Mi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| services.rabbitmq.default_user | plane | | Credentials are requried to access the hosted stateful deployment of `rabbitmq`. Use this key to set the username for the stateful deployment. | +| services.rabbitmq.default_password | plane | | Credentials are requried to access the hosted stateful deployment of `rabbitmq`. Use this key to set the password for the stateful deployment. | +| services.rabbitmq.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.rabbitmq.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of rabbitmq. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.rabbitmq.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of rabbitmq. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.rabbitmq.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of rabbitmq. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.rabbitmq.labels | {} | | This key allows you to set custom labels for the stateful deployment of rabbitmq. This is useful for organizing and selecting resources in your Kubernetes cluster. | +| services.rabbitmq.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of rabbitmq. This is useful for adding metadata or configuration hints to your resources. | +| services.rabbitmq.external_rabbitmq_url | | | Users can also decide to use the remote hosted service and link to Plane deployment. Ignoring all the above keys, set `services.rabbitmq.local_setup` to `false` and set this key with remote connection url. | + +#### OpenSearch Setup + +| Setting | Default | Required | Description | +| ------------------------------------- | :--------------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.opensearch.local_setup | false | | Plane uses `opensearch` as the search and analytics engine. This can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. AWS OpenSearch Service or similar services). Set this to `true` when you choose to setup stateful deployment of `opensearch`. Mark it as `false` when using a remotely hosted service | +| services.opensearch.image | opensearchproject/opensearch:3.3.2 | | Using this key, user must provide the docker image name to setup the stateful deployment of `opensearch`. (must be set when `services.opensearch.local_setup=true`) | +| services.opensearch.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of `opensearch`. (must be set when `services.opensearch.local_setup=true`) | +| services.opensearch.servicePort | 9200 | | This key sets the default port number to be used while setting up stateful deployment of `opensearch`. | +| services.opensearch.volumeSize | 5Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| services.opensearch.username | plane | | Credentials are required to access the hosted stateful deployment of `opensearch`. Use this key to set the username for the stateful deployment. | +| services.opensearch.password | Secure@Pass#123!%^&\* | | Credentials are required to access the hosted stateful deployment of `opensearch`. Use this key to set the password. **Password Complexity Requirements:** Must be at least 8 characters long and contain at least one uppercase letter, one lowercase letter, one digit, and one special character (e.g., `!@#$%^&*`). | +| services.opensearch.memoryLimit | 3Gi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.opensearch.cpuLimit | 750m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.opensearch.memoryRequest | 2Gi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.opensearch.cpuRequest | 500m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.opensearch.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.opensearch.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of opensearch. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.opensearch.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of opensearch. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.opensearch.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of opensearch. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.opensearch.labels | {} | | This key allows you to set custom labels for the stateful deployment of opensearch. This is useful for organizing and selecting resources in your Kubernetes cluster. | +| services.opensearch.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of opensearch. This is useful for adding metadata or configuration hints to your resources. | +| env.opensearch_remote_url | | | Users can also decide to use the remote hosted service and link to Plane deployment. Set `services.opensearch.local_setup` to `false` and set this key with remote connection url. | +| env.opensearch_remote_username | | | Username for remote OpenSearch service. Required when `services.opensearch.local_setup=false` and `env.opensearch_remote_url` is set. Note: This is not a secret and should be configured in values.yaml, not in external secrets. | +| env.opensearch_remote_password | | | Password for remote OpenSearch service. Required when `services.opensearch.local_setup=false` and `env.opensearch_remote_url` is set. Can be configured in values.yaml or provided via external secrets (`opensearch_existingSecret` with `OPENSEARCH_PASSWORD`). **Password Complexity Requirements:** Must be at least 8 characters long and contain at least one uppercase letter, one lowercase letter, one digit, and one special character. | +| env.opensearch_index_prefix | plane\_ | | Prefix to be used for OpenSearch indices. This helps organize indices in a multi-tenant or multi-environment setup. | + +#### Doc Store (Minio\/S3) Setup + +| Setting | Default | Required | Description | +| ------------------------------------- | :----------------: | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.minio.local_setup | true | | Plane uses `minio` as the default file storage drive. This storage can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws S3 or similar services). Set this to `true` when you choose to setup stateful deployment of `minio`. Mark it as `false` when using a remotely hosted database | +| services.minio.image | minio/minio:latest | | Using this key, user must provide the docker image name to setup the stateful deployment of `minio`. (must be set when `services.minio.local_setup=true`) | +| services.minio.image_mc | minio/mc:latest | | Using this key, user must provide the docker image name to setup the job deployment of `minio client`. (must be set when `services.minio.local_setup=true`) | +| services.minio.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of minio. (must be set when services.minio.local_setup=true) | +| services.minio.volumeSize | 3Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| services.minio.root_user | admin | | Storage credentials are requried to access the hosted stateful deployment of `minio`. Use this key to set the username for the stateful deployment. | +| services.minio.root_password | password | | Storage credentials are requried to access the hosted stateful deployment of `minio`. Use this key to set the password for the stateful deployment. | +| services.minio.env.minio_endpoint_ssl | false | | (Optional) Env to enforce HTTPS when connecting to minio uploads bucket | +| env.docstore_bucket | uploads | Yes | Storage bucket name is required as part of configuration. This is where files will be uploaded irrespective of if you are using `Minio` or external `S3` (or compatible) storage service | +| env.doc_upload_size_limit | 5242880 | Yes | Document Upload Size Limit (default to 5Mb) | +| services.minio.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.minio.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of minio. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.minio.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of minio. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.minio.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of minio. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.minio.labels | {} | | This key allows you to set custom labels for the stateful deployment of minio. This is useful for organizing and selecting resources in your Kubernetes cluster. | +| services.minio.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of minio. This is useful for adding metadata or configuration hints to your resources. | +| env.aws_access_key | | | External `S3` (or compatible) storage service provides `access key` for the application to connect and do the necessary upload or download operations. To be provided when `services.minio.local_setup=false` | +| env.aws_secret_access_key | | | External `S3` (or compatible) storage service provides `secret access key` for the application to connect and do the necessary upload or download operations. To be provided when `services.minio.local_setup=false` | +| env.aws_region | | | External `S3` (or compatible) storage service providers creates any buckets in user selected region. This is also shared with the user as `region` for the application to connect and do the necessary upload or download operations. To be provided when `services.minio.local_setup=false` | +| env.aws_s3_endpoint_url | | | External `S3` (or compatible) storage service providers shares a `endpoint_url` for the integration purpose for the application to connect and do the necessary upload or download operations. To be provided when `services.minio.local_setup=false` | +| env.use_storage_proxy | false | | When set to `true`, all S3 (or compatible) file GET requests from the browser are proxied through Plane's API service instead of accessing the S3 endpoint directly. Enable this if your storage endpoint is not accessible publicly or you want to control download access through the API. | + +#### Web Deployment + +| Setting | Default | Required | Description | +| ------------------------------ | :------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.web.replicas | 1 | Yes | Kubernetes helps you with scaling up or down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 | +| services.web.memoryLimit | 1000Mi | | Every deployment in Kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.web.cpuLimit | 500m | | Every deployment in Kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.web.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.web.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.web.image | `makeplane/web-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.web.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of web. | +| services.web.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.web.nodeSelector | {} | | This key allows you to set the node selector for the deployment of web. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.web.tolerations | [] | | This key allows you to set the tolerations for the deployment of web. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.web.affinity | {} | | This key allows you to set the affinity rules for the deployment of web. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.web.labels | {} | | Custom labels to add to the web deployment | +| services.web.annotations | {} | | Custom annotations to add to the web deployment | + +#### Space Deployment + +| Setting | Default | Required | Description | +| -------------------------------- | :--------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.space.replicas | 1 | Yes | Kubernetes helps you with scaling up or down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 | +| services.space.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.space.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.space.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.space.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.space.image | `makeplane/space-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.space.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of space. | +| services.space.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.space.nodeSelector | {} | | This key allows you to set the node selector for the deployment of space. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.space.tolerations | [] | | This key allows you to set the tolerations for the deployment of space. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.space.affinity | {} | | This key allows you to set the affinity rules for the deployment of space. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.space.labels | {} | | Custom labels to add to the space deployment | +| services.space.annotations | {} | | Custom annotations to add to the space deployment | + +#### Admin Deployment + +| Setting | Default | Required | Description | +| -------------------------------- | :--------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.admin.replicas | 1 | Yes | Kubernetes helps you with scaling up or down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 | +| services.admin.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.admin.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.admin.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.admin.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.admin.image | `makeplane/admin-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.admin.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of admin. | +| services.admin.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.admin.nodeSelector | {} | | This key allows you to set the node selector for the deployment of admin. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.admin.tolerations | [] | | This key allows you to set the tolerations for the deployment of admin. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.admin.affinity | {} | | This key allows you to set the affinity rules for the deployment of admin. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.admin.labels | {} | | Custom labels to add to the admin deployment. | +| services.admin.annotations | {} | | Custom annotations to add to the admin deployment. | + +#### Live Service Deployment + +| Setting | Default | Required | Description | +| ---------------------------------- | :--------------------------------: | :------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.live.replicas | 1 | Yes | Kubernetes helps you with scaling up\/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 | +| services.live.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.live.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.live.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.live.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.live.image | `makeplane/live-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.live.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of live. | +| env.live_sentry_dsn | | | (optional) Live service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry provided DSN for this integration. | +| env.live_sentry_environment | | | (optional) Live service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry environment name (as configured in Sentry) for this integration. | +| env.live_sentry_traces_sample_rate | | | (optional) Live service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry trace sample rate (as configured in Sentry) for this integration. | +| env.live_server_secret_key | htbqvBJAgpm9bzvf3r4urJer0ENReatceh | | Live Server Secret Key | +| env.external_iframely_url | "" | | External Iframely service URL. If provided, the local Iframely deployment will be skipped and the live service will use this external URL | +| services.live.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service. | +| services.live.nodeSelector | {} | | This key allows you to set the node selector for the deployment of live. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.live.tolerations | [] | | This key allows you to set the tolerations for the deployment of live. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.live.affinity | {} | | This key allows you to set the affinity rules for the deployment of live. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.live.labels | {} | | Custom labels to add to the live deployment. | +| services.live.annotations | {} | | Custom annotations to add to the live deployment. | + +#### Monitor Deployment + +| Setting | Default | Required | Description | +| ---------------------------------- | :----------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.monitor.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.monitor.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.monitor.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.monitor.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.monitor.image | `makeplane/monitor-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.monitor.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of monitor. | +| services.monitor.volumeSize | 100Mi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| services.monitor.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.monitor.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of monitor. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.monitor.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of monitor. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.monitor.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of monitor. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.monitor.labels | {} | | Custom labels to add to the monitor deployment | +| services.monitor.annotations | {} | | Custom annotations to add to the monitor deployment | + +#### API Deployment + +| Setting | Default | Required | Description | +| ------------------------------ | :----------------------------: | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.api.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 | +| services.api.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.api.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.api.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.api.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.api.image | `makeplane/backend-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.api.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of api. | +| env.sentry_dsn | | | (optional) API service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry-provided DSN for this integration. | +| env.sentry_environment | | | (optional) API service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry environment name (as configured in Sentry) for this integration. | +| env.api_key_rate_limit | 60/minute | | (optional) User can set the maximum number of requests the API can handle in a given time frame. | +| env.web_url | | | (optional) Custom Web URL for the application. If not set, it will be auto-generated based on the license domain and SSL settings. | +| services.api.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.api.nodeSelector | {} | | This key allows you to set the node selector for the deployment of api. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.api.tolerations | [] | | This key allows you to set the tolerations for the deployment of api. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.api.affinity | {} | | This key allows you to set the affinity rules for the deployment of api. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.api.labels | {} | | Custom labels to add to the API deployment | +| services.api.annotations | {} | | Custom annotations to add to the API deployment | + +#### Silo Deployment + +| Setting | Default | Required | Description | +| :-------------------------------------------- | :--------------------------------- | :-------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.silo.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 | +| services.silo.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.silo.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.silo.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.silo.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.silo.image | `makeplane/silo-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.silo.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of `silo`. | +| services.silo.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.silo.nodeSelector | {} | | This key allows you to set the node selector for the deployment of silo. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.silo.tolerations | [] | | This key allows you to set the tolerations for the deployment of silo. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.silo.affinity | {} | | This key allows you to set the affinity rules for the deployment of silo. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.silo.labels | {} | | Custom labels to add to the silo deployment | +| services.silo.annotations | {} | | Custom annotations to add to the silo deployment | +| services.silo.connectors.slack.enabled | false | | Slack Integration | +| services.silo.connectors.slack.client_id | "" | required if `services.silo.connectors.slack.enabled` is `true` | Slack Client ID | +| services.silo.connectors.slack.client_secret | "" | required if `services.silo.connectors.slack.enabled` is `true` | Slack Client Secret | +| services.silo.connectors.github.enabled | false | | Github App Integration | +| services.silo.connectors.github.client_id | "" | required if `services.silo.connectors.github.enabled` is `true` | Github Client ID | +| services.silo.connectors.github.client_secret | "" | required if `services.silo.connectors.github.enabled` is `true` | Github Client Secret | +| services.silo.connectors.github.app_name | "" | required if `services.silo.connectors.github.enabled` is `true` | Github App Name | +| services.silo.connectors.github.app_id | "" | required if `services.silo.connectors.github.enabled` is `true` | Github App ID | +| services.silo.connectors.github.private_key | "" | required if `services.silo.connectors.github.enabled` is `true` | Github Private Key | +| services.silo.connectors.gitlab.enabled | false | | Gitlab App Integration | +| services.silo.connectors.gitlab.client_id | "" | required if `services.silo.connectors.gitlab.enabled` is `true` | Gitlab Client ID | +| services.silo.connectors.gitlab.client_secret | "" | required if `services.silo.connectors.gitlab.enabled` is `true` | Gitlab Client Secret | +| env.silo_envs.mq_prefetch_count | 10 | | Prefetch count for RabbitMQ | +| env.silo_envs.batch_size | 60 | | Batch size for Silo | +| env.silo_envs.request_interval | 400 | | Request interval for Silo | +| env.silo_envs.sentry_dsn | | | Sentry DSN | +| env.silo_envs.sentry_environment | | | Sentry Environment | +| env.silo_envs.sentry_traces_sample_rate | | | Sentry Traces Sample Rate | +| env.silo_envs.hmac_secret_key | <random-32-bit-string> | | HMAC Secret Key | +| env.silo_envs.aes_secret_key | "dsOdt7YrvxsTIFJ37pOaEVvLxN8KGBCr" | | AES Secret Key | + +#### Plane AI deployment + +::: info Plane AI database +Plane AI uses a separate PostgreSQL database. Create a new database (e.g. `plane_pi`) and connect it using `env.pg_pi_db_remote_url` in values, or **PLANE_PI_DATABASE_URL** when using `pi_api_env_existingSecret`. +::: + +| Setting | Default | Required | Description | +| --------------------------------- | :--------------------------------: | :------: | ---------------------------------------------------------------------------------------------------------------------------------------- | +| services.pi.enabled | false | No | Set to `true` to enable the Plane AI service and its API, worker, beat, and migrator workloads. | +| services.pi.replicas | 1 | Yes | Number of replicas for the Plane AI API deployment. It must be >=1. | +| services.pi.memoryLimit | 1000Mi | | Memory limit for the Plane AI API deployment. | +| services.pi.cpuLimit | 500m | | CPU limit for the Plane AI API deployment. | +| services.pi.memoryRequest | 50Mi | | Memory request for the Plane AI API deployment. | +| services.pi.cpuRequest | 50m | | CPU request for the Plane AI API deployment. | +| services.pi.image | makeplane/plane-pi-commercial | | Docker image for the Plane AI service. | +| services.pi.pullPolicy | Always | | Image pull policy for the Plane AI deployment. | +| services.pi.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the Plane AI API service. | +| services.pi.nodeSelector | {} | | Node selector for the Plane AI API deployment. | +| services.pi.tolerations | [] | | Tolerations for the Plane AI API deployment. | +| services.pi.affinity | {} | | Affinity rules for the Plane AI API deployment. | +| services.pi.labels | {} | | Custom labels to add to the Plane AI API deployment. | +| services.pi.annotations | {} | | Custom annotations to add to the Plane AI API deployment. | +| env.pg_pi_db_name | plane_pi | | PostgreSQL database name used by Plane AI when `postgres.local_setup=true`. | +| env.pg_pi_db_remote_url | "" | | PostgreSQL connection URL for Plane AI when using a remote database. Required when `postgres.local_setup=false` and Plane AI is enabled. | +| env.pi_envs.follower_postgres_uri | Same as Plane DATABASE_URL | No | Connection string for a Plane PostgreSQL DB read replica. Used for read-heavy operations to reduce load on the primary database. | +| env.pi_envs.internal_secret | tyfvfqvBJAgpm9bzvf3r4urJer0Ehfdubk | | Internal secret used by Plane AI for OAuth and internal APIs. | +| env.pi_envs.plane_api_host | "" | | Override for the Plane API host URL used by Plane AI. Defaults to the license domain. | +| env.pi_envs.cors_allowed_origins | "" | | CORS allowed origins for Plane AI API. Defaults to the license domain. | + +#### Plane AI Worker Deployment + +| Setting | Default | Required | Description | +| -------------------------------- | :-----: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.pi_worker.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for the Plane AI worker. This key helps you set the number of replicas. It must be >=1. | +| services.pi_worker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for the Plane AI worker deployment to use. | +| services.pi_worker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for the Plane AI worker deployment to use. | +| services.pi_worker.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for the Plane AI worker deployment to use. | +| services.pi_worker.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for the Plane AI worker deployment to use. | +| services.pi_worker.nodeSelector | {} | | This key allows you to set the node selector for the deployment of `pi_worker`. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.pi_worker.tolerations | [] | | This key allows you to set the tolerations for the deployment of `pi_worker`. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.pi_worker.affinity | {} | | This key allows you to set the affinity rules for the deployment of `pi_worker`. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.pi_worker.labels | {} | | Custom labels to add to the Plane AI worker deployment | +| services.pi_worker.annotations | {} | | Custom annotations to add to the Plane AI worker deployment | + +#### Plane AI Beat-Worker Deployment + +| Setting | Default | Required | Description | +| ------------------------------------- | :-----: | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.pi_beat_worker.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for the Plane AI beat-worker. This key helps you set the number of replicas. It must be >=1. | +| services.pi_beat_worker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for the Plane AI beat-worker deployment to use. | +| services.pi_beat_worker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for the Plane AI beat-worker deployment to use. | +| services.pi_beat_worker.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for the Plane AI beat-worker deployment to use. | +| services.pi_beat_worker.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for the Plane AI beat-worker deployment to use. | +| services.pi_beat_worker.nodeSelector | {} | | This key allows you to set the node selector for the deployment of `pi_beat_worker`. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.pi_beat_worker.tolerations | [] | | This key allows you to set the tolerations for the deployment of `pi_beat_worker`. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.pi_beat_worker.affinity | {} | | This key allows you to set the affinity rules for the deployment of `pi_beat_worker`. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.pi_beat_worker.labels | {} | | Custom labels to add to the Plane AI beat-worker deployment | +| services.pi_beat_worker.annotations | {} | | Custom annotations to add to the Plane AI beat-worker deployment | + +#### Worker Deployment + +| Setting | Default | Required | Description | +| ----------------------------- | :-----: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.worker.replicas | 1 | Yes | Kubernetes helps you with scaling up or down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 | +| services.worker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.worker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.worker.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.worker.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.worker.nodeSelector | {} | | This key allows you to set the node selector for the deployment of worker. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.worker.tolerations | [] | | This key allows you to set the tolerations for the deployment of worker. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.worker.affinity | {} | | This key allows you to set the affinity rules for the deployment of worker. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.worker.labels | {} | | Custom labels to add to the worker deployment | +| services.worker.annotations | {} | | Custom annotations to add to the worker deployment | + +#### Beat-Worker Deployment + +| Setting | Default | Required | Description | +| --------------------------------- | :-----: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.beatworker.replicas | 1 | Yes | Kubernetes helps you with scaling up or down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 | +| services.beatworker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.beatworker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.beatworker.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.beatworker.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.beatworker.nodeSelector | {} | | This key allows you to set the node selector for the deployment of beatworker. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.beatworker.tolerations | [] | | This key allows you to set the tolerations for the deployment of beatworker. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.beatworker.affinity | {} | | This key allows you to set the affinity rules for the deployment of beatworker. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.beatworker.labels | {} | | Custom labels to add to the beat-worker deployment | +| services.beatworker.annotations | {} | | Custom annotations to add to the beat-worker deployment | + +#### Email Service Deployment + +| Setting | Default | Required | Description | +| ------------------------------------ | -------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.email_service.enabled | false | | Set to `true` to enable the email service deployment | +| services.email_service.replicas | 1 | | Number of replicas for the email service deployment | +| services.email_service.memoryLimit | 1000Mi | | Memory limit for the email service deployment | +| services.email_service.cpuLimit | 500m | | CPU limit for the email service deployment | +| services.email_service.memoryRequest | 50Mi | | Memory request for the email service deployment | +| services.email_service.cpuRequest | 50m | | CPU request for the email service deployment | +| services.email_service.image | makeplane/email-commercial | | Docker image for the email service deployment | +| services.email_service.pullPolicy | Always | | Image pull policy for the email service deployment | +| services.email_service.nodeSelector | {} | | This key allows you to set the node selector for the deployment of `email_service`. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.email_service.tolerations | [] | | This key allows you to set the tolerations for the deployment of `email_service`. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.email_service.affinity | {} | | This key allows you to set the affinity rules for the deployment of `email_service`. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.email_service.labels | {} | | Custom labels to add to the email service deployment | +| services.email_service.annotations | {} | | Custom annotations to add to the email service deployment | +| env.email_service_envs.smtp_domain | | Yes | The SMTP Domain to be used with email service | + +::: info +When the email service is enabled, the cert-issuer will be automatically created to handle TLS certificates for the email service. +::: + +#### Outbox Poller Service Deployment + +| Setting | Default | Required | Description | +| ------------------------------------------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.outbox_poller.enabled | false | | Set to true to enable the outbox poller service deployment | +| services.outbox_poller.replicas | 1 | | Number of replicas for the outbox poller service deployment | +| services.outbox_poller.memoryLimit | 1000Mi | | Memory limit for the outbox poller service deployment | +| services.outbox_poller.cpuLimit | 500m | | CPU limit for the outbox poller service deployment | +| services.outbox_poller.memoryRequest | 50Mi | | Memory request for the outbox poller service deployment | +| services.outbox_poller.cpuRequest | 50m | | CPU request for the outbox poller service deployment | +| services.outbox_poller.pullPolicy | Always | | Image pull policy for the outbox poller service deployment | +| services.outbox_poller.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | +| services.outbox_poller.nodeSelector | {} | | This key allows you to set the node selector for the deployment of outbox_poller. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.outbox_poller.tolerations | [] | | This key allows you to set the tolerations for the deployment of outbox_poller. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.outbox_poller.affinity | {} | | This key allows you to set the affinity rules for the deployment of outbox_poller. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.outbox_poller.labels | {} | | Custom labels to add to the outbox poller deployment | +| services.outbox_poller.annotations | {} | | Custom annotations to add to the outbox poller deployment | +| env.outbox_poller_envs.memory_limit_mb | 400 | | Memory limit in MB for the outbox poller | +| env.outbox_poller_envs.interval_min | 0.25 | | Minimum interval in minutes for polling | +| env.outbox_poller_envs.interval_max | 2 | | Maximum interval in minutes for polling | +| env.outbox_poller_envs.batch_size | 250 | | Batch size for processing outbox messages | +| env.outbox_poller_envs.memory_check_interval | 30 | | Memory check interval in seconds | +| env.outbox_poller_envs.pool.size | 4 | | Pool size for database connections | +| env.outbox_poller_envs.pool.min_size | 2 | | Minimum pool size for database connections | +| env.outbox_poller_envs.pool.max_size | 10 | | Maximum pool size for database connections | +| env.outbox_poller_envs.pool.timeout | 30.0 | | Pool timeout in seconds | +| env.outbox_poller_envs.pool.max_idle | 300.0 | | Maximum idle time for connections in seconds | +| env.outbox_poller_envs.pool.max_lifetime | 3600 | | Maximum lifetime for connections in seconds | +| env.outbox_poller_envs.pool.reconnect_timeout | 5.0 | | Reconnect timeout in seconds | +| env.outbox_poller_envs.pool.health_check_interval | 60 | | Health check interval in seconds | + +#### Automation Consumer Deployment + +| Setting | Default | Required | Description | +| ---------------------------------------------------- | -------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.automation_consumer.enabled | false | | Set to true to enable the automation consumer service deployment | +| services.automation_consumer.replicas | 1 | | Number of replicas for the automation consumer service deployment | +| services.automation_consumer.memoryLimit | 1000Mi | | Memory limit for the automation consumer service deployment | +| services.automation_consumer.cpuLimit | 500m | | CPU limit for the automation consumer service deployment | +| services.automation_consumer.memoryRequest | 50Mi | | Memory request for the automation consumer service deployment | +| services.automation_consumer.cpuRequest | 50m | | CPU request for the automation consumer service deployment | +| services.automation_consumer.pullPolicy | Always | | Image pull policy for the automation consumer service deployment | +| services.automation_consumer.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | +| services.automation_consumer.nodeSelector | {} | | This key allows you to set the node selector for the deployment of automation_consumer. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.automation_consumer.tolerations | [] | | This key allows you to set the tolerations for the deployment of automation_consumer. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.automation_consumer.affinity | {} | | This key allows you to set the affinity rules for the deployment of automation_consumer. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.automation_consumer.labels | {} | | Custom labels to add to the automation consumer deployment | +| services.automation_consumer.annotations | {} | | Custom annotations to add to the automation consumer deployment | +| env.automation_consumer_envs.event_stream_queue_name | "plane.event_stream.automations" | | Event stream queue name for automations | +| env.automation_consumer_envs.event_stream_prefetch | 10 | | Event stream prefetch count | +| env.automation_consumer_envs.exchange_name | "plane.event_stream" | | Exchange name for event stream | +| env.automation_consumer_envs.event_types | "issue" | | Event types to process | + +#### Iframely Deployment + +| Setting | Default | Required | Description | +| ----------------------------------- | ------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.iframely.enabled | false | | Set to true to enable the Iframely service deployment | +| services.iframely.replicas | 1 | | Number of replicas for the Iframely service deployment | +| services.iframely.memoryLimit | 1000Mi | | Memory limit for the Iframely service deployment | +| services.iframely.cpuLimit | 500m | | CPU limit for the Iframely service deployment | +| services.iframely.memoryRequest | 50Mi | | Memory request for the Iframely service deployment | +| services.iframely.cpuRequest | 50m | | CPU request for the Iframely service deployment | +| services.iframely.image | makeplane/iframely:v1.2.0 | | Docker image for the Iframely service deployment | +| services.iframely.pullPolicy | Always | | Image pull policy for the Iframely service deployment | +| services.iframely.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | +| services.iframely.nodeSelector | {} | | This key allows you to set the node selector for the deployment of iframely. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.iframely.tolerations | [] | | This key allows you to set the tolerations for the deployment of iframely. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.iframely.affinity | {} | | This key allows you to set the affinity rules for the deployment of iframely. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.iframely.labels | {} | | Custom labels to add to the iframely deployment | +| services.iframely.annotations | {} | | Custom annotations to add to the iframely deployment | + +#### External Secrets Config + +To configure the external secrets for your application, you need to define specific environment variables for each secret category. Below is a list of the required secrets and their respective environment variables. + +| Secret Name | Env Var Name | Required | Description | Example Value | +| ------------------------- | --------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| rabbitmq_existingSecret | RABBITMQ_DEFAULT_USER | Required if `rabbitmq.local_setup=true` | The default RabbitMQ user | plane | +| | RABBITMQ_DEFAULT_PASS | Required if `rabbitmq.local_setup=true` | The default RabbitMQ password | plane | +| pgdb_existingSecret | POSTGRES_PASSWORD | Required if `postgres.local_setup=true` | Password for PostgreSQL database | plane | +| | POSTGRES_DB | Required if `postgres.local_setup=true` | Name of the PostgreSQL database | plane | +| | POSTGRES_USER | Required if `postgres.local_setup=true` | PostgreSQL user | plane | +| opensearch_existingSecret | OPENSEARCH_ENABLED | Yes | Flag to enable OpenSearch | 1 (enabled) or 0 (disabled) | +| | OPENSEARCH_URL | Required if OpenSearch is enabled | OpenSearch connection URL | **k8s service example:** `http://plane-opensearch.plane-ns.svc.cluster.local:9200` **external service example:** `https://your-opensearch-host:9200` | +| | OPENSEARCH_USERNAME | Required if OpenSearch is enabled | Username for OpenSearch | **local setup:** plane **remote setup:** your_remote_username | +| | OPENSEARCH_PASSWORD | Required if OpenSearch is enabled | Password for OpenSearch | **local setup:** Secure@Pass#123!%^&\* **remote setup:** your_remote_password | +| | OPENSEARCH_INITIAL_ADMIN_PASSWORD | Required if `opensearch.local_setup=true` | Initial admin password for local OpenSearch | Secure@Pass#123!%^&\* | +| | OPENSEARCH_INDEX_PREFIX | Optional | Prefix for OpenSearch indices | plane\_ | +| doc_store_existingSecret | USE_MINIO | Yes | Flag to enable MinIO as the storage backend | 1 | +| | MINIO_ROOT_USER | Yes | MinIO root user | admin | +| | MINIO_ROOT_PASSWORD | Yes | MinIO root password | password | +| | AWS_ACCESS_KEY_ID | Yes | AWS Access Key ID | your_aws_key | +| | AWS_SECRET_ACCESS_KEY | Yes | AWS Secret Access Key | your_aws_secret | +| | AWS_S3_BUCKET_NAME | Yes | AWS S3 Bucket Name | your_bucket_name | +| | AWS_S3_ENDPOINT_URL | Yes | Endpoint URL for AWS S3 or MinIO | `http://plane-minio.plane-ns.svc.cluster.local:9000` | +| | AWS_REGION | Optional | AWS region where your S3 bucket is located | your_aws_region | +| | FILE_SIZE_LIMIT | Yes | Limit for file uploads in your system | 5MB | +| app_env_existingSecret | SECRET_KEY | Yes | Random secret key | 60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5 | +| | REDIS_URL | Yes | Redis URL | `redis://plane-redis.plane-ns.svc.cluster.local:6379/` | +| | DATABASE_URL | Yes | PostgreSQL connection URL | k8s service example: `postgresql://plane:plane@plane-pgdb.plane-ns.svc.cluster.local:5432/plane` external service example: `postgresql://username:password@your-db-host:5432/plane` | +| | AMQP_URL | Yes | RabbitMQ connection URL | k8s service example: `amqp://plane:plane@plane-rabbitmq.plane-ns.svc.cluster.local:5672/` external service example: `amqp://username:password@your-rabbitmq-host:5672/` | +| live_env_existingSecret | REDIS_URL | Yes | Redis URL | `redis://plane-redis.plane-ns.svc.cluster.local:6379/` | +| silo_env_existingSecret | SILO_HMAC_SECRET_KEY | Yes | Silo HMAC secret Key | `` | +| | REDIS_URL | Yes | Redis URL | redis://plane-redis.plane-ns.svc.cluster.local:6379/ | +| | DATABASE_URL | Yes | PostgreSQL connection URL | k8s service example: postgresql://plane:plane@plane-pgdb.plane-ns.svc.cluster.local:5432/plane external service example: postgresql://username:password@your-db-host:5432/plane | +| | AMQP_URL | Yes | RabbitMQ connection URL | k8s service example: amqp://plane:plane@plane-rabbitmq.plane-ns.svc.cluster.local:5672/ external service example: amqp://username:password@your-rabbitmq-host:5672/ | +| | GITHUB_APP_NAME | Required if `services.silo.connectors.github.enabled` is true | GitHub app name | your_github_app_name | +| | GITHUB_APP_ID | Required if `services.silo.connectors.github.enabled` is true | GitHub app ID | your_github_app_id | +| | GITHUB_CLIENT_ID | Required if `services.silo.connectors.github.enabled` is true | GitHub client ID | your_github_client_id | +| | GITHUB_CLIENT_SECRET | Required if `services.silo.connectors.github.enabled` is true | GitHub client secret key | your_github_client_secret_key | +| | GITHUB_PRIVATE_KEY | Required if `services.silo.connectors.github.enabled` is true | GitHub private key | your_github_private_key | +| | SLACK_CLIENT_ID | Required if `services.silo.connectors.slack.enabled` is true | Slack client ID | your_slack_client_id | +| | SLACK_CLIENT_SECRET | Required if `services.silo.connectors.slack.enabled` is true | Slack client secret key | your_slack_client_secret_key | +| | GITLAB_CLIENT_ID | Required if `services.silo.connectors.gitlab.enabled` is true | GitLab client ID | your_gitlab_client_id | +| | GITLAB_CLIENT_SECRET | Required if `services.silo.connectors.gitlab.enabled` is true | GitLab client secret key | your_gitlab_client_secret_key | +| pi_api_env_existingSecret | PLANE_PI_DATABASE_URL | Required if `services.pi.enabled=true` | PostgreSQL connection URL for Plane AI database | **k8s service example**: `postgresql://plane:plane@plane-pgdb.plane-ns.svc.cluster.local/plane_pi`

**external**: `postgresql://username:password@your-db-host:5432/plane_pi` | +| | FOLLOWER_POSTGRES_URI | No | Connection string for a PostgreSQL read replica | Same as DATABASE_URL. Used for read-heavy operations to reduce load on the primary database. **k8s**: `postgresql://plane:plane@plane-pgdb.plane-ns.svc.cluster.local:5432/plane` | +| | AMQP_URL | Required if `services.pi.enabled=true` | RabbitMQ connection URL | **k8s service example**: `amqp://plane:plane@plane-rabbitmq.plane-ns.svc.cluster.local:5672/`

**external**: `amqp://username:password@your-rabbitmq-host:5672/` | +| | AES_SECRET_KEY | Required if `services.pi.enabled=true` | AES secret key for Plane AI | dsOdt7YrvxsTIFJ37pOaEVvLxN8KGBCr (or your own value) | +| | OPENAI_API_KEY | required if `services.pi.ai_providers.openai.enabled` is true | OpenAI API key | your_openai_api_key | +| | CLAUDE_API_KEY | required if `services.pi.ai_providers.claude.enabled` is true | Claude API key | your_claude_api_key | +| | GROQ_API_KEY | required if `services.pi.ai_providers.groq.enabled` is true | Groq API key | your_groq_api_key | +| | COHERE_API_KEY | required if `services.pi.ai_providers.cohere.enabled` is true | Cohere API key | your_cohere_api_key | +| | CUSTOM_LLM_API_KEY | required if `services.pi.ai_providers.custom_llm.enabled` is true | Custom LLM API key | your_custom_llm_api_key | +| | BR_AWS_SECRET_ACCESS_KEY | required if `services.pi.ai_providers.embedding_model.enabled` is true | AWS secret for embedding model | your_aws_secret_access_key | +| | BR_AWS_SESSION_TOKEN | required if embedding model uses temporary credentials | AWS session token for embedding model | your_aws_session_token | + +#### Ingress and SSL Setup + +| Setting | Default | Required | Description | +| --------------------------- | --------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ingress.enabled | true | | Ingress setup in kubernetes is a common practice to expose application to the intended audience. Set it to false if you are using external ingress providers like Cloudflare | +| ingress.minioHost | | | Based on above configuration, if you want to expose the minio web console to set of users, use this key to set the host mapping or leave it as EMPTY to not expose interface. | +| ingress.rabbitmqHost | | | Based on above configuration, if you want to expose the rabbitmq web console to set of users, use this key to set the host mapping or leave it as EMPTY to not expose interface. | +| ingress.ingressClass | nginx | Yes | Kubernetes cluster setup comes with various options of ingressClass. Based on your setup, set this value to the right one (eg. nginx, traefik, etc). Leave it to default in case you are using external ingress provider. | +| ingress.ingress_annotations | { `"nginx.ingress.kubernetes.io/proxy-body-size": "5m"` } | | Ingress controllers comes with various configuration options which can be passed as annotations. Setting this value lets you change the default value to user required. | +| ssl.createIssuer | false | | Kubernets cluster setup supports creating issuer type resource. After deployment, this is step towards creating secure access to the ingress url. Issuer is required for you generate SSL certifiate. Kubernetes can be configured to use any of the certificate authority to generate SSL (depending on CertManager configuration). Set it to true to create the issuer. Applicable only when ingress.enabled=true | +| ssl.issuer | http | | CertManager configuration allows user to create issuers using http or any of the other DNS Providers like cloudflare, digitalocean, etc. As of now Plane supports http, cloudflare, digitalocean | +| ssl.token | | | To create issuers using DNS challenge, set the issuer api token of dns provider like cloudflare or digitalocean (not required for http) | +| ssl.server | https://acme-v02.api.letsencrypt.org/directory | | Issuer creation configuration need the certificate generation authority server url. Default URL is the Let's Encrypt server | +| ssl.email | plane@example.com | | Certificate generation authority needs a valid email id before generating certificate. Required when ssl.createIssuer=true | +| ssl.generateCerts | false | | After creating the issuers, user can still not create the certificate untill sure of configuration. Setting this to true will try to generate SSL certificate and associate with ingress. Applicable only when ingress.enabled=true and ssl.createIssuer=true | +| ssl.tls_secret_name | | | If you have a custom TLS secret name, set this to the name of the secret. Applicable only when ingress.enabled=true and ssl.createIssuer=false | + +#### Common Environment Settings + +| Setting | Default | Required | Description | +| ---------------- | :------------------------------------------------: | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| env.storageClass | longhorn | | Creating the persitant volumes for the stateful deployments needs the `storageClass` name. Set the correct value as per your kubernetes cluster configuration. | +| env.secret_key | 60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5 | Yes | This must be a random string which is used for hashing/encrypting the sensitive data within the application. Once set, changing this might impact the already hashed/encrypted data | + +#### Extra Environment Variables + +| Setting | Default | Required | Description | +| -------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| extraEnv | [] | No | Global extra environment variables that will be applied to all workloads. This allows you to add custom environment variables to all deployments (web, api, worker, etc.). Useful for proxy settings, custom configurations, or any environment-specific variables. Some example variables are HTTP_PROXY, HTTPS_PROXY, NO_PROXY. | + +## Custom Ingress Routes + +If you are planning to use 3rd party ingress providers, here is the available route configuration. + +| Host | Path | Service | Required | +| ----------------------- | :-------------: | --------------------------------------- | :-------------------------------------------------------------------------- | +| plane.example.com | / | | Yes | +| plane.example.com | /spaces/\* | | Yes | +| plane.example.com | /god-mode/\* | | Yes | +| plane.example.com | /live/\* | | Yes | +| plane.example.com | /silo/\* | | Yes (if `services.silo.enabled=true` ) | +| plane.example.com | /pi/\* | | Yes (if `services.pi.enabled=true`) | +| plane.example.com | /api/\* | | Yes | +| plane.example.com | /auth/\* | | Yes | +| plane.example.com | /graphql/\* | | Yes | +| plane.example.com | /marketplace/\* | | Yes | +| plane.example.com | /uploads/\* | | Yes (Only if using local setup) | +| plane-minio.example.com | / | | (Optional) if using local setup, this will enable minio console access | +| plane-mq.example.com | / | | (Optional) if using local setup, this will enable management console access | diff --git a/apps/developer-docs/docs/self-hosting/methods/kubernetes.md b/apps/developer-docs/docs/self-hosting/methods/kubernetes.md new file mode 100644 index 00000000..98286933 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/kubernetes.md @@ -0,0 +1,1025 @@ +--- +title: Kubernetes +description: Deploy Plane on Kubernetes using Helm charts. Complete guide for production-ready Kubernetes deployment with scaling and management. +keywords: plane kubernetes, helm chart, k8s deployment, kubernetes cluster, plane helm, production deployment, self-hosting +--- + +# Kubernetes + +This guide shows you the steps to deploy a self-hosted instance of Plane using Kubernetes. + +::: tip +If you want to upgrade from Community to the Commercial edition, see [Upgrade to Commercial Edition](/self-hosting/upgrade-from-community). +::: + +## Install Plane + +Plane Pro and Plane Business are enabled on this edition, so the Free plan on this edition is easier to trial our paid plans from. + +### Prerequisites + +- A working Kubernetes cluster +- `kubectl` and `helm` on the client system that you will use to install our Helm charts + +::: info +Ensure you use use the latest Helm chart version. +::: + +### Procedure + +1. Open terminal or any other command-line app that has access to Kubernetes tools on your local system. +2. Set the following environment variables: + +```bash +PLANE_VERSION=v2.6.3 +``` + +```bash +DOMAIN_NAME= +``` + +::: warning +When configuring the PLANE_VERSION environment variable, **do not** set it to `stable`. Always specify the latest version number (e.g., `2.4.0`). Using `stable` can lead to unexpected issues. +::: + +3. Add the Plane helm chart repo. + +```bash +helm repo add plane https://helm.plane.so/ +``` + +4. Use one of the following ways to deploy Plane: + - **Quick setup**: + This is the fastest way to deploy Plane with the default settings. This will create stateful deployments for Postgres, Redis/Valkey, and Minio with a persistent volume claim using the `longhorn` storage class. This also sets up the Ingress routes for you using `nginx` ingress class. To customize these settings, see the [Custom ingress routes](#custom-ingress-routes). + + Run the following command to deploy Plane: + + ``` + helm upgrade --install plane-app plane/plane-enterprise \ + --create-namespace \ + --namespace plane \ + --set license.licenseDomain=${DOMAIN_NAME} \ + --set license.licenseServer=https://prime.plane.so \ + --set planeVersion=${PLANE_VERSION} \ + --set ingress.enabled=true \ + --set ingress.ingressClass=nginx \ + --set env.storageClass=longhorn \ + --timeout 10m \ + --wait \ + --wait-for-jobs + ``` + + ::: info + This is the minimum required to set up Plane Commercial edition. You can change the default namespace from `plane`, the default app name from `plane-app`, the default storage class from `longhorn`, and the default ingress class from `nginx` to whatever you would like to.

+ To use a custom StorageClass, add `--set env.storageClass=` to the command above.

+ You can also pass other settings referring to the **Configuration Settings** toggle section below. + ::: + + - **Advanced setup**: + + ::: warning + When self-hosting Plane for production use, it is strongly recommended to configure [external database and storage](/self-hosting/methods/kubernetes#configuration-settings). This ensures that your data remains secure and accessible even if the local machine crashes or encounters hardware issues. Relying solely on local storage for these components increases the risk of data loss and service disruption. + ::: + + For more control over your setup, follow the steps below: + + i. Run the script below to download the `values.yaml` file and edit using any editor like Vim or Nano. + + Make sure you set the required environment variables listed below: + - `planeVersion: v2.6.3` + - `license.licenseDomain: ` + - `license.licenseServer: https://prime.plane.so` + - `ingress.enabled: ` + - `ingress.ingressClass: ` + - `env.storageClass: ` + + See the **Configuration settings** toggle section for more details. + + ```bash + helm upgrade --install plane-app plane/plane-enterprise \ + --create-namespace \ + --namespace plane \ + -f values.yaml \ + --timeout 10m \ + --wait \ + --wait-for-jobs + ``` + + ii. If you've purchased a paid plan, [activate your license key](/self-hosting/manage/manage-licenses/activate-pro-and-business#activate-your-license) to unlock premium features. + +## Configuration settings + +#### License + +| Setting | Default | Required | Description | +| --------------------- | :-----------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| planeVersion | v2.6.3 | Yes | Specifies the version of Plane to be deployed. Copy this from prime.plane.so. | +| license.licenseDomain | 'plane.example.com' | Yes | The fully-qualified domain name (FQDN) in the format `sudomain.domain.tld` or `domain.tld` that the license is bound to. It is also attached to your `ingress` host to access Plane. | + +### Airgapped Settings + +| Setting | Default | Required | Description | +| ---------------------- | :-----: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| airgapped.enabled | false | No | Enable airgapped mode for the Plane API. | +| airgapped.s3Secrets | [] | No | List of Kubernetes Secrets containing CA certificates to install. Each entry requires `name` (Secret name) and `key` (filename in the Secret). Example: `kubectl -n plane create secret generic plane-s3-ca --from-file=s3-custom-ca.crt=/path/to/ca.crt`. Supports multiple certs (e.g. S3 + internal CA). Available in v2.6.3 and later. | +| airgapped.s3SecretName | "" | No | **Deprecated**
Name of a single Kubernetes Secret containing the S3 CA cert. Used only when `s3Secrets` is empty. Use `s3Secrets` instead. | +| airgapped.s3SecretKey | "" | No | **Deprecated**
Key (filename) of the cert file inside the Secret. Used only when `s3Secrets` is empty. Set together with `airgapped.s3SecretName`. Use `s3Secrets` instead. | + +#### CA certificate configuration (For airgapped deployments only) + +Plane supports custom CA certificates for connecting to S3-compatible storage and other internal services in airgapped environments. + +- **New deployments:** Use `airgapped.s3Secrets` as shown in the table above. +- **Existing deployments using `s3SecretName` and `s3SecretKey`:** Your configuration still works. Migrate only if you need to use multiple CA certificates. + +#### Migrating to the new configuration + +:::warning +Requires Plane v2.6.3 or later. +::: + +The new `s3Secrets` configuration supports multiple CA certificates, useful if you need to trust certificates from different sources (e.g., S3 endpoint CA and internal PKI). If you only need a single certificate, migration is optional. + +To migrate: + +1. Add your existing secret to the `s3Secrets` list: + +```yaml +airgapped: + enabled: true + s3Secrets: + - name: plane-s3-ca # your existing s3SecretName value + key: s3-custom-ca.crt # your existing s3SecretKey value + + + # s3SecretName and s3SecretKey can be removed after migration +``` + +2. Remove `s3SecretName` and `s3SecretKey` from your values file. + +3. Upgrade your Helm release. + +#### Docker Registry + +| Setting | Default | Required | Description | +| ----------------------------- | :-----------------: | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| dockerRegistry.enabled | false | No | Enable to configure image pull secrets for pulling images from a private docker registry. When enabled, you can either provide credentials to create a new secret or use an existing Kubernetes secret. | +| dockerRegistry.existingSecret | | No | Name of an existing Kubernetes secret containing docker registry credentials. When specified, the chart will use this secret for `imagePullSecrets` instead of creating a new one. The secret should be of type `kubernetes.io/dockerconfigjson`. If left empty, credentials below will be used to create a new secret. | +| dockerRegistry.registry | index.docker.io/v1/ | No | Docker registry URL. Only used when `dockerRegistry.existingSecret` is empty. | +| dockerRegistry.loginid | | No | Login ID / Username for the docker registry. Only used when `dockerRegistry.existingSecret` is empty. | +| dockerRegistry.password | | No | Password or Token for the docker registry. Only used when `dockerRegistry.existingSecret` is empty. | + +#### Postgres + +| Setting | Default | Required | Description | +| ----------------------------------- | :--------------------: | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.postgres.local_setup | true | | Plane uses `postgres` as the primary database to store all the transactional data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to `true` when you choose to setup stateful deployment of `postgres`. Mark it as `false` when using a remotely hosted database | +| services.postgres.image | `postgres:15.7-alpine` | | Using this key, user must provide the docker image name to setup the stateful deployment of `postgres`. (must be set when `services.postgres.local_setup=true`) | +| services.postgres.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of postgres. (must be set when `services.postgres.local_setup=true`) | +| services.postgres.servicePort | 5432 | | This key sets the default port number to be used while setting up stateful deployment of `postgres`. | +| services.postgres.volumeSize | 2Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| env.pgdb_username | plane | | Database credentials are requried to access the hosted stateful deployment of `postgres`. Use this key to set the username for the stateful deployment. | +| env.pgdb_password | plane | | Database credentials are requried to access the hosted stateful deployment of `postgres`. Use this key to set the password for the stateful deployment. | +| env.pgdb_name | plane | | Database name to be used while setting up stateful deployment of `Postgres` | +| services.postgres.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.postgres.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of postgres. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.postgres.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of postgres. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.postgres.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of postgres. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.postgres.labels | {} | | This key allows you to set custom labels for the stateful deployment of postgres. This is useful for organizing and selecting resources in your Kubernetes cluster. | +| services.postgres.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of postgres. This is useful for adding metadata or configuration hints to your resources. | +| env.pgdb_remote_url | | | Users can also decide to use the remote hosted database and link to Plane deployment. Ignoring all the above keys, set `services.postgres.local_setup` to `false` and set this key with remote connection url. | + +#### Redis/Valkey Setup + +| Setting | Default | Required | Description | +| -------------------------------- | :---------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.redis.local_setup | true | | Plane uses `redis` to cache the session authentication and other static data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to `true` when you choose to setup stateful deployment of `redis`. Mark it as `false` when using a remotely hosted database | +| services.redis.image | `valkey/valkey:7.2.11-alpine` | | Using this key, user must provide the docker image name to setup the stateful deployment of `redis`. (must be set when `services.redis.local_setup=true`) | +| services.redis.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of redis. (must be set when services.redis.local_setup=true) | +| services.redis.servicePort | 6379 | | This key sets the default port number to be used while setting up stateful deployment of `redis`. | +| services.redis.volumeSize | 500Mi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| services.redis.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.redis.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of redis. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.redis.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of redis. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.redis.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of redis. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.redis.labels | {} | | This key allows you to set custom labels for the stateful deployment of redis. This is useful for organizing and selecting resources in your Kubernetes cluster. | +| services.redis.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of redis. This is useful for adding metadata or configuration hints to your resources. | +| env.remote_redis_url | | | Users can also decide to use the remote hosted database and link to Plane deployment. Ignoring all the above keys, set `services.redis.local_setup` to `false` and set this key with remote connection url. | + +#### RabbitMQ Setup + +| Setting | Default | Required | Description | +| --------------------------------------- | :---------------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.rabbitmq.local_setup | true | | Plane uses `rabbitmq` as message queuing system. This can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws mq or similar services). Set this to `true` when you choose to setup stateful deployment of `rabbitmq`. Mark it as `false` when using a remotely hosted service | +| services.rabbitmq.image | `rabbitmq:3.13.6-management-alpine` | | Using this key, user must provide the docker image name to setup the stateful deployment of `rabbitmq`. (must be set when `services.rabbitmq.local_setup=true`) | +| services.rabbitmq.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of `rabbitmq`. (must be set when `services.rabbitmq.local_setup=true`) | +| services.rabbitmq.servicePort | 5672 | | This key sets the default port number to be used while setting up stateful deployment of `rabbitmq`. | +| services.rabbitmq.managementPort | 15672 | | This key sets the default management port number to be used while setting up stateful deployment of `rabbitmq`. | +| services.rabbitmq.volumeSize | 100Mi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| services.rabbitmq.default_user | plane | | Credentials are requried to access the hosted stateful deployment of `rabbitmq`. Use this key to set the username for the stateful deployment. | +| services.rabbitmq.default_password | plane | | Credentials are requried to access the hosted stateful deployment of `rabbitmq`. Use this key to set the password for the stateful deployment. | +| services.rabbitmq.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.rabbitmq.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of rabbitmq. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.rabbitmq.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of rabbitmq. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.rabbitmq.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of rabbitmq. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.rabbitmq.labels | {} | | This key allows you to set custom labels for the stateful deployment of rabbitmq. This is useful for organizing and selecting resources in your Kubernetes cluster. | +| services.rabbitmq.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of rabbitmq. This is useful for adding metadata or configuration hints to your resources. | +| services.rabbitmq.external_rabbitmq_url | | | Users can also decide to use the remote hosted service and link to Plane deployment. Ignoring all the above keys, set `services.rabbitmq.local_setup` to `false` and set this key with remote connection url. | + +#### OpenSearch Setup + +| Setting | Default | Required | Description | +| ------------------------------------- | :--------------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.opensearch.local_setup | false | | Plane uses `opensearch` as the search and analytics engine. This can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. AWS OpenSearch Service or similar services). Set this to `true` when you choose to setup stateful deployment of `opensearch`. Mark it as `false` when using a remotely hosted service | +| services.opensearch.image | opensearchproject/opensearch:3.3.2 | | Using this key, user must provide the docker image name to setup the stateful deployment of `opensearch`. (must be set when `services.opensearch.local_setup=true`) | +| services.opensearch.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of `opensearch`. (must be set when `services.opensearch.local_setup=true`) | +| services.opensearch.servicePort | 9200 | | This key sets the default port number to be used while setting up stateful deployment of `opensearch`. | +| services.opensearch.volumeSize | 5Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| services.opensearch.username | plane | | Credentials are required to access the hosted stateful deployment of `opensearch`. Use this key to set the username for the stateful deployment. | +| services.opensearch.password | Secure@Pass#123!%^&\* | | Credentials are required to access the hosted stateful deployment of `opensearch`. Use this key to set the password. **Password Complexity Requirements:** Must be at least 8 characters long and contain at least one uppercase letter, one lowercase letter, one digit, and one special character (e.g., `!@#$%^&*`). | +| services.opensearch.memoryLimit | 3Gi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.opensearch.cpuLimit | 750m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.opensearch.memoryRequest | 2Gi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.opensearch.cpuRequest | 500m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.opensearch.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.opensearch.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of opensearch. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.opensearch.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of opensearch. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.opensearch.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of opensearch. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.opensearch.labels | {} | | This key allows you to set custom labels for the stateful deployment of opensearch. This is useful for organizing and selecting resources in your Kubernetes cluster. | +| services.opensearch.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of opensearch. This is useful for adding metadata or configuration hints to your resources. | +| env.opensearch_remote_url | | | Users can also decide to use the remote hosted service and link to Plane deployment. Set `services.opensearch.local_setup` to `false` and set this key with remote connection url. | +| env.opensearch_remote_username | | | Username for remote OpenSearch service. Required when `services.opensearch.local_setup=false` and `env.opensearch_remote_url` is set. Note: This is not a secret and should be configured in values.yaml, not in external secrets. | +| env.opensearch_remote_password | | | Password for remote OpenSearch service. Required when `services.opensearch.local_setup=false` and `env.opensearch_remote_url` is set. Can be configured in values.yaml or provided via external secrets (`opensearch_existingSecret` with `OPENSEARCH_PASSWORD`). **Password Complexity Requirements:** Must be at least 8 characters long and contain at least one uppercase letter, one lowercase letter, one digit, and one special character. | +| env.opensearch_index_prefix | plane\_ | | Prefix to be used for OpenSearch indices. This helps organize indices in a multi-tenant or multi-environment setup. | + +#### Doc Store (Minio\/S3) Setup + +| Setting | Default | Required | Description | +| ------------------------------------- | :----------------: | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.minio.local_setup | true | | Plane uses `minio` as the default file storage drive. This storage can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws S3 or similar services). Set this to `true` when you choose to setup stateful deployment of `minio`. Mark it as `false` when using a remotely hosted database | +| services.minio.image | minio/minio:latest | | Using this key, user must provide the docker image name to setup the stateful deployment of `minio`. (must be set when `services.minio.local_setup=true`) | +| services.minio.image_mc | minio/mc:latest | | Using this key, user must provide the docker image name to setup the job deployment of `minio client`. (must be set when `services.minio.local_setup=true`) | +| services.minio.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of minio. (must be set when services.minio.local_setup=true) | +| services.minio.volumeSize | 3Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| services.minio.root_user | admin | | Storage credentials are requried to access the hosted stateful deployment of `minio`. Use this key to set the username for the stateful deployment. | +| services.minio.root_password | password | | Storage credentials are requried to access the hosted stateful deployment of `minio`. Use this key to set the password for the stateful deployment. | +| services.minio.env.minio_endpoint_ssl | false | | (Optional) Env to enforce HTTPS when connecting to minio uploads bucket | +| env.docstore_bucket | uploads | Yes | Storage bucket name is required as part of configuration. This is where files will be uploaded irrespective of if you are using `Minio` or external `S3` (or compatible) storage service | +| env.doc_upload_size_limit | 5242880 | Yes | Document Upload Size Limit (default to 5Mb) | +| services.minio.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.minio.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of minio. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.minio.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of minio. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.minio.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of minio. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.minio.labels | {} | | This key allows you to set custom labels for the stateful deployment of minio. This is useful for organizing and selecting resources in your Kubernetes cluster. | +| services.minio.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of minio. This is useful for adding metadata or configuration hints to your resources. | +| env.aws_access_key | | | External `S3` (or compatible) storage service provides `access key` for the application to connect and do the necessary upload or download operations. To be provided when `services.minio.local_setup=false` | +| env.aws_secret_access_key | | | External `S3` (or compatible) storage service provides `secret access key` for the application to connect and do the necessary upload or download operations. To be provided when `services.minio.local_setup=false` | +| env.aws_region | | | External `S3` (or compatible) storage service providers creates any buckets in user selected region. This is also shared with the user as `region` for the application to connect and do the necessary upload or download operations. To be provided when `services.minio.local_setup=false` | +| env.aws_s3_endpoint_url | | | External `S3` (or compatible) storage service providers shares a `endpoint_url` for the integration purpose for the application to connect and do the necessary upload or download operations. To be provided when `services.minio.local_setup=false` | +| env.use_storage_proxy | false | | When set to `true`, all S3 (or compatible) file GET requests from the browser are proxied through Plane's API service instead of accessing the S3 endpoint directly. Enable this if your storage endpoint is not accessible publicly or you want to control download access through the API. | + +#### Web Deployment + +| Setting | Default | Required | Description | +| ------------------------------ | :------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.web.replicas | 1 | Yes | Kubernetes helps you with scaling up or down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 | +| services.web.memoryLimit | 1000Mi | | Every deployment in Kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.web.cpuLimit | 500m | | Every deployment in Kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.web.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.web.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.web.image | `makeplane/web-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.web.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of web. | +| services.web.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.web.nodeSelector | {} | | This key allows you to set the node selector for the deployment of web. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.web.tolerations | [] | | This key allows you to set the tolerations for the deployment of web. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.web.affinity | {} | | This key allows you to set the affinity rules for the deployment of web. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.web.labels | {} | | Custom labels to add to the web deployment | +| services.web.annotations | {} | | Custom annotations to add to the web deployment | + +#### Space Deployment + +| Setting | Default | Required | Description | +| -------------------------------- | :--------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.space.replicas | 1 | Yes | Kubernetes helps you with scaling up or down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 | +| services.space.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.space.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.space.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.space.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.space.image | `makeplane/space-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.space.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of space. | +| services.space.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.space.nodeSelector | {} | | This key allows you to set the node selector for the deployment of space. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.space.tolerations | [] | | This key allows you to set the tolerations for the deployment of space. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.space.affinity | {} | | This key allows you to set the affinity rules for the deployment of space. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.space.labels | {} | | Custom labels to add to the space deployment | +| services.space.annotations | {} | | Custom annotations to add to the space deployment | + +#### Admin Deployment + +| Setting | Default | Required | Description | +| -------------------------------- | :--------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.admin.replicas | 1 | Yes | Kubernetes helps you with scaling up or down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 | +| services.admin.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.admin.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.admin.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.admin.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.admin.image | `makeplane/admin-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.admin.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of admin. | +| services.admin.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.admin.nodeSelector | {} | | This key allows you to set the node selector for the deployment of admin. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.admin.tolerations | [] | | This key allows you to set the tolerations for the deployment of admin. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.admin.affinity | {} | | This key allows you to set the affinity rules for the deployment of admin. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.admin.labels | {} | | Custom labels to add to the admin deployment. | +| services.admin.annotations | {} | | Custom annotations to add to the admin deployment. | + +#### Live Service Deployment + +| Setting | Default | Required | Description | +| ---------------------------------- | :--------------------------------: | :------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.live.replicas | 1 | Yes | Kubernetes helps you with scaling up\/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 | +| services.live.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.live.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.live.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.live.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.live.image | `makeplane/live-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.live.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of live. | +| env.live_sentry_dsn | | | (optional) Live service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry provided DSN for this integration. | +| env.live_sentry_environment | | | (optional) Live service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry environment name (as configured in Sentry) for this integration. | +| env.live_sentry_traces_sample_rate | | | (optional) Live service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry trace sample rate (as configured in Sentry) for this integration. | +| env.live_server_secret_key | htbqvBJAgpm9bzvf3r4urJer0ENReatceh | | Live Server Secret Key | +| env.external_iframely_url | "" | | External Iframely service URL. If provided, the local Iframely deployment will be skipped and the live service will use this external URL | +| services.live.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service. | +| services.live.nodeSelector | {} | | This key allows you to set the node selector for the deployment of live. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.live.tolerations | [] | | This key allows you to set the tolerations for the deployment of live. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.live.affinity | {} | | This key allows you to set the affinity rules for the deployment of live. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.live.labels | {} | | Custom labels to add to the live deployment. | +| services.live.annotations | {} | | Custom annotations to add to the live deployment. | + +#### Monitor Deployment + +| Setting | Default | Required | Description | +| ---------------------------------- | :----------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.monitor.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.monitor.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.monitor.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.monitor.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.monitor.image | `makeplane/monitor-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.monitor.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of monitor. | +| services.monitor.volumeSize | 100Mi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| services.monitor.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.monitor.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of monitor. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.monitor.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of monitor. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.monitor.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of monitor. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.monitor.labels | {} | | Custom labels to add to the monitor deployment | +| services.monitor.annotations | {} | | Custom annotations to add to the monitor deployment | + +#### API Deployment + +| Setting | Default | Required | Description | +| ------------------------------ | :----------------------------: | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.api.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 | +| services.api.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.api.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.api.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.api.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.api.image | `makeplane/backend-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.api.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of api. | +| env.sentry_dsn | | | (optional) API service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry-provided DSN for this integration. | +| env.sentry_environment | | | (optional) API service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry environment name (as configured in Sentry) for this integration. | +| env.api_key_rate_limit | 60/minute | | (optional) User can set the maximum number of requests the API can handle in a given time frame. | +| env.web_url | | | (optional) Custom Web URL for the application. If not set, it will be auto-generated based on the license domain and SSL settings. | +| services.api.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.api.nodeSelector | {} | | This key allows you to set the node selector for the deployment of api. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.api.tolerations | [] | | This key allows you to set the tolerations for the deployment of api. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.api.affinity | {} | | This key allows you to set the affinity rules for the deployment of api. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.api.labels | {} | | Custom labels to add to the API deployment | +| services.api.annotations | {} | | Custom annotations to add to the API deployment | + +#### Silo Deployment + +| Setting | Default | Required | Description | +| :-------------------------------------------- | :--------------------------------- | :-------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.silo.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 | +| services.silo.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.silo.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.silo.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.silo.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.silo.image | `makeplane/silo-commercial` | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | +| services.silo.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of `silo`. | +| services.silo.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service | +| services.silo.nodeSelector | {} | | This key allows you to set the node selector for the deployment of silo. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.silo.tolerations | [] | | This key allows you to set the tolerations for the deployment of silo. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.silo.affinity | {} | | This key allows you to set the affinity rules for the deployment of silo. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.silo.labels | {} | | Custom labels to add to the silo deployment | +| services.silo.annotations | {} | | Custom annotations to add to the silo deployment | +| services.silo.connectors.slack.enabled | false | | Slack Integration | +| services.silo.connectors.slack.client_id | "" | required if `services.silo.connectors.slack.enabled` is `true` | Slack Client ID | +| services.silo.connectors.slack.client_secret | "" | required if `services.silo.connectors.slack.enabled` is `true` | Slack Client Secret | +| services.silo.connectors.github.enabled | false | | Github App Integration | +| services.silo.connectors.github.client_id | "" | required if `services.silo.connectors.github.enabled` is `true` | Github Client ID | +| services.silo.connectors.github.client_secret | "" | required if `services.silo.connectors.github.enabled` is `true` | Github Client Secret | +| services.silo.connectors.github.app_name | "" | required if `services.silo.connectors.github.enabled` is `true` | Github App Name | +| services.silo.connectors.github.app_id | "" | required if `services.silo.connectors.github.enabled` is `true` | Github App ID | +| services.silo.connectors.github.private_key | "" | required if `services.silo.connectors.github.enabled` is `true` | Github Private Key | +| services.silo.connectors.gitlab.enabled | false | | Gitlab App Integration | +| services.silo.connectors.gitlab.client_id | "" | required if `services.silo.connectors.gitlab.enabled` is `true` | Gitlab Client ID | +| services.silo.connectors.gitlab.client_secret | "" | required if `services.silo.connectors.gitlab.enabled` is `true` | Gitlab Client Secret | +| env.silo_envs.mq_prefetch_count | 10 | | Prefetch count for RabbitMQ | +| env.silo_envs.batch_size | 60 | | Batch size for Silo | +| env.silo_envs.request_interval | 400 | | Request interval for Silo | +| env.silo_envs.sentry_dsn | | | Sentry DSN | +| env.silo_envs.sentry_environment | | | Sentry Environment | +| env.silo_envs.sentry_traces_sample_rate | | | Sentry Traces Sample Rate | +| env.silo_envs.hmac_secret_key | <random-32-bit-string> | | HMAC Secret Key | +| env.silo_envs.aes_secret_key | "dsOdt7YrvxsTIFJ37pOaEVvLxN8KGBCr" | | AES Secret Key | + +#### Plane AI deployment + +::: info Plane AI database +Plane AI uses a separate PostgreSQL database. Create a new database (e.g. `plane_pi`) and connect it using `env.pg_pi_db_remote_url` in values, or **PLANE_PI_DATABASE_URL** when using `pi_api_env_existingSecret`. +::: + +| Setting | Default | Required | Description | +| --------------------------------- | :--------------------------------: | :------: | ---------------------------------------------------------------------------------------------------------------------------------------- | +| services.pi.enabled | false | No | Set to `true` to enable the Plane AI service and its API, worker, beat, and migrator workloads. | +| services.pi.replicas | 1 | Yes | Number of replicas for the Plane AI API deployment. It must be >=1. | +| services.pi.memoryLimit | 1000Mi | | Memory limit for the Plane AI API deployment. | +| services.pi.cpuLimit | 500m | | CPU limit for the Plane AI API deployment. | +| services.pi.memoryRequest | 50Mi | | Memory request for the Plane AI API deployment. | +| services.pi.cpuRequest | 50m | | CPU request for the Plane AI API deployment. | +| services.pi.image | makeplane/plane-pi-commercial | | Docker image for the Plane AI service. | +| services.pi.pullPolicy | Always | | Image pull policy for the Plane AI deployment. | +| services.pi.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the Plane AI API service. | +| services.pi.nodeSelector | {} | | Node selector for the Plane AI API deployment. | +| services.pi.tolerations | [] | | Tolerations for the Plane AI API deployment. | +| services.pi.affinity | {} | | Affinity rules for the Plane AI API deployment. | +| services.pi.labels | {} | | Custom labels to add to the Plane AI API deployment. | +| services.pi.annotations | {} | | Custom annotations to add to the Plane AI API deployment. | +| env.pg_pi_db_name | plane_pi | | PostgreSQL database name used by Plane AI when `postgres.local_setup=true`. | +| env.pg_pi_db_remote_url | "" | | PostgreSQL connection URL for Plane AI when using a remote database. Required when `postgres.local_setup=false` and Plane AI is enabled. | +| env.pi_envs.follower_postgres_uri | Same as Plane DATABASE_URL | No | Connection string for a Plane PostgreSQL DB read replica. Used for read-heavy operations to reduce load on the primary database. | +| env.pi_envs.internal_secret | tyfvfqvBJAgpm9bzvf3r4urJer0Ehfdubk | | Internal secret used by Plane AI for OAuth and internal APIs. | +| env.pi_envs.plane_api_host | "" | | Override for the Plane API host URL used by Plane AI. Defaults to the license domain. | +| env.pi_envs.cors_allowed_origins | "" | | CORS allowed origins for Plane AI API. Defaults to the license domain. | + +#### Plane AI Worker Deployment + +| Setting | Default | Required | Description | +| -------------------------------- | :-----: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.pi_worker.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for the Plane AI worker. This key helps you set the number of replicas. It must be >=1. | +| services.pi_worker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for the Plane AI worker deployment to use. | +| services.pi_worker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for the Plane AI worker deployment to use. | +| services.pi_worker.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for the Plane AI worker deployment to use. | +| services.pi_worker.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for the Plane AI worker deployment to use. | +| services.pi_worker.nodeSelector | {} | | This key allows you to set the node selector for the deployment of `pi_worker`. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.pi_worker.tolerations | [] | | This key allows you to set the tolerations for the deployment of `pi_worker`. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.pi_worker.affinity | {} | | This key allows you to set the affinity rules for the deployment of `pi_worker`. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.pi_worker.labels | {} | | Custom labels to add to the Plane AI worker deployment | +| services.pi_worker.annotations | {} | | Custom annotations to add to the Plane AI worker deployment | + +#### Plane AI Beat-Worker Deployment + +| Setting | Default | Required | Description | +| ------------------------------------- | :-----: | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.pi_beat_worker.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for the Plane AI beat-worker. This key helps you set the number of replicas. It must be >=1. | +| services.pi_beat_worker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for the Plane AI beat-worker deployment to use. | +| services.pi_beat_worker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for the Plane AI beat-worker deployment to use. | +| services.pi_beat_worker.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for the Plane AI beat-worker deployment to use. | +| services.pi_beat_worker.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for the Plane AI beat-worker deployment to use. | +| services.pi_beat_worker.nodeSelector | {} | | This key allows you to set the node selector for the deployment of `pi_beat_worker`. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.pi_beat_worker.tolerations | [] | | This key allows you to set the tolerations for the deployment of `pi_beat_worker`. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.pi_beat_worker.affinity | {} | | This key allows you to set the affinity rules for the deployment of `pi_beat_worker`. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.pi_beat_worker.labels | {} | | Custom labels to add to the Plane AI beat-worker deployment | +| services.pi_beat_worker.annotations | {} | | Custom annotations to add to the Plane AI beat-worker deployment | + +#### Worker Deployment + +| Setting | Default | Required | Description | +| ----------------------------- | :-----: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.worker.replicas | 1 | Yes | Kubernetes helps you with scaling up or down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 | +| services.worker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.worker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.worker.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.worker.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.worker.nodeSelector | {} | | This key allows you to set the node selector for the deployment of worker. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.worker.tolerations | [] | | This key allows you to set the tolerations for the deployment of worker. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.worker.affinity | {} | | This key allows you to set the affinity rules for the deployment of worker. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.worker.labels | {} | | Custom labels to add to the worker deployment | +| services.worker.annotations | {} | | Custom annotations to add to the worker deployment | + +#### Beat-Worker Deployment + +| Setting | Default | Required | Description | +| --------------------------------- | :-----: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| services.beatworker.replicas | 1 | Yes | Kubernetes helps you with scaling up or down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 | +| services.beatworker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | +| services.beatworker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | +| services.beatworker.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | +| services.beatworker.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | +| services.beatworker.nodeSelector | {} | | This key allows you to set the node selector for the deployment of beatworker. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.beatworker.tolerations | [] | | This key allows you to set the tolerations for the deployment of beatworker. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.beatworker.affinity | {} | | This key allows you to set the affinity rules for the deployment of beatworker. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.beatworker.labels | {} | | Custom labels to add to the beat-worker deployment | +| services.beatworker.annotations | {} | | Custom annotations to add to the beat-worker deployment | + +#### Email Service Deployment + +| Setting | Default | Required | Description | +| ------------------------------------ | -------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.email_service.enabled | false | | Set to `true` to enable the email service deployment | +| services.email_service.replicas | 1 | | Number of replicas for the email service deployment | +| services.email_service.memoryLimit | 1000Mi | | Memory limit for the email service deployment | +| services.email_service.cpuLimit | 500m | | CPU limit for the email service deployment | +| services.email_service.memoryRequest | 50Mi | | Memory request for the email service deployment | +| services.email_service.cpuRequest | 50m | | CPU request for the email service deployment | +| services.email_service.image | makeplane/email-commercial | | Docker image for the email service deployment | +| services.email_service.pullPolicy | Always | | Image pull policy for the email service deployment | +| services.email_service.nodeSelector | {} | | This key allows you to set the node selector for the deployment of `email_service`. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.email_service.tolerations | [] | | This key allows you to set the tolerations for the deployment of `email_service`. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.email_service.affinity | {} | | This key allows you to set the affinity rules for the deployment of `email_service`. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.email_service.labels | {} | | Custom labels to add to the email service deployment | +| services.email_service.annotations | {} | | Custom annotations to add to the email service deployment | +| env.email_service_envs.smtp_domain | | Yes | The SMTP Domain to be used with email service | + +::: info +When the email service is enabled, the cert-issuer will be automatically created to handle TLS certificates for the email service. +::: + +#### Outbox Poller Service Deployment + +| Setting | Default | Required | Description | +| ------------------------------------------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.outbox_poller.enabled | false | | Set to true to enable the outbox poller service deployment | +| services.outbox_poller.replicas | 1 | | Number of replicas for the outbox poller service deployment | +| services.outbox_poller.memoryLimit | 1000Mi | | Memory limit for the outbox poller service deployment | +| services.outbox_poller.cpuLimit | 500m | | CPU limit for the outbox poller service deployment | +| services.outbox_poller.memoryRequest | 50Mi | | Memory request for the outbox poller service deployment | +| services.outbox_poller.cpuRequest | 50m | | CPU request for the outbox poller service deployment | +| services.outbox_poller.pullPolicy | Always | | Image pull policy for the outbox poller service deployment | +| services.outbox_poller.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | +| services.outbox_poller.nodeSelector | {} | | This key allows you to set the node selector for the deployment of outbox_poller. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.outbox_poller.tolerations | [] | | This key allows you to set the tolerations for the deployment of outbox_poller. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.outbox_poller.affinity | {} | | This key allows you to set the affinity rules for the deployment of outbox_poller. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.outbox_poller.labels | {} | | Custom labels to add to the outbox poller deployment | +| services.outbox_poller.annotations | {} | | Custom annotations to add to the outbox poller deployment | +| env.outbox_poller_envs.memory_limit_mb | 400 | | Memory limit in MB for the outbox poller | +| env.outbox_poller_envs.interval_min | 0.25 | | Minimum interval in minutes for polling | +| env.outbox_poller_envs.interval_max | 2 | | Maximum interval in minutes for polling | +| env.outbox_poller_envs.batch_size | 250 | | Batch size for processing outbox messages | +| env.outbox_poller_envs.memory_check_interval | 30 | | Memory check interval in seconds | +| env.outbox_poller_envs.pool.size | 4 | | Pool size for database connections | +| env.outbox_poller_envs.pool.min_size | 2 | | Minimum pool size for database connections | +| env.outbox_poller_envs.pool.max_size | 10 | | Maximum pool size for database connections | +| env.outbox_poller_envs.pool.timeout | 30.0 | | Pool timeout in seconds | +| env.outbox_poller_envs.pool.max_idle | 300.0 | | Maximum idle time for connections in seconds | +| env.outbox_poller_envs.pool.max_lifetime | 3600 | | Maximum lifetime for connections in seconds | +| env.outbox_poller_envs.pool.reconnect_timeout | 5.0 | | Reconnect timeout in seconds | +| env.outbox_poller_envs.pool.health_check_interval | 60 | | Health check interval in seconds | + +#### Automation Consumer Deployment + +| Setting | Default | Required | Description | +| ---------------------------------------------------- | -------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.automation_consumer.enabled | false | | Set to true to enable the automation consumer service deployment | +| services.automation_consumer.replicas | 1 | | Number of replicas for the automation consumer service deployment | +| services.automation_consumer.memoryLimit | 1000Mi | | Memory limit for the automation consumer service deployment | +| services.automation_consumer.cpuLimit | 500m | | CPU limit for the automation consumer service deployment | +| services.automation_consumer.memoryRequest | 50Mi | | Memory request for the automation consumer service deployment | +| services.automation_consumer.cpuRequest | 50m | | CPU request for the automation consumer service deployment | +| services.automation_consumer.pullPolicy | Always | | Image pull policy for the automation consumer service deployment | +| services.automation_consumer.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | +| services.automation_consumer.nodeSelector | {} | | This key allows you to set the node selector for the deployment of automation_consumer. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.automation_consumer.tolerations | [] | | This key allows you to set the tolerations for the deployment of automation_consumer. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.automation_consumer.affinity | {} | | This key allows you to set the affinity rules for the deployment of automation_consumer. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.automation_consumer.labels | {} | | Custom labels to add to the automation consumer deployment | +| services.automation_consumer.annotations | {} | | Custom annotations to add to the automation consumer deployment | +| env.automation_consumer_envs.event_stream_queue_name | "plane.event_stream.automations" | | Event stream queue name for automations | +| env.automation_consumer_envs.event_stream_prefetch | 10 | | Event stream prefetch count | +| env.automation_consumer_envs.exchange_name | "plane.event_stream" | | Exchange name for event stream | +| env.automation_consumer_envs.event_types | "issue" | | Event types to process | + +#### Iframely Deployment + +| Setting | Default | Required | Description | +| ----------------------------------- | ------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.iframely.enabled | false | | Set to true to enable the Iframely service deployment | +| services.iframely.replicas | 1 | | Number of replicas for the Iframely service deployment | +| services.iframely.memoryLimit | 1000Mi | | Memory limit for the Iframely service deployment | +| services.iframely.cpuLimit | 500m | | CPU limit for the Iframely service deployment | +| services.iframely.memoryRequest | 50Mi | | Memory request for the Iframely service deployment | +| services.iframely.cpuRequest | 50m | | CPU request for the Iframely service deployment | +| services.iframely.image | makeplane/iframely:v1.2.0 | | Docker image for the Iframely service deployment | +| services.iframely.pullPolicy | Always | | Image pull policy for the Iframely service deployment | +| services.iframely.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | +| services.iframely.nodeSelector | {} | | This key allows you to set the node selector for the deployment of iframely. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | +| services.iframely.tolerations | [] | | This key allows you to set the tolerations for the deployment of iframely. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | +| services.iframely.affinity | {} | | This key allows you to set the affinity rules for the deployment of iframely. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | +| services.iframely.labels | {} | | Custom labels to add to the iframely deployment | +| services.iframely.annotations | {} | | Custom annotations to add to the iframely deployment | + +#### External Secrets Config + +To configure the external secrets for your application, you need to define specific environment variables for each secret category. Below is a list of the required secrets and their respective environment variables. + +| Secret Name | Env Var Name | Required | Description | Example Value | +| ------------------------- | --------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| rabbitmq_existingSecret | RABBITMQ_DEFAULT_USER | Required if `rabbitmq.local_setup=true` | The default RabbitMQ user | plane | +| | RABBITMQ_DEFAULT_PASS | Required if `rabbitmq.local_setup=true` | The default RabbitMQ password | plane | +| pgdb_existingSecret | POSTGRES_PASSWORD | Required if `postgres.local_setup=true` | Password for PostgreSQL database | plane | +| | POSTGRES_DB | Required if `postgres.local_setup=true` | Name of the PostgreSQL database | plane | +| | POSTGRES_USER | Required if `postgres.local_setup=true` | PostgreSQL user | plane | +| opensearch_existingSecret | OPENSEARCH_ENABLED | Yes | Flag to enable OpenSearch | 1 (enabled) or 0 (disabled) | +| | OPENSEARCH_URL | Required if OpenSearch is enabled | OpenSearch connection URL | **k8s service example:** `http://plane-opensearch.plane-ns.svc.cluster.local:9200` **external service example:** `https://your-opensearch-host:9200` | +| | OPENSEARCH_USERNAME | Required if OpenSearch is enabled | Username for OpenSearch | **local setup:** plane **remote setup:** your_remote_username | +| | OPENSEARCH_PASSWORD | Required if OpenSearch is enabled | Password for OpenSearch | **local setup:** Secure@Pass#123!%^&\* **remote setup:** your_remote_password | +| | OPENSEARCH_INITIAL_ADMIN_PASSWORD | Required if `opensearch.local_setup=true` | Initial admin password for local OpenSearch | Secure@Pass#123!%^&\* | +| | OPENSEARCH_INDEX_PREFIX | Optional | Prefix for OpenSearch indices | plane\_ | +| doc_store_existingSecret | USE_MINIO | Yes | Flag to enable MinIO as the storage backend | 1 | +| | MINIO_ROOT_USER | Yes | MinIO root user | admin | +| | MINIO_ROOT_PASSWORD | Yes | MinIO root password | password | +| | AWS_ACCESS_KEY_ID | Yes | AWS Access Key ID | your_aws_key | +| | AWS_SECRET_ACCESS_KEY | Yes | AWS Secret Access Key | your_aws_secret | +| | AWS_S3_BUCKET_NAME | Yes | AWS S3 Bucket Name | your_bucket_name | +| | AWS_S3_ENDPOINT_URL | Yes | Endpoint URL for AWS S3 or MinIO | `http://plane-minio.plane-ns.svc.cluster.local:9000` | +| | AWS_REGION | Optional | AWS region where your S3 bucket is located | your_aws_region | +| | FILE_SIZE_LIMIT | Yes | Limit for file uploads in your system | 5MB | +| app_env_existingSecret | SECRET_KEY | Yes | Random secret key | 60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5 | +| | REDIS_URL | Yes | Redis URL | `redis://plane-redis.plane-ns.svc.cluster.local:6379/` | +| | DATABASE_URL | Yes | PostgreSQL connection URL | k8s service example: `postgresql://plane:plane@plane-pgdb.plane-ns.svc.cluster.local:5432/plane` external service example: `postgresql://username:password@your-db-host:5432/plane` | +| | AMQP_URL | Yes | RabbitMQ connection URL | k8s service example: `amqp://plane:plane@plane-rabbitmq.plane-ns.svc.cluster.local:5672/` external service example: `amqp://username:password@your-rabbitmq-host:5672/` | +| live_env_existingSecret | REDIS_URL | Yes | Redis URL | `redis://plane-redis.plane-ns.svc.cluster.local:6379/` | +| silo_env_existingSecret | SILO_HMAC_SECRET_KEY | Yes | Silo HMAC secret Key | `` | +| | REDIS_URL | Yes | Redis URL | redis://plane-redis.plane-ns.svc.cluster.local:6379/ | +| | DATABASE_URL | Yes | PostgreSQL connection URL | k8s service example: postgresql://plane:plane@plane-pgdb.plane-ns.svc.cluster.local:5432/plane external service example: postgresql://username:password@your-db-host:5432/plane | +| | AMQP_URL | Yes | RabbitMQ connection URL | k8s service example: amqp://plane:plane@plane-rabbitmq.plane-ns.svc.cluster.local:5672/ external service example: amqp://username:password@your-rabbitmq-host:5672/ | +| | GITHUB_APP_NAME | Required if `services.silo.connectors.github.enabled` is true | GitHub app name | your_github_app_name | +| | GITHUB_APP_ID | Required if `services.silo.connectors.github.enabled` is true | GitHub app ID | your_github_app_id | +| | GITHUB_CLIENT_ID | Required if `services.silo.connectors.github.enabled` is true | GitHub client ID | your_github_client_id | +| | GITHUB_CLIENT_SECRET | Required if `services.silo.connectors.github.enabled` is true | GitHub client secret key | your_github_client_secret_key | +| | GITHUB_PRIVATE_KEY | Required if `services.silo.connectors.github.enabled` is true | GitHub private key | your_github_private_key | +| | SLACK_CLIENT_ID | Required if `services.silo.connectors.slack.enabled` is true | Slack client ID | your_slack_client_id | +| | SLACK_CLIENT_SECRET | Required if `services.silo.connectors.slack.enabled` is true | Slack client secret key | your_slack_client_secret_key | +| | GITLAB_CLIENT_ID | Required if `services.silo.connectors.gitlab.enabled` is true | GitLab client ID | your_gitlab_client_id | +| | GITLAB_CLIENT_SECRET | Required if `services.silo.connectors.gitlab.enabled` is true | GitLab client secret key | your_gitlab_client_secret_key | +| pi_api_env_existingSecret | PLANE_PI_DATABASE_URL | Required if `services.pi.enabled=true` | PostgreSQL connection URL for Plane AI database | **k8s service example**: `postgresql://plane:plane@plane-pgdb.plane-ns.svc.cluster.local/plane_pi`

**external**: `postgresql://username:password@your-db-host:5432/plane_pi` | +| | FOLLOWER_POSTGRES_URI | No | Connection string for a PostgreSQL read replica | Same as DATABASE_URL. Used for read-heavy operations to reduce load on the primary database. **k8s**: `postgresql://plane:plane@plane-pgdb.plane-ns.svc.cluster.local:5432/plane` | +| | AMQP_URL | Required if `services.pi.enabled=true` | RabbitMQ connection URL | **k8s service example**: `amqp://plane:plane@plane-rabbitmq.plane-ns.svc.cluster.local:5672/`

**external**: `amqp://username:password@your-rabbitmq-host:5672/` | +| | AES_SECRET_KEY | Required if `services.pi.enabled=true` | AES secret key for Plane AI | dsOdt7YrvxsTIFJ37pOaEVvLxN8KGBCr (or your own value) | +| | OPENAI_API_KEY | required if `services.pi.ai_providers.openai.enabled` is true | OpenAI API key | your_openai_api_key | +| | CLAUDE_API_KEY | required if `services.pi.ai_providers.claude.enabled` is true | Claude API key | your_claude_api_key | +| | GROQ_API_KEY | required if `services.pi.ai_providers.groq.enabled` is true | Groq API key | your_groq_api_key | +| | COHERE_API_KEY | required if `services.pi.ai_providers.cohere.enabled` is true | Cohere API key | your_cohere_api_key | +| | CUSTOM_LLM_API_KEY | required if `services.pi.ai_providers.custom_llm.enabled` is true | Custom LLM API key | your_custom_llm_api_key | +| | BR_AWS_SECRET_ACCESS_KEY | required if `services.pi.ai_providers.embedding_model.enabled` is true | AWS secret for embedding model | your_aws_secret_access_key | +| | BR_AWS_SESSION_TOKEN | required if embedding model uses temporary credentials | AWS session token for embedding model | your_aws_session_token | + +#### Ingress and SSL Setup + +| Setting | Default | Required | Description | +| --------------------------- | --------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ingress.enabled | true | | Ingress setup in kubernetes is a common practice to expose application to the intended audience. Set it to false if you are using external ingress providers like Cloudflare | +| ingress.minioHost | | | Based on above configuration, if you want to expose the minio web console to set of users, use this key to set the host mapping or leave it as EMPTY to not expose interface. | +| ingress.rabbitmqHost | | | Based on above configuration, if you want to expose the rabbitmq web console to set of users, use this key to set the host mapping or leave it as EMPTY to not expose interface. | +| ingress.ingressClass | nginx | Yes | Kubernetes cluster setup comes with various options of ingressClass. Based on your setup, set this value to the right one (eg. nginx, traefik, etc). Leave it to default in case you are using external ingress provider. | +| ingress.ingress_annotations | { `"nginx.ingress.kubernetes.io/proxy-body-size": "5m"` } | | Ingress controllers comes with various configuration options which can be passed as annotations. Setting this value lets you change the default value to user required. | +| ssl.createIssuer | false | | Kubernets cluster setup supports creating issuer type resource. After deployment, this is step towards creating secure access to the ingress url. Issuer is required for you generate SSL certifiate. Kubernetes can be configured to use any of the certificate authority to generate SSL (depending on CertManager configuration). Set it to true to create the issuer. Applicable only when ingress.enabled=true | +| ssl.issuer | http | | CertManager configuration allows user to create issuers using http or any of the other DNS Providers like cloudflare, digitalocean, etc. As of now Plane supports http, cloudflare, digitalocean | +| ssl.token | | | To create issuers using DNS challenge, set the issuer api token of dns provider like cloudflare or digitalocean (not required for http) | +| ssl.server | https://acme-v02.api.letsencrypt.org/directory | | Issuer creation configuration need the certificate generation authority server url. Default URL is the Let's Encrypt server | +| ssl.email | plane@example.com | | Certificate generation authority needs a valid email id before generating certificate. Required when ssl.createIssuer=true | +| ssl.generateCerts | false | | After creating the issuers, user can still not create the certificate untill sure of configuration. Setting this to true will try to generate SSL certificate and associate with ingress. Applicable only when ingress.enabled=true and ssl.createIssuer=true | +| ssl.tls_secret_name | | | If you have a custom TLS secret name, set this to the name of the secret. Applicable only when ingress.enabled=true and ssl.createIssuer=false | + +#### Common Environment Settings + +| Setting | Default | Required | Description | +| ---------------- | :------------------------------------------------: | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| env.storageClass | longhorn | | Creating the persitant volumes for the stateful deployments needs the `storageClass` name. Set the correct value as per your kubernetes cluster configuration. | +| env.secret_key | 60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5 | Yes | This must be a random string which is used for hashing/encrypting the sensitive data within the application. Once set, changing this might impact the already hashed/encrypted data | + +#### Extra Environment Variables + +| Setting | Default | Required | Description | +| -------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| extraEnv | [] | No | Global extra environment variables that will be applied to all workloads. This allows you to add custom environment variables to all deployments (web, api, worker, etc.). Useful for proxy settings, custom configurations, or any environment-specific variables. Some example variables are HTTP_PROXY, HTTPS_PROXY, NO_PROXY. | + +## Custom Ingress Routes + +If you are planning to use 3rd party ingress providers, here is the available route configuration. + +| Host | Path | Service | Required | +| ----------------------- | :-------------: | --------------------------------------- | :-------------------------------------------------------------------------- | +| plane.example.com | / | | Yes | +| plane.example.com | /spaces/\* | | Yes | +| plane.example.com | /god-mode/\* | | Yes | +| plane.example.com | /live/\* | | Yes | +| plane.example.com | /silo/\* | | Yes (if `services.silo.enabled=true` ) | +| plane.example.com | /pi/\* | | Yes (if `services.pi.enabled=true`) | +| plane.example.com | /api/\* | | Yes | +| plane.example.com | /auth/\* | | Yes | +| plane.example.com | /graphql/\* | | Yes | +| plane.example.com | /marketplace/\* | | Yes | +| plane.example.com | /uploads/\* | | Yes (Only if using local setup) | +| plane-minio.example.com | / | | (Optional) if using local setup, this will enable minio console access | +| plane-mq.example.com | / | | (Optional) if using local setup, this will enable management console access | + +::: details Install Community Edition +The Commercial edition comes with a free plan and the flexibility to upgrade to a paid plan at any point. If you still want to install the Community edition, follow the steps below: + +#### Prerequisites + +- A working Kubernetes cluster +- `kubectl` and `helm` on the client system that you will use to install our Helm charts + +#### Installation + +1. Open Terminal or any other command-line app that has access to Kubernetes tools on your local system. +2. Add the Helm Repo + + ```bash + helm repo add makeplane https://helm.plane.so/ + helm repo update + ``` + +3. Use one of the following ways to deploy Plane: - + **Quick setup** + + This is the fastest way to deploy Plane with default settings. This will create stateful deployments for Postgres, Redis, and Minio with a persistent volume claim using the `longhorn` storage class. This also sets up the ingress routes for you using `nginx` ingress class. + + ::: tip + To customize this, see `Custom ingress routes` below. + ::: + + Continue to be on the same Terminal window as you have so far, copy the code below, and paste it on your Terminal screen. + + ```bash + helm install plane-app makeplane/plane-ce \ + --create-namespace \ + --namespace plane-ce \ + --set planeVersion=stable \ + --set ingress.appHost="plane.example.com" \ + --set ingress.minioHost="plane-minio.example.com" \ + --set ingress.ingressClass=nginx \ + --set postgres.storageClass=longhorn \ + --set redis.storageClass=longhorn \ + --set minio.storageClass=longhorn \ + --timeout 10m \ + --wait \ + --wait-for-jobs + ``` + + ::: tip + This is the minimum required to set up Plane-CE. You can change the default namespace from `plane-ce`, the default app name from `plane-app`, the default storage class from `[postgres, redis, minio].storageClass`, and the default ingress class from `ingress.ingressClass` to whatever you would like to. + + You can also pass other settings referring to `Configuration Settings` section. + ::: + - **Advanced setup** + For more control over your set-up, run the script below to download the `values.yaml` file and and edit using any editor like Vim or Nano. + + ```bash + helm show values makeplane/plane-ce > values.yaml + vi values.yaml + ``` + + ::: tip + See **Configuration settings** below for more details. + ::: + + After saving the `values.yaml` file, continue to be on the same Terminal window as on the previous steps, copy the code below, and paste it on your Terminal screen. + + ```bash + helm install plane-app makeplane/plane-ce \ + --create-namespace \ + --namespace plane-ce \ + -f values.yaml \ + --timeout 10m \ + --wait \ + --wait-for-jobs + ``` + + #### Configuration settings + + ##### Plane Version + + | Setting | Default | Required | Description | + | ------------ | ------- | -------- | ----------- | + | planeVersion | v1.1.0 | Yes | | + + ##### Postgres DB Setup + + | Setting | Default | Required | Description | + | -------------------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | postgres.local_setup | true | | Plane uses postgres as the primary database to store all the transactional data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to true when you choose to setup stateful deployment of postgres. Mark it as false when using a remotely hosted database | + | postgres.image | postgres:15.7-alpine | | Using this key, user must provide the docker image name to setup the stateful deployment of postgres. (must be set when `postgres.local_setup=true`) | + | postgres.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of postgres. (must be set when `postgres.local_setup=true`) | + | postgres.servicePort | 5432 | | This key sets the default port number to be used while setting up stateful deployment of postgres. | + | postgres.volumeSize | 5Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | + | env.pgdb_username | plane | | Database credentials are requried to access the hosted stateful deployment of postgres. Use this key to set the username for the stateful deployment. | + | env.pgdb_password | plane | | Database credentials are requried to access the hosted stateful deployment of postgres. Use this key to set the password for the stateful deployment. | + | env.pgdb_name | plane | | Database name to be used while setting up stateful deployment of Postgres | + | env.pgdb_remote_url | | | Users can also decide to use the remote hosted database and link to Plane deployment. Ignoring all the above keys, set postgres.local_setup to false and set this key with remote connection url. | + | postgres.storageClass | `` | | Creating the persitant volumes for the stateful deployments needs the storageClass name. Set the correct value as per your kubernetes cluster configuration. | + | postgres.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | + | postgres.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of postgres. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | + | postgres.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of postgres. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | + | postgres.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of postgres. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | + | postgres.labels | {} | | This key allows you to set custom labels for the stateful deployment of postgres. This is useful for organizing and selecting resources in your Kubernetes cluster. | + | postgres.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of postgres. This is useful for adding metadata or configuration hints to your resources. | + + ##### Redis/Valkey Setup + + | Setting | Default | Required | Description | + | ----------------------- | ----------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | redis.local_setup | true | | Plane uses redis to cache the session authentication and other static data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to true when you choose to setup stateful deployment of redis. Mark it as false when using a remotely hosted database | + | redis.image | `valkey/valkey:7.2.5-alpine` | | Using this key, user must provide the docker image name to setup the stateful deployment of redis. (must be set when redis.local_setup=true) | + | redis.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of redis. (must be set when `redis.local_setup=true`) | + | redis.servicePort | 6379 | | This key sets the default port number to be used while setting up stateful deployment of redis. | + | redis.volumeSize | 1Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | + | env.remote_redis_url | | | Users can also decide to use the remote hosted database and link to Plane deployment. Ignoring all the above keys, set redis.local_setup to false and set this key with remote connection url. | + | redis.storageClass | `` | | Creating the persitant volumes for the stateful deployments needs the storageClass name. Set the correct value as per your kubernetes cluster configuration. | + | redis.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | + | redis.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of redis. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | + | redis.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of redis. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | + | redis.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of redis. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | + | redis.labels | {} | | This key allows you to set custom labels for the stateful deployment of redis. This is useful for organizing and selecting resources in your Kubernetes cluster. | + | redis.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of redis. This is useful for adding metadata or configuration hints to your resources. | + + ##### RabbitMQ Setup + + | Setting | Default | Required | Description | + | ------------------------------ | --------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | rabbitmq.local_setup | true | | Plane uses rabbitmq as message queuing system. This can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws mq or similar services). Set this to true when you choose to setup stateful deployment of rabbitmq. Mark it as false when using a remotely hosted service | + | rabbitmq.image | rabbitmq:3.13.6-management-alpine | | Using this key, user must provide the docker image name to setup the stateful deployment of rabbitmq. (must be set when `rabbitmq.local_setup=true`) | + | rabbitmq.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of rabbitmq. (must be set when `rabbitmq.local_setup=true`) | + | rabbitmq.servicePort | 5672 | | This key sets the default port number to be used while setting up stateful deployment of rabbitmq. | + | rabbitmq.managementPort | 15672 | | This key sets the default management port number to be used while setting up stateful deployment of rabbitmq. | + | rabbitmq.volumeSize | 100Mi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | + | rabbitmq.storageClass | `` | | Creating the persitant volumes for the stateful deployments needs the storageClass name. Set the correct value as per your kubernetes cluster configuration. | + | rabbitmq.default_user | plane | | Credentials are requried to access the hosted stateful deployment of rabbitmq. Use this key to set the username for the stateful deployment. | + | rabbitmq.default_password | plane | | Credentials are requried to access the hosted stateful deployment of rabbitmq. Use this key to set the password for the stateful deployment. | + | rabbitmq.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | + | rabbitmq.external_rabbitmq_url | | | Users can also decide to use the remote hosted service and link to Plane deployment. Ignoring all the above keys, set rabbitmq.local_setup to false and set this key with remote connection url. | + | rabbitmq.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of rabbitmq. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | + | rabbitmq.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of rabbitmq. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | + | rabbitmq.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of rabbitmq. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | + | rabbitmq.labels | {} | | This key allows you to set custom labels for the stateful deployment of rabbitmq. This is useful for organizing and selecting resources in your Kubernetes cluster. | + | rabbitmq.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of rabbitmq. This is useful for adding metadata or configuration hints to your resources. | + + ##### Doc Store (Minio/S3) Setup + + | Setting | Default | Required | Description | + | ---------------------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | + | minio.local_setup | true | | Plane uses minio as the default file storage drive. This storage can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws S3 or similar services). Set this to true when you choose to setup stateful deployment of postgres. Mark it as false when using a remotely hosted database | + | minio.image | minio/minio:latest | | Using this key, user must provide the docker image name to setup the stateful deployment of minio. (must be set when `minio.local_setup=true`) | + | minio.image_mc | minio/mc:latest | | Using this key, user must provide the docker image name to setup the job deployment of minio client. (must be set when `minio.local_setup=true`) | + | minio.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of minio. (must be set when `minio.local_setup=true`) | + | minio.volumeSize | 5Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | + | minio.root_user | admin | | Storage credentials are requried to access the hosted stateful deployment of minio. Use this key to set the username for the stateful deployment. | + | minio.root_password | password | | Storage credentials are requried to access the hosted stateful deployment of minio. Use this key to set the password for the stateful deployment. | + | minio.env.minio_endpoint_ssl | false | | (Optional) Env to enforce HTTPS when connecting to minio uploads bucket | + | env.docstore_bucket | uploads | Yes | Storage bucket name is required as part of configuration. This is where files will be uploaded irrespective of if you are using Minio or external S3 (or compatible) storage service | + | env.doc_upload_size_limit | 5242880 | Yes | Document Upload Size Limit (default to 5Mb) | + | env.aws_access_key | | | External S3 (or compatible) storage service provides access key for the application to connect and do the necessary upload/download operations. To be provided when `minio.local_setup=false` | + | env.aws_secret_access_key | | | External S3 (or compatible) storage service provides secret access key for the application to connect and do the necessary upload/download operations. To be provided when `minio.local_setup=false` | + | env.aws_region | | | External S3 (or compatible) storage service providers creates any buckets in user selected region. This is also shared with the user as region for the application to connect and do the necessary upload/download operations. To be provided when `minio.local_setup=false` | + | env.aws_s3_endpoint_url | | | External S3 (or compatible) storage service providers shares a endpoint_url for the integration purpose for the application to connect and do the necessary upload/download operations. To be provided when minio.`local_setup=false` | + | minio.storageClass | `` | | Creating the persitant volumes for the stateful deployments needs the storageClass name. Set the correct value as per your kubernetes cluster configuration. | + | minio.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | + | minio.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of minio. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | + | minio.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of minio. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | + | minio.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of minio. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | + | minio.labels | {} | | This key allows you to set custom labels for the stateful deployment of minio. This is useful for organizing and selecting resources in your Kubernetes cluster. | + | minio.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of minio. This is useful for adding metadata or configuration hints to your resources. | + + ##### Web Deployment + + | Setting | Default | Required | Description | + | --------------------- | ------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | web.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be `>=1` | + | web.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | + | web.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | + | web.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | + | web.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | + | web.image | makeplane/plane-frontend | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | + | web.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of web. | + | web.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | + | web.nodeSelector | {} | | This key allows you to set the node selector for the deployment of web. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | + | web.tolerations | [] | | This key allows you to set the tolerations for the deployment of web. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | + | web.affinity | {} | | This key allows you to set the affinity rules for the deployment of web. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | + | web.labels | {} | | Custom labels to add to the web deployment | + | web.annotations | {} | | Custom annotations to add to the web deployment | + + ##### Space Deployment + + | Setting | Default | Required | Description | + | ----------------------- | --------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | space.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be `>=1` | + | space.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | + | space.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | + | space.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | + | space.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | + | space.image | makeplane/plane-space | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | + | space.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of space. | + | space.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | + | space.nodeSelector | {} | | This key allows you to set the node selector for the deployment of space. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | + | space.tolerations | [] | | This key allows you to set the tolerations for the deployment of space. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | + | space.affinity | {} | | This key allows you to set the affinity rules for the deployment of space. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | + | space.labels | {} | | Custom labels to add to the space deployment | + | space.annotations | {} | | Custom annotations to add to the space deployment | + + ##### Admin Deployment + + | Setting | Default | Required | Description | + | ----------------------- | --------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | admin.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be `>=1` | + | admin.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | + | admin.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | + | admin.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | + | admin.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | + | admin.image | makeplane/plane-admin | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | + | admin.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of admin. | + | admin.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | + | admin.nodeSelector | {} | | This key allows you to set the node selector for the deployment of admin. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | + | admin.tolerations | [] | | This key allows you to set the tolerations for the deployment of admin. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | + | admin.affinity | {} | | This key allows you to set the affinity rules for the deployment of admin. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | + | admin.labels | {} | | Custom labels to add to the admin deployment | + | admin.annotations | {} | | Custom annotations to add to the admin deployment | + + ##### Live Service Deployment + + | Setting | Default | Required | Description | + | ---------------------- | ---------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | live.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be `>=1` | + | live.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | + | live.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | + | live.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | + | live.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | + | live.image | makeplane/plane-live | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | + | live.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of live. | + | live.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | + | live.nodeSelector | {} | | This key allows you to set the node selector for the deployment of live. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | + | live.tolerations | [] | | This key allows you to set the tolerations for the deployment of live. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | + | live.affinity | {} | | This key allows you to set the affinity rules for the deployment of live. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | + | live_server_secret_key | htbqvBJAgpm9bzvf3r4urJer0ENReatceh | Yes | This key sets the secret key for the live server. This is required for secure communication and authentication in the live server component. | + | live.labels | {} | | Custom labels to add to the live deployment | + | live.annotations | {} | | Custom annotations to add to the live deployment | + + ##### API Deployment + + | Setting | Default | Required | Description | + | ---------------------- | ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | api.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be `>=1` | + | api.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | + | api.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | + | api.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | + | api.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | + | api.image | makeplane/plane-backend | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | + | api.pullPolicy | Always | | Using this key, user can set the pull policy for the deployment of api. | + | env.sentry_dsn | | | (optional) API service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry provided DSN for this integration. | + | env.sentry_environment | | | (optional) API service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry environment name (as configured in Sentry) for this integration. | + | env.api_key_rate_limit | 60/minute | | (optional) User can set the maximum number of requests the API can handle in a given time frame. | + | api.assign_cluster_ip | false | | Set it to true if you want to assign ClusterIP to the service | + | api.nodeSelector | {} | | This key allows you to set the node selector for the deployment of api. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | + | api.tolerations | [] | | This key allows you to set the tolerations for the deployment of api. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | + | api.affinity | {} | | This key allows you to set the affinity rules for the deployment of api. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | + | api.labels | {} | | Custom labels to add to the API deployment | + | api.annotations | {} | | Custom annotations to add to the API deployment | + + ##### Worker Deployment + + | Setting | Default | Required | Description | + | -------------------- | ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | worker.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be `>=1` | + | worker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | + | worker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | + | worker.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | + | worker.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | + | worker.image | makeplane/plane-backend | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | + | worker.nodeSelector | {} | | This key allows you to set the node selector for the deployment of worker. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | + | worker.tolerations | [] | | This key allows you to set the tolerations for the deployment of worker. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | + | worker.affinity | {} | | This key allows you to set the affinity rules for the deployment of worker. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | + | worker.labels | {} | | Custom labels to add to the worker deployment | + | worker.annotations | {} | | Custom annotations to add to the worker deployment | + + ##### Beat-Worker Deployment + + | Setting | Default | Required | Description | + | ------------------------ | ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | beatworker.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be `>=1` | + | beatworker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. | + | beatworker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. | + | beatworker.memoryRequest | 50Mi | | Every deployment in kubernetes can be set to use minimum memory they are allowed to use. This key sets the memory request for this deployment to use. | + | beatworker.cpuRequest | 50m | | Every deployment in kubernetes can be set to use minimum cpu they are allowed to use. This key sets the cpu request for this deployment to use. | + | beatworker.image | makeplane/plane-backend | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment | + | beatworker.nodeSelector | {} | | This key allows you to set the node selector for the deployment of beatworker. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. | + | beatworker.tolerations | [] | | This key allows you to set the tolerations for the deployment of beatworker. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. | + | beatworker.affinity | {} | | This key allows you to set the affinity rules for the deployment of beatworker. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. | + | beatworker.labels | {} | | Custom labels to add to the beat-worker deployment | + | beatworker.annotations | {} | | Custom annotations to add to the beat-worker deployment | + + ##### Common Environment Settings + + | Setting | Default | Required | Description | + | -------------------------- | -------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | env.secret_key | 60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5 | Yes | This must a random string which is used for hashing/encrypting the sensitive data within the application. Once set, changing this might impact the already hashed/encrypted data | + | env.default_cluster_domain | cluster.local | Yes | Set this value as configured in your kubernetes cluster. cluster.local is usally the default in most cases. | diff --git a/apps/developer-docs/docs/self-hosting/methods/one-click.md b/apps/developer-docs/docs/self-hosting/methods/one-click.md new file mode 100644 index 00000000..da751dce --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/one-click.md @@ -0,0 +1,77 @@ +--- +title: One-click deploy +description: Deploy Plane instantly with one-click installers on popular cloud providers. Quick setup on AWS, DigitalOcean, Render, and Railway. +keywords: plane one-click deploy, cloud deployment, aws plane, digitalocean plane, quick install, plane cloud hosting, self-hosting +--- + +# One-click deploy + +::: infoThis feature is included in our paid plans, but for a limited time, our community users can access it for free.::: + +### Requirements + +- Operating systems: Debian, Ubuntu, CentOS +- Supported CPU architectures: AMD64, ARM64, x86_64, AArch64 + +### Download the latest stable release + +Run ↓ on any CLI. + +``` +curl -fsSL https://raw.githubusercontent.com/makeplane/plane/master/deploy/1-click/install.sh | sh - +``` + +### Download the Preview release + +`Preview` builds do not support ARM64, AArch64 CPU architectures + +Run ↓ on any CLI. + +``` +export BRANCH=preview +curl -fsSL https://raw.githubusercontent.com/makeplane/plane/preview/deploy/1-click/install.sh | sh - +``` + +### Successful installation + +You should see ↓ if there are no hitches. That output will also list the IP address you can use to access your Plane instance. + +![Install Output](/images/one-click-deploy/one-click-install.png) + +### Manage your Plane instance + +Use `plane-app` [OPERATOR] to manage your Plane instance easily. Get a list of all operators with `plane-app ---help`. + +![Plane Help](/images/one-click-deploy/one-click-help.png) + +1. Basic operators + 1. `plane-app start` starts the Plane server. + 2. `plane-app restart` restarts the Plane server. + 3. `plane-app stop` stops the Plane server. + +2. Advanced operators + + `plane-app --configure` will show advanced configurators. + ![Advanced operators](/images/one-click-deploy/one-click-advanced.png) + - Change your proxy or listening port +
Default: 80
+ - Change your domain name +
Default: Deployed server's public IP address
+ - File upload size +
Default: 5MB
+ - Specify external database address when using an external database +
Default: `Empty`
+
Default folder: `/opt/plane/data/postgres`
+ - Specify external Redis URL when using external Redis +
Default: `Empty`
+
Default folder: `/opt/plane/data/redis`
+ - Configure AWS S3 bucket +
Use only when you or your users want to use S3
+
Default folder: `/opt/plane/data/minio`
+ +3. Version operators + 1. `plane-app --upgrade` gets the latest stable version of `docker-compose.yaml`, `.env`, and Docker images + 2. `plane-app --update-installer` updates the installer and the `plane-app` utility. + 3. `plane-app --uninstall` uninstalls the Plane application and all Docker containers from the server but leaves the data stored in + Postgres, Redis, and Minio alone. + 4. `plane-app --install` installs the Plane app again. diff --git a/apps/developer-docs/docs/self-hosting/methods/overview.md b/apps/developer-docs/docs/self-hosting/methods/overview.md new file mode 100644 index 00000000..a801aaa0 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/overview.md @@ -0,0 +1,74 @@ +--- +title: Deployment methods +description: Choose the best deployment method for your infrastructure. Deploy Plane with Docker, Kubernetes, Podman, or in airgapped environments. +keywords: plane deployment methods, docker compose, kubernetes, helm, podman, airgapped deployment, self-hosting +--- + +# Install Plane + +Choose a deployment method based on your infrastructure and requirements. + +## System requirements + +- **CPU:** 2 cores (x64/AMD64 or AArch64/ARM64) +- **RAM:** 4GB (8GB recommended for production) +- **OS:** Ubuntu, Debian, CentOS, Amazon Linux 2 or 2023, macOS, Windows with WSL2 + +## Deployment methods + +Plane supports a wide range of deployment options from simple single-container setups to enterprise-grade Kubernetes clusters. + +### Container deployments + +Core deployment methods for running Plane with containerized services: + + + + Install Plane using Docker Compose with all required services. Ideal for small to medium teams. + + + Single container with all Plane services. Perfect for testing and small deployments. + + + + + + + Production-grade deployment using Helm charts for high availability and auto-scaling. + + + +### Platform deployments + +Deploy Plane using specialized platforms and orchestration tools: + + + + Deploy Plane on Docker Swarm cluster for distributed container orchestration. + + + Deploy with Podman as a Docker alternative using systemd integration. + + + + + + Deploy Plane using Coolify's platform for simplified container management. + + + Deploy and manage Plane through Portainer's web interface. + + + +### Airgapped deployments + +For environments without internet access or with strict security requirements: + + + + Deploy Plane in isolated networks using Docker Compose with pre-loaded images. + + + Deploy in airgapped Kubernetes clusters using Helm charts with offline images. + + diff --git a/apps/developer-docs/docs/self-hosting/methods/podman-quadlets.md b/apps/developer-docs/docs/self-hosting/methods/podman-quadlets.md new file mode 100644 index 00000000..c8d03395 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/podman-quadlets.md @@ -0,0 +1,174 @@ +--- +title: Deploy Plane with Podman Quadlets +description: Install Plane using Podman Quadlets. Guide for deploying Plane with Podman as a Docker alternative with systemd integration. +keywords: plane podman, podman quadlets, systemd containers, docker alternative, rootless containers, plane podman deployment, self-hosting +--- + +# Deploy Plane with Podman Quadlets + +This guide shows you the steps to deploy a self-hosted instance of Plane using Podman Quadlets. + +## Prerequisites + +Before we start, make sure you've got these covered: + +- A non-root user account with `systemd --user support` (most modern Linux setups have this) +- Podman version **4.4 or higher** + +## Set up Podman + +1. Add the Podman repository. + + ```bash + echo 'deb http://download.opensuse.org/repositories/home:/alvistack/Debian_12/ /' | sudo tee /etc/apt/sources.list.d/home:alvistack.list + ``` + +2. Add the GPG key. + + ```bash + curl -fsSL https://download.opensuse.org/repositories/home:alvistack/Debian_12/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/home_alvistack.gpg > /dev/null + ``` + +3. Refresh your package lists. + + ```bash + sudo apt update + ``` + +4. Install Podman and its dependencies. + + ```bash + sudo apt install -y podman uidmap netavark passt + ``` + + The `uidmap` package handles user namespace mapping, `netavark` takes care of networking, and `passt` helps with network connectivity. + +5. Download and extract Podman Quadlets. + + ```bash + mkdir podman-quadlets + curl -fsSL https://prime.plane.so/releases/v2.6.3/podman-quadlets.tar.gz -o podman-quadlets.tar.gz + tar -xvzf podman-quadlets.tar.gz -C podman-quadlets + ``` + + The directory contains an `install.sh` script that will handle the installation and configuration. + +## Install Plane + +The installation script sets up Plane and configures all required services. You have two options: + +### Without sudo access + +```bash +./install.sh --domain your-domain.com --base-dir /your/custom/path +``` + +This installs Plane in your specified directory, which is useful if you want to maintain control over the installation location. + +### With sudo access + +```bash +./install.sh --domain your-domain.com +``` + +This installs Plane in `/opt/plane`, which is a standard system location. + +::: info +Systemd configurations are installed in `~/.config/containers/systemd/` +::: + +## Configure external services (optional) + +If you use external services for database, Redis, RabbitMQ, OpenSearch, or object storage (MinIO/S3), edit `plane.env` in your Plane installation directory (e.g. `/opt/plane` or your custom path from `--base-dir`) before starting services. +See [Environment variables](/self-hosting/govern/environment-variables) for more details. + +- **Database** — In the **DB SETTINGS** section, set `DATABASE_URL` or individual variables (`PGHOST`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`, `POSTGRES_PORT`). + +- **Redis** — In the **REDIS SETTINGS** section, set `REDIS_URL` or `REDIS_HOST` and `REDIS_PORT`. + +- **RabbitMQ** — Set `AMQP_URL` (e.g. `amqp://username:password@your-rabbitmq-host:5672/vhost`). + +- **OpenSearch** — Set `OPENSEARCH_ENABLED=1`, `OPENSEARCH_URL`, and optionally `OPENSEARCH_USERNAME` and `OPENSEARCH_PASSWORD`. See [Configure OpenSearch for advanced search](/self-hosting/govern/advanced-search). + +- **MinIO / S3** — In the **DATA STORE SETTINGS** section, set `USE_MINIO=0` for external S3, then set `AWS_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_S3_ENDPOINT_URL`, and `AWS_S3_BUCKET_NAME`. + +After editing `plane.env`, start or restart services as described in [Start Plane](#start-plane) so the changes take effect. + +## Start Plane + +::: warning +Note that you should run these commands without `sudo`. +::: + +1. Reload systemd to recognize new configurations. + + ```bash + systemctl --user daemon-reload + ``` + +2. Start the network service. + + ```bash + systemctl --user start plane-nw-network.service + ``` + +3. Start core dependencies. + + ```bash + systemctl --user start plane-{db,redis,mq,minio}.service + ``` + +4. Start backend services. + + ```bash + systemctl --user start {api,worker,beat-worker,migrator,monitor}.service + ``` + +5. Start frontend services. + + ```bash + systemctl --user start {web,space,admin,live,proxy}.service + ``` + + The startup sequence is important: network first, then dependencies, followed by backend services, and finally frontend services. + +6. If you've purchased a paid plan, [activate your license key](/self-hosting/manage/manage-licenses/activate-pro-and-business#activate-your-license) to unlock premium features. + +### Verify service status + +Check that all services are running correctly: + +1. Check network status. + + ```bash + systemctl --user status plane-nw-network.service + ``` + +2. Check core dependencies. + + ```bash + systemctl --user status plane-{db,redis,mq,minio}.service + ``` + +3. Check backend services. + + ```bash + systemctl --user status {api,worker,beat-worker,migrator,monitor}.service + ``` + +4. Check frontend services. + ```bash + systemctl --user status {web,space,admin,live,proxy}.service + ``` + +Your Plane installation should now be running successfully with Podman Quadlets. This setup provides automatic service restart capabilities and standard systemd management commands for maintaining your installation. + +## Troubleshoot + +To debug service issues, examine the logs using: + +```bash +journalctl --user -u --no-pager +``` + +The logs will provide detailed information about any configuration issues or errors that may occur. diff --git a/apps/developer-docs/docs/self-hosting/methods/portainer.md b/apps/developer-docs/docs/self-hosting/methods/portainer.md new file mode 100644 index 00000000..91ebe6da --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/methods/portainer.md @@ -0,0 +1,65 @@ +--- +title: Deploy Plane with Portainer +description: Deploy and manage Plane using Portainer container management UI. Visual Docker management with stack deployment and monitoring. +keywords: plane portainer, portainer deployment, docker management ui, plane container management, portainer stack, self-hosting +--- + +# Deploy Plane with Portainer + +This guide shows you the steps to deploy a self-hosted instance of Plane using Portainer. + +## Install Plane + +### Prerequisites + +- Before you get started, make sure you have a Portainer environment set up and ready to go. +- Your setup should support either amd64 or arm64 architectures. + +### Procedure + +1. **Download the required deployment files** + + `portainer-compose.yml` – Defines Plane's services and dependencies. + + ```bash + curl -fsSL https://prime.plane.so/releases//portainer-compose.yml -o portainer-compose.yml + ``` + + `variables.env` – Stores environment variables for your deployment. + + ```bash + curl -fsSL https://prime.plane.so/releases//variables.env -o plane.env + ``` + + ::: warning + The `` value should be v1.8.2 or higher. + ::: + +2. Click **+ Add stack** on Portainer. + +3. Copy and paste the contents of `portainer-compose.yml` into the editor. + +4. Load environment variables from the `variables.env` file. + +5. **Configure environment variables** + Before deploying, edit the following variables: + - `DOMAIN_NAME` – (required) Your application's domain name. + - `SITE_ADDRESS` – (required) The full domain name (FQDN) of your instance. + - `MACHINE_SIGNATURE` – (required) A unique identifier for your machine. You can generate this by running below code in terminal: + ```sh + sed -i 's/MACHINE_SIGNATURE=.*/MACHINE_SIGNATURE='$(openssl rand -hex 16)'/' plane.env + ``` + - `CERT_EMAIL` – (optional) Email address for SSL certificate generation (only needed if you're setting up HTTPS). + +6. **Configure external DB, Redis, and RabbitMQ** + ::: warning + When self-hosting Plane for production use, it is strongly recommended to configure external database and storage. This ensures that your data remains secure and accessible even if the local machine crashes or encounters hardware issues. Relying solely on local storage for these components increases the risk of data loss and service disruption. + ::: + - `DATABASE_URL` – Connection string for your external database. + - `REDIS_URL` – Connection string for your external Redis instance. + - `AMQP_URL` – Connection string for your external RabbitMQ server. + +7. Click **Deploy the stack**. + That's it! Once the deployment is complete, Plane should be up and running on your configured domain. + +8. If you've purchased a paid plan, [activate your license key](/self-hosting/manage/manage-licenses/activate-pro-and-business#activate-your-license) to unlock premium features. diff --git a/apps/developer-docs/docs/self-hosting/overview.md b/apps/developer-docs/docs/self-hosting/overview.md new file mode 100644 index 00000000..57ae2e3c --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/overview.md @@ -0,0 +1,60 @@ +--- +title: Deploy Plane on your infrastructure +description: Deploy Plane on your own infrastructure with Docker, Kubernetes, or Podman. Complete self-hosting guides for open-source project management with full control and customization. +keywords: self-host plane, plane docker, plane kubernetes, self-hosted project management, docker compose plane, kubernetes helm plane, on-premise deployment +--- + +# Deploy Plane on your infrastructure + +Take complete control of your project management infrastructure by deploying Plane on your own servers. Self-hosting Plane gives you full ownership of your data and the flexibility to deploy wherever you need - from a single Docker container to enterprise Kubernetes clusters. + +## Why self-host Plane? + +**Data sovereignty and privacy** +Keep all your project data within your own infrastructure. Perfect for organizations with strict data residency requirements or privacy regulations. + +**Complete control** +Customize every aspect of Plane to match your workflows. Control when and how updates are applied, and integrate with your existing tools and infrastructure. + +**Compliance and security** +Meet regulatory requirements like GDPR, HIPAA, SOC 2, or industry-specific standards by maintaining full control over data storage and access. + +**No vendor lock-in** +Your data remains accessible in open formats. Migrate, backup, or customize without restrictions. + +## Deployment methods + +Choose the deployment method that best fits your infrastructure and team size: + + + + Quick setup with minimal configuration, ideal for small to medium teams. + + + Production-grade deployment using Helm for high availability and auto-scaling. + + + +[Other deployment methods](/self-hosting/methods/overview) + +## Configuration and governance + +Once deployed, configure your Plane instance to match your organization's needs: + + + + Configure instance-wide settings, manage users, and access God Mode for advanced administrative controls. + + + Set up SSO, OAuth, LDAP, or other authentication methods. Support for Google, GitHub, GitLab, and custom providers. + + + + + + Configure SMTP for email notifications, invitations, and alerts. Integrate with SendGrid, AWS SES, or your own mail server. + + + Connect to managed databases (PostgreSQL, Redis) and cloud storage (S3, MinIO, GCS) for scalable, production-ready deployments. + + diff --git a/apps/developer-docs/docs/self-hosting/plane-architecture.md b/apps/developer-docs/docs/self-hosting/plane-architecture.md new file mode 100644 index 00000000..0c70d855 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/plane-architecture.md @@ -0,0 +1,70 @@ +--- +title: Plane self-hosted architecture +description: Understand Plane self-hosted architecture including web server, API, workers, database, Redis, and storage components. System requirements and service topology. +keywords: plane architecture, plane system design, plane components, plane services, postgresql, redis, minio, plane infrastructure +--- + +# Plane self-hosted architecture + +Plane consists of multiple services working together to provide project management capabilities. + +![Plane architecture](/images/airgapped/plane-architecture.webp#hero) + +### Frontend services + +**Web** +The main application interface where users interact with projects, work items, and pages. This service serves the UI and handles client-side routing. + +**Space** +This powers public sharing. It lets you publish projects, views, pages to the web, so others can view without needing to log in. + +**Admin** +Instance administration interface for workspace owners and administrators. Manages billing, licensing, workspace settings, and user permissions. + +### API server + +**API** +The core REST API that handles all data operations. All frontend services communicate with this API for creating, reading, updating, and deleting data. + +**Worker** +Background job processor that handles async operations like file processing, notification dispatch, and data imports. Workers pull jobs from RabbitMQ and execute them independently. + +**Beat worker** +Scheduled task executor that runs periodic jobs like data cleanup, report generation, and reminder notifications. Uses a cron-like scheduling system. + +**Migrator** +Database schema management service that runs on deployment to apply schema changes and data migrations. Runs once during upgrades then exits. + +### Supporting services + +**Proxy** +Handles incoming traffic and routes it to the appropriate services. Manages certificates and reverse proxying. In Docker deployments, Plane uses Caddy for automatic SSL certificate management and traffic routing. + +**Live** +Real-time collaboration service powered by WebSockets. Handles cursor positions, live updates, and presence indicators for multiple users working simultaneously. + +**Monitor** +Used for license validation and activation. It checks the license status and ensures your instance is compliant. + +**Silo** +Integration backend that manages connections to GitHub, GitLab, and Slack. Handles OAuth flows, webhook processing, and API communication with external systems. + +**Intake** +Email ingestion service that converts incoming emails into work items or comments. Requires SMTP configuration and DNS setup. + +### Infrastructure dependencies + +**PostgreSQL** +Primary relational database storing all application data including projects, work items, users, and configuration. Plane requires PostgreSQL 15.7+ or 16.x. + +**Redis/Valkey** +In-memory cache and session store. Used for caching frequently accessed data, storing user sessions, and managing real-time collaboration state. + +**RabbitMQ** +Message queue for asynchronous task processing. Workers pull jobs from queues for background operations like imports, exports, and notifications. + +**MinIO/S3** +Object storage for file uploads, attachments, and generated exports; can be replaced with any S3-compatible storage system. + +**OpenSearch** +Optional search indexing service for enhanced search capabilities. Not required for basic Plane functionality. diff --git a/apps/developer-docs/docs/self-hosting/self-hosting-101.md b/apps/developer-docs/docs/self-hosting/self-hosting-101.md new file mode 100644 index 00000000..8068550a --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/self-hosting-101.md @@ -0,0 +1,249 @@ +--- +title: Self-hosting 101 +description: What self-hosting Plane involves, how it's licensed, what your team operates, and how to plan a deployment. +keywords: self-hosting plane, plane licensing, plane editions, agpl, commercial edition, airgapped, plane operations +--- + +# Self-hosting 101 + +Self-hosting Plane means running the full application stack on infrastructure you control. This page covers what that involves, how Plane is licensed, what your team operates, and how to plan a deployment. + +If you're ready to deploy, jump to the [deployment scenarios on the overview](/self-hosting/overview). + +--- + +## 1. What self-hosting Plane means + +Self-hosting Plane means deploying Plane, the project and knowledge management platform, on infrastructure you control. Plane ships as a bundled Docker or Kubernetes deployment that includes the application and everything it depends on, so you can stand it up with a single command on a single machine. + +Everything user-facing runs inside your network: the web app, the API, file uploads, real-time collaboration, search indexing, and AI inference paths. You control the data, the network path users take to reach Plane, the upgrade timing, the auth provider, and the integrations Plane talks to internally. + +For the [Commercial Edition](/self-hosting/editions-and-versions#commercial), Plane operates the license validation server at the [Prime portal](https://prime.plane.so/licenses). The [Airgapped Edition](/self-hosting/editions-and-versions#airgapped) removes that dependency and runs entirely offline. + +For production deployments, you can also point Plane at managed services for the database and storage layers (RDS, Cloud SQL, S3, GCS, and similar) instead of running them yourself. See [Plane Architecture](/self-hosting/plane-architecture) for the full system anatomy and [External services](/self-hosting/govern/database-and-storage) for the managed-service options. + +--- + +## 2. Cloud vs self-hosted + +| Dimension | [Plane Cloud](https://app.plane.so/sign-in) | Self-hosted | +| --------------------------------- | ------------------------------------------- | --------------------------------------------- | +| Time to first user | About 30 seconds | 20mins via Docker | +| Data residency | US (default), EU available on demand | Anywhere you deploy | +| Network isolation | Public internet | VPC, private network, or fully air-gapped | +| Upgrade timing | Continuous | You choose your window | +| Feature freshness | New features ship here first | Commercial gets them next, Community last | +| Operational overhead | Zero | Real and ongoing | +| Backups and DR | Plane operates | You design and operate | +| Support escalation | One vendor | Your platform team first, then Plane support | +| Compliance posture | Plane's certifications apply | Your perimeter, your audit | +| Integration with internal systems | Egress only (public APIs) | Direct access to private services | +| Cost model | Per-seat subscription | Per-seat subscription + On demand | +| Audit logs, SCIM, advanced auth | Business Plus | Available on plans (Commercial and Airgapped) | + +[Cloud and self-hosted migration](/self-hosting/manage/migrate-plane) is supported in the Cloud-to-self-hosted direction. + +--- + +## 3. How licensing works + +Plane's licensing has two layers, and confusing them is the most common mistake new self-hosters make. + +**The edition is the codebase you run.** There are three self-hosted editions: [Community](/self-hosting/editions-and-versions#community), [Commercial](/self-hosting/editions-and-versions#commercial), and [Airgapped](/self-hosting/editions-and-versions#airgapped). Each has its own release cycle. They are separate codebases, not feature toggles on the same binary. + +**The plan is the set of features your license key unlocks** on the Commercial and Airgapped editions. Plans are Free, Pro, Business, and Enterprise Grid. You activate a plan by pasting a license key from the [Prime portal](https://prime.plane.so/licenses) into your workspace settings. + +### The three editions + +**Community Edition** is open source under [AGPL v3.0](https://github.com/makeplane/plane/blob/preview/LICENSE.txt). Free, no license key, full source available, you can audit and modify it. Feature parity with the Free tier of Cloud, with no Pro, Business, or Enterprise features. To unlock paid features, switch to Commercial. See [Upgrade Community to Commercial](/self-hosting/upgrade-from-community). + +**Commercial Edition** is closed-source. It includes a built-in Free tier of 12 user seats per workspace, which means you can run Commercial in production at small scale without buying a license. To unlock Pro, Business, or Enterprise Grid features, you activate a license key. Commercial gets full feature parity with Cloud. + +**Airgapped Edition** is the Commercial Edition adapted for environments without internet access. Same features, offline license activation, updates pulled from your own Docker registry. See [Airgapped requirements](/self-hosting/methods/airgapped-requirements). + +### How license activation works + +License keys come from the [Prime portal](https://prime.plane.so/licenses), which you log into with the email you used to purchase. To activate on a self-hosted instance: + +1. Copy the license key from Prime. +2. In your Plane workspace, go to **Workspace Settings > Billing and plans**. +3. Click **Activate this Workspace**, paste the key, click **Activate**. + +Each license key is bound to one workspace and one machine. To move it (different server, different workspace, reinstall), use **Delink license key** in Billing and plans, then run `prime-cli restart`, then activate elsewhere. The full procedure is at [Activate Pro and Business](/self-hosting/manage/manage-licenses/activate-pro-and-business). + +The **Sync plan** button in Billing and plans pulls the latest subscription state from Prime, including plan type, seat count, expiration, and feature flags. Use it whenever Prime and your workspace disagree. + +For Enterprise Grid, see [Activate Enterprise](/self-hosting/manage/manage-licenses/activate-enterprise). For airgapped activation, see [Activate airgapped](/self-hosting/manage/manage-licenses/activate-airgapped) and [Activate airgapped Enterprise](/self-hosting/manage/manage-licenses/activate-airgapped-enterprise). + +### When something goes wrong + +License-state failures (expiry, seat overflow, key conflicts, network errors during activation) surface as specific errors. The full reference is at [License errors](/self-hosting/troubleshoot/license-errors). Point your operations runbook there, since these are usually fast fixes once you know the symptom. + +### AGPL in practice + +If you're running the Community Edition, AGPL v3.0 has a few real-world boundaries: + +- **Running Community for your internal team.** Fine, no obligations beyond AGPL terms. +- **Modifying Community for internal use.** Fine, AGPL specifically allows this. +- **Hosting Community as a service for external customers.** AGPL requires you to publish your modifications, including any changes you've made. +- **Embedding Community inside a commercial product.** AGPL-affecting territory. Either comply with AGPL's source-disclosure requirements or [talk to sales](https://plane.so/talk-to-sales) about a commercial license. + +Most teams self-hosting Plane for internal project management never hit these boundaries. If you're not sure where you sit, ask your legal team or contact us before building anything customer-facing on top of Community. + +--- + +## 4. When self-hosted fits + +Self-hosting Plane fits the following scenarios. + +**1. Regulatory or contractual data residency.** GDPR with strict country-level residency, sector rules (HIPAA, financial regulations), or customer contracts that specify where data sits. Self-hosting puts data on infrastructure you can point an auditor at. + +**2. Air-gapped or sovereign cloud.** Your network has no outbound internet, or you operate in a sovereign cloud where third-party SaaS isn't permitted. The [Airgapped Edition](/self-hosting/methods/airgapped-requirements) is built for this. + +**3. Internal-only integrations.** You need Plane to integrate with services that aren't on the public internet: internal Git, internal ticketing, internal SSO, internal monitoring. Self-hosted lives on the same network as the things it talks to. + +**4. Upgrade timing control.** Multi-week change windows, frozen periods around financial reporting, change-advisory-board approvals. Self-hosted lets you upgrade on your calendar. See [Upgrade Plane](/self-hosting/manage/upgrade-plane). + +--- + +## 5. What you're running + +Plane is a multi-service application: eight application services plus a data layer. + +### Application services + +- **Web.** The main user-facing Next.js app at your primary domain. +- **Space.** Public-facing project pages (deployed views, intake forms, and similar). +- **Admin.** The admin console for instance-level configuration. +- **Live.** Real-time collaboration backend (Yjs WebSocket server) for pages and work items. +- **API.** The Django REST API that everything else calls. +- **Worker.** Celery background workers for async jobs (notifications, webhooks, exports, AI). +- **Beat.** Celery scheduler that triggers periodic tasks. +- **Migrator.** One-shot DB migration job that runs on each upgrade. + +### Data layer + +- **Postgres.** Primary database. Holds workspaces, projects, work items, pages, users, and everything transactional. +- **Redis.** Cache, session store, real-time pub/sub. +- **RabbitMQ.** Message broker for the Celery workers. +- **Object storage.** S3-compatible (MinIO, AWS S3, GCS, Azure Blob). File uploads, attachments, exports, and AI artifacts. +- **OpenSearch** _(optional)._ Full-text search index. Without it, search falls back to Postgres-based search. + +The full breakdown of versions, ports, resource recommendations, and dependency graph is at [Plane Architecture](/self-hosting/plane-architecture). For the complete environment-variable surface across all services, see [Environment variables](/self-hosting/govern/environment-variables). + +--- + +## 6. What your team operates + +### Skills your operators need + +- **Container operations.** Docker basics for [Docker Compose](/self-hosting/methods/docker-compose), Kubernetes and Helm if you go [HA](/self-hosting/methods/kubernetes). +- **Postgres operations.** Backups, restores, upgrades, basic tuning. +- **TLS and DNS.** [Custom domains](/self-hosting/govern/custom-domain), [SSL certificates](/self-hosting/govern/configure-ssl), [reverse proxy](/self-hosting/govern/reverse-proxy) configuration. +- **Identity provider setup.** [SAML](/self-hosting/govern/saml-sso), [OIDC](/self-hosting/govern/oidc-sso), [LDAP](/self-hosting/govern/ldap), or OAuth, depending on what your org uses. +- **Object storage operations.** Buckets, lifecycle policies, [private bucket](/self-hosting/govern/private-bucket) configuration. +- **Secrets management.** [External secrets](/self-hosting/govern/external-secrets) integration if you use Vault, AWS Secrets Manager, or similar. + +### Time budget + +- **Week 1: deploy and stand up.** Pick a [deployment method](/self-hosting/methods/overview), provision infrastructure, install, configure auth, smoke-test. 1 to 3 days of an FTE for Docker Compose, 3 to 7 days for production Kubernetes. +- **Month 1: harden for production.** Backup and restore drill, monitoring, integrations, user onboarding, runbook documentation. Another 3 to 5 days spread across the month. +- **Year 1: operate.** Roughly 4 minor upgrades plus 1 to 2 major upgrades, ongoing patches, capacity planning, and user support escalations. Plan for 0.1 to 0.25 FTE at small scale, 0.5 to 1.0 FTE at large scale. + +### On-call and incident response + +Self-hosted Plane sits inside your operational perimeter. Decide ownership and escalation before you go live: who's on the rotation, what your internal runbook covers, when you escalate to Plane support. The [troubleshooting docs](/self-hosting/troubleshoot/overview) cover common cases, and your [support tier](/self-hosting/overview#get-help) determines response times when you need to escalate. + +--- + +## 7. What changes between editions + +Editions differ in three ways that matter operationally: feature availability, release cadence, and license model. + +**Feature availability.** Community has parity with the Free tier of Cloud. Commercial and Airgapped get full parity with Cloud's paid plans (Pro, Business, Enterprise Grid), license-gated. + +**Release cadence.** Cloud is the test bed. New features ship there first, then Commercial, then Community. If your team needs the latest features quickly, that affects which edition fits. + +**License model.** Community is AGPL with no key. Commercial uses an online license key tied to one workspace and one machine. Airgapped uses an offline license bundle. Edition transitions are supported but generally one-directional in practice. See [Community to Commercial](/self-hosting/upgrade-from-community) and [Community to Airgapped](/self-hosting/manage/community-to-airgapped). + +Full feature, version, and codebase comparison: [Plane Editions](/self-hosting/editions-and-versions). Latest changes by edition: [Changelog](https://plane.so/changelog). + +--- + +## 8. Lifecycle: deploy, configure, operate, upgrade + +Self-hosting Plane is four phases. + +### Deploy + +Pick a method, provision infrastructure, install the binary or chart, run the migrator, smoke-test. Hours to days. + +Decisions: which [deployment method](/self-hosting/methods/overview), where Postgres and object storage live (managed services or self-run), which domain, which network topology. Changing these later is painful. + +### Configure + +Authentication, network and TLS, secrets, integrations, and [instance admin](/self-hosting/govern/instance-admin) setup. Days to a week for a hardened production deployment. + +[Authentication](/self-hosting/govern/authentication) is often a full day's work. Picking and configuring [Google](/self-hosting/govern/google-oauth), [GitHub](/self-hosting/govern/github-oauth), [SAML](/self-hosting/govern/saml-sso), [OIDC](/self-hosting/govern/oidc-sso), or [LDAP](/self-hosting/govern/ldap), plus the [reset password flow](/self-hosting/govern/reset-password) and [email delivery](/self-hosting/govern/communication), takes most of the configure phase. + +### Operate + +Day-2 work: user management, monitoring, backups, support, [logs](/self-hosting/manage/view-logs), capacity. Ongoing. + +The non-negotiable here is [Backup and restore](/self-hosting/manage/backup-restore). Set it up before users log in. Test the restore path within the first month. A backup you haven't restored from is a backup you don't actually have. + +### Upgrade + +Plane ships frequently. Minor upgrades every 4 to 8 weeks, majors less often. Each upgrade involves a change window, a backup, the upgrade itself via [Upgrade Plane](/self-hosting/manage/upgrade-plane), and post-upgrade verification. + +Skipping upgrades for 6+ months means bigger upgrade-time risk, more breaking changes to handle at once, and missed security patches. + +--- + +## 9. Patterns and anti-patterns + +Patterns we've seen repeated across self-hosted deployments. + +**Anti-pattern: Postgres in the same Docker container as Plane in production.** Fine for [Docker AIO](/self-hosting/methods/docker-aio) evaluations. In production, you can't snapshot the database independently, you can't scale it, and a container restart is a database restart. Use a managed Postgres or run it in a separate, properly backed-up container. [External services](/self-hosting/govern/database-and-storage) walks through both options. + +**Anti-pattern: skipping backups for the first three months.** Backups should be running before the first real user logs in. See [Backup and restore](/self-hosting/manage/backup-restore). + +**Anti-pattern: one environment, no staging.** Every upgrade becomes a production incident. A small staging instance, even on a single VM, lets you run upgrades there first. + +**Anti-pattern: underestimating object storage growth.** File uploads, page attachments, AI artifacts, and exports add up faster than people expect. Set lifecycle rules early and monitor growth. The [private bucket](/self-hosting/govern/private-bucket) and [database and storage](/self-hosting/govern/database-and-storage) docs cover the basics. + +**Anti-pattern: ignoring upgrades for six months or more.** The longer you wait, the bigger the gap, the more breaking changes accumulate, and the more security patches you've missed. Pick a cadence, quarterly minimum. + +**Anti-pattern: one person knows the configuration.** Document your specific setup (env vars, IdP configuration, backup destinations, certificate renewal procedure) somewhere your team can find it. The [environment variables reference](/self-hosting/govern/environment-variables) is where to start. + +**Pattern that works: managed services for the data layer.** Run Plane application services in containers, but use managed Postgres and managed object storage (RDS, Cloud SQL, S3, GCS). You inherit your cloud provider's backup, scaling, and durability story for the highest-stakes pieces. + +**Pattern that works: separate licenses per environment.** A separate license key for staging keeps prod clean and makes upgrade testing realistic. + +**Pattern that works: integrate with your existing observability stack.** Ship Plane logs and metrics into whatever you already use rather than inventing monitoring for it. Start with [View logs](/self-hosting/manage/view-logs). + +--- + +## 10. Planning checklist + +Before you go to production, have a clear answer to each of these: + +1. **Why self-hosting.** The specific reason: data residency, air-gapped network, internal integrations, upgrade control, scale economics, customization. +2. **Owner.** A specific person or team responsible for the system. +3. **Backup and disaster recovery target.** RPO and RTO in concrete numbers. +4. **Upgrade cadence.** Monthly, quarterly, or another rhythm. See [Upgrade Plane](/self-hosting/manage/upgrade-plane). +5. **Escalation path.** Internal on-call rotation and the support tier you escalate to externally. + +Once these are settled, head to the [Self-hosting overview](/self-hosting/overview) and pick a deployment scenario. + +--- + +## 11. Next steps + +**Try Plane self-hosted.** [Docker AIO](/self-hosting/methods/docker-aio) gives you a single container with embedded services in about 10 minutes (POC only). + +**Plan a production deployment.** Read [Plane Editions](/self-hosting/editions-and-versions) to pick your edition. Read [Plane Architecture](/self-hosting/plane-architecture) to plan capacity and network. Then pick a [deployment method](/self-hosting/methods/overview). + +**Talk to a human.** [Talk to sales](https://plane.so/talk-to-sales) for pricing, contracts, professional services, and airgapped. [Community Discord](https://discord.gg/plane) for open questions. [GitHub issues](https://github.com/makeplane/plane/issues) for bugs and feature requests. + +[**Continue to: Self-hosting overview →**](/self-hosting/overview) diff --git a/apps/developer-docs/docs/self-hosting/telemetry.md b/apps/developer-docs/docs/self-hosting/telemetry.md new file mode 100644 index 00000000..a511cb86 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/telemetry.md @@ -0,0 +1,85 @@ +--- +title: Data collection and usage +description: Understand what telemetry data Plane collects, how it is used, and how to opt out. Privacy-focused data collection for self-hosted instances. +keywords: plane telemetry, data collection, plane privacy, analytics opt-out, plane usage data, self-hosted privacy +--- + +# Data collection and usage + +Plane collects anonymized data to enhance your user experience, ensure product stability, and drive continuous improvements. This article talks about what we collect, what we don't, and how we use collected data. + +## What Plane collects + +With the exception of the instance admin's email address on self-hosted Plane, we don't collect any personally identifiable information (PII). All usage data is anonymized and isn't connected to individual users. + +### Instance admin details + +Plane collects just the instance admin's name and email to communicate essential upgrade news, security alerts, and other critical notifications. + +### Instance setup + +When an instance is created, Plane collects just the instance ID so we can track upgrades and troubleshoot paid features. + +### Usage for billing + +Plane tracks the number of workspaces, members, and roles to bill you correctly and ensure accurate reporting in your workspace's **Billing and plans** screen and the Prime portal. + +### Application data + +Plane tracks anonymized workspace activity, including but not limited to the number of projects, issues, cycles, modules, comments, issue types, custom properties, timesheet downloads, active importers, and integrations. This data helps us understand product usage, adoption, and build new features. + +### User interaction and behavior + +Plane collects anonymized data for how you work with Plane when creating, updating, or deleting Plane entities, including but not restricted to projects, issues, cycles, modules, and others. This data helps us understand user journeys, pain points, and overall behavior. + +### Performance data + +Plane collects machine configurations, environment details, OS flavors, network throughput, and stack traces to help troubleshoot and prevent performance hiccups. + +## Data sample + +```json +{ + "Created At": "October 7, 2024, 1:50 PM", + "Page Count": "14", + "Is Telemetry Enabled": "true", + "Updated At": "October 7, 2024, 1:50 PM", + "User Count": "97", + "Cycle Count": "62", + "Cycle Issue Count": "710", + "Module Issue Count": "428", + "ID": "1234567890", + "Updated By ID": null, + "Current Version": "0.23.0", + "Issue Count": "2,000", + "Instance ID": "1234567890", + "Latest Version": "0.23.0", + "Module Count": "81", + "Name": "Test", + "Workspace Count": "11", + "Project Count": "20" +} +``` + +## How we use the data + +- **Improving your experience** + Analyzing real-world usage guides us in making Plane more intuitive and user-friendly. + +- **Detecting and fixing bugs** + Identifying bugs and errors as they occur enables us to quickly investigate and fix issues, minimize downtime, and improve product performance. + +- **Enhancing security** + Monitoring for abnormal patterns helps us identify and mitigate potential security threats or vulnerabilities, ensuring data protection and platform integrity. + +- **Driving product decisions** + Understanding how you interact with Plane helps us prioritize future developments and focus on high-impact features and improvements that drive better outcomes. + +## Disable telemetry + +As much as we'd love to understand your usage better, we agree that you should be in control of your data. If for any reason you don't want to send us that data, you can turn telemetry collection off in three clicks. + +1. Go to your instance's God Mode by appending `/god-mode` to the domain you have hosted Plane on. +2. In the **General Settings** pane on the right, disable the **Telemetry** toggle button. + ![Disable telemetry](/images/disable-telemetry.webp#hero) +3. Click **Save changes**. diff --git a/apps/developer-docs/docs/self-hosting/troubleshoot/cli-errors.md b/apps/developer-docs/docs/self-hosting/troubleshoot/cli-errors.md new file mode 100644 index 00000000..ebc44ebf --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/troubleshoot/cli-errors.md @@ -0,0 +1,27 @@ +--- +title: CLI errors +description: Troubleshoot cli errors. Common issues, error messages, and solutions for self-hosted Plane. +keywords: plane cli errors, prime cli troubleshooting, command line issues, plane cli fix, self-hosting troubleshooting +--- + +# CLI errors + +This page helps you troubleshoot common issues you might run into when using the Plane CLI tools. It covers potential causes of errors and provides straightforward steps to resolve them. + +## Failed to update Prime CLI + +
+ Error: Failed to update Prime CLI, please contact support or try again later. +
+ +This error typically happens if you're using an older version of the Prime CLI. To fix it, follow these steps: + +1. Start by taking a [data backup](/self-hosting/manage/backup-restore#backup-data) to be safe. +2. Run this command to remove the existing CLI: + ```bash + rm -rf /usr/bin/prime-cli + ``` +3. Install the latest version of the CLI with: + ```bash + curl -fsSL https://prime.plane.so/install/ | sh + ``` diff --git a/apps/developer-docs/docs/self-hosting/troubleshoot/installation-errors.md b/apps/developer-docs/docs/self-hosting/troubleshoot/installation-errors.md new file mode 100644 index 00000000..25a15eaf --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/troubleshoot/installation-errors.md @@ -0,0 +1,29 @@ +--- +title: Installation errors +description: Troubleshoot installation errors. Common issues, error messages, and solutions for self-hosted Plane. +keywords: plane installation errors, deployment issues, docker errors, kubernetes errors, plane setup troubleshooting, self-hosting +--- + +# Installation errors + +This guide is designed to help you resolve common issues encountered while installing Plane. Each section includes potential causes and step-by-step solutions for identified problems. + +## Error during Docker Compose execution + +
+ Error: Error during docker compose execution. Please check permissions and try again. +
+ +- This error typically occurs when the user doesn't have sudo or root privileges. To resolve this, ensure you're logged in as the root user or as a user with sudo access before attempting the installation again. + +- The issue may also be caused by using the older version of Docker Compose `docker-compose`. To fix this, install the latest version of Docker Compose `docker compose` and make sure the old version is removed. + +## Migrator container exited + +
+ Error: plane-migrator-1 container exited with status 1 +
+ +This error typically occurs if you have configured an external database that is running on localhost. Since the connection is being attempted from inside the container, localhost won’t work, as the database is not running within the container. + +To resolve this issue, ensure that the database is hosted on a network-accessible server rather than localhost. Update the database URL to reflect the correct server address. See [how to configure external db](/self-hosting/govern/database-and-storage). diff --git a/apps/developer-docs/docs/self-hosting/troubleshoot/license-errors.md b/apps/developer-docs/docs/self-hosting/troubleshoot/license-errors.md new file mode 100644 index 00000000..4e8f3b11 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/troubleshoot/license-errors.md @@ -0,0 +1,51 @@ +--- +title: License errors +description: Troubleshoot Plane license activation errors. Fix common issues with Pro, Business, Enterprise, and Airgapped license keys. +keywords: plane license errors, license activation issues, license troubleshooting, plane license fix, commercial license, self-hosting +--- + +# Errors related to licenses + +This guide is designed to help you resolve common issues encountered while activating the license key for a workspace. Each section includes potential causes and step-by-step solutions for identified problems. + +## Before troubleshooting + +**Try syncing your plan first.** Many license issues resolve automatically when your workspace pulls the latest subscription information from the Prime server. You can sync your plan from the workspace settings in the Plane application. + +If the issue persists after syncing or you see other errors, continue with the specific error troubleshooting below. + +## License is invalid + +
+ Error: Your license is invalid or already in use. +
+ +- This issue usually occurs when your server has trouble connecting to ours to verify the license. Try running `prime-cli restart`, and it should resolve the problem. + +- If you migrated Plane to a new server without first [delinking the license key](/self-hosting/manage/manage-licenses/activate-pro-and-business#delink-license-key) from the old server, this error might happen. In that case, please reach out to the support team to have the license key delinked. For future reference, if you need to reinstall Plane or move to another server, follow [this guide](/self-hosting/manage/migrate-plane) to ensure a smooth transition. + +## Something went wrong + +
+ Error: Something went wrong please try again later +
+ +This error usually occurs when the license validation service is unavailable. Here's how you can troubleshoot it: + +1. Confirm that your Plane instance is running the latest version. +2. If it's not up-to-date, [update to the latest version](/self-hosting/manage/upgrade-plane#prerequisites). + +Updating typically resolves this issue. If the problem persists, double-check your network connection and any firewall rules that might block access to the license validation service. + +## Payment server is not configured + +
+ Error: Payment server is not configured +
+ +This usually occurs when the environment confiuration is incorrect. The Env variable `payment_server_url` is missing in the setup. In this case, follow the below steps. + +1. Backup the `plane.env` file. See [Backup plane.env](/self-hosting/manage/backup-restore#backup-plane-env). +2. Run `prime-cli repair` to allow Prime CLI to attempt automatic fixes to the `plane.env` file. +3. Try activating your workspace with the license key. +4. If needed, you can configure the instance in [God mode](/self-hosting/govern/instance-admin#settings) or adjust the environment variables directly in the new plane.env file. diff --git a/apps/developer-docs/docs/self-hosting/troubleshoot/overview.md b/apps/developer-docs/docs/self-hosting/troubleshoot/overview.md new file mode 100644 index 00000000..d9e669e1 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/troubleshoot/overview.md @@ -0,0 +1,47 @@ +--- +title: Troubleshooting +description: Diagnose and resolve issues with your self-hosted Plane instance. +keywords: plane troubleshooting, self-hosted errors, docker logs, plane integration issues, plane debug +--- + +# Troubleshooting + +When something goes wrong, start by identifying which service is affected, then check the relevant logs. + +## Identify the service + +| Problem area | Service | Logs to check | +| ------------------------------------------------------------ | ---------- | ------------------ | +| API errors, data not saving, 500 errors | api | `plane-api` | +| License activation or validation errors | monitor | `plane-monitor` | +| GitHub, GitLab, Slack integrations, imports not working | silo | `plane-silo` | +| SSL errors, 502/504 errors, routing issues | proxy | `plane-proxy` | +| File uploads or attachments failing | minio | `plane-minio` | +| Plane AI not working, AI chat errors | pi | `plane-pi` | +| UI not loading, blank screens, page errors | web | `plane-web` | +| Public pages or published views not working | space | `plane-space` | +| Instance settings | admin | `plane-admin` | +| Imports stuck, notifications delayed, file processing issues | worker | `plane-worker` | +| Scheduled tasks or reminders not running | beat | `plane-beat` | +| Upgrade failures, database schema errors | migrator | `plane-migrator` | +| Real-time sync, live cursors, or presence not working | live | `plane-live` | +| Intake Email not working | intake | `plane-intake` | +| Search not returning results | opensearch | `plane-opensearch` | + +See [View logs](/self-hosting/manage/view-logs) for commands to access logs in Docker deployments. + +## Reporting issues to support + +When [contacting support](https://docs.plane.so/support/get-help), include: + +- **Container logs** for the affected service (see table above) +- **Browser Network logs** (open DevTools → Network tab → reproduce the issue → export as HAR file) + +This helps us diagnose the problem faster. + +## Common issues + +- [Installation errors](/self-hosting/troubleshoot/installation-errors) +- [License errors](/self-hosting/troubleshoot/license-errors) +- [CLI errors](/self-hosting/troubleshoot/cli-errors) +- [Storage errors](/self-hosting/troubleshoot/storage-errors) diff --git a/apps/developer-docs/docs/self-hosting/troubleshoot/storage-errors.md b/apps/developer-docs/docs/self-hosting/troubleshoot/storage-errors.md new file mode 100644 index 00000000..bdefab08 --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/troubleshoot/storage-errors.md @@ -0,0 +1,97 @@ +--- +title: Storage errors +description: Troubleshoot Plane storage and upload errors. Fix S3, MinIO, and file upload issues for self-hosted Plane instances. +keywords: plane storage errors, s3 errors, minio issues, file upload errors, storage troubleshooting, plane uploads, self-hosting +--- + +# Storage errors + +This guide is designed to help you resolve common issues encountered while configuring storage in Plane. Each section includes potential causes and step-by-step solutions for identified problems. + +## Bucket policy exceeds size limit + +
+ Error: An error occurred (PolicyTooLarge) when calling the PutBucketPolicy operation: Policy exceeds the maximum allowed document size. +
+ +This error occurs when the bucket policy exceeds the 20KB size limit allowed by MinIO. It typically happens when trying to add complex policies or when unnecessary data bloats the policy size. + +To resolve this issue, you can define a streamlined bucket policy file and apply it correctly within the MinIO container. Follow these steps: + +1. **Create a bucket policy JSON file** + - On your local machine, create a file named `bucket-policy.json` with the following content: + + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": ["*"] + }, + "Action": ["s3:GetObject"], + "Resource": ["arn:aws:s3:::uploads/*"], + "Condition": { + "StringEquals": { + "s3:ExistingObjectTag/publicAccess": ["true"] + } + } + } + ] + } + ``` + + - Save this file in an accessible location. + +2. **Set up and apply the policy** + ::: warning + **IMPORTANT** + Make sure to execute all the `mc` commands **within the MinIO container** (either by attaching to it or using `docker exec`). + ::: + +- Configure MinIO alias: + +```bash +mc alias set myminio http://plane-plane-minio:9000 +``` + +Replace `plane-plane-minio:9000` with your MinIO server address. + +- Tag existing objects: + +```bash +mc find myminio/uploads --exec "mc tag set {} publicAccess=true" +``` + +- Copy the policy file to the MinIO container: + +```bash +docker cp bucket-policy.json :/tmp +``` + +Replace `` with the actual ID of your MinIO container. You can find the container ID by running `docker ps`. + +- Apply the policy to the bucket: + +```bash +mc anonymous set-json /tmp/bucket-policy.json myminio/uploads +``` + +3. **Verification** + - Verify that objects are correctly tagged: + + ```bash + mc tag list myminio/uploads/ + ``` + + - Test public access using: + ```bash + curl http://:9000/uploads/ + ``` + +### Notes + +- Verify that the `access-key` and `secret-key` used for setting up the alias have adequate permissions to manage the bucket. +- If your MinIO server is hosted on a different machine or address, replace `plane-plane-minio:9000` with the appropriate server URL. +- After applying the policy, test the setup to confirm it is working as expected. diff --git a/apps/developer-docs/docs/self-hosting/upgrade-from-community.md b/apps/developer-docs/docs/self-hosting/upgrade-from-community.md new file mode 100644 index 00000000..81874dfc --- /dev/null +++ b/apps/developer-docs/docs/self-hosting/upgrade-from-community.md @@ -0,0 +1,239 @@ +--- +title: Upgrade from Community to Commercial Edition +description: Upgrade self-hosted Plane to the latest version. Step-by-step guide for updating your Plane installation safely. +keywords: plane upgrade community to commercial, edition migration, plane pro upgrade, plane business upgrade, self-hosting upgrade +--- + +# Upgrade from Community to Commercial Edition + +The Commercial edition comes with the free plan and the flexibility to upgrade to a paid plan at any point. + +> [!WARNING] +> The instructions provided on this page are specific to installations using Docker. If you are running Plane on Kubernetes, you'll need to manually create a database dump and back up your file storage by copying the relevant volumes or storage paths. + +## Prerequisites + +- Install the [Commercial Edition](/self-hosting/methods/docker-compose#install-plane) on a fresh machine, not the one running the Plane Community Edition. +- Be sure to log in as the root user or as a user with sudo access. The `/opt` folder requires sudo or root privileges. + +:::tabs key:upgrade-options +== Standard setup (built-in DB & storage) {#standard-setup} + +This upgrade path is for installations using Plane's default PostgreSQL database and MinIO object storage. + +## Back up data on Community instance + +1. Download the latest version of `setup.sh`. + + ```bash + curl -fsSL https://github.com/makeplane/plane/releases/latest/download/setup.sh -o setup.sh + ``` + +2. Run the setup.sh backup script to take the backup of the Community Edition instance. + + ```bash + ./setup.sh backup + ``` + +3. When done, your data will be backed up to the folder shown on the screen. + e.g., `/plane-selfhost/plane-app/backup/20240522-1027` + This folder will contain 3 `tar.gz` files. + - `pgdata.tar.gz` + - `redisdata.tar.gz` + - `uploads.tar.gz` + +4. Copy all the three files from the server running the Community Edition to any folder on the server running the Commercial Edition. + + e.g., `~/ce-backup` + +## Restore data on Commercial instance + +1. Start any command-line interface like Terminal and go into the folder with the back-up files. + ``` + cd ~/ce-backup + ``` +2. Copy and paste the script below on Terminal and hit Enter. + + ``` + TARGET_DIR=/opt/plane/data + sudo mkdir -p $TARGET_DIR + + for FILE in *.tar.gz; do + if [ -e "$FILE" ]; then + tar -xzvf "$FILE" -C "$TARGET_DIR" + else + echo "No .tar.gz files found in the current directory." + exit 1 + fi + done + + # Remove destinations first, then mv + sudo rm -rf $TARGET_DIR/db && mv $TARGET_DIR/pgdata $TARGET_DIR/db + sudo rm -rf $TARGET_DIR/redis && mv $TARGET_DIR/redisdata $TARGET_DIR/redis + + mkdir -p $TARGET_DIR/minio + sudo rm -rf $TARGET_DIR/minio/uploads && mv $TARGET_DIR/uploads $TARGET_DIR/minio/uploads + ``` + +3. This script will extract your Community Edition data and restore it to `/opt/plane/data`. + +== Managed services (external DB and storage) {#managed-services} + +This upgrade path is for installations using external or managed database and object storage services (like AWS RDS and S3). Since your data already lives in external services, you only need to update your configuration — no backup and restore required. + +## Update configuration for Commercial Edition + +1. Open the `plane.env` file located at `/opt/plane/plane.env`. + +2. Configure database connection. + 1. Find the `DATABASE_URL` environment variable. + 2. Verify it points to your external database: + + ```ini + DATABASE_URL=postgresql://user:password@your-db-host:5432/plane + ``` + + If you need to change it, update the value with your managed database connection string. + + 3. Configure object storage + 1. Find the `#DATASTORE SETTINGS` section in `plane.env` + 2. Update these environment variables for your external storage: + + ```ini + USE_MINIO=0 + AWS_REGION=us-east-1 + AWS_ACCESS_KEY_ID= + AWS_SECRET_ACCESS_KEY= + AWS_S3_ENDPOINT_URL=https://s3.amazonaws.com + AWS_S3_BUCKET_NAME=plane-uploads + ``` + + :::info + Setting `USE_MINIO=0` disables the local MinIO service and enables external object storage (S3 or S3-compatible services). + ::: + +3. Restart Plane services to apply the configuration: + ```bash + prime-cli restart + ``` + +Your Commercial Edition instance is now connected to your existing external database and storage. +::: + +:::details Manual backup and restore without CLI + +Use this method if you prefer to back up data manually or if the setup.sh script isn't working for your environment. + +### What gets migrated + +- PostgreSQL database (all Plane data) +- MinIO uploads (attachments, images, files) + +### Prerequisites + +- Plane CE and Commercial versions should be compatible +- Shell access to both servers +- Docker installed on both servers + +### Back up data on Community instance + +1. Create backup folders: + + ```bash + mkdir -p ~/ce-backups/db + mkdir -p ~/ce-backups/minio/uploads + cd ~/ce-backups + ``` + +2. Back up PostgreSQL data: + + ```bash + docker cp plane-app-plane-db-1:/var/lib/postgresql/data/. db/ + ``` + +3. Back up MinIO uploads: + + ```bash + docker cp plane-app-plane-minio-1:/export/uploads minio/uploads/ + ``` + +4. Verify backup sizes: + + ```bash + du -sh db minio/uploads + ``` + + Make sure sizes look reasonable (not just a few KB). + +5. Transfer backup to Commercial server: + + ```bash + scp -r ~/ce-backups user@commercial-server:/tmp/ + ``` + +### Restore data on Commercial instance + +1. Stop Plane: + + ```bash + prime-cli stop + ``` + + Verify all containers are down: + + ```bash + docker ps + ``` + +2. Back up existing Commercial data (safety precaution): + + ```bash + mv /opt/plane/data/db /opt/plane/data/db.bak + mv /opt/plane/data/minio/uploads /opt/plane/data/minio/uploads.bak + ``` + +3. Restore PostgreSQL: + + ```bash + mv /tmp/ce-backups/db /opt/plane/data/db + ``` + +4. Restore MinIO uploads: + + ```bash + mv /tmp/ce-backups/minio/uploads /opt/plane/data/minio/uploads + ``` + +5. Start Plane: + + ```bash + prime-cli restart + ``` + +### Validate the migration + +- Login works +- Projects are visible +- Attachments open correctly + +### Rollback + +If something fails, restore from the backup you created in step 2: + +```bash +prime-cli stop + +rm -rf /opt/plane/data/db +mv /opt/plane/data/db.bak /opt/plane/data/db + +rm -rf /opt/plane/data/minio/uploads +mv /opt/plane/data/minio/uploads.bak /opt/plane/data/minio/uploads + +prime-cli restart +``` + +::: + +## What's next + +- [Activate a paid plan license](/self-hosting/manage/manage-licenses/activate-pro-and-business). diff --git a/apps/developer-docs/package.json b/apps/developer-docs/package.json new file mode 100644 index 00000000..107b7167 --- /dev/null +++ b/apps/developer-docs/package.json @@ -0,0 +1,40 @@ +{ + "name": "developer-docs", + "version": "1.0.0", + "private": true, + "description": "Plane developer documentation — developers.plane.so", + "keywords": [ + "api", + "documentation", + "plane" + ], + "homepage": "https://developers.plane.so", + "license": "Apache-2.0", + "author": "Plane", + "repository": { + "type": "git", + "url": "https://github.com/makeplane/docs.git", + "directory": "apps/developer-docs" + }, + "type": "module", + "scripts": { + "dev": "vitepress dev docs --port 5174", + "build": "(git fetch --unshallow || true) && vitepress build docs", + "preview": "vitepress preview docs --port 4174", + "check:types": "tsc --noEmit -p tsconfig.json" + }, + "dependencies": { + "@plane/docs-theme": "workspace:*" + }, + "devDependencies": { + "@types/node": "catalog:", + "@voidzero-dev/vitepress-theme": "catalog:", + "mermaid": "catalog:", + "typescript": "catalog:", + "vitepress": "catalog:", + "vitepress-plugin-llms": "catalog:", + "vitepress-plugin-mermaid": "catalog:", + "vitepress-plugin-tabs": "catalog:", + "vue": "catalog:" + } +} diff --git a/apps/developer-docs/tsconfig.json b/apps/developer-docs/tsconfig.json new file mode 100644 index 00000000..a4c03058 --- /dev/null +++ b/apps/developer-docs/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["docs/.vitepress/**/*.ts", "docs/.vitepress/**/*.mts", "docs/.vitepress/**/*.vue"], + "exclude": ["docs/.vitepress/cache", "docs/.vitepress/dist", "docs/.vitepress/.temp"] +} diff --git a/apps/developer-docs/vercel.json b/apps/developer-docs/vercel.json new file mode 100644 index 00000000..1f764538 --- /dev/null +++ b/apps/developer-docs/vercel.json @@ -0,0 +1,131 @@ +{ + "cleanUrls": true, + "headers": [ + { + "source": "/(.*)", + "headers": [ + { + "key": "Link", + "value": "; rel=\"describedby\"; type=\"text/plain\", ; rel=\"service-doc\"; type=\"text/html\", ; rel=\"sitemap\"; type=\"application/xml\"" + }, + { + "key": "X-Robots-Tag", + "value": "index, follow" + } + ] + } + ], + "redirects": [ + { + "source": "/api-reference", + "destination": "/api-reference/introduction" + }, + { + "source": "/api-reference/byoa/build-plane-app", + "destination": "/dev-tools/build-plane-app" + }, + { + "source": "/webhooks/intro-webhooks", + "destination": "/dev-tools/intro-webhooks" + }, + { + "source": "/api-reference/cycle-issue/overview", + "destination": "/api-reference/cycle/overview" + }, + { + "source": "/api-reference/cycle-issue/add-cycle-issue", + "destination": "/api-reference/cycle/add-cycle-work-items" + }, + { + "source": "/api-reference/cycle-issue/list-cycle-issues", + "destination": "/api-reference/cycle/list-cycle-work-items" + }, + { + "source": "/api-reference/cycle-issue/delete-cycle-issue", + "destination": "/api-reference/cycle/remove-cycle-work-item" + }, + { + "source": "/api-reference/module-issue/overview", + "destination": "/api-reference/module/overview" + }, + { + "source": "/api-reference/module-issue/add-module-issue", + "destination": "/api-reference/module/add-module-work-items" + }, + { + "source": "/api-reference/module-issue/list-module-issues", + "destination": "/api-reference/module/list-module-work-items" + }, + { + "source": "/api-reference/module-issue/delete-module-issue", + "destination": "/api-reference/module/remove-module-work-item" + }, + { + "source": "/plane-one/governance/authentication/custom-sso", + "destination": "/self-hosting/govern/authentication" + }, + { + "source": "/plane-one/governance/workspaces-and-teams", + "destination": "/self-hosting/overview" + }, + { + "source": "/plane-one/manage/advanced-deploy", + "destination": "/self-hosting/overview" + }, + { + "source": "/plane-one/manage/prime-cli", + "destination": "/self-hosting/manage/prime-cli" + }, + { + "source": "/plane-one/manage/prime-client", + "destination": "/self-hosting/manage/manage-licenses/activate-pro-and-business" + }, + { + "source": "/plane-one/self-host/methods/docker", + "destination": "/self-hosting/methods/docker-compose" + }, + { + "source": "/plane-one/self-host/methods/kubernetes", + "destination": "/self-hosting/methods/kubernetes" + }, + { + "source": "/plane-one/self-host/guides", + "destination": "/self-hosting/overview" + }, + { + "source": "/plane-one/self-host/overview", + "destination": "/self-hosting/overview" + }, + { + "source": "/plane-one/introduction", + "destination": "/self-hosting/overview" + }, + { + "source": "/dev-tools/build-plane-app", + "destination": "/dev-tools/build-plane-app/overview" + }, + { + "source": "/self-hosting", + "destination": "/self-hosting/overview" + }, + { + "source": "/self-hosting/govern/plane-ai", + "destination": "/self-hosting/govern/plane-ai/configure-plane-ai" + }, + { + "source": "/self-hosting/govern/aws-opensearch-embedding", + "destination": "/self-hosting/govern/plane-ai/configure-embedding-model" + }, + { + "source": "/dev-tools/mcp-server-claude-code", + "destination": "/dev-tools/mcp-server#claude-code" + } + ], + "rewrites": [ + { + "source": "/:path*", + "has": [{ "type": "header", "key": "accept", "value": ".*text/markdown.*" }], + "destination": "/:path*.md" + } + ] +} diff --git a/.env.example b/apps/docs/.env.example similarity index 100% rename from .env.example rename to apps/docs/.env.example diff --git a/apps/docs/AGENTS.md b/apps/docs/AGENTS.md new file mode 100644 index 00000000..69e70c14 --- /dev/null +++ b/apps/docs/AGENTS.md @@ -0,0 +1,82 @@ +# AGENTS.md — docs.plane.so (`apps/docs`) + +The [Plane](https://plane.so) product documentation site, built with VitePress and hosted at +[docs.plane.so](https://docs.plane.so). All content lives in `docs/` as Markdown. Repo-wide rules +(workspace, theme, formatting, branches) are in the root `AGENTS.md`; this file covers this app only. +`CLAUDE.md` is a symlink to this file. + +## Commands + +```bash +pnpm dev:docs # from the repo root → http://localhost:5173 +pnpm --filter docs build # or: cd apps/docs && pnpm build → docs/.vitepress/dist +pnpm --filter docs preview # → http://localhost:4173 +pnpm --filter docs check:types +``` + +## Structure + +```text +docs/ + .vitepress/ + config.ts # VitePress config — nav, sidebar, search, head tags (analytics, consent) + theme/ + index.ts # createPlaneTheme({ brand }) — this site's branding only + site.css # site-specific CSS (keep tiny; shared styles live in packages/theme) + public/ # fonts/, icons/, robots.txt (no images — see below) + index.md # Home page (hero layout) + introduction/ # Quickstart, tutorials, core-concepts overview + core-concepts/ # Issues, projects, workspaces, pages, cycles, modules + integrations/ # GitHub, GitLab, Slack, Sentry, draw.io + importers/ # Jira, Asana, Linear, ClickUp, CSV, Notion + authentication/ # SSO, group sync + automations/ # Custom automations + workflows-and-approvals/ # Workflows + workspaces-and-users/ # Billing, seats, licenses, navigation + ai/ # Plane AI features + support/ # Keyboard shortcuts, get help + templates/ # Page, project, work-item templates +vercel.json # cleanUrls, headers, redirects, Accept: text/markdown rewrite +``` + +## Content conventions + +- All content files are Markdown (`.md`). Use GitHub-flavored Markdown. + +- Each file should have a front matter block at minimum with `title`: + + ```yaml + --- + title: Page Title + description: One-sentence summary (used for SEO meta and og:description) + --- + ``` + +- Page headings (`#`) must match the sidebar label defined in `docs/.vitepress/config.ts`. When renaming a + page, update both the file heading and the sidebar entry. + +- Use relative links between docs (e.g., `[Cycles](/core-concepts/cycles)`). Do not use `.md` extensions in + links. Links to the developer docs use the full `https://developers.plane.so/...` URL. + +- Images are hosted externally at `https://media.docs.plane.so/`. Do not commit binary assets. Reference them + directly in Markdown. + +- Use the `tabs` plugin (`vitepress-plugin-tabs`) for multi-tab code blocks where appropriate. Shared + components available in Markdown: ``, ``, `` (from `@plane/docs-theme`). + +## Navigation and sidebar + +The sidebar and top nav are configured entirely in `docs/.vitepress/config.ts`. When you add a new page: + +1. Create the `.md` file in the appropriate `docs/` subdirectory. +2. Add an entry to the relevant sidebar section in `config.ts`. +3. If it needs a top-nav link, add it to `themeConfig.nav` (header buttons are nav items flagged + `planeButton: "primary" | "secondary"`). + +## What NOT to do + +- Do not commit image or font binaries. Use the external CDN. +- Do not edit generated files in `docs/.vitepress/dist/`. +- Do not add analytics keys, API keys, or secrets to any file (`.env` locally, platform env vars in CI). +- Do not rewrite the VitePress config structure without understanding the existing sidebar/nav shape — the + sidebar is hand-curated and order matters. diff --git a/apps/docs/CLAUDE.md b/apps/docs/CLAUDE.md new file mode 120000 index 00000000..47dc3e3d --- /dev/null +++ b/apps/docs/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/docs/.vitepress/config.ts b/apps/docs/docs/.vitepress/config.ts similarity index 95% rename from docs/.vitepress/config.ts rename to apps/docs/docs/.vitepress/config.ts index e6bb0b36..5a04671b 100644 --- a/docs/.vitepress/config.ts +++ b/apps/docs/docs/.vitepress/config.ts @@ -31,7 +31,7 @@ const posthogHead: HeadConfig[] = posthogKey "script", {}, `!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys onSessionId".split(" "),n=0;n diff --git a/apps/docs/docs/.vitepress/theme/index.ts b/apps/docs/docs/.vitepress/theme/index.ts new file mode 100644 index 00000000..f7bb9bcd --- /dev/null +++ b/apps/docs/docs/.vitepress/theme/index.ts @@ -0,0 +1,17 @@ +/** + * docs.plane.so theme = shared Plane docs theme (@plane/docs-theme, packages/theme) + * + this site's branding. + */ +import { createPlaneTheme } from "@plane/docs-theme"; +import "./site.css"; + +export default createPlaneTheme({ + brand: { + logoOnLight: "https://media.docs.plane.so/logo/new-logo-white.png", + logoOnDark: "https://media.docs.plane.so/logo/new-logo-dark.png", + logoAlt: "Plane", + menuTitle: "Plane Docs", + footerBg: "https://media.docs.plane.so/logo/og-docs.webp", + monoIcon: "https://media.docs.plane.so/logo/favicon-32x32.png", + }, +}); diff --git a/apps/docs/docs/.vitepress/theme/site.css b/apps/docs/docs/.vitepress/theme/site.css new file mode 100644 index 00000000..b2c85d60 --- /dev/null +++ b/apps/docs/docs/.vitepress/theme/site.css @@ -0,0 +1 @@ +/* docs.plane.so — site-specific styles (everything shared lives in @plane/docs-theme, packages/theme) */ diff --git a/docs/ai/ai-skills.md b/apps/docs/docs/ai/ai-skills.md similarity index 100% rename from docs/ai/ai-skills.md rename to apps/docs/docs/ai/ai-skills.md diff --git a/docs/ai/ai-usage.md b/apps/docs/docs/ai/ai-usage.md similarity index 100% rename from docs/ai/ai-usage.md rename to apps/docs/docs/ai/ai-usage.md diff --git a/docs/ai/mcp-connectors.md b/apps/docs/docs/ai/mcp-connectors.md similarity index 100% rename from docs/ai/mcp-connectors.md rename to apps/docs/docs/ai/mcp-connectors.md diff --git a/docs/ai/mcp-server.md b/apps/docs/docs/ai/mcp-server.md similarity index 100% rename from docs/ai/mcp-server.md rename to apps/docs/docs/ai/mcp-server.md diff --git a/docs/ai/plane-ai-credits.md b/apps/docs/docs/ai/plane-ai-credits.md similarity index 100% rename from docs/ai/plane-ai-credits.md rename to apps/docs/docs/ai/plane-ai-credits.md diff --git a/docs/ai/plane-ai.md b/apps/docs/docs/ai/plane-ai.md similarity index 100% rename from docs/ai/plane-ai.md rename to apps/docs/docs/ai/plane-ai.md diff --git a/docs/authentication/group-sync.md b/apps/docs/docs/authentication/group-sync.md similarity index 100% rename from docs/authentication/group-sync.md rename to apps/docs/docs/authentication/group-sync.md diff --git a/docs/authentication/sso.md b/apps/docs/docs/authentication/sso.md similarity index 100% rename from docs/authentication/sso.md rename to apps/docs/docs/authentication/sso.md diff --git a/docs/automations/custom-automations.md b/apps/docs/docs/automations/custom-automations.md similarity index 100% rename from docs/automations/custom-automations.md rename to apps/docs/docs/automations/custom-automations.md diff --git a/docs/automations/overview.md b/apps/docs/docs/automations/overview.md similarity index 100% rename from docs/automations/overview.md rename to apps/docs/docs/automations/overview.md diff --git a/docs/automations/plane-runner.md b/apps/docs/docs/automations/plane-runner.md similarity index 100% rename from docs/automations/plane-runner.md rename to apps/docs/docs/automations/plane-runner.md diff --git a/docs/communication-and-collaboration/comments-and-activity.md b/apps/docs/docs/communication-and-collaboration/comments-and-activity.md similarity index 100% rename from docs/communication-and-collaboration/comments-and-activity.md rename to apps/docs/docs/communication-and-collaboration/comments-and-activity.md diff --git a/docs/communication-and-collaboration/inbox.md b/apps/docs/docs/communication-and-collaboration/inbox.md similarity index 100% rename from docs/communication-and-collaboration/inbox.md rename to apps/docs/docs/communication-and-collaboration/inbox.md diff --git a/docs/communication-and-collaboration/notifications.md b/apps/docs/docs/communication-and-collaboration/notifications.md similarity index 100% rename from docs/communication-and-collaboration/notifications.md rename to apps/docs/docs/communication-and-collaboration/notifications.md diff --git a/docs/communication-and-collaboration/project-updates.md b/apps/docs/docs/communication-and-collaboration/project-updates.md similarity index 100% rename from docs/communication-and-collaboration/project-updates.md rename to apps/docs/docs/communication-and-collaboration/project-updates.md diff --git a/docs/communication-and-collaboration/subscribers.md b/apps/docs/docs/communication-and-collaboration/subscribers.md similarity index 100% rename from docs/communication-and-collaboration/subscribers.md rename to apps/docs/docs/communication-and-collaboration/subscribers.md diff --git a/docs/core-concepts/account/overview.md b/apps/docs/docs/core-concepts/account/overview.md similarity index 100% rename from docs/core-concepts/account/overview.md rename to apps/docs/docs/core-concepts/account/overview.md diff --git a/docs/core-concepts/account/settings.md b/apps/docs/docs/core-concepts/account/settings.md similarity index 100% rename from docs/core-concepts/account/settings.md rename to apps/docs/docs/core-concepts/account/settings.md diff --git a/docs/core-concepts/analytics.md b/apps/docs/docs/core-concepts/analytics.md similarity index 100% rename from docs/core-concepts/analytics.md rename to apps/docs/docs/core-concepts/analytics.md diff --git a/docs/core-concepts/cycles.md b/apps/docs/docs/core-concepts/cycles.md similarity index 100% rename from docs/core-concepts/cycles.md rename to apps/docs/docs/core-concepts/cycles.md diff --git a/docs/core-concepts/deploy.md b/apps/docs/docs/core-concepts/deploy.md similarity index 100% rename from docs/core-concepts/deploy.md rename to apps/docs/docs/core-concepts/deploy.md diff --git a/docs/core-concepts/drafts.md b/apps/docs/docs/core-concepts/drafts.md similarity index 100% rename from docs/core-concepts/drafts.md rename to apps/docs/docs/core-concepts/drafts.md diff --git a/docs/core-concepts/export.md b/apps/docs/docs/core-concepts/export.md similarity index 100% rename from docs/core-concepts/export.md rename to apps/docs/docs/core-concepts/export.md diff --git a/docs/core-concepts/intake.md b/apps/docs/docs/core-concepts/intake.md similarity index 100% rename from docs/core-concepts/intake.md rename to apps/docs/docs/core-concepts/intake.md diff --git a/docs/core-concepts/issues.md b/apps/docs/docs/core-concepts/issues.md similarity index 100% rename from docs/core-concepts/issues.md rename to apps/docs/docs/core-concepts/issues.md diff --git a/docs/core-concepts/issues/bulk-ops.md b/apps/docs/docs/core-concepts/issues/bulk-ops.md similarity index 100% rename from docs/core-concepts/issues/bulk-ops.md rename to apps/docs/docs/core-concepts/issues/bulk-ops.md diff --git a/docs/core-concepts/issues/display-options.md b/apps/docs/docs/core-concepts/issues/display-options.md similarity index 100% rename from docs/core-concepts/issues/display-options.md rename to apps/docs/docs/core-concepts/issues/display-options.md diff --git a/docs/core-concepts/issues/epics.md b/apps/docs/docs/core-concepts/issues/epics.md similarity index 100% rename from docs/core-concepts/issues/epics.md rename to apps/docs/docs/core-concepts/issues/epics.md diff --git a/docs/core-concepts/issues/estimates.md b/apps/docs/docs/core-concepts/issues/estimates.md similarity index 100% rename from docs/core-concepts/issues/estimates.md rename to apps/docs/docs/core-concepts/issues/estimates.md diff --git a/docs/core-concepts/issues/labels.md b/apps/docs/docs/core-concepts/issues/labels.md similarity index 100% rename from docs/core-concepts/issues/labels.md rename to apps/docs/docs/core-concepts/issues/labels.md diff --git a/docs/core-concepts/issues/layouts.md b/apps/docs/docs/core-concepts/issues/layouts.md similarity index 100% rename from docs/core-concepts/issues/layouts.md rename to apps/docs/docs/core-concepts/issues/layouts.md diff --git a/docs/core-concepts/issues/overview.md b/apps/docs/docs/core-concepts/issues/overview.md similarity index 100% rename from docs/core-concepts/issues/overview.md rename to apps/docs/docs/core-concepts/issues/overview.md diff --git a/docs/core-concepts/issues/plane-query-language.md b/apps/docs/docs/core-concepts/issues/plane-query-language.md similarity index 100% rename from docs/core-concepts/issues/plane-query-language.md rename to apps/docs/docs/core-concepts/issues/plane-query-language.md diff --git a/docs/core-concepts/issues/properties.md b/apps/docs/docs/core-concepts/issues/properties.md similarity index 100% rename from docs/core-concepts/issues/properties.md rename to apps/docs/docs/core-concepts/issues/properties.md diff --git a/docs/core-concepts/issues/states.md b/apps/docs/docs/core-concepts/issues/states.md similarity index 100% rename from docs/core-concepts/issues/states.md rename to apps/docs/docs/core-concepts/issues/states.md diff --git a/docs/core-concepts/issues/time-tracking.md b/apps/docs/docs/core-concepts/issues/time-tracking.md similarity index 100% rename from docs/core-concepts/issues/time-tracking.md rename to apps/docs/docs/core-concepts/issues/time-tracking.md diff --git a/docs/core-concepts/issues/timeline-dependency.md b/apps/docs/docs/core-concepts/issues/timeline-dependency.md similarity index 100% rename from docs/core-concepts/issues/timeline-dependency.md rename to apps/docs/docs/core-concepts/issues/timeline-dependency.md diff --git a/docs/core-concepts/issues/visualise_filter.md b/apps/docs/docs/core-concepts/issues/visualise_filter.md similarity index 100% rename from docs/core-concepts/issues/visualise_filter.md rename to apps/docs/docs/core-concepts/issues/visualise_filter.md diff --git a/docs/core-concepts/issues/work-item-url.md b/apps/docs/docs/core-concepts/issues/work-item-url.md similarity index 100% rename from docs/core-concepts/issues/work-item-url.md rename to apps/docs/docs/core-concepts/issues/work-item-url.md diff --git a/docs/core-concepts/modules.md b/apps/docs/docs/core-concepts/modules.md similarity index 100% rename from docs/core-concepts/modules.md rename to apps/docs/docs/core-concepts/modules.md diff --git a/docs/core-concepts/pages/editor-blocks.md b/apps/docs/docs/core-concepts/pages/editor-blocks.md similarity index 100% rename from docs/core-concepts/pages/editor-blocks.md rename to apps/docs/docs/core-concepts/pages/editor-blocks.md diff --git a/docs/core-concepts/pages/inline-comments.md b/apps/docs/docs/core-concepts/pages/inline-comments.md similarity index 100% rename from docs/core-concepts/pages/inline-comments.md rename to apps/docs/docs/core-concepts/pages/inline-comments.md diff --git a/docs/core-concepts/pages/overview.md b/apps/docs/docs/core-concepts/pages/overview.md similarity index 100% rename from docs/core-concepts/pages/overview.md rename to apps/docs/docs/core-concepts/pages/overview.md diff --git a/docs/core-concepts/pages/wiki.md b/apps/docs/docs/core-concepts/pages/wiki.md similarity index 100% rename from docs/core-concepts/pages/wiki.md rename to apps/docs/docs/core-concepts/pages/wiki.md diff --git a/docs/core-concepts/power-k.md b/apps/docs/docs/core-concepts/power-k.md similarity index 100% rename from docs/core-concepts/power-k.md rename to apps/docs/docs/core-concepts/power-k.md diff --git a/docs/core-concepts/projects/initiatives.md b/apps/docs/docs/core-concepts/projects/initiatives.md similarity index 100% rename from docs/core-concepts/projects/initiatives.md rename to apps/docs/docs/core-concepts/projects/initiatives.md diff --git a/docs/core-concepts/projects/manage-project-members.md b/apps/docs/docs/core-concepts/projects/manage-project-members.md similarity index 100% rename from docs/core-concepts/projects/manage-project-members.md rename to apps/docs/docs/core-concepts/projects/manage-project-members.md diff --git a/docs/core-concepts/projects/milestones.md b/apps/docs/docs/core-concepts/projects/milestones.md similarity index 100% rename from docs/core-concepts/projects/milestones.md rename to apps/docs/docs/core-concepts/projects/milestones.md diff --git a/docs/core-concepts/projects/overview.md b/apps/docs/docs/core-concepts/projects/overview.md similarity index 100% rename from docs/core-concepts/projects/overview.md rename to apps/docs/docs/core-concepts/projects/overview.md diff --git a/docs/core-concepts/projects/project-labels.md b/apps/docs/docs/core-concepts/projects/project-labels.md similarity index 100% rename from docs/core-concepts/projects/project-labels.md rename to apps/docs/docs/core-concepts/projects/project-labels.md diff --git a/docs/core-concepts/projects/project-overview.md b/apps/docs/docs/core-concepts/projects/project-overview.md similarity index 100% rename from docs/core-concepts/projects/project-overview.md rename to apps/docs/docs/core-concepts/projects/project-overview.md diff --git a/docs/core-concepts/projects/project-states.md b/apps/docs/docs/core-concepts/projects/project-states.md similarity index 100% rename from docs/core-concepts/projects/project-states.md rename to apps/docs/docs/core-concepts/projects/project-states.md diff --git a/docs/core-concepts/projects/recurring-work-items.md b/apps/docs/docs/core-concepts/projects/recurring-work-items.md similarity index 100% rename from docs/core-concepts/projects/recurring-work-items.md rename to apps/docs/docs/core-concepts/projects/recurring-work-items.md diff --git a/docs/core-concepts/projects/run-project.md b/apps/docs/docs/core-concepts/projects/run-project.md similarity index 100% rename from docs/core-concepts/projects/run-project.md rename to apps/docs/docs/core-concepts/projects/run-project.md diff --git a/docs/core-concepts/stickies.md b/apps/docs/docs/core-concepts/stickies.md similarity index 100% rename from docs/core-concepts/stickies.md rename to apps/docs/docs/core-concepts/stickies.md diff --git a/docs/core-concepts/views.md b/apps/docs/docs/core-concepts/views.md similarity index 100% rename from docs/core-concepts/views.md rename to apps/docs/docs/core-concepts/views.md diff --git a/docs/core-concepts/workspaces/members.md b/apps/docs/docs/core-concepts/workspaces/members.md similarity index 100% rename from docs/core-concepts/workspaces/members.md rename to apps/docs/docs/core-concepts/workspaces/members.md diff --git a/docs/core-concepts/workspaces/overview.md b/apps/docs/docs/core-concepts/workspaces/overview.md similarity index 100% rename from docs/core-concepts/workspaces/overview.md rename to apps/docs/docs/core-concepts/workspaces/overview.md diff --git a/docs/core-concepts/workspaces/teamspaces.md b/apps/docs/docs/core-concepts/workspaces/teamspaces.md similarity index 100% rename from docs/core-concepts/workspaces/teamspaces.md rename to apps/docs/docs/core-concepts/workspaces/teamspaces.md diff --git a/docs/customers.md b/apps/docs/docs/customers.md similarity index 100% rename from docs/customers.md rename to apps/docs/docs/customers.md diff --git a/docs/dashboards.md b/apps/docs/docs/dashboards.md similarity index 100% rename from docs/dashboards.md rename to apps/docs/docs/dashboards.md diff --git a/docs/devices/desktop.md b/apps/docs/docs/devices/desktop.md similarity index 100% rename from docs/devices/desktop.md rename to apps/docs/docs/devices/desktop.md diff --git a/docs/devices/mobile.md b/apps/docs/docs/devices/mobile.md similarity index 100% rename from docs/devices/mobile.md rename to apps/docs/docs/devices/mobile.md diff --git a/docs/importers/asana.md b/apps/docs/docs/importers/asana.md similarity index 100% rename from docs/importers/asana.md rename to apps/docs/docs/importers/asana.md diff --git a/docs/importers/clickup.md b/apps/docs/docs/importers/clickup.md similarity index 100% rename from docs/importers/clickup.md rename to apps/docs/docs/importers/clickup.md diff --git a/docs/importers/confluence.md b/apps/docs/docs/importers/confluence.md similarity index 100% rename from docs/importers/confluence.md rename to apps/docs/docs/importers/confluence.md diff --git a/docs/importers/csv.md b/apps/docs/docs/importers/csv.md similarity index 100% rename from docs/importers/csv.md rename to apps/docs/docs/importers/csv.md diff --git a/docs/importers/flatfile.md b/apps/docs/docs/importers/flatfile.md similarity index 100% rename from docs/importers/flatfile.md rename to apps/docs/docs/importers/flatfile.md diff --git a/docs/importers/github-imp.md b/apps/docs/docs/importers/github-imp.md similarity index 100% rename from docs/importers/github-imp.md rename to apps/docs/docs/importers/github-imp.md diff --git a/docs/importers/jira.md b/apps/docs/docs/importers/jira.md similarity index 100% rename from docs/importers/jira.md rename to apps/docs/docs/importers/jira.md diff --git a/docs/importers/linear.md b/apps/docs/docs/importers/linear.md similarity index 100% rename from docs/importers/linear.md rename to apps/docs/docs/importers/linear.md diff --git a/docs/importers/notion.md b/apps/docs/docs/importers/notion.md similarity index 100% rename from docs/importers/notion.md rename to apps/docs/docs/importers/notion.md diff --git a/docs/importers/overview.md b/apps/docs/docs/importers/overview.md similarity index 100% rename from docs/importers/overview.md rename to apps/docs/docs/importers/overview.md diff --git a/docs/index.md b/apps/docs/docs/index.md similarity index 99% rename from docs/index.md rename to apps/docs/docs/index.md index ca6d8d73..4256910b 100644 --- a/docs/index.md +++ b/apps/docs/docs/index.md @@ -2,6 +2,7 @@ layout: doc title: Plane Docs description: Everything you need to learn Plane, manage projects, and build powerful workflows. +aside: false prev: false next: false copyPage: false diff --git a/docs/intake/intake-email.md b/apps/docs/docs/intake/intake-email.md similarity index 100% rename from docs/intake/intake-email.md rename to apps/docs/docs/intake/intake-email.md diff --git a/docs/intake/intake-forms.md b/apps/docs/docs/intake/intake-forms.md similarity index 100% rename from docs/intake/intake-forms.md rename to apps/docs/docs/intake/intake-forms.md diff --git a/docs/intake/overview.md b/apps/docs/docs/intake/overview.md similarity index 100% rename from docs/intake/overview.md rename to apps/docs/docs/intake/overview.md diff --git a/docs/integrations/about.md b/apps/docs/docs/integrations/about.md similarity index 100% rename from docs/integrations/about.md rename to apps/docs/docs/integrations/about.md diff --git a/docs/integrations/bitbucket.md b/apps/docs/docs/integrations/bitbucket.md similarity index 100% rename from docs/integrations/bitbucket.md rename to apps/docs/docs/integrations/bitbucket.md diff --git a/docs/integrations/cursor.md b/apps/docs/docs/integrations/cursor.md similarity index 100% rename from docs/integrations/cursor.md rename to apps/docs/docs/integrations/cursor.md diff --git a/docs/integrations/draw-io.md b/apps/docs/docs/integrations/draw-io.md similarity index 100% rename from docs/integrations/draw-io.md rename to apps/docs/docs/integrations/draw-io.md diff --git a/docs/integrations/github.md b/apps/docs/docs/integrations/github.md similarity index 100% rename from docs/integrations/github.md rename to apps/docs/docs/integrations/github.md diff --git a/docs/integrations/gitlab.md b/apps/docs/docs/integrations/gitlab.md similarity index 100% rename from docs/integrations/gitlab.md rename to apps/docs/docs/integrations/gitlab.md diff --git a/docs/integrations/sentry.md b/apps/docs/docs/integrations/sentry.md similarity index 100% rename from docs/integrations/sentry.md rename to apps/docs/docs/integrations/sentry.md diff --git a/docs/integrations/slack.md b/apps/docs/docs/integrations/slack.md similarity index 100% rename from docs/integrations/slack.md rename to apps/docs/docs/integrations/slack.md diff --git a/docs/introduction/core-concepts.md b/apps/docs/docs/introduction/core-concepts.md similarity index 100% rename from docs/introduction/core-concepts.md rename to apps/docs/docs/introduction/core-concepts.md diff --git a/docs/introduction/home.md b/apps/docs/docs/introduction/home.md similarity index 100% rename from docs/introduction/home.md rename to apps/docs/docs/introduction/home.md diff --git a/docs/introduction/quickstart.md b/apps/docs/docs/introduction/quickstart.md similarity index 100% rename from docs/introduction/quickstart.md rename to apps/docs/docs/introduction/quickstart.md diff --git a/docs/introduction/tutorials/collaborate-on-work-items.md b/apps/docs/docs/introduction/tutorials/collaborate-on-work-items.md similarity index 100% rename from docs/introduction/tutorials/collaborate-on-work-items.md rename to apps/docs/docs/introduction/tutorials/collaborate-on-work-items.md diff --git a/docs/introduction/tutorials/create-pages.md b/apps/docs/docs/introduction/tutorials/create-pages.md similarity index 100% rename from docs/introduction/tutorials/create-pages.md rename to apps/docs/docs/introduction/tutorials/create-pages.md diff --git a/docs/introduction/tutorials/create-project.md b/apps/docs/docs/introduction/tutorials/create-project.md similarity index 100% rename from docs/introduction/tutorials/create-project.md rename to apps/docs/docs/introduction/tutorials/create-project.md diff --git a/docs/introduction/tutorials/create-work-items.md b/apps/docs/docs/introduction/tutorials/create-work-items.md similarity index 100% rename from docs/introduction/tutorials/create-work-items.md rename to apps/docs/docs/introduction/tutorials/create-work-items.md diff --git a/docs/introduction/tutorials/create-workspace.md b/apps/docs/docs/introduction/tutorials/create-workspace.md similarity index 100% rename from docs/introduction/tutorials/create-workspace.md rename to apps/docs/docs/introduction/tutorials/create-workspace.md diff --git a/docs/introduction/tutorials/invite-members.md b/apps/docs/docs/introduction/tutorials/invite-members.md similarity index 100% rename from docs/introduction/tutorials/invite-members.md rename to apps/docs/docs/introduction/tutorials/invite-members.md diff --git a/docs/introduction/tutorials/organize-and-view-work.md b/apps/docs/docs/introduction/tutorials/organize-and-view-work.md similarity index 100% rename from docs/introduction/tutorials/organize-and-view-work.md rename to apps/docs/docs/introduction/tutorials/organize-and-view-work.md diff --git a/docs/introduction/tutorials/overview.md b/apps/docs/docs/introduction/tutorials/overview.md similarity index 100% rename from docs/introduction/tutorials/overview.md rename to apps/docs/docs/introduction/tutorials/overview.md diff --git a/docs/introduction/tutorials/plan-and-create-cycles.md b/apps/docs/docs/introduction/tutorials/plan-and-create-cycles.md similarity index 100% rename from docs/introduction/tutorials/plan-and-create-cycles.md rename to apps/docs/docs/introduction/tutorials/plan-and-create-cycles.md diff --git a/docs/logo/dark.svg b/apps/docs/docs/logo/dark.svg similarity index 100% rename from docs/logo/dark.svg rename to apps/docs/docs/logo/dark.svg diff --git a/docs/logo/favicon.svg b/apps/docs/docs/logo/favicon.svg similarity index 100% rename from docs/logo/favicon.svg rename to apps/docs/docs/logo/favicon.svg diff --git a/docs/logo/light.svg b/apps/docs/docs/logo/light.svg similarity index 100% rename from docs/logo/light.svg rename to apps/docs/docs/logo/light.svg diff --git a/docs/pages/collections.md b/apps/docs/docs/pages/collections.md similarity index 100% rename from docs/pages/collections.md rename to apps/docs/docs/pages/collections.md diff --git a/docs/pages/edit-ms-office-files.md b/apps/docs/docs/pages/edit-ms-office-files.md similarity index 100% rename from docs/pages/edit-ms-office-files.md rename to apps/docs/docs/pages/edit-ms-office-files.md diff --git a/docs/pages/nested-pages.md b/apps/docs/docs/pages/nested-pages.md similarity index 100% rename from docs/pages/nested-pages.md rename to apps/docs/docs/pages/nested-pages.md diff --git a/docs/pages/organize-pages.md b/apps/docs/docs/pages/organize-pages.md similarity index 100% rename from docs/pages/organize-pages.md rename to apps/docs/docs/pages/organize-pages.md diff --git a/docs/pages/page-labels.md b/apps/docs/docs/pages/page-labels.md similarity index 100% rename from docs/pages/page-labels.md rename to apps/docs/docs/pages/page-labels.md diff --git a/docs/pages/report-page.md b/apps/docs/docs/pages/report-page.md similarity index 100% rename from docs/pages/report-page.md rename to apps/docs/docs/pages/report-page.md diff --git a/docs/projects/project-audit-logs.md b/apps/docs/docs/projects/project-audit-logs.md similarity index 100% rename from docs/projects/project-audit-logs.md rename to apps/docs/docs/projects/project-audit-logs.md diff --git a/docs/projects/project-releases.md b/apps/docs/docs/projects/project-releases.md similarity index 100% rename from docs/projects/project-releases.md rename to apps/docs/docs/projects/project-releases.md diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Bold.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Bold.ttf new file mode 100644 index 00000000..2e437e21 Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Bold.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-BoldItalic.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-BoldItalic.ttf new file mode 100644 index 00000000..f2695fce Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-BoldItalic.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLight.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLight.ttf new file mode 100644 index 00000000..573ef764 Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLight.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLightItalic.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLightItalic.ttf new file mode 100644 index 00000000..ea13f86d Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ExtraLightItalic.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Italic.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Italic.ttf new file mode 100644 index 00000000..3cb28a39 Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Italic.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Light.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Light.ttf new file mode 100644 index 00000000..df167f09 Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Light.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-LightItalic.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-LightItalic.ttf new file mode 100644 index 00000000..c9072e96 Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-LightItalic.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Medium.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Medium.ttf new file mode 100644 index 00000000..39f178db Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Medium.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-MediumItalic.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-MediumItalic.ttf new file mode 100644 index 00000000..0d887f76 Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-MediumItalic.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Regular.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Regular.ttf new file mode 100644 index 00000000..81ca3dcc Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Regular.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBold.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBold.ttf new file mode 100644 index 00000000..73dd5a4f Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBold.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBoldItalic.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBoldItalic.ttf new file mode 100644 index 00000000..a41b0d3d Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-SemiBoldItalic.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Thin.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Thin.ttf new file mode 100644 index 00000000..e173f5a1 Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-Thin.ttf differ diff --git a/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ThinItalic.ttf b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ThinItalic.ttf new file mode 100644 index 00000000..85292757 Binary files /dev/null and b/apps/docs/docs/public/fonts/IBMPlexMono/IBMPlexMono-ThinItalic.ttf differ diff --git a/apps/docs/docs/public/fonts/Inter/InterVariable.woff2 b/apps/docs/docs/public/fonts/Inter/InterVariable.woff2 new file mode 100644 index 00000000..5a8d3e72 Binary files /dev/null and b/apps/docs/docs/public/fonts/Inter/InterVariable.woff2 differ diff --git a/docs/public/icons/arrow-left-right.svg b/apps/docs/docs/public/icons/arrow-left-right.svg similarity index 100% rename from docs/public/icons/arrow-left-right.svg rename to apps/docs/docs/public/icons/arrow-left-right.svg diff --git a/docs/public/icons/book-open.svg b/apps/docs/docs/public/icons/book-open.svg similarity index 100% rename from docs/public/icons/book-open.svg rename to apps/docs/docs/public/icons/book-open.svg diff --git a/docs/public/icons/building-2.svg b/apps/docs/docs/public/icons/building-2.svg similarity index 100% rename from docs/public/icons/building-2.svg rename to apps/docs/docs/public/icons/building-2.svg diff --git a/docs/public/icons/kanban.svg b/apps/docs/docs/public/icons/kanban.svg similarity index 100% rename from docs/public/icons/kanban.svg rename to apps/docs/docs/public/icons/kanban.svg diff --git a/docs/public/icons/plug.svg b/apps/docs/docs/public/icons/plug.svg similarity index 100% rename from docs/public/icons/plug.svg rename to apps/docs/docs/public/icons/plug.svg diff --git a/docs/public/icons/terminal.svg b/apps/docs/docs/public/icons/terminal.svg similarity index 100% rename from docs/public/icons/terminal.svg rename to apps/docs/docs/public/icons/terminal.svg diff --git a/docs/public/robots.txt b/apps/docs/docs/public/robots.txt similarity index 100% rename from docs/public/robots.txt rename to apps/docs/docs/public/robots.txt diff --git a/docs/releases.md b/apps/docs/docs/releases.md similarity index 100% rename from docs/releases.md rename to apps/docs/docs/releases.md diff --git a/docs/roles-and-permissions/custom-roles.md b/apps/docs/docs/roles-and-permissions/custom-roles.md similarity index 100% rename from docs/roles-and-permissions/custom-roles.md rename to apps/docs/docs/roles-and-permissions/custom-roles.md diff --git a/docs/roles-and-permissions/member-roles.md b/apps/docs/docs/roles-and-permissions/member-roles.md similarity index 100% rename from docs/roles-and-permissions/member-roles.md rename to apps/docs/docs/roles-and-permissions/member-roles.md diff --git a/docs/roles-and-permissions/overview.md b/apps/docs/docs/roles-and-permissions/overview.md similarity index 100% rename from docs/roles-and-permissions/overview.md rename to apps/docs/docs/roles-and-permissions/overview.md diff --git a/docs/roles-and-permissions/permission-schemes.md b/apps/docs/docs/roles-and-permissions/permission-schemes.md similarity index 100% rename from docs/roles-and-permissions/permission-schemes.md rename to apps/docs/docs/roles-and-permissions/permission-schemes.md diff --git a/docs/roles-and-permissions/permissions-matrix.md b/apps/docs/docs/roles-and-permissions/permissions-matrix.md similarity index 100% rename from docs/roles-and-permissions/permissions-matrix.md rename to apps/docs/docs/roles-and-permissions/permissions-matrix.md diff --git a/docs/support/get-help.md b/apps/docs/docs/support/get-help.md similarity index 100% rename from docs/support/get-help.md rename to apps/docs/docs/support/get-help.md diff --git a/docs/support/keyboard-shortcuts.md b/apps/docs/docs/support/keyboard-shortcuts.md similarity index 100% rename from docs/support/keyboard-shortcuts.md rename to apps/docs/docs/support/keyboard-shortcuts.md diff --git a/docs/templates/page-templates.md b/apps/docs/docs/templates/page-templates.md similarity index 100% rename from docs/templates/page-templates.md rename to apps/docs/docs/templates/page-templates.md diff --git a/docs/templates/project-templates.md b/apps/docs/docs/templates/project-templates.md similarity index 100% rename from docs/templates/project-templates.md rename to apps/docs/docs/templates/project-templates.md diff --git a/docs/templates/work-item-templates.md b/apps/docs/docs/templates/work-item-templates.md similarity index 100% rename from docs/templates/work-item-templates.md rename to apps/docs/docs/templates/work-item-templates.md diff --git a/docs/work-items/custom-relations.md b/apps/docs/docs/work-items/custom-relations.md similarity index 100% rename from docs/work-items/custom-relations.md rename to apps/docs/docs/work-items/custom-relations.md diff --git a/docs/work-items/project-work-item-types.md b/apps/docs/docs/work-items/project-work-item-types.md similarity index 100% rename from docs/work-items/project-work-item-types.md rename to apps/docs/docs/work-items/project-work-item-types.md diff --git a/docs/work-items/workspace-work-item-types.md b/apps/docs/docs/work-items/workspace-work-item-types.md similarity index 100% rename from docs/work-items/workspace-work-item-types.md rename to apps/docs/docs/work-items/workspace-work-item-types.md diff --git a/docs/workflows-and-approvals/workflows.md b/apps/docs/docs/workflows-and-approvals/workflows.md similarity index 100% rename from docs/workflows-and-approvals/workflows.md rename to apps/docs/docs/workflows-and-approvals/workflows.md diff --git a/docs/workspace-administration/workspace-governance.md b/apps/docs/docs/workspace-administration/workspace-governance.md similarity index 100% rename from docs/workspace-administration/workspace-governance.md rename to apps/docs/docs/workspace-administration/workspace-governance.md diff --git a/docs/workspaces-and-users/add-remove-seats.md b/apps/docs/docs/workspaces-and-users/add-remove-seats.md similarity index 100% rename from docs/workspaces-and-users/add-remove-seats.md rename to apps/docs/docs/workspaces-and-users/add-remove-seats.md diff --git a/docs/workspaces-and-users/audit-logs.md b/apps/docs/docs/workspaces-and-users/audit-logs.md similarity index 100% rename from docs/workspaces-and-users/audit-logs.md rename to apps/docs/docs/workspaces-and-users/audit-logs.md diff --git a/docs/workspaces-and-users/billing-and-plans.md b/apps/docs/docs/workspaces-and-users/billing-and-plans.md similarity index 100% rename from docs/workspaces-and-users/billing-and-plans.md rename to apps/docs/docs/workspaces-and-users/billing-and-plans.md diff --git a/docs/workspaces-and-users/customize-navigation.md b/apps/docs/docs/workspaces-and-users/customize-navigation.md similarity index 100% rename from docs/workspaces-and-users/customize-navigation.md rename to apps/docs/docs/workspaces-and-users/customize-navigation.md diff --git a/docs/workspaces-and-users/manage-licenses.md b/apps/docs/docs/workspaces-and-users/manage-licenses.md similarity index 100% rename from docs/workspaces-and-users/manage-licenses.md rename to apps/docs/docs/workspaces-and-users/manage-licenses.md diff --git a/docs/workspaces-and-users/search-workspace.md b/apps/docs/docs/workspaces-and-users/search-workspace.md similarity index 100% rename from docs/workspaces-and-users/search-workspace.md rename to apps/docs/docs/workspaces-and-users/search-workspace.md diff --git a/docs/workspaces-and-users/upgrade-plan.md b/apps/docs/docs/workspaces-and-users/upgrade-plan.md similarity index 100% rename from docs/workspaces-and-users/upgrade-plan.md rename to apps/docs/docs/workspaces-and-users/upgrade-plan.md diff --git a/docs/your-work.md b/apps/docs/docs/your-work.md similarity index 100% rename from docs/your-work.md rename to apps/docs/docs/your-work.md diff --git a/apps/docs/package.json b/apps/docs/package.json new file mode 100644 index 00000000..2d53a020 --- /dev/null +++ b/apps/docs/package.json @@ -0,0 +1,33 @@ +{ + "name": "docs", + "version": "3.0.0", + "private": true, + "description": "Plane product documentation — docs.plane.so", + "homepage": "https://docs.plane.so", + "license": "MIT", + "author": "Plane", + "repository": { + "type": "git", + "url": "https://github.com/makeplane/docs.git", + "directory": "apps/docs" + }, + "type": "module", + "scripts": { + "dev": "vitepress dev docs --port 5173", + "build": "vitepress build docs", + "preview": "vitepress preview docs --port 4173", + "check:types": "tsc --noEmit -p tsconfig.json" + }, + "dependencies": { + "@plane/docs-theme": "workspace:*" + }, + "devDependencies": { + "@types/node": "catalog:", + "@voidzero-dev/vitepress-theme": "catalog:", + "typescript": "catalog:", + "vitepress": "catalog:", + "vitepress-plugin-llms": "catalog:", + "vitepress-plugin-tabs": "catalog:", + "vue": "catalog:" + } +} diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json new file mode 100644 index 00000000..a4c03058 --- /dev/null +++ b/apps/docs/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["docs/.vitepress/**/*.ts", "docs/.vitepress/**/*.mts", "docs/.vitepress/**/*.vue"], + "exclude": ["docs/.vitepress/cache", "docs/.vitepress/dist", "docs/.vitepress/.temp"] +} diff --git a/vercel.json b/apps/docs/vercel.json similarity index 100% rename from vercel.json rename to apps/docs/vercel.json diff --git a/docs/.vitepress/theme/Layout.vue b/docs/.vitepress/theme/Layout.vue deleted file mode 100644 index 0be3a6b6..00000000 --- a/docs/.vitepress/theme/Layout.vue +++ /dev/null @@ -1,25 +0,0 @@ - - - diff --git a/docs/.vitepress/theme/components/Card.vue b/docs/.vitepress/theme/components/Card.vue deleted file mode 100644 index 2e952579..00000000 --- a/docs/.vitepress/theme/components/Card.vue +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - diff --git a/docs/.vitepress/theme/components/CardGroup.vue b/docs/.vitepress/theme/components/CardGroup.vue deleted file mode 100644 index 335095f6..00000000 --- a/docs/.vitepress/theme/components/CardGroup.vue +++ /dev/null @@ -1,30 +0,0 @@ - - - diff --git a/docs/.vitepress/theme/components/Tags.vue b/docs/.vitepress/theme/components/Tags.vue deleted file mode 100644 index bab1e6f9..00000000 --- a/docs/.vitepress/theme/components/Tags.vue +++ /dev/null @@ -1,27 +0,0 @@ - - - diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts deleted file mode 100644 index 42de6831..00000000 --- a/docs/.vitepress/theme/index.ts +++ /dev/null @@ -1,314 +0,0 @@ -import type { Theme } from "vitepress"; -import type { ThemeContext } from "@voidzero-dev/vitepress-theme"; -import VoidZeroTheme from "@voidzero-dev/vitepress-theme"; -import { themeContextKey } from "@voidzero-dev/vitepress-theme"; -import { onMounted, onUnmounted, watch, nextTick } from "vue"; -import { useData, useRoute } from "vitepress"; -import { enhanceAppWithTabs } from "vitepress-plugin-tabs/client"; -import mediumZoom from "medium-zoom"; -import Card from "./components/Card.vue"; -import CardGroup from "./components/CardGroup.vue"; -import Tags from "./components/Tags.vue"; -import Layout from "./Layout.vue"; -import "./style.css"; - -/** - * OSSHeader (used on doc pages) injects this context for the bar logo — *not* `themeConfig.logo`. - * The `viteplus` entry in the package overwrites it with "Vite+" assets; we use the base - * `VoidZeroTheme` and provide Plane branding here. - */ -const planeThemeContext: ThemeContext = { - /* OSSHeader renders logoDark in light mode and logoLight in dark mode. */ - logoDark: "https://media.docs.plane.so/logo/new-logo-white.png", - logoLight: "https://media.docs.plane.so/logo/new-logo-dark.png", - logoAlt: "Plane", - footerBg: "https://media.docs.plane.so/logo/og-docs.webp", - monoIcon: "https://media.docs.plane.so/logo/favicon-32x32.png", -}; - -/** - * Handles tab activation based on URL hash - */ -function handleTabHash() { - if (typeof document === "undefined") return; - - const hash = window.location.hash.slice(1); // Remove the '#' - if (!hash) return; - - const tabButtons = document.querySelectorAll('[role="tab"]'); - - if (tabButtons.length === 0) { - return; - } - - tabButtons.forEach((button) => { - const labelText = button.textContent?.trim().toLowerCase().replace(/\s+/g, "-"); - - if (labelText === hash) { - const element = button as HTMLElement; - - // Dispatch a proper mouse event - const clickEvent = new MouseEvent("click", { - view: window, - bubbles: true, - cancelable: true, - }); - - element.dispatchEvent(clickEvent); - element.click(); - element.focus(); - } - }); -} - -/** - * Adds click listeners to tabs to update URL hash - */ -function setupTabHashUpdates() { - if (typeof document === "undefined") return; - - const tabButtons = document.querySelectorAll('[role="tab"]'); - - tabButtons.forEach((button) => { - const element = button as HTMLElement; - - // Remove existing listener if any - element.removeEventListener("click", updateHashOnTabClick); - - // Add new listener - element.addEventListener("click", updateHashOnTabClick); - }); -} - -function updateHashOnTabClick(event: Event) { - const button = event.currentTarget as HTMLElement; - const labelText = button.textContent?.trim().toLowerCase().replace(/\s+/g, "-"); - - if (labelText) { - // Update URL hash without triggering scroll - history.replaceState(null, "", `#${labelText}`); - } -} - -/** - * Move "Sign in" CTA to the right utility area (between search and theme toggle). - * The upstream OSSHeader renders nav links on the left; we remap only this CTA. - * Returns true if the link was found and moved (or already in the target region). - */ -function moveSignInToUtilityArea(): boolean { - if (typeof document === "undefined") return false; - - const signInLink = document.querySelector( - '.docs-layout header a.VPLink[href*="sign-in"]', - ) as HTMLAnchorElement | null; - if (!signInLink) return false; - - // Keep the CTA hidden while we're still deciding where it belongs. - signInLink.classList.add("sign-in-relocating"); - - const markRelocated = () => { - signInLink.classList.add("sign-in-relocated"); - signInLink.classList.remove("sign-in-relocating"); - }; - - if (signInLink.classList.contains("sign-in-relocated")) { - signInLink.classList.remove("sign-in-relocating"); - return true; - } - - const appearanceToggle = document.querySelector( - ".docs-layout header .VPNavBarAppearance", - ) as HTMLElement | null; - - if ( - appearanceToggle?.parentElement && - signInLink.parentElement === appearanceToggle.parentElement - ) { - markRelocated(); - return true; - } - - const extraMenu = document.querySelector( - ".docs-layout header .VPNavBarExtra", - ) as HTMLElement | null; - if (extraMenu?.parentElement && signInLink.parentElement === extraMenu.parentElement) { - markRelocated(); - return true; - } - - // Desktop (xl+): insert before theme toggle inside the utilities row. - if ( - appearanceToggle?.parentElement && - signInLink.parentElement !== appearanceToggle.parentElement - ) { - appearanceToggle.parentElement.insertBefore(signInLink, appearanceToggle); - markRelocated(); - return true; - } - - // Tablet fallback (lg-xl): keep it in right controls row before the extra menu. - if (extraMenu?.parentElement && signInLink.parentElement !== extraMenu.parentElement) { - extraMenu.parentElement.insertBefore(signInLink, extraMenu); - markRelocated(); - return true; - } - - return false; -} - -let signInRelocateWarned = false; - -function runSignInRelocationWithRetries() { - if (typeof document === "undefined" || !document.querySelector(".docs-layout")) { - return; - } - - const tryOnce = (attempt: number) => { - if (moveSignInToUtilityArea()) { - return; - } - if (attempt < 40) { - window.setTimeout(() => tryOnce(attempt + 1), 75); - } else if (!signInRelocateWarned) { - const link = document.querySelector('.docs-layout header a.VPLink[href*="sign-in"]'); - if (link) { - link.classList.remove("sign-in-relocating"); - } - if (link && !link.classList.contains("sign-in-relocated")) { - signInRelocateWarned = true; - console.warn( - "[plane-docs] Sign-in could not be relocated (header not ready or selectors changed).", - ); - } - } - }; - - tryOnce(0); -} - -function debounce(fn: () => void, ms: number) { - let t: ReturnType | undefined; - return () => { - if (t) clearTimeout(t); - t = setTimeout(fn, ms); - }; -} - -export default { - extends: VoidZeroTheme, - Layout, - enhanceApp(ctx) { - VoidZeroTheme.enhanceApp?.(ctx); - ctx.app.provide(themeContextKey, planeThemeContext); - enhanceAppWithTabs(ctx.app); - ctx.app.component("Card", Card); - ctx.app.component("CardGroup", CardGroup); - ctx.app.component("Tags", Tags); - }, - setup() { - if (typeof window === "undefined") return; - - const route = useRoute(); - const { isDark } = useData(); - - /** - * VitePress’s inline "check-dark-mode" script only adds the `dark` class and - * never removes it, so a stale `class="dark"` on (e.g. after SSG) can - * persist in light mode. That leaves `.dark …` global/navbar rules applied - * until a theme toggle re-syncs. Keep `documentElement` in lockstep with - * `isDark` (same source of truth as the toggle). - */ - const syncOssHeaderThemeAttr = (dark: boolean) => { - const header = document.querySelector(".docs-layout header") as HTMLElement | null; - const bar = header?.parentElement; - if (!bar) return; - if (dark) bar.setAttribute("data-theme", "dark"); - else bar.removeAttribute("data-theme"); - }; - - watch( - isDark, - (dark) => { - document.documentElement.classList.toggle("dark", dark); - syncOssHeaderThemeAttr(dark); - }, - { immediate: true }, - ); - - onMounted(() => { - syncOssHeaderThemeAttr(isDark.value); - }); - - const zoom = mediumZoom(".vp-doc img", { - background: "rgba(0, 0, 0, 0.8)", - }); - - let headerObserver: MutationObserver | null = null; - let onResize: (() => void) | null = null; - - onMounted(() => { - runSignInRelocationWithRetries(); - - // Delay tab hash handling to ensure tabs are rendered - setTimeout(() => { - handleTabHash(); - setupTabHashUpdates(); - }, 100); - - const onHeaderMutations = debounce(() => { - runSignInRelocationWithRetries(); - }, 100); - - const tryAttachHeaderObserver = () => { - if (headerObserver) return; - const h = document.querySelector(".docs-layout header"); - if (!h) return; - headerObserver = new MutationObserver(onHeaderMutations); - headerObserver.observe(h, { childList: true, subtree: true }); - }; - tryAttachHeaderObserver(); - if (!headerObserver) { - const id = window.setInterval(() => { - tryAttachHeaderObserver(); - if (headerObserver) { - clearInterval(id); - } - }, 120); - window.setTimeout(() => clearInterval(id), 5000); - } - - onResize = debounce(() => { - runSignInRelocationWithRetries(); - }, 150); - window.addEventListener("resize", onResize); - - // Listen for hash changes - window.addEventListener("hashchange", () => { - nextTick(handleTabHash); - }); - }); - - onUnmounted(() => { - headerObserver?.disconnect(); - headerObserver = null; - if (onResize) { - window.removeEventListener("resize", onResize); - onResize = null; - } - }); - - // Watch for route changes - watch( - () => route.path, - () => { - nextTick(() => { - zoom.detach(); - zoom.attach(":not(a) > img:not(.VPImage)"); - handleTabHash(); - setupTabHashUpdates(); - runSignInRelocationWithRetries(); - }); - }, - ); - }, -} satisfies Theme; diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css deleted file mode 100644 index 314ffbd1..00000000 --- a/docs/.vitepress/theme/style.css +++ /dev/null @@ -1,1416 +0,0 @@ -/** @format */ - -@import "@voidzero-dev/vitepress-theme/src/styles/index.css"; - -@source "../../**/*.{vue,md}"; -@source "../**/*.{vue,md}"; - -@theme { - --color-plane-400: #0088cc; - --color-plane-500: #006399; - --color-plane-600: #005280; - --font-heading: "Inter", sans-serif; - --font-sans: "Inter", sans-serif; - --font-mono: "IBM Plex Mono", monospace; -} - -/* ================================================ - THEME VARIANTS - ================================================ */ - -@custom-variant light (&:where([data-theme*="light"], [data-theme*="light"] *)); -@custom-variant dark (&:where([data-theme*="dark"], [data-theme*="dark"] *)); - -/* ================================================ - COLOR SYSTEM - ================================================ */ - -@layer base { - :root { - /* Alpha colors - for transparency effects */ - --alpha-white-0: oklch(1 0 0 / 0%); - --alpha-white-200: oklch(1 0 0 / 10%); - --alpha-white-500: oklch(1 0 0 / 30%); - --alpha-white-700: oklch(1 0 0 / 50%); - --alpha-black-0: oklch(0.1482 0.0034 196.79 / 0%); - --alpha-black-200: oklch(0.1482 0.0034 196.79 / 10%); - --alpha-black-500: oklch(0.1482 0.0034 196.79 / 30%); - --alpha-black-700: oklch(0.1482 0.0034 196.79 / 50%); - - /* Neutral colors - for text and backgrounds */ - --neutral-white: oklch(1 0 0); - --neutral-100: oklch(0.9848 0.0003 230.66); - --neutral-200: oklch(0.9696 0.0007 230.67); - --neutral-300: oklch(0.9543 0.001 230.67); - --neutral-500: oklch(0.9235 0.001733 230.6853); - --neutral-700: oklch(0.8612 0.0032 230.71); - --neutral-800: oklch(0.6668 0.0079 230.82); - --neutral-1000: oklch(0.5288 0.0083 230.88); - --neutral-1100: oklch(0.4377 0.0066 230.87); - --neutral-1200: oklch(0.2378 0.0029 230.83); - --neutral-black: oklch(0.1472 0.0034 230.83); - - /* Brand colors */ - --brand-100: oklch(0.9847 0.0083 236.56); - --brand-300: oklch(0.9428 0.0341 230.22); - --brand-500: oklch(0.8414 0.0947 233.08); - --brand-700: oklch(0.6766 0.1665 243.91); - --brand-900: oklch(0.4347 0.104093 242.4823); - --brand-default: oklch(0.4799 0.1158 242.91); - - /* Semantic colors */ - --green-100: oklch(0.9819 0.0181 155.83); - --green-500: oklch(0.7914 0.2091 151.66); - --green-700: oklch(0.632 0.185972 147.3695); - --amber-100: oklch(0.9869 0.0214 95.28); - --amber-500: oklch(0.829 0.1712 81.04); - --amber-700: oklch(0.6671 0.1685 53.38); - --red-100: oklch(0.9705 0.0129 17.38); - --red-500: oklch(0.7022 0.1892 22.23); - --red-700: oklch(0.583 0.238666 28.4765); - - /* Font sizes */ - --text-xs: 0.75rem; /* 12px */ - --text-sm: 0.875rem; /* 14px */ - --text-base: 1rem; /* 16px */ - --text-lg: 1.125rem; /* 18px */ - --text-xl: 1.25rem; /* 20px */ - --text-2xl: 1.5rem; /* 24px */ - --text-3xl: 1.875rem; /* 30px */ - --text-4xl: 2.25rem; /* 36px */ - - /* Font weights */ - --font-weight-light: 300; - --font-weight-normal: 400; - --font-weight-medium: 500; - --font-weight-semibold: 600; - --font-weight-bold: 700; - } -} - -/* ================================================ - INTER VARIABLE FONT - ================================================ */ - -@font-face { - font-family: "Inter"; - src: url("/fonts/Inter/InterVariable.woff2") format("woff2"); - font-weight: 100 900; - font-style: normal; - font-display: swap; -} - -/* ================================================ - IBM PLEX MONO FONT (FOR CODE BLOCKS) - ================================================ */ - -@font-face { - font-family: "IBM Plex Mono"; - src: url("/fonts/IBMPlexMono/IBMPlexMono-Regular.ttf") format("truetype"); - font-weight: 400; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: "IBM Plex Mono"; - src: url("/fonts/IBMPlexMono/IBMPlexMono-Medium.ttf") format("truetype"); - font-weight: 500; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: "IBM Plex Mono"; - src: url("/fonts/IBMPlexMono/IBMPlexMono-SemiBold.ttf") format("truetype"); - font-weight: 600; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: "IBM Plex Mono"; - src: url("/fonts/IBMPlexMono/IBMPlexMono-Bold.ttf") format("truetype"); - font-weight: 700; - font-style: normal; - font-display: swap; -} - -/* ================================================ - PLANE DEVELOPER DOCS - CUSTOM STYLES - ================================================ */ - -/* Brand colors and fonts */ -:root, -[data-theme="light"], -[data-theme="dark"], -html.dark { - /* Brand colors */ - --vp-c-brand-1: #006399; - --vp-c-brand-2: #006399; - --vp-c-brand-3: #006399; - --vp-c-brand-soft: rgba(0, 99, 153, 0.14); - --plane-500: #006399; - - /* Inline code — neutral body-text color instead of the VitePress default - (brand blue), which is hard to read on the dark pill background. - Linked code keeps --vp-code-link-color (brand) so links stay visible. */ - --vp-code-color: var(--vp-c-text-1); - - /* Fonts — Inter for body and headings (override VoidZero APK Protocol) */ - --font-heading: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - --font-mono: - "IBM Plex Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, - "Liberation Mono", "Courier New", monospace; - --vp-font-family-base: - "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, - sans-serif; - --vp-font-family-mono: - "IBM Plex Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, - "Liberation Mono", "Courier New", monospace; -} - -/* Font rendering optimizations */ -body, -.docs-layout { - font-family: var(--vp-font-family-base); -} - -.docs-layout h1, -.docs-layout h2, -.docs-layout h3, -.docs-layout h4, -.docs-layout h5, -.docs-layout h6, -.vp-doc h1, -.vp-doc h2, -.vp-doc h3, -.vp-doc h4, -.vp-doc h5, -.vp-doc h6, -.font-heading { - font-family: var(--vp-font-family-base) !important; -} - -body { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - text-rendering: optimizeLegibility; - font-feature-settings: "cv01", "cv02", "zero"; -} - -/* Typography tuning */ -.vp-doc { - line-height: 1.7; - letter-spacing: -0.011em; -} - -.vp-doc h1 { - letter-spacing: -0.022em; - line-height: 1.2; -} - -.vp-doc h2 { - letter-spacing: -0.017em; - line-height: 1.3; -} - -.vp-doc h3 { - letter-spacing: -0.014em; - line-height: 1.4; -} - -/* Inline doc links (lists, paragraphs, etc.) — match primary button brand per theme */ -.vp-doc a:not(.card-link):not(.home-doc-actions__btn):not(.header-anchor):not(.copy-page__item) { - color: var(--vp-c-brand-1); -} - -.vp-doc - a:not(.card-link):not(.home-doc-actions__btn):not(.header-anchor):not(.copy-page__item):hover { - color: #0078b8; -} - -.dark - .vp-doc - a:not(.card-link):not(.home-doc-actions__btn):not(.header-anchor):not(.copy-page__item):hover, -[data-theme="dark"] - .vp-doc - a:not(.card-link):not(.home-doc-actions__btn):not(.header-anchor):not(.copy-page__item):hover, -html.dark - .vp-doc - a:not(.card-link):not(.home-doc-actions__btn):not(.header-anchor):not(.copy-page__item):hover { - color: #3aa5d4; -} - -/* Dark mode — override @voidzero-dev/vitepress-theme (docs set data-theme="dark" on the layout wrapper) */ -.dark:not([data-theme]), -[data-theme="dark"], -html.dark { - /* VoidZero maps page bg to --color-primary, not only --vp-c-bg */ - --color-primary: #141415; - --vp-c-bg: #141415; - --vp-c-bg-soft: #1f2122; - --vp-c-bg-alt: #141618; - --vp-c-bg-mute: #252829; - - /* Brand colors */ - --vp-c-brand-1: #2893cc; - --vp-c-brand-2: #2893cc; - --vp-c-brand-3: #2893cc; - --vp-c-brand-soft: rgba(40, 147, 204, 0.14); - --plane-500: #2893cc; -} - -/* Ensure page shell picks up Plane dark background (theme sets bg on [data-theme="dark"] and .docs-layout) */ -html.dark body, -html.dark:not([data-theme]) .docs-layout, -[data-theme="dark"], -[data-theme="dark"] .docs-layout { - background-color: #141415; -} - -/* ================================================ - HIDE ASIDE FOR API PAGES - ================================================ */ - -/* Hide ALL aside elements when .api-page is present */ -.api-page .aside, -.api-page .aside-container, -.api-page .VPDocAside, -.api-page aside, -.api-page .VPDocAsideOutline { - display: none !important; - width: 0 !important; - min-width: 0 !important; - opacity: 0 !important; - visibility: hidden !important; -} - -/* Remove the aside column entirely */ -.api-page.VPDoc > .container { - display: block !important; - max-width: 100% !important; -} - -.api-page .VPDoc > .container > .content { - max-width: 100% !important; - padding-right: 24px !important; -} - -/* Target specific VitePress structure */ -.api-page .content-container { - max-width: 100% !important; -} - -.api-page .main { - max-width: 100% !important; -} - -/* Force full width on content */ -.api-page .vp-doc { - max-width: 100% !important; -} - -/* ================================================ - TWO-COLUMN API LAYOUT - ================================================ */ - -.api-two-column { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); - gap: 40px; - align-items: start; -} - -@media (max-width: 1100px) { - .api-two-column { - grid-template-columns: 1fr; - gap: 24px; - } -} - -.api-left { - min-width: 0; -} - -.api-right { - position: sticky; - top: 100px; - min-width: 0; -} - -@media (max-width: 1100px) { - .api-right { - position: static; - } -} - -/* ================================================ - API ENDPOINT BADGE - ================================================ */ - -.api-endpoint-badge { - display: inline-flex; - align-items: center; - gap: 12px; - margin-bottom: 20px; -} - -.api-endpoint-badge .method { - font-size: 12px; - font-weight: 700; - padding: 5px 12px; - border-radius: 6px; - font-family: var(--vp-font-family-mono); - text-transform: uppercase; -} - -.api-endpoint-badge .method.get { - background: #dcfce7; - color: #166534; -} - -.api-endpoint-badge .method.post { - background: #dbeafe; - color: #1e40af; -} - -.api-endpoint-badge .method.patch, -.api-endpoint-badge .method.put { - background: #fef3c7; - color: #92400e; -} - -.api-endpoint-badge .method.delete { - background: #fee2e2; - color: #991b1b; -} - -.dark .api-endpoint-badge .method.get { - background: rgba(34, 197, 94, 0.15); - color: #4ade80; -} - -.dark .api-endpoint-badge .method.post { - background: rgba(59, 130, 246, 0.15); - color: #60a5fa; -} - -.dark .api-endpoint-badge .method.patch, -.dark .api-endpoint-badge .method.put { - background: rgba(251, 191, 36, 0.15); - color: #fbbf24; -} - -.dark .api-endpoint-badge .method.delete { - background: rgba(239, 68, 68, 0.15); - color: #f87171; -} - -.api-endpoint-badge .path { - font-family: var(--vp-font-family-mono); - font-size: 14px; - color: #6b7280; -} - -.dark .api-endpoint-badge .path { - color: #9ca3af; -} - -/* ================================================ - SECTION HEADERS - ================================================ */ - -.params-section { - margin: 24px 0; -} - -.params-section h3 { - font-size: 13px !important; - font-weight: 600 !important; - color: #6b7280 !important; - text-transform: uppercase; - letter-spacing: 0.05em; - margin: 0 0 12px 0 !important; - padding: 0 !important; - border: none !important; -} - -.dark .params-section h3 { - color: #9ca3af !important; -} - -.params-list { - border-top: 1px solid #e5e7eb; -} - -.dark .params-list { - border-top-color: #2a2a2a; -} - -.returns-section { - margin: 24px 0; -} - -.returns-section h3 { - font-size: 13px !important; - font-weight: 600 !important; - color: #6b7280 !important; - text-transform: uppercase; - letter-spacing: 0.05em; - margin: 0 0 12px 0 !important; - padding: 0 !important; - border: none !important; -} - -.dark .returns-section h3 { - color: #9ca3af !important; -} - -/* ================================================ - CODE BLOCKS - DARK MODE FIX - ================================================ */ - -div[class*="language-"] { - border-radius: 8px !important; - overflow: hidden; - border: 1px solid #e5e7eb; - background: #fafafa !important; -} - -.dark div[class*="language-"] { - background: #0f0f0f !important; - border-color: #2a2a2a !important; -} - -div[class*="language-"] pre { - background: transparent !important; -} - -.dark div[class*="language-"] pre { - background: transparent !important; -} - -.dark .shiki, -.dark .shiki span { - background: transparent !important; -} - -/* Language label */ -div[class*="language-"] > span.lang { - color: #9ca3af; - font-size: 12px; -} - -/* ================================================ - UTILITY CLASSES - ================================================ */ - -/* Keep markdown helper class, but don't override Tailwind's global `.hidden` utility. */ -.vp-doc .hidden { - display: none !important; -} - -/* Remove default VitePress h2/h3 borders */ -.vp-doc h2 { - border-top: none !important; - margin-top: 32px; -} - -.vp-doc h3 { - border-top: none !important; -} - -/* ================================================ - CARD GROUP COMPONENT STYLES - ================================================ */ - -.card-group { - display: grid; - gap: 16px; - margin: 24px 0; -} - -.card-group-2 { - grid-template-columns: 1fr; -} - -@media (min-width: 768px) { - .card-group-2 { - grid-template-columns: repeat(2, 1fr); - } -} - -.card-group-3 { - grid-template-columns: 1fr; -} - -@media (min-width: 768px) { - .card-group-3 { - grid-template-columns: repeat(2, 1fr); - } -} - -@media (min-width: 1024px) { - .card-group-3 { - grid-template-columns: repeat(3, 1fr); - } -} - -.card-group-4 { - grid-template-columns: 1fr; -} - -@media (min-width: 768px) { - .card-group-4 { - grid-template-columns: repeat(2, 1fr); - } -} - -@media (min-width: 1024px) { - .card-group-4 { - grid-template-columns: repeat(4, 1fr); - } -} - -/* ================================================ - CARD COMPONENT STYLES - ================================================ */ - -.vp-doc a.card-link { - color: inherit; - text-decoration: none !important; -} - -.card-link { - display: block; - padding: 24px; - border-radius: 12px; - border: 1px solid #e5e7eb; - background: #ffffff; - transition: all 0.2s ease; - text-decoration: none !important; - color: inherit; -} - -.card-link:hover { - border-color: #006399; - box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); - text-decoration: none !important; -} - -.dark .card-link { - border-color: #2a2a2a; - background: rgba(20, 20, 20, 0.5); -} - -.dark .card-link:hover { - border-color: #2893cc; - box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5); -} - -.card-icon { - font-size: 32px; - margin-bottom: 16px; - line-height: 1; -} - -.card-title { - font-size: 16px !important; - font-weight: 600 !important; - color: #111827 !important; - margin: 0 0 12px 0 !important; - padding: 0 !important; - border: none !important; - transition: color 0.2s ease; -} - -.card-link:hover .card-title { - color: #006399 !important; -} - -.dark .card-title { - color: #f9fafb !important; -} - -.dark .card-link:hover .card-title { - color: #f9fafb !important; -} - -.card-description { - font-size: 14px; - color: #6b7280; - line-height: 1.6; - margin: 0; -} - -.dark .card-description { - color: #9ca3af; -} - -/* Home (index) hero actions */ -.vp-doc .home-doc-actions { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 12px; - margin: 20px 0 36px; - padding: 0; -} - -.vp-doc .home-doc-actions__btn { - display: inline-flex; - align-items: center; - justify-content: center; - min-height: 40px; - padding: 9px 18px; - border-radius: 8px; - font-size: 15px; - font-weight: 500; - line-height: 1.25; - text-decoration: none !important; - transition: - background 0.2s ease, - color 0.2s ease, - border-color 0.2s ease, - box-shadow 0.2s ease; -} - -.vp-doc .home-doc-actions__btn--primary { - background: #006399; - color: #ffffff !important; - border: 1px solid #006399; -} - -.vp-doc .home-doc-actions__btn--primary:hover { - background: #0078b8; - border-color: #0078b8; - color: #ffffff !important; - box-shadow: 0 2px 8px rgba(0, 99, 153, 0.25); -} - -.vp-doc .home-doc-actions__btn--secondary { - background: transparent; - color: #0a0a0a !important; - border: 1px solid #e5e7eb; -} - -.vp-doc .home-doc-actions__btn--secondary:hover { - background: #f7f8f9; - border-color: #d1d5db; - color: #0a0a0a !important; -} - -.dark .vp-doc .home-doc-actions__btn--primary { - background: #2893cc; - border-color: #2893cc; - color: #ffffff !important; -} - -.dark .vp-doc .home-doc-actions__btn--primary:hover { - background: #3aa5d4; - border-color: #3aa5d4; -} - -.dark .vp-doc .home-doc-actions__btn--secondary { - background: #252829; - color: #ffffff !important; - border-color: #3f4244; -} - -.dark .vp-doc .home-doc-actions__btn--secondary:hover { - background: #2f3236; - border-color: #4a4e52; - color: #ffffff !important; -} - -/* Index page: hide prev/next doc footer and its top margin */ -.docs-layout .content-container:has(.home-feature-cards) .VPDocFooter { - display: none !important; - margin: 0 !important; -} - -/* Home (index) feature cards — light panels, CTA line, 3×2 grid (see design ref) */ -.vp-doc .home-feature-cards { - gap: 22px; - margin: 0 0 40px; -} - -.vp-doc .home-feature-cards .card-link { - display: flex; - flex-direction: column; - align-items: flex-start; - height: 100%; - padding: 28px; - border-radius: 14px; - border: none; - background: #f7f8f9; - box-shadow: none; - transition: background 0.2s ease; -} - -.vp-doc .home-feature-cards .card-link:hover { - border: none; - box-shadow: none; - background: #eef0f2; -} - -.vp-doc .home-feature-cards .card-link:hover .card-title { - color: #111827 !important; -} - -.vp-doc .home-feature-cards .card-icon { - display: flex; - align-items: center; - justify-content: center; - margin: 0 0 10px 0; - color: #0a0a0a; -} - -.vp-doc .home-feature-cards .card-icon svg { - width: 24px; - height: 24px; - flex-shrink: 0; -} - -.vp-doc .home-feature-cards .card-title { - font-size: 1.125rem !important; - line-height: 1.3 !important; - font-weight: 600 !important; - color: #0a0a0a !important; - margin: 0 0 8px 0 !important; -} - -.vp-doc .home-feature-cards .card-description { - font-size: 15px; - line-height: 1.6; - color: #4a5568; - margin: 0 0 0 0; - flex: 1 1 auto; -} - -/* Card CTA layout (colors live in Card.vue to beat voidzero .vp-doc a { color: inherit }) */ -.vp-doc .home-feature-cards .card-cta { - display: block; - margin-top: 20px; - padding-top: 0; - font-size: 15px; - line-height: 1.3; -} - -.vp-doc .home-feature-cards .card-link--with-cta .card-cta { - margin-top: auto; - padding-top: 16px; -} - -.dark .vp-doc .home-feature-cards .card-link { - background: #181a1b; -} - -.dark .vp-doc .home-feature-cards .card-link:hover { - background: #1d1f20; -} - -.dark .vp-doc .home-feature-cards .card-icon { - color: #f4f4f4; -} - -.dark .vp-doc .home-feature-cards .card-title { - color: #f4f4f4 !important; -} - -.dark .vp-doc .home-feature-cards .card-link:hover .card-title { - color: #f4f4f4 !important; -} - -.dark .vp-doc .home-feature-cards .card-description { - color: #a1a1aa; -} - -/* ================================================ - NAVBAR — PLANE (logo + title, search, sign-in, theme, socials) - Doc pages use OSSHeader (not VitePress default .VPNavBar), but reuse VPNav* pieces. -================================================ */ - -/* Remove any inherited top layout offset beneath fixed header */ -.docs-layout { - --vp-layout-top-height: 0px !important; - --docs-divider: #ececec; -} - -html.dark .docs-layout, -[data-theme="dark"] .docs-layout { - --docs-divider: #2a2a2a; -} - -.docs-layout header.wrapper { - border-bottom-color: var(--docs-divider) !important; -} - -/* Navbar utility dividers — fixed height, centered (border-left on wrappers varied with switch vs social icon size) */ -@media (min-width: 1280px) { - .docs-layout header div:has(> .VPNavBarAppearance):has(> .VPNavBarSocialLinks) { - gap: 8px !important; - align-items: center; - } - - .docs-layout header .VPNavBarAppearance, - .docs-layout header .VPNavBarSocialLinks { - display: flex; - align-items: center; - border-left: none !important; - padding-left: 0 !important; - margin-left: 0 !important; - } - - .docs-layout header .VPNavBarAppearance::before, - .docs-layout header .VPNavBarSocialLinks::before { - content: ""; - flex-shrink: 0; - width: 1px; - height: 20px; - margin-right: 8px; - background-color: var(--docs-divider); - } -} - -/* Navbar side borders + corner triangles (tick row is a sibling of header, not inside it) */ -@media (min-width: 768px) { - .docs-layout header.wrapper { - border-left-color: var(--docs-divider) !important; - border-right-color: var(--docs-divider) !important; - } - - .docs-layout header.wrapper + .wrapper { - border-left-color: var(--docs-divider) !important; - border-right-color: var(--docs-divider) !important; - } - - .docs-layout .tick-left::before { - border-left-color: var(--docs-divider) !important; - } - - .docs-layout .tick-right::after { - border-right-color: var(--docs-divider) !important; - } - - html.dark .docs-layout .tick-left::before, - [data-theme="dark"] .docs-layout .tick-left::before { - border-left-color: var(--docs-divider) !important; - } - - html.dark .docs-layout .tick-right::after, - [data-theme="dark"] .docs-layout .tick-right::after { - border-right-color: var(--docs-divider) !important; - } -} - -/* OSSHeader stacks the logo link as flex-col; we want mark + "Plane" in a row. */ -.docs-layout header a[href="/"].flex.flex-col { - flex-direction: row; - align-items: center; - gap: 0.5rem; -} - -.docs-layout header a[href="/"] img.h-4 { - height: 1.3rem; - width: auto; -} - -/* Search control sizing/shape */ -@media (min-width: 768px) { - .docs-layout header .VPNavBarSearchButton { - min-width: min(300px, 28vw); - height: 36px; - padding: 0 12px; - justify-content: flex-start; - gap: 8px; - border-radius: 12px !important; - border: 1px solid #e7e7e7; - background: #fff; - } - - .docs-layout header .VPNavBarSearchButton .text { - flex: 0 1 auto; - text-align: left; - } - - .docs-layout header .VPNavBarSearchButton .keys { - margin-left: auto; - } - - .dark .docs-layout header .VPNavBarSearchButton { - border-color: #323232; - background: #111; - } -} - -/* Sign in — primary CTA (works before/after relocation) */ -.docs-layout header a.VPLink[href*="sign-in"] { - background: #006399 !important; - color: #ffffff !important; - padding: 5px 14px !important; - border-radius: 6px !important; - font-weight: 500 !important; - font-size: 14px !important; - line-height: 1.25 !important; - text-decoration: none !important; - display: inline-flex !important; - align-items: center !important; - margin: 0 !important; - opacity: 1 !important; - transition: - background 0.2s ease, - box-shadow 0.2s ease, - transform 0.2s ease !important; -} - -.docs-layout header a.VPLink[href*="sign-in"].sign-in-relocating { - visibility: hidden !important; -} - -.docs-layout header a.VPLink[href*="sign-in"] .vp-external-link-icon { - display: none !important; -} - -.docs-layout header a.VPLink[href*="sign-in"]:hover { - background: #0078b8 !important; - box-shadow: 0 2px 8px rgba(0, 99, 153, 0.25) !important; - transform: translateY(-1px); - opacity: 1 !important; -} - -.dark .docs-layout header a.VPLink[href*="sign-in"] { - background: #2893cc !important; -} - -.dark .docs-layout header a.VPLink[href*="sign-in"]:hover { - background: #3aa5d4 !important; - box-shadow: 0 2px 8px rgba(40, 147, 204, 0.25) !important; -} - -/* Remove extra desktop strip between navbar and docs content/sidebar. */ -@media (min-width: 768px) { - .docs-layout .content-wrapper { - border-left-color: var(--docs-divider) !important; - border-right-color: var(--docs-divider) !important; - } - - .docs-layout .VPSidebar { - border-right-color: var(--docs-divider) !important; - } - - .docs-layout .VPDoc .aside-container { - border-left-color: var(--docs-divider) !important; - } -} - -@media (min-width: 1024px) { - /* Extend side borders upward so they visually touch the navbar edge. */ - .docs-layout .content-wrapper::before, - .docs-layout .content-wrapper::after { - content: ""; - position: absolute; - top: -5px; - width: 1px; - height: 5px; - background: var(--docs-divider); - pointer-events: none; - } - - .docs-layout .content-wrapper::before { - left: 0; - } - - .docs-layout .content-wrapper::after { - right: 0; - } - - .docs-layout .VPLocalNav.has-sidebar { - display: none !important; - } - - /* Do not zero VPContent padding-top: the OSS header is `lg:fixed`, and the theme - uses padding-top: var(--vp-nav-height) here so the main column starts below the bar. - Removing it makes the center doc sit under the navbar while sidebars are sticky. */ -} - -/* 3-column doc: left nav + right outline share one baseline; body copy sits slightly lower */ -@media (min-width: 1280px) { - .docs-layout .VPSidebar .nav { - /* .VPSidebar already has top padding; extra padding here was stacking—omit so nav links sit 20px higher. */ - padding-top: 0 !important; - } - - /* Theme VPDoc uses `padding: 0 32px` which adds 32px to the *right* of the whole - three-column row — empty gap past the "On this page" rail. */ - .docs-layout .VPDoc.has-aside { - padding-right: 0 !important; - padding-left: 0 !important; - } - - .docs-layout .VPDoc.has-aside .aside .aside-container { - padding-top: 20px !important; - } - - .docs-layout .VPDoc.has-aside .content { - /* Theme default adds large top padding here; use 32px so the body copy sits slightly - lower than both side columns. Kept on `.content` (not `.main`) so the `doc-before` - slot — which hosts the "Copy page" control — shares the H1's vertical origin. */ - padding: 32px 32px 128px !important; - } - - /* Right "On this page" rail: the theme’s `.aside-container` is 224px, so the flex - item `.aside` often shrink-wraps to ~224px. That makes inner `width: 100%` only - fill 224 of the 256px max. Lock the rail to the design width so outline uses it. */ - .docs-layout .VPDoc.has-aside .aside { - flex: 0 0 256px; - width: 256px; - max-width: 256px; - min-width: 0; - padding-left: 0; - box-sizing: border-box; - } - - .docs-layout .VPDoc.has-aside .aside .aside-container { - display: block; - width: 100% !important; - min-width: 0 !important; - max-width: none !important; - padding-left: 4px; - padding-right: 4px; - box-sizing: border-box; - } - - .docs-layout .VPDoc.has-aside .aside .aside-content { - width: 100%; - min-width: 0; - box-sizing: border-box; - } - - .docs-layout .VPDoc.has-aside .aside .VPDocAside { - width: 100%; - min-width: 0; - } - - .docs-layout .VPDoc.has-aside .VPDocAsideOutline, - .docs-layout .VPDoc.has-aside .VPDocAsideOutline .content { - width: 100%; - min-width: 0; - box-sizing: border-box; - } - - .docs-layout .VPDoc.has-aside .VPDocAsideOutline .outline-title { - width: 100%; - min-width: 0; - box-sizing: border-box; - } - - .docs-layout .VPDocAsideOutline .VPDocOutlineItem { - width: 100%; - list-style: none; - box-sizing: border-box; - } - - .docs-layout .VPDocAsideOutline .VPDocOutlineItem.root { - margin: 0; - padding: 0; - } - - .docs-layout .VPDocAsideOutline .VPDocOutlineItem li { - width: 100%; - min-width: 0; - box-sizing: border-box; - } - - /* VPDocOutlineItem uses nowrap + ellipsis; allow long headings to wrap. */ - .docs-layout .VPDocAsideOutline a.outline-link { - white-space: normal; - overflow: visible; - text-overflow: unset; - line-height: 1.45; - padding: 2px 0 4px; - width: 100%; - box-sizing: border-box; - } - - /* Air between sibling items in the right-rail outline. */ - .docs-layout .VPDocAsideOutline .VPDocOutlineItem li + li { - margin-top: 6px; - } - - .docs-layout .VPDocAsideOutline .VPDocOutlineItem li > .VPDocOutlineItem.nested { - margin-top: 4px; - } - - .docs-layout .VPDocOutlineItem.nested { - padding-left: 4px; - padding-right: 4px; - } -} - -/* No right rail (outline empty): collapse the aside, let the main column use that - width, and keep the same 32px inset on both sides as the default left padding. */ -@media (min-width: 1280px) { - .docs-layout - .VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds)) - .aside { - display: none !important; - flex: 0 0 0 !important; - width: 0 !important; - min-width: 0 !important; - max-width: 0 !important; - margin: 0 !important; - padding: 0 !important; - overflow: hidden !important; - } - - .docs-layout - .VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds)) - .content { - flex: 1 1 auto; - max-width: none !important; - padding-right: 104px !important; - padding-left: 110px !important; - } - - .docs-layout - .VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds)) - .content-container { - max-width: none !important; - width: 100%; - margin: 0; - box-sizing: border-box; - } -} - -/* ================================================ - COPY PAGE MENU — placement of components/CopyPageMenu.vue - The control is server-rendered in the `doc-before` slot (first child of - `.VPDoc .content-container`, above
) and, once mounted, teleported into a - `.copy-page-slot` host inserted right after the page H1 inside `.vp-doc`. Both - positions are styled here; visual styles are scoped in the component. - ================================================ */ -.docs-layout .VPDoc .content-container { - position: relative; /* anchor for the pre-teleport position */ -} - -/* < 768px: below the title, left-aligned, in flow */ -@media (max-width: 767px) { - /* Pre-teleport position would sit above the title — keep it hidden to avoid a jump. */ - .docs-layout .VPDoc .content-container > .copy-page { - display: none; - } - - .docs-layout .vp-doc > div > .copy-page-slot { - margin: 14px 0 16px; - } -} - -/* ≥ 768px: on the title row, flush with the right edge of the text column */ -@media (min-width: 768px) { - .docs-layout .VPDoc .content-container > .copy-page, - .docs-layout .vp-doc > div > .copy-page-slot > .copy-page { - position: absolute; /* relative to .content-container / .vp-doc — same top edge */ - top: 2px; /* (h1 line box ~38px − 34px button) / 2 */ - right: 0; - margin: 0; - z-index: 10; /* below --vp-z-index-local-nav (20) and the image lightbox */ - } - - /* Reserve the control's footprint on the H1 only when it actually rendered - (so `copyPage: false` pages keep the full width). */ - .docs-layout .vp-doc > div > h1:has(+ .copy-page-slot > .copy-page), - .docs-layout .VPDoc .content-container > .copy-page ~ .main .vp-doc > div > h1 { - padding-right: 156px; /* ≈ 132px control + 24px gap — re-measure if the label changes */ - } -} - -/* ================================================ - HERO IMAGE STYLES - ================================================ */ - -.vp-doc p:has(> img[src$="#hero"]), -article p:has(> img[src$="#hero"]) { - max-width: 841px; - padding: 36px 56px 0px 56px; - background: #f1f3f3; - border-radius: 12px; - box-sizing: border-box; - overflow: hidden; -} - -.vp-doc img[src$="#hero"], -article img[src$="#hero"] { - width: 100%; - height: auto; - display: block; - margin: 0; - border-radius: 8px 8px 0px 0px; - border-width: 1px 1px 0px 1px; - border-style: solid; - border-color: #eaebeb; - object-fit: cover; - box-shadow: - 0px 2px 4px -1px rgba(41, 47, 61, 0.04), - 0px 4px 6px -1px rgba(41, 47, 61, 0.05); -} - -.vp-doc p:has(> img[src$="#hero-tl"]), -article p:has(> img[src$="#hero-tl"]) { - max-width: 841px; - padding: 36px 0px 0px 56px; - background: #f1f3f3; - border-radius: 12px; - box-sizing: border-box; - overflow: hidden; -} - -.vp-doc img[src$="#hero-tl"], -article img[src$="#hero-tl"] { - width: 100%; - height: auto; - display: block; - margin: 0; - border-radius: 8px 0px 0px 0px; - border-width: 1px 0px 0px 1px; - border-style: solid; - border-color: #eaebeb; - object-fit: cover; - box-shadow: - 0px 2px 4px -1px rgba(41, 47, 61, 0.04), - 0px 4px 6px -1px rgba(41, 47, 61, 0.05); -} - -.vp-doc p:has(> img[src$="#hero-tr"]), -article p:has(> img[src$="#hero-tr"]) { - max-width: 841px; - padding: 36px 56px 0px 0px; - background: #f1f3f3; - border-radius: 12px; - box-sizing: border-box; - overflow: hidden; -} - -.vp-doc img[src$="#hero-tr"], -article img[src$="#hero-tr"] { - width: 100%; - height: auto; - display: block; - margin: 0; - border-radius: 0px 8px 0px 0px; - border-width: 1px 1px 0px 0px; - border-style: solid; - border-color: #eaebeb; - object-fit: cover; - box-shadow: - 0px 2px 4px -1px rgba(41, 47, 61, 0.04), - 0px 4px 6px -1px rgba(41, 47, 61, 0.05); -} - -.vp-doc p:has(> img[src$="#hero-bl"]), -article p:has(> img[src$="#hero-bl"]) { - max-width: 841px; - padding: 0px 0px 36px 56px; - background: #f1f3f3; - border-radius: 12px; - box-sizing: border-box; - overflow: hidden; -} - -.vp-doc img[src$="#hero-bl"], -article img[src$="#hero-bl"] { - width: 100%; - height: auto; - display: block; - margin: 0; - border-radius: 0px 0px 0px 8px; - border-width: 0px 0px 1px 1px; - border-style: solid; - border-color: #eaebeb; - object-fit: cover; - box-shadow: - 0px 2px 4px -1px rgba(41, 47, 61, 0.04), - 0px 4px 6px -1px rgba(41, 47, 61, 0.05); -} - -.vp-doc p:has(> img[src$="#hero-br"]), -article p:has(> img[src$="#hero-br"]) { - max-width: 841px; - padding: 0px 56px 36px 0px; - background: #f1f3f3; - border-radius: 12px; - box-sizing: border-box; - overflow: hidden; -} - -.vp-doc img[src$="#hero-br"], -article img[src$="#hero-br"] { - width: 100%; - height: auto; - display: block; - margin: 0; - border-radius: 0px 0px 8px 0px; - border-width: 0px 1px 1px 0px; - border-style: solid; - border-color: #eaebeb; - object-fit: cover; - box-shadow: - 0px 2px 4px -1px rgba(41, 47, 61, 0.04), - 0px 4px 6px -1px rgba(41, 47, 61, 0.05); -} - -/* ================================================ - MOBILE IMAGE LAYOUT - ================================================ */ - -.mobile-img-container { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - flex-wrap: wrap; -} - -.mobile-img-box { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - max-width: 25rem; - min-width: 15rem; - text-align: center; - margin: 10px; -} - -/* ================================================ - HOMEPAGE FEATURE ICONS - DARK MODE - ================================================ */ - -/* Feature card icons use currentColor in SVG, but since they're - loaded as tags, currentColor defaults to black. This filter - inverts them for visibility in dark mode. */ -.dark .VPFeature .VPImage { - filter: invert(1) hue-rotate(180deg); -} - -.dark .VPFeatures .VPFeature .icon img { - filter: invert(1) hue-rotate(180deg); -} - -/* ================================================ - MEDIUM ZOOM - IMAGE LIGHTBOX - ================================================ */ - -.vp-doc img { - cursor: zoom-in; -} - -.medium-zoom-overlay { - z-index: 999; -} - -.medium-zoom-image--opened { - z-index: 1000; -} diff --git a/docs/.vitepress/types/modules.d.ts b/docs/.vitepress/types/modules.d.ts deleted file mode 100644 index 32843240..00000000 --- a/docs/.vitepress/types/modules.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -declare module "*.vue" { - import type { DefineComponent } from "vue"; - - const component: DefineComponent, Record, unknown>; - export default component; -} - -declare module "vitepress-plugin-tabs/client" { - export function enhanceAppWithTabs(app: unknown): void; -} diff --git a/package.json b/package.json index 209c71f5..64a95c9b 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,8 @@ { - "name": "docs", - "version": "3.0.0", + "name": "plane-docs", "private": true, + "description": "Plane documentation monorepo: docs.plane.so (apps/docs), developers.plane.so (apps/developer-docs) and the shared VitePress theme (packages/theme).", "homepage": "https://plane.so", - "license": "MIT", "author": "Plane", "repository": { "type": "git", @@ -11,29 +10,23 @@ }, "type": "module", "scripts": { - "dev": "vitepress dev docs", - "build": "vitepress build docs", - "preview": "vitepress preview docs", - "fix:format": "oxfmt --write .", - "check:format": "oxfmt --check ." - }, - "dependencies": { - "@tailwindcss/vite": "^4.2.1", - "@voidzero-dev/vitepress-theme": "^4.8.4", - "lucide-vue-next": "^0.577.0", - "medium-zoom": "^1.1.0", - "tailwindcss": "^4.2.1", - "vitepress": "^1.6.3", - "vitepress-plugin-tabs": "^0.8.0", - "vue": "^3.5.13" + "dev": "turbo run dev", + "dev:docs": "turbo run dev --filter=docs", + "dev:developer-docs": "turbo run dev --filter=developer-docs", + "build": "turbo run build", + "preview": "turbo run preview", + "check": "pnpm check:format && pnpm check:types", + "check:types": "turbo run check:types", + "check:format": "oxfmt --check .", + "fix:format": "oxfmt --write ." }, "devDependencies": { - "@types/node": "^25.6.0", "oxfmt": "^0.36.0", - "vitepress-plugin-llms": "^1.13.1" + "turbo": "^2.10.10", + "typescript": "catalog:" }, "engines": { "node": ">=24.0.0" }, - "packageManager": "pnpm@11.8.0+sha512.c1f5e7c4cb241c8f174b743851d82f42b802324afc8b0f116b96adb15aa06664948dde36960a3ba1079ba5b4b29dd0140135b94b5b5f5263592249d68e555f26" + "packageManager": "pnpm@11.22.0+sha512.1ff870c4c6133dfd88fb2afc46dd13d47f09c9794b438c6fdb47ca98caf3bc16381ee0be93a091b8e3824cf01f889f46d7d9e20910fb0be1ab0fb5baa80dd621" } diff --git a/packages/theme/README.md b/packages/theme/README.md new file mode 100644 index 00000000..c077c3f6 --- /dev/null +++ b/packages/theme/README.md @@ -0,0 +1,51 @@ +# `@plane/docs-theme` — shared Plane docs theme + +The visual identity of [docs.plane.so](https://docs.plane.so) (`apps/docs`) and +[developers.plane.so](https://developers.plane.so) (`apps/developer-docs`): design tokens, fonts, +`PlaneHeader`, the doc layout, `Card` / `CardGroup` / `Tags`, the Copy page menu and the cookie-consent +banner — all built on top of `@voidzero-dev/vitepress-theme`. + +It is a workspace package consumed **as source** (`exports` point at `src/index.ts`; VitePress/Vite compile +the `.ts` / `.vue` / `.css` directly, so there is no build step and no publish). + +## Using it in an app + +```ts +// apps//docs/.vitepress/theme/index.ts +import { createPlaneTheme } from "@plane/docs-theme"; +import "./site.css"; // site-specific rules only + +export default createPlaneTheme({ + brand: { logoOnLight, logoOnDark, logoAlt, menuTitle, footerBg, monoIcon }, + components: { + /* extra globally-registered components */ + }, + setup() { + /* extra client setup */ + }, +}); +``` + +- Header buttons come from `themeConfig.nav` items flagged `planeButton: "primary" | "secondary"`. +- `src/index.ts` is the only place that imports `src/css/index.css` (the Tailwind root). Never import the + theme CSS from an app. +- Anything site-specific — logos, analytics, API-reference components, `site.css` — belongs in the app, not + here. + +## Developing + +Edit files under `src/` and run either app's dev server (`pnpm dev:docs` / `pnpm dev:developer-docs` from +the repo root); changes hot-reload through the workspace link. `pnpm check:types` type-checks the package +(and each app re-checks it as part of its own program). Formatting is the repo-wide `oxfmt` (`pnpm fix:format`). + +## Layout + +``` +src/ + index.ts createPlaneTheme(options) + client setup (appearance sync, medium-zoom, tab hashes) + options.ts PlaneThemeOptions / planeOptionsKey + layout/ Layout.vue, doc-layout.vue (PlaneHeader + bordered content wrapper), slots/header helpers + components/ PlaneHeader, CopyPageMenu, CookieConsent, Card, CardGroup, Tags, brand icons + css/ index.css → fonts, tokens, base, layout, components, api + types/ ambient shims (*.vue, @vp-* aliases) and the VitePress config augmentation +``` diff --git a/packages/theme/package.json b/packages/theme/package.json new file mode 100644 index 00000000..583a9731 --- /dev/null +++ b/packages/theme/package.json @@ -0,0 +1,26 @@ +{ + "name": "@plane/docs-theme", + "version": "0.0.0", + "private": true, + "description": "Shared VitePress theme for docs.plane.so and developers.plane.so (consumed as source — no build step).", + "type": "module", + "exports": { + ".": "./src/index.ts", + "./package.json": "./package.json" + }, + "scripts": { + "check:types": "tsc --noEmit -p tsconfig.json" + }, + "dependencies": { + "@voidzero-dev/vitepress-theme": "catalog:", + "lucide-vue-next": "catalog:", + "medium-zoom": "catalog:", + "vitepress-plugin-tabs": "catalog:" + }, + "devDependencies": { + "@types/node": "catalog:", + "typescript": "catalog:", + "vitepress": "catalog:", + "vue": "catalog:" + } +} diff --git a/packages/theme/src/components/Card.vue b/packages/theme/src/components/Card.vue new file mode 100644 index 00000000..64b78b8f --- /dev/null +++ b/packages/theme/src/components/Card.vue @@ -0,0 +1,81 @@ + + + diff --git a/packages/theme/src/components/CardGroup.vue b/packages/theme/src/components/CardGroup.vue new file mode 100644 index 00000000..16256d57 --- /dev/null +++ b/packages/theme/src/components/CardGroup.vue @@ -0,0 +1,20 @@ + + + diff --git a/packages/theme/src/components/CookieConsent.vue b/packages/theme/src/components/CookieConsent.vue new file mode 100644 index 00000000..99f20fc2 --- /dev/null +++ b/packages/theme/src/components/CookieConsent.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/docs/.vitepress/theme/components/CopyPageMenu.vue b/packages/theme/src/components/CopyPageMenu.vue similarity index 98% rename from docs/.vitepress/theme/components/CopyPageMenu.vue rename to packages/theme/src/components/CopyPageMenu.vue index 75b5f168..762a91ec 100644 --- a/docs/.vitepress/theme/components/CopyPageMenu.vue +++ b/packages/theme/src/components/CopyPageMenu.vue @@ -1,5 +1,3 @@ - - + + + + diff --git a/packages/theme/src/components/Tags.vue b/packages/theme/src/components/Tags.vue new file mode 100644 index 00000000..ddcdfb72 --- /dev/null +++ b/packages/theme/src/components/Tags.vue @@ -0,0 +1,11 @@ + + + diff --git a/packages/theme/src/components/card-brand-icons.ts b/packages/theme/src/components/card-brand-icons.ts new file mode 100644 index 00000000..55d323a7 --- /dev/null +++ b/packages/theme/src/components/card-brand-icons.ts @@ -0,0 +1,25 @@ +/** + * Brand SVG icons for (checked before Lucide). + * Brand marks keep their own colors; single-color glyphs use currentColor so they + * follow the card's icon surface (brand blue in light/dark). + */ +export const cardBrandIcons: Record = { + asana: ``, + clickup: ``, + confluence: ``, + csv: ``, + drawio: ``, + flatfile: ``, + github: ``, + gitlab: ``, + jira: ``, + linear: ``, + notion: ``, + sentry: ``, + slack: ``, + coolify: ``, + docker: ``, + kubernetes: ``, + podman: ``, + portainer: ``, +}; diff --git a/docs/.vitepress/theme/components/copy-page-icons.ts b/packages/theme/src/components/copy-page-icons.ts similarity index 99% rename from docs/.vitepress/theme/components/copy-page-icons.ts rename to packages/theme/src/components/copy-page-icons.ts index 9272c6f8..862db0d9 100644 --- a/docs/.vitepress/theme/components/copy-page-icons.ts +++ b/packages/theme/src/components/copy-page-icons.ts @@ -1,5 +1,3 @@ -/** @format */ - /** * Inline SVG glyphs for the "Copy page" menu (rendered via v-html, sized by CSS). * All paths use fill="currentColor" so they follow the surrounding text color. diff --git a/packages/theme/src/css/api.css b/packages/theme/src/css/api.css new file mode 100644 index 00000000..ae49c709 --- /dev/null +++ b/packages/theme/src/css/api.css @@ -0,0 +1,124 @@ +/* ================================================ + PLANE DOCS THEME — API reference layout + `.api-page` is toggled on `.VPDoc` by the site (developer docs) for + /api-reference/* pages; the two-column markup lives in the .md files. + ================================================ */ + +/* Hide the aside for API reference pages */ +.api-page .aside, +.api-page .aside-container, +.api-page .VPDocAside, +.api-page aside, +.api-page .VPDocAsideOutline { + display: none !important; + width: 0 !important; + min-width: 0 !important; + opacity: 0 !important; + visibility: hidden !important; +} + +.api-page.VPDoc > .container { + display: block !important; + max-width: 100% !important; +} + +.api-page .VPDoc > .container > .content { + max-width: 100% !important; + padding-right: 24px !important; +} + +.api-page .content-container, +.api-page .main, +.api-page .vp-doc { + max-width: 100% !important; +} + +/* Two-column API layout */ +.api-two-column { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 40px; + align-items: start; +} + +.api-left { + min-width: 0; +} + +.api-right { + position: sticky; + top: 100px; + min-width: 0; +} + +@media (max-width: 1100px) { + .api-two-column { + grid-template-columns: 1fr; + gap: 24px; + } + + .api-right { + position: static; + } +} + +/* API endpoint badge */ +.api-endpoint-badge { + display: inline-flex; + align-items: center; + gap: 12px; + margin-bottom: 20px; +} + +.api-endpoint-badge .method { + font-size: 12px; + font-weight: 700; + padding: 5px 12px; + border-radius: 6px; + font-family: var(--vp-font-family-mono); + text-transform: uppercase; +} + +.api-endpoint-badge .method.get { + background: var(--vp-custom-block-tip-bg); + color: var(--vp-custom-block-tip-text); +} + +.api-endpoint-badge .method.post { + background: var(--vp-custom-block-info-bg); + color: var(--vp-custom-block-info-text); +} + +.api-endpoint-badge .method.patch, +.api-endpoint-badge .method.put { + background: var(--vp-custom-block-warning-bg); + color: var(--vp-custom-block-warning-text); +} + +.api-endpoint-badge .method.delete { + background: var(--vp-custom-block-danger-bg); + color: var(--vp-custom-block-danger-text); +} + +.api-endpoint-badge .path { + font-family: var(--vp-font-family-mono); + font-size: 14px; + color: var(--vp-c-text-2); +} + +/* Section headers */ +.params-section h3, +.returns-section h3 { + font-size: 13px !important; + font-weight: 600 !important; + color: var(--vp-c-text-2) !important; + text-transform: uppercase; + letter-spacing: 0.05em; + margin: 0 0 12px 0 !important; + padding: 0 !important; + border: none !important; +} + +.params-list { + border-top: 1px solid var(--vp-c-divider); +} diff --git a/packages/theme/src/css/base.css b/packages/theme/src/css/base.css new file mode 100644 index 00000000..4a2cbf7e --- /dev/null +++ b/packages/theme/src/css/base.css @@ -0,0 +1,123 @@ +/* ================================================ + PLANE DOCS THEME — base typography, code, misc doc content + ================================================ */ + +/* --- Font rendering --- */ +body, +.docs-layout { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + font-feature-settings: + "cv01" on, + "cv02" on, + "zero" on; +} + +/* --- Doc prose --- */ +.vp-doc { + line-height: 1.7; + letter-spacing: -0.011em; +} + +.vp-doc h1 { + letter-spacing: -0.022em; + line-height: 1.2; +} + +.vp-doc h2 { + border-top: none !important; + margin-top: 32px; + letter-spacing: -0.017em; + line-height: 1.3; +} + +.vp-doc h3 { + border-top: none !important; + letter-spacing: -0.014em; + line-height: 1.4; +} + +/* --- Code blocks --- */ +div[class*="language-"] { + border-radius: 8px !important; + overflow: hidden; + border: 1px solid var(--vp-c-divider); + background: var(--vp-code-block-bg) !important; +} + +div[class*="language-"] pre { + background: transparent !important; +} + +:is(.dark:not([data-theme]), [data-theme="dark"]) .shiki, +:is(.dark:not([data-theme]), [data-theme="dark"]) .shiki span { + background: transparent !important; +} + +div[class*="language-"] > span.lang { + color: #9ca3af; + font-size: 12px; +} + +/* `[!code word]` highlight (VoidZero fork lacks it) */ +.highlighted-word { + background-color: var(--vp-code-line-highlight-color); + transition: background-color 0.5s; + display: inline-block; +} + +/* --- kbd --- */ +html:not(.dark) .VPContent kbd { + --kbd-color-background: #f7f7f7; + --kbd-color-border: #cbcccd; + --kbd-color-text: #222325; +} + +.VPContent kbd { + --kbd-color-background: #898b90; + --kbd-color-border: #3d3e42; + --kbd-color-text: #222325; + + background-color: var(--kbd-color-background); + color: var(--kbd-color-text); + border-radius: 0.25rem; + border: 1px solid var(--kbd-color-border); + box-shadow: 0 2px 0 1px var(--kbd-color-border); + font-family: var(--vp-font-family-base); + font-size: 0.75em; + line-height: 1; + min-width: 0.75rem; + text-align: center; + padding: 2px 5px; + position: relative; + top: -1px; +} + +/* --- Images: medium-zoom lightbox --- */ +.vp-doc img { + cursor: zoom-in; +} + +.medium-zoom-overlay { + z-index: 9999 !important; + background: rgba(0, 0, 0, 0.8) !important; +} + +.medium-zoom-image, +.medium-zoom-image--opened { + z-index: 10000 !important; +} + +.medium-zoom-image--opened { + cursor: zoom-out; +} + +/* --- Mobile screenshots side-by-side --- */ +.mobile-img-container { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + flex-wrap: wrap; +} diff --git a/packages/theme/src/css/components.css b/packages/theme/src/css/components.css new file mode 100644 index 00000000..54128040 --- /dev/null +++ b/packages/theme/src/css/components.css @@ -0,0 +1,596 @@ +/* ================================================ + PLANE DOCS THEME — markdown components + Card / CardGroup / Tags, home-page pieces, hero-image frames + ================================================ */ + +/* ------------------------------------------------ + CardGroup grid + ------------------------------------------------ */ +.card-group { + display: grid; + gap: 16px; + margin: 24px 0; +} + +.card-group-2, +.card-group-3, +.card-group-4 { + grid-template-columns: 1fr; +} + +@media (min-width: 768px) { + .card-group-2, + .card-group-3, + .card-group-4 { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (min-width: 1024px) { + .card-group-3 { + grid-template-columns: repeat(3, 1fr); + } + + .card-group-4 { + grid-template-columns: repeat(4, 1fr); + } +} + +/* ------------------------------------------------ + Card + ------------------------------------------------ */ +.card-link, +.vp-doc a.card-link, +.vp-doc a.card-link:hover { + color: inherit; + text-decoration: none !important; +} + +.card-link { + display: block; + padding: 24px; + border-radius: 12px; + border: 1px solid var(--vp-c-divider); + background: #ffffff; + transition: all 0.2s ease; +} + +.card-link:hover { + border-color: var(--vp-c-brand-1); + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); +} + +:is(.dark:not([data-theme]), [data-theme="dark"]) .card-link { + background: rgba(20, 20, 20, 0.5); +} + +:is(.dark:not([data-theme]), [data-theme="dark"]) .card-link:hover { + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5); +} + +.card-link--static { + cursor: default; +} + +.card-head { + display: flex; + align-items: flex-start; + gap: 0.875rem; + margin-bottom: 0.75rem; +} + +.card-icon { + flex-shrink: 0; + margin: 0; + line-height: 0; +} + +.card-icon__surface { + display: flex; + align-items: center; + justify-content: center; + width: 2.5rem; + height: 2.5rem; + border-radius: 0.625rem; + background: color-mix(in srgb, var(--vp-c-brand-1) 10%, transparent); + color: var(--vp-c-brand-1); + transition: + background 0.2s ease, + color 0.2s ease; +} + +:is(.dark:not([data-theme]), [data-theme="dark"]) .card-icon__surface { + background: color-mix(in srgb, var(--vp-c-brand-1) 18%, transparent); +} + +.card-icon__lucide { + display: block; +} + +.card-icon__custom svg { + display: block; + width: 1.25rem; + height: 1.25rem; +} + +.card-link:hover .card-icon__surface { + background: color-mix(in srgb, var(--vp-c-brand-1) 16%, transparent); +} + +.card-link--static:hover .card-icon__surface { + background: color-mix(in srgb, var(--vp-c-brand-1) 10%, transparent); +} + +.card-link .card-title { + flex: 1; + min-width: 0; + align-self: center; + margin: 0 !important; + padding: 0.125rem 0 0 !important; + border: none !important; + font-size: 1.0625rem !important; + font-weight: 600 !important; + line-height: 1.35 !important; + letter-spacing: -0.01em; + color: #111827 !important; + transition: color 0.2s ease; +} + +.card-link:hover .card-title { + color: var(--vp-c-brand-1) !important; +} + +.card-link--static:hover .card-title { + color: #111827 !important; +} + +:is(.dark:not([data-theme]), [data-theme="dark"]) .card-link .card-title, +:is(.dark:not([data-theme]), [data-theme="dark"]) .card-link:hover .card-title { + color: #f9fafb !important; +} + +.card-description { + flex: 1; + margin: 0; + font-size: 14px; + line-height: 1.6; + color: var(--vp-c-text-2); +} + +/* Markdown slot content: paragraphs inherit the card's type scale */ +.card-description > p { + margin: 0; + font-size: inherit; + line-height: inherit; +} + +.card-description > p + p { + margin-top: 0.5em; +} + +.card-cta { + display: inline-flex; + align-items: center; + gap: 0.375rem; + margin-top: 1rem; + font-size: 0.875rem; + font-weight: 500; + color: var(--vp-c-brand-1); + transition: + color 0.2s ease, + gap 0.2s ease; +} + +.card-cta__arrow { + width: 1rem; + height: 1rem; + flex-shrink: 0; + transition: transform 0.2s ease; +} + +.card-link:hover .card-cta { + color: var(--plane-brand-hover); +} + +.card-link:hover .card-cta__arrow { + transform: translateX(2px); +} + +.card-link--static:hover .card-cta, +.card-link--static:hover .card-cta__arrow { + color: var(--vp-c-brand-1); + transform: none; +} + +/* Inline link list inside static cards (e.g. quick-start cards) */ +.card-links { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.375rem 0.625rem; + margin: 0; + padding-top: 1rem; +} + +.card-links a, +.vp-doc .card-links a { + display: inline-flex; + align-items: center; + gap: 0.25rem; + font-size: 0.875rem; + font-weight: 500; + color: var(--vp-c-brand-1); + cursor: pointer; + text-decoration: none; + transition: color 0.2s ease; +} + +.card-links a:hover, +.vp-doc .card-links a:hover { + color: var(--plane-brand-hover); + text-decoration: none; +} + +.card-links__sep { + color: var(--vp-c-text-3); + user-select: none; +} + +/* ------------------------------------------------ + Tags + ------------------------------------------------ */ +.plantag, +a.plantag { + display: inline-flex; + align-items: center; + padding: 2px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: 500; + background: var(--vp-c-brand-soft); + color: var(--vp-c-brand-1); + text-decoration: none; + line-height: 1.4; +} + +a.plantag:hover { + color: var(--plane-brand-hover); +} + +/* ------------------------------------------------ + Home page (layout: doc) — action buttons + ------------------------------------------------ */ +.home-doc-actions { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin: 0 0 3rem; +} + +a.home-doc-actions__btn, +.home-doc-actions__btn { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 40px; + padding: 9px 18px; + border-radius: 8px; + border: 1px solid transparent; + font-size: 15px; + font-weight: 500; + line-height: 1.25; + text-decoration: none !important; + cursor: pointer; + transition: + background 0.2s ease, + color 0.2s ease, + border-color 0.2s ease, + box-shadow 0.2s ease, + transform 0.2s ease; +} + +/* Navbar — smaller variant */ +a.home-doc-actions__btn--nav, +.home-doc-actions__btn--nav { + min-height: auto; + padding: 4px 15px; + border-radius: 6px; + font-size: 14px; + margin: 0 !important; +} + +/* Primary */ +a.home-doc-actions__btn--primary, +a.home-doc-actions__btn--primary:is(:link, :visited, :focus, :active) { + background: var(--vp-button-brand-bg) !important; + border-color: var(--vp-button-brand-border) !important; + color: var(--vp-button-brand-text) !important; +} + +a.home-doc-actions__btn--primary:hover { + background: var(--vp-button-brand-hover-bg) !important; + border-color: var(--vp-button-brand-hover-border) !important; + color: var(--vp-button-brand-hover-text) !important; + box-shadow: 0 2px 8px color-mix(in srgb, var(--vp-c-brand-1) 25%, transparent); +} + +a.home-doc-actions__btn--primary.home-doc-actions__btn--nav:hover { + transform: translateY(-1px); +} + +/* Secondary */ +a.home-doc-actions__btn--secondary, +a.home-doc-actions__btn--secondary:is(:link, :visited, :focus, :active) { + background: var(--vp-button-alt-bg) !important; + border-color: var(--vp-button-alt-border) !important; + color: var(--vp-button-alt-text) !important; +} + +a.home-doc-actions__btn--secondary:hover { + background: var(--vp-button-alt-hover-bg) !important; + border-color: var(--vp-button-alt-hover-border) !important; + color: var(--vp-button-alt-hover-text) !important; +} + +:is(.dark:not([data-theme]), [data-theme="dark"]) a.home-doc-actions__btn--secondary, +:is(.dark:not([data-theme]), [data-theme="dark"]) + a.home-doc-actions__btn--secondary:is(:link, :visited, :focus, :active) { + background: #252829 !important; + border-color: #3f4244 !important; + color: #ffffff !important; +} + +:is(.dark:not([data-theme]), [data-theme="dark"]) a.home-doc-actions__btn--secondary:hover { + background: #2f3236 !important; + border-color: #4a4e52 !important; + color: #ffffff !important; +} + +/* ------------------------------------------------ + Home page — feature / quick-start card grids + Works for both markups: + (class on the grid) +
(wrapper around the grid) + ------------------------------------------------ */ +.home-feature-cards { + margin: 2rem 0 3rem; +} + +.home-quick-start { + margin: 1.5rem 0 3rem; +} + +.home-feature-cards.card-group, +.home-feature-cards .card-group, +.home-quick-start.card-group, +.home-quick-start .card-group { + display: grid; + gap: 1rem; + grid-template-columns: 1fr; +} + +@media (min-width: 640px) { + .home-feature-cards.card-group, + .home-feature-cards .card-group, + .home-quick-start.card-group, + .home-quick-start .card-group { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (min-width: 960px) { + .home-feature-cards.card-group, + .home-feature-cards .card-group, + .home-quick-start.card-group, + .home-quick-start .card-group { + grid-template-columns: repeat(3, 1fr); + } +} + +.home-feature-cards .card-link, +.home-quick-start .card-link { + display: flex; + flex-direction: column; + height: 100%; + min-height: 10rem; + border: 1px solid transparent; + background: #f7f8f9; + padding: 1.5rem; + border-radius: 0.875rem; + box-shadow: none; + transition: + background 0.2s ease, + border-color 0.2s ease, + transform 0.2s ease; +} + +.home-feature-cards .card-link:hover, +.home-quick-start .card-link:hover { + background: #eef0f2; + border-color: color-mix(in srgb, var(--vp-c-brand-1) 12%, transparent); + box-shadow: none; + transform: translateY(-1px); +} + +.home-quick-start .card-link--static:hover { + background: #f7f8f9; + border-color: transparent; + transform: none; +} + +:is(.dark:not([data-theme]), [data-theme="dark"]) .home-feature-cards .card-link, +:is(.dark:not([data-theme]), [data-theme="dark"]) .home-quick-start .card-link { + background: #181a1b; + border-color: transparent; +} + +:is(.dark:not([data-theme]), [data-theme="dark"]) .home-feature-cards .card-link:hover, +:is(.dark:not([data-theme]), [data-theme="dark"]) .home-quick-start .card-link:hover { + background: #1f2224; + border-color: color-mix(in srgb, var(--vp-c-brand-1) 22%, transparent); + box-shadow: none; +} + +:is(.dark:not([data-theme]), [data-theme="dark"]) .home-quick-start .card-link--static:hover { + background: #181a1b; + border-color: transparent; +} + +.home-feature-cards .card-head, +.home-quick-start .card-head { + margin-bottom: 0.625rem; +} + +.home-feature-cards .card-link .card-title, +.home-quick-start .card-link .card-title, +.home-feature-cards .card-link:hover .card-title, +.home-quick-start .card-link:hover .card-title { + color: #0a0a0a !important; + font-size: 1rem !important; +} + +:is(.dark:not([data-theme]), [data-theme="dark"]) .home-feature-cards .card-link .card-title, +:is(.dark:not([data-theme]), [data-theme="dark"]) .home-quick-start .card-link .card-title, +:is(.dark:not([data-theme]), [data-theme="dark"]) .home-feature-cards .card-link:hover .card-title, +:is(.dark:not([data-theme]), [data-theme="dark"]) .home-quick-start .card-link:hover .card-title { + color: #f4f4f4 !important; +} + +.home-feature-cards .card-description, +.home-quick-start .card-description { + font-size: 0.875rem; + line-height: 1.55; + color: #5c6570; +} + +:is(.dark:not([data-theme]), [data-theme="dark"]) .home-feature-cards .card-description, +:is(.dark:not([data-theme]), [data-theme="dark"]) .home-quick-start .card-description { + color: #9ca3af; +} + +.home-feature-cards .card-cta, +.home-quick-start .card-links { + margin-top: auto; + padding-top: 1rem; +} + +.home-feature-cards .card-icon__surface, +.home-quick-start .card-icon__surface { + width: 2.75rem; + height: 2.75rem; + border-radius: 0.6875rem; +} + +.home-feature-cards .card-icon__custom svg, +.home-quick-start .card-icon__custom svg { + width: 1.375rem; + height: 1.375rem; +} + +/* Hide footer prev/next on home when card grids are present */ +.Layout:has(.home-feature-cards) .VPDocFooter .prev, +.Layout:has(.home-feature-cards) .VPDocFooter .next, +.Layout:has(.home-feature-cards) .VPDocFooter .pager-link, +.Layout:has(.home-quick-start) .VPDocFooter .prev, +.Layout:has(.home-quick-start) .VPDocFooter .next, +.Layout:has(.home-quick-start) .VPDocFooter .pager-link { + display: none; +} + +/* ------------------------------------------------ + Hero image frames in markdown: ![](url#hero | #hero-tl | #hero-tr | #hero-bl | #hero-br) + The suffix picks which corner of the screenshot is anchored to the frame. + ------------------------------------------------ */ +.vp-doc p:has(> img[src$="#hero"]), +.vp-doc p:has(> img[src$="#hero-tl"]), +.vp-doc p:has(> img[src$="#hero-tr"]), +.vp-doc p:has(> img[src$="#hero-bl"]), +.vp-doc p:has(> img[src$="#hero-br"]), +article p:has(> img[src$="#hero"]), +article p:has(> img[src$="#hero-tl"]), +article p:has(> img[src$="#hero-tr"]), +article p:has(> img[src$="#hero-bl"]), +article p:has(> img[src$="#hero-br"]) { + max-width: 841px; + background: #f1f3f3; + border-radius: 12px; + box-sizing: border-box; + overflow: hidden; +} + +.vp-doc img[src$="#hero"], +.vp-doc img[src$="#hero-tl"], +.vp-doc img[src$="#hero-tr"], +.vp-doc img[src$="#hero-bl"], +.vp-doc img[src$="#hero-br"], +article img[src$="#hero"], +article img[src$="#hero-tl"], +article img[src$="#hero-tr"], +article img[src$="#hero-bl"], +article img[src$="#hero-br"] { + width: 100%; + height: auto; + display: block; + margin: 0; + border-style: solid; + border-color: #eaebeb; + object-fit: cover; + box-shadow: + 0px 2px 4px -1px rgba(41, 47, 61, 0.04), + 0px 4px 6px -1px rgba(41, 47, 61, 0.05); +} + +.vp-doc p:has(> img[src$="#hero"]), +article p:has(> img[src$="#hero"]) { + padding: 36px 56px 0; +} + +.vp-doc img[src$="#hero"], +article img[src$="#hero"] { + border-radius: 8px 8px 0 0; + border-width: 1px 1px 0 1px; +} + +.vp-doc p:has(> img[src$="#hero-tl"]), +article p:has(> img[src$="#hero-tl"]) { + padding: 36px 0 0 56px; +} + +.vp-doc img[src$="#hero-tl"], +article img[src$="#hero-tl"] { + border-radius: 8px 0 0 0; + border-width: 1px 0 0 1px; +} + +.vp-doc p:has(> img[src$="#hero-tr"]), +article p:has(> img[src$="#hero-tr"]) { + padding: 36px 56px 0 0; +} + +.vp-doc img[src$="#hero-tr"], +article img[src$="#hero-tr"] { + border-radius: 0 8px 0 0; + border-width: 1px 1px 0 0; +} + +.vp-doc p:has(> img[src$="#hero-bl"]), +article p:has(> img[src$="#hero-bl"]) { + padding: 0 0 36px 56px; +} + +.vp-doc img[src$="#hero-bl"], +article img[src$="#hero-bl"] { + border-radius: 0 0 0 8px; + border-width: 0 0 1px 1px; +} + +.vp-doc p:has(> img[src$="#hero-br"]), +article p:has(> img[src$="#hero-br"]) { + padding: 0 56px 36px 0; +} + +.vp-doc img[src$="#hero-br"], +article img[src$="#hero-br"] { + border-radius: 0 0 8px 0; + border-width: 0 1px 1px 0; +} diff --git a/packages/theme/src/css/fonts.css b/packages/theme/src/css/fonts.css new file mode 100644 index 00000000..cf3bd690 --- /dev/null +++ b/packages/theme/src/css/fonts.css @@ -0,0 +1,41 @@ +/* Self-hosted fonts (files live in each app's docs/public/fonts/ — apps/docs and apps/developer-docs). */ + +@font-face { + font-family: "Inter"; + src: url("/fonts/Inter/InterVariable.woff2") format("woff2"); + font-weight: 100 900; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "IBM Plex Mono"; + src: url("/fonts/IBMPlexMono/IBMPlexMono-Regular.ttf") format("truetype"); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "IBM Plex Mono"; + src: url("/fonts/IBMPlexMono/IBMPlexMono-Medium.ttf") format("truetype"); + font-weight: 500; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "IBM Plex Mono"; + src: url("/fonts/IBMPlexMono/IBMPlexMono-SemiBold.ttf") format("truetype"); + font-weight: 600; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "IBM Plex Mono"; + src: url("/fonts/IBMPlexMono/IBMPlexMono-Bold.ttf") format("truetype"); + font-weight: 700; + font-style: normal; + font-display: swap; +} diff --git a/packages/theme/src/css/index.css b/packages/theme/src/css/index.css new file mode 100644 index 00000000..01df1e89 --- /dev/null +++ b/packages/theme/src/css/index.css @@ -0,0 +1,19 @@ +/* ================================================ + PLANE DOCS THEME — shared stylesheet entry (@plane/docs-theme) + Imported once, by ../index.ts. Consumed by apps/docs and apps/developer-docs. + ================================================ */ + +/* VoidZero design system (tokens → base → docs → marketing) — includes Tailwind v4 */ +@import "@voidzero-dev/vitepress-theme/src/styles/index.css"; + +/* Plane layer, in cascade order */ +@import "./fonts.css"; +@import "./tokens.css"; +@import "./base.css"; +@import "./layout.css"; +@import "./components.css"; +@import "./api.css"; + +/* Tailwind content sources: this package's Vue components live outside each app's Vite root + (= the app's docs/ dir, which Tailwind scans automatically), so register them explicitly. */ +@source "../**/*.vue"; diff --git a/packages/theme/src/css/layout.css b/packages/theme/src/css/layout.css new file mode 100644 index 00000000..f01a9d09 --- /dev/null +++ b/packages/theme/src/css/layout.css @@ -0,0 +1,442 @@ +/* ================================================ + PLANE DOCS THEME — layout shell, header skin, sidebar / aside geometry + ================================================ */ + +/* ------------------------------------------------ + Layout borders & corner ticks + ------------------------------------------------ */ +@media (min-width: 768px) { + .docs-layout .content-wrapper, + .docs-layout .wrapper { + border-left-color: var(--docs-divider); + border-right-color: var(--docs-divider); + } +} + +.docs-layout .tick-left::before { + border-left-color: var(--docs-divider); +} + +.docs-layout .tick-right::after { + border-right-color: var(--docs-divider); +} + +.docs-layout .VPSidebar { + border-right: 1px solid var(--docs-divider); +} + +/* Hide local nav when the sidebar is present */ +@media (min-width: 1024px) { + .docs-layout .VPLocalNav { + display: none !important; + } +} + +/* ------------------------------------------------ + Sidebars: flush under the fixed header + ------------------------------------------------ */ +@media (min-width: 1024px) { + /* Both rails keep VitePress' 20px top inset so their first rows share a baseline. */ + .docs-layout .VPSidebar { + top: calc(var(--vp-nav-height) + var(--vp-banner-height, 0px) - 4px); + } + + .docs-layout .VPDoc .aside-container { + top: calc(var(--vp-nav-height) + var(--vp-banner-height, 0px) - 4px); + } +} + +/* --- Doc layout shell (1280px+): left nav + right outline share one baseline --- */ +@media (min-width: 1280px) { + .docs-layout .VPSidebar .nav { + padding-top: 0; + } + + .docs-layout .VPDoc .content { + padding-top: 32px; + } + + .docs-layout .VPDoc .aside { + max-width: 256px; + } + + .docs-layout .VPDoc .aside-container { + width: 256px; + padding-left: 24px; + border-left-color: var(--docs-divider); + } + + /* Outline: allow long headings to wrap, add air between siblings */ + .docs-layout .VPDocOutlineItem .outline-link { + white-space: normal; + text-overflow: unset; + overflow: visible; + line-height: 1.45; + padding: 2px 0 4px; + } + + .docs-layout .VPDocAsideOutline .VPDocOutlineItem li + li { + margin-top: 6px; + } + + .docs-layout .VPDocAsideOutline .VPDocOutlineItem li > .VPDocOutlineItem.nested { + margin-top: 4px; + } +} + +/* --- No right outline (aside: false, or the page has no headings): widen main column --- */ +.docs-layout .VPDoc:not(.has-aside) .content-container { + max-width: 960px; + padding-left: 24px; + padding-right: 24px; +} + +@media (min-width: 1280px) { + .docs-layout .VPDoc:not(.has-aside) .content-container { + max-width: 1104px; + padding-left: 48px; + padding-right: 48px; + } + + .docs-layout + .VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds)) + .aside { + display: none !important; + flex: 0 0 0 !important; + width: 0 !important; + min-width: 0 !important; + max-width: 0 !important; + margin: 0 !important; + padding: 0 !important; + overflow: hidden !important; + } + + .docs-layout + .VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds)) + .content { + flex: 1 1 auto; + max-width: none !important; + } + + .docs-layout + .VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds)) + .content-container { + max-width: 1104px; + padding-left: 48px; + padding-right: 48px; + } +} + +/* ------------------------------------------------ + Copy page menu (components/CopyPageMenu.vue) — placement + The control is server-rendered in the `doc-before` slot (first child of + `.VPDoc .content-container`, above
) and, once mounted, teleported into a + `.copy-page-slot` host inserted right after the page H1 inside `.vp-doc`. Both + positions are styled here; visual styles are scoped in the component. + ------------------------------------------------ */ +.docs-layout .VPDoc .content-container { + position: relative; /* anchor for the pre-teleport position */ +} + +/* < 768px: below the title, left-aligned, in flow */ +@media (max-width: 767px) { + /* Pre-teleport position would sit above the title — keep it hidden to avoid a jump. */ + .docs-layout .VPDoc .content-container > .copy-page { + display: none; + } + + .docs-layout .vp-doc > div > .copy-page-slot { + margin: 14px 0 16px; + } +} + +/* ≥ 768px: on the title row, flush with the right edge of the text column */ +@media (min-width: 768px) { + .docs-layout .VPDoc .content-container > .copy-page, + .docs-layout .vp-doc > div > .copy-page-slot > .copy-page { + position: absolute; /* relative to .content-container / .vp-doc — same top edge */ + top: 2px; /* (h1 line box ~38px − 34px button) / 2 */ + right: 0; + margin: 0; + z-index: 10; /* below --vp-z-index-local-nav (20) and the image lightbox */ + } + + /* Reserve the control's footprint on the H1 only when it actually rendered + (so `copyPage: false` pages keep the full width). */ + .docs-layout .vp-doc > div > h1:has(+ .copy-page-slot > .copy-page), + .docs-layout .VPDoc .content-container > .copy-page ~ .main .vp-doc > div > h1 { + padding-right: 156px; /* ≈ 132px control + 24px gap — re-measure if the label changes */ + } +} + +/* ------------------------------------------------ + Header (components/PlaneHeader.vue) — shell, search, nav, actions + ------------------------------------------------ */ +header.plane-header.wrapper, +.plane-header-shell { + font-family: var(--vp-font-family-base); + background-color: var(--plane-header-bg) !important; + border-bottom-color: var(--plane-header-border) !important; +} + +@media (min-width: 768px) { + .docs-layout header.plane-header.wrapper, + .docs-layout .plane-header-shell .wrapper { + border-left-color: var(--docs-divider) !important; + border-right-color: var(--docs-divider) !important; + } +} + +.docs-layout .border-stroke { + border-color: var(--docs-divider) !important; +} + +/* Match content-wrapper width (centered when fixed) */ +@media (min-width: 768px) { + .docs-layout .plane-header-shell { + width: calc(100vw - 2rem); + max-width: calc(100vw - 2rem); + margin-left: auto; + margin-right: auto; + box-sizing: border-box; + } +} + +@media (min-width: 90rem) { + .docs-layout .plane-header-shell { + width: 90rem; + max-width: 90rem; + } +} + +@media (min-width: 1024px) { + .docs-layout .plane-header-shell--docs { + left: 50%; + right: auto; + transform: translateX(-50%); + width: calc(100vw - 2rem); + max-width: calc(100vw - 2rem); + } + + .docs-layout .plane-header-shell--docs.plane-header-shell { + overflow-x: clip; + } +} + +@media (min-width: 90rem) { + .docs-layout .plane-header-shell--docs { + width: 90rem; + max-width: 90rem; + } +} + +@media (min-width: 768px) { + .plane-header .VPNavBarSearchButton, + .plane-header .search-bar { + background-color: var(--plane-header-search-bg) !important; + border: 1px solid var(--plane-header-search-border) !important; + border-radius: 12px; + color: var(--plane-header-search-text) !important; + } +} + +@media (min-width: 1024px) { + .plane-header-shell { + overflow-x: clip; + } + + .docs-layout .plane-header.wrapper { + width: 100%; + max-width: 100%; + margin-left: auto; + margin-right: auto; + box-sizing: border-box; + overflow-x: clip; + } + + .plane-header .plane-header__start, + .plane-header .plane-header__actions { + min-width: 0; + overflow: hidden; + } + + .plane-header .plane-header__search { + width: 240px; + max-width: 240px; + } + + .plane-header .VPNavBarSearch { + width: 100%; + min-width: 0; + max-width: 240px; + } + + .plane-header .VPNavBarSearchButton, + .plane-header .DocSearch.DocSearch-Button { + width: 100% !important; + max-width: 240px !important; + min-width: 0 !important; + } + + .plane-header .DocSearch.DocSearch-Button, + .plane-header .VPNavBarSearchButton, + .plane-header .search-bar { + height: 36px; + border-radius: 12px; + background: var(--plane-header-search-bg) !important; + border: 1px solid var(--plane-header-search-border) !important; + color: var(--plane-header-search-text) !important; + justify-content: flex-start !important; + text-align: left !important; + gap: 8px; + } + + .plane-header .DocSearch-Button-Container { + justify-content: flex-start !important; + flex: 1; + min-width: 0; + gap: 0.5rem; + } + + .plane-header .DocSearch-Search-Icon { + color: var(--plane-header-search-text) !important; + } + + .plane-header .DocSearch-Button-Placeholder, + .plane-header .VPNavBarSearchButton .text, + .plane-header .search-bar__text { + color: var(--plane-header-search-text) !important; + text-align: left !important; + flex: 0 1 auto; + font-size: 13px; + } + + .plane-header .DocSearch-Button-Keys, + .plane-header .VPNavBarSearchButton .keys, + .plane-header .search-bar__keys { + color: var(--plane-header-search-text) !important; + border: 1px solid var(--plane-header-search-keys-border) !important; + margin-left: auto !important; + flex-shrink: 0; + font-size: 12px; + font-weight: 500; + } + + .plane-header__nav .VPLink.link, + .plane-header__nav .VPLink.text-base, + .plane-header .VPNavBarMenuLink:not(.home-doc-actions__btn), + .plane-header .VPNavBarMenuGroup .button { + color: var(--plane-header-text) !important; + font-size: 14px !important; + font-weight: 400; + padding: 0.375rem 0.5rem; + min-width: 0; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + transition: color 0.2s ease; + } + + .plane-header__nav .VPLink.link:hover, + .plane-header .VPNavBarMenuLink:not(.home-doc-actions__btn):hover, + .plane-header .VPNavBarMenuGroup .button:hover { + color: var(--vp-c-brand-1) !important; + opacity: 1; + } + + .plane-header a.home-doc-actions__btn--secondary.VPNavBarMenuLink, + .plane-header a.home-doc-actions__btn--secondary.VPNavBarMenuLink:hover { + color: inherit !important; + opacity: 1 !important; + } + + .plane-header .VPNavBarMenuLink.active, + .plane-header__nav .VPLink.link.active { + color: var(--vp-c-brand-1) !important; + } + + .plane-header .VPNavBarAppearance, + .plane-header .VPNavBarSocialLinks { + display: flex !important; + align-items: center; + flex-shrink: 0; + } + + .plane-header .VPNavBarSocialLinks { + gap: 0.25rem; + } + + .plane-header .VPNavBarSocialLinks .VPSocialLink { + color: var(--plane-header-text); + transition: color 0.2s ease; + } + + .plane-header .VPNavBarSocialLinks .VPSocialLink:hover { + color: var(--vp-c-brand-1); + } + + .plane-header .VPSwitchAppearance { + --vp-c-bg: var(--plane-header-search-bg); + } +} + +/* Theme switcher border follows the layout hairlines */ +.docs-layout .VPSwitchAppearance { + --vp-c-border: var(--docs-divider); + --vp-input-border-color: var(--vp-c-border); +} + +@media (min-width: 1024px) and (max-width: 1279px) { + .plane-header__title { + display: none; + } +} + +@media (min-width: 1280px) { + .plane-header { + gap: 0.5rem 1.25rem; + --plane-header-padding-x: 1.5rem; + } + + .plane-header__nav .VPLink.link, + .plane-header__nav .VPLink.text-base, + .plane-header .VPNavBarMenuLink:not(.home-doc-actions__btn), + .plane-header .VPNavBarMenuGroup .button { + font-size: 14px !important; + padding: 0.375rem 0.75rem; + } +} + +/* Navbar buttons — hide external-link ↗ icon */ +.plane-header a.home-doc-actions__btn.vp-external-link-icon::after, +.plane-header .VPLink.home-doc-actions__btn.vp-external-link-icon::after, +.plane-header a.home-doc-actions__btn.no-icon::after, +.plane-header .VPLink.home-doc-actions__btn.no-icon::after { + display: none !important; + content: none !important; +} + +.plane-header a.home-doc-actions__btn .vp-external-link-icon, +.plane-header a.home-doc-actions__btn svg { + display: none !important; +} + +/* Navbar primary & secondary buttons */ +.plane-header a.home-doc-actions__btn--primary.VPNavBarMenuLink, +.plane-header a.home-doc-actions__btn--secondary.VPNavBarMenuLink, +.plane-header .VPLink.home-doc-actions__btn--primary, +.plane-header .VPLink.home-doc-actions__btn--secondary { + padding: 4px 15px !important; + font-size: 14px !important; + border-radius: 6px !important; + min-height: auto !important; + line-height: 1.25 !important; + font-weight: 500 !important; + white-space: nowrap; +} + +/* Don't apply nav text-link hover to the button variants */ +.plane-header .VPNavBarMenuLink.home-doc-actions__btn:hover { + opacity: 1 !important; +} diff --git a/packages/theme/src/css/tokens.css b/packages/theme/src/css/tokens.css new file mode 100644 index 00000000..e5bb735a --- /dev/null +++ b/packages/theme/src/css/tokens.css @@ -0,0 +1,202 @@ +/* ================================================ + PLANE DOCS THEME — design tokens + Loaded after @voidzero-dev/vitepress-theme/src/styles/index.css. + + Selector contract + - Light values live on `:root`. + - Dark values live on `.dark:not([data-theme]), [data-theme="dark"]` — the + exact pair VoidZero uses, so our declarations win by source order with the + same specificity and no `!important`. Never use bare `html.dark` here. + ================================================ */ + +:root { + /* --- Fonts (VoidZero maps --font-* → --vp-font-family-*; headings read --font-heading) --- */ + --vp-font-family-base: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --vp-font-family-mono: + "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", + monospace; + --font-sans: var(--vp-font-family-base); + --font-mono: var(--vp-font-family-mono); + --font-heading: var(--vp-font-family-base); + + /* --- Layout --- */ + --vp-nav-height: 84px; /* PlaneHeader height (VoidZero default 82px) */ + --docs-divider: #ececec; /* header / sidebar / wrapper hairlines */ + --color-stroke: var(--docs-divider); /* VoidZero .content-wrapper / ticks / border-stroke */ + + /* --- Brand --- */ + --color-brand: #006399; + --vp-c-brand-1: #006399; + --vp-c-brand-2: #0078b8; /* hover (VoidZero uses brand-2 for hover states) */ + --vp-c-brand-3: #006399; + --vp-c-brand-soft: rgba(0, 99, 153, 0.14); + --plane-brand-hover: #0078b8; + --plane-link-hover: #0078b8; + + /* --- Text / backgrounds / borders --- */ + --color-primary: #0a0a0a; /* Tailwind text-primary utility (VoidZero #16171d) */ + --vp-c-text-1: #0a0a0a; + --vp-c-text-2: #6b7280; + --vp-c-text-3: #9ca3af; + --vp-c-bg: #ffffff; + --vp-c-bg-alt: #f6f6f7; + --vp-c-bg-soft: #f6f6f7; + --vp-c-bg-elv: #ffffff; + --vp-c-divider: #e5e7eb; + --vp-c-border: #e5e7eb; + --vp-c-gutter: #e5e7eb; + + /* --- Code --- */ + /* Inline code in neutral body-text color (VitePress default is brand blue, + hard to read on the pill background). Linked code keeps the brand color. */ + --vp-code-color: var(--vp-c-text-1); + --vp-code-block-bg: #fafafa; + + /* --- Buttons (VPButton / hero actions) --- */ + --vp-button-brand-border: #006399; + --vp-button-brand-bg: #006399; + --vp-button-brand-text: #ffffff; + --vp-button-brand-hover-border: #0078b8; + --vp-button-brand-hover-bg: #0078b8; + --vp-button-brand-hover-text: #ffffff; + --vp-button-alt-border: #e5e7eb; + --vp-button-alt-text: #0a0a0a; + --vp-button-alt-bg: transparent; + --vp-button-alt-hover-border: #d1d5db; + --vp-button-alt-hover-text: #0a0a0a; + --vp-button-alt-hover-bg: #f7f8f9; + + /* --- Callouts (tip / info / warning / danger / caution / details) --- */ + --vp-c-tip-1: #166534; + --vp-c-tip-2: #14532d; + --vp-c-tip-3: #166534; + --vp-c-tip-soft: #dcfce7; + --vp-custom-block-tip-text: #166534; + --vp-custom-block-tip-bg: #dcfce7; + --vp-custom-block-tip-code-bg: #dcfce7; + + --vp-custom-block-info-text: #1e40af; + --vp-custom-block-info-bg: #dbeafe; + --vp-custom-block-info-code-bg: #dbeafe; + + --vp-c-warning-1: #92400e; + --vp-c-warning-2: #78350f; + --vp-c-warning-3: #92400e; + --vp-c-warning-soft: #fef3c7; + --vp-custom-block-warning-text: #92400e; + --vp-custom-block-warning-bg: #fef3c7; + --vp-custom-block-warning-code-bg: #fef3c7; + + --vp-c-danger-1: #991b1b; + --vp-c-danger-2: #7f1d1d; + --vp-c-danger-3: #991b1b; + --vp-c-danger-soft: #fee2e2; + --vp-custom-block-danger-text: #991b1b; + --vp-custom-block-danger-bg: #fee2e2; + --vp-custom-block-danger-code-bg: #fee2e2; + + --vp-c-caution-1: var(--vp-c-danger-1); + --vp-c-caution-2: var(--vp-c-danger-2); + --vp-c-caution-3: var(--vp-c-danger-3); + --vp-c-caution-soft: var(--vp-c-danger-soft); + --vp-custom-block-caution-text: var(--vp-custom-block-danger-text); + --vp-custom-block-caution-bg: var(--vp-custom-block-danger-bg); + --vp-custom-block-caution-code-bg: var(--vp-custom-block-danger-code-bg); + + /* `::: details` stays neutral (VoidZero aliases it to info → blue). */ + --vp-custom-block-details-text: var(--vp-c-text-1); + --vp-custom-block-details-bg: var(--vp-c-default-soft); + --vp-custom-block-details-code-bg: var(--vp-c-default-soft); + + /* --- Header (components/PlaneHeader.vue) --- */ + --plane-header-padding-y: 1.125rem; + --plane-header-padding-x: 1.375rem; + --plane-header-logo-height: 1.6875rem; + --plane-header-bg: #ffffff; + --plane-header-border: var(--docs-divider); + --plane-header-text: var(--color-primary); + --plane-header-muted: var(--vp-c-text-2); + --plane-header-divider: var(--docs-divider); + --plane-header-search-bg: #ffffff; + --plane-header-search-border: #e7e7e7; + --plane-header-search-text: #867e8e; + --plane-header-search-keys-border: #e5e4e7; +} + +.dark:not([data-theme]), +[data-theme="dark"] { + --docs-divider: #2a2a2a; + --color-nickel: var(--docs-divider); /* VoidZero dark:border-nickel / .content-wrapper */ + + --color-brand: #2893cc; + --vp-c-brand-1: #2893cc; + --vp-c-brand-2: #3aa5d4; + --vp-c-brand-3: #2893cc; + --vp-c-brand-soft: rgba(40, 147, 204, 0.14); + --plane-brand-hover: #3aa5d4; + --plane-link-hover: #3aa5d4; + + --color-primary: #141415; + --vp-c-text-1: rgba(255, 255, 255, 0.9); + --vp-c-text-2: #9ca3af; + --vp-c-text-3: #6b7280; + --vp-c-bg: #141415; + --vp-c-bg-alt: #141618; + --vp-c-bg-soft: #1f2122; + --vp-c-bg-mute: #252829; + --vp-c-bg-elv: #141618; + --vp-c-divider: #2a2a2a; + --vp-c-border: #2a2a2a; + --vp-c-gutter: #2a2a2a; + + --vp-code-block-bg: #0f0f0f; + + --vp-button-brand-border: #2893cc; + --vp-button-brand-bg: #2893cc; + --vp-button-brand-text: #ffffff; + --vp-button-brand-hover-border: #3aa5d4; + --vp-button-brand-hover-bg: #3aa5d4; + --vp-button-brand-hover-text: #ffffff; + --vp-button-alt-border: #3f4244; + --vp-button-alt-text: #ffffff; + --vp-button-alt-bg: transparent; + --vp-button-alt-hover-border: #4a4e52; + --vp-button-alt-hover-text: #ffffff; + --vp-button-alt-hover-bg: #252829; + --vp-button-alt-active-bg: #2f3236; + + --vp-c-tip-1: #4ade80; + --vp-c-tip-2: #86efac; + --vp-c-tip-3: #4ade80; + --vp-c-tip-soft: rgba(34, 197, 94, 0.15); + --vp-custom-block-tip-text: #4ade80; + --vp-custom-block-tip-bg: rgba(34, 197, 94, 0.15); + --vp-custom-block-tip-code-bg: rgba(34, 197, 94, 0.15); + + --vp-custom-block-info-text: #60a5fa; + --vp-custom-block-info-bg: rgba(59, 130, 246, 0.15); + --vp-custom-block-info-code-bg: rgba(59, 130, 246, 0.15); + + --vp-c-warning-1: #fbbf24; + --vp-c-warning-2: #fcd34d; + --vp-c-warning-3: #fbbf24; + --vp-c-warning-soft: rgba(251, 191, 36, 0.15); + --vp-custom-block-warning-text: #fbbf24; + --vp-custom-block-warning-bg: rgba(251, 191, 36, 0.15); + --vp-custom-block-warning-code-bg: rgba(251, 191, 36, 0.15); + + --vp-c-danger-1: #f87171; + --vp-c-danger-2: #fca5a5; + --vp-c-danger-3: #f87171; + --vp-c-danger-soft: rgba(239, 68, 68, 0.15); + --vp-custom-block-danger-text: #f87171; + --vp-custom-block-danger-bg: rgba(239, 68, 68, 0.15); + --vp-custom-block-danger-code-bg: rgba(239, 68, 68, 0.15); + + --plane-header-bg: var(--color-primary); + --plane-header-text: #ffffff; + --plane-header-search-bg: #111111; + --plane-header-search-border: #323232; + --plane-header-search-text: #867e8e; + --plane-header-search-keys-border: #323232; +} diff --git a/packages/theme/src/index.ts b/packages/theme/src/index.ts new file mode 100644 index 00000000..88a32d6d --- /dev/null +++ b/packages/theme/src/index.ts @@ -0,0 +1,206 @@ +/// +/// +/// +/** + * Plane docs theme (`@plane/docs-theme`) — shared by docs.plane.so (apps/docs) and + * developers.plane.so (apps/developer-docs). + * + * Each site's `docs/.vitepress/theme/index.ts` is a thin `createPlaneTheme({...})` call; + * everything site-specific (branding, extra components, `site.css`) stays in the app. + * This file also owns the single stylesheet entry (`./css/index.css`) — never import the + * theme CSS from anywhere else, or Tailwind gets a second root. + */ +import type { Theme } from "vitepress"; +import { useData, useRoute } from "vitepress"; +import { h, nextTick, onMounted, onUnmounted, watch } from "vue"; +import { enhanceAppWithTabs } from "vitepress-plugin-tabs/client"; +import mediumZoom from "medium-zoom"; +import { themeContextKey } from "@voidzero-dev/vitepress-theme"; +import VPBadge from "@vp-default/VPBadge.vue"; + +import "./css/index.css"; + +import PlaneLayout from "./layout/Layout.vue"; +import Card from "./components/Card.vue"; +import CardGroup from "./components/CardGroup.vue"; +import Tags from "./components/Tags.vue"; +import CookieConsent from "./components/CookieConsent.vue"; +import { planeOptionsKey, type PlaneThemeOptions } from "./options"; + +export type { PlaneThemeOptions } from "./options"; +export { planeOptionsKey } from "./options"; +export { Card, CardGroup, Tags, CookieConsent, PlaneLayout }; + +/* --------------------------------------------------------------------------- + * Client-side helpers + * ------------------------------------------------------------------------- */ + +/** Mirror `html.dark` onto the header shell (`data-theme`), which is SSR-rendered light. */ +function syncHeaderTheme() { + if (typeof document === "undefined") return; + const isDark = document.documentElement.classList.contains("dark"); + document.querySelectorAll("header.plane-header, header.wrapper").forEach((header) => { + if (isDark) header.setAttribute("data-theme", "dark"); + else header.removeAttribute("data-theme"); + }); +} + +/** Deep links to tabs (`vitepress-plugin-tabs`): `#label` activates the matching tab. */ +function handleTabHash() { + if (typeof document === "undefined") return; + const hash = window.location.hash.slice(1); + if (!hash) return; + document.querySelectorAll('[role="tab"]').forEach((button) => { + const labelText = button.textContent?.trim().toLowerCase().replace(/\s+/g, "-"); + if (labelText === hash) { + button.dispatchEvent( + new MouseEvent("click", { view: window, bubbles: true, cancelable: true }), + ); + button.click(); + button.focus(); + } + }); +} + +function updateHashOnTabClick(event: Event) { + const button = event.currentTarget as HTMLElement; + const labelText = button.textContent?.trim().toLowerCase().replace(/\s+/g, "-"); + if (labelText) history.replaceState(null, "", `#${labelText}`); +} + +function setupTabHashUpdates() { + if (typeof document === "undefined") return; + document.querySelectorAll('[role="tab"]').forEach((button) => { + button.removeEventListener("click", updateHashOnTabClick); + button.addEventListener("click", updateHashOnTabClick); + }); +} + +/* --------------------------------------------------------------------------- + * Theme factory + * ------------------------------------------------------------------------- */ + +export function createPlaneTheme(options: PlaneThemeOptions): Theme { + const cookieConsent = options.cookieConsent ?? true; + + return { + Layout() { + return h(PlaneLayout, null, cookieConsent ? { "layout-bottom": () => h(CookieConsent) } : {}); + }, + + enhanceApp(ctx) { + const { app, router, siteData } = ctx; + + // What VoidZeroTheme.enhanceApp does, minus its "default to dark when the visitor + // has no stored preference" behaviour — both sites follow the system setting. + const variant = siteData.value.themeConfig?.variant || "voidzero"; + if (typeof document !== "undefined") { + document.documentElement.setAttribute("data-variant", variant); + watch( + () => router.route.data.frontmatter?.theme, + (theme) => { + if (theme) document.documentElement.setAttribute("data-theme", theme); + else document.documentElement.removeAttribute("data-theme"); + }, + { immediate: true }, + ); + } + app.component("Badge", VPBadge); + + app.provide(themeContextKey, { + /* VoidZero naming: logoDark = dark mark (light bg), logoLight = light mark (dark bg) */ + logoDark: options.brand.logoOnLight, + logoLight: options.brand.logoOnDark, + logoAlt: options.brand.logoAlt ?? "Plane", + footerBg: options.brand.footerBg, + monoIcon: options.brand.monoIcon, + }); + app.provide(planeOptionsKey, options); + + enhanceAppWithTabs(app); + + app.component("Card", Card); + app.component("CardGroup", CardGroup); + app.component("Tags", Tags); + for (const [name, component] of Object.entries(options.components ?? {})) { + app.component(name, component); + } + + options.enhanceApp?.(ctx); + }, + + setup() { + if (typeof window === "undefined") return; + + const route = useRoute(); + const { isDark } = useData(); + let zoom: ReturnType | null = null; + let htmlClassObserver: MutationObserver | null = null; + + // Keep `html.dark` in lock-step with VitePress' `isDark` (VitePress' inline + // check-dark-mode script only ever *adds* the class). + watch( + isDark, + (dark) => { + document.documentElement.classList.toggle("dark", dark); + syncHeaderTheme(); + }, + { immediate: true }, + ); + + const initZoom = () => { + zoom?.detach(); + zoom = mediumZoom(".vp-doc :not(a) > img:not(.VPImage)", { + background: "rgba(0, 0, 0, 0.8)", + }); + }; + + const scheduleHeaderSync = () => { + nextTick(() => { + syncHeaderTheme(); + requestAnimationFrame(syncHeaderTheme); + }); + }; + + onMounted(() => { + nextTick(() => { + initZoom(); + scheduleHeaderSync(); + }); + + setTimeout(() => { + handleTabHash(); + setupTabHashUpdates(); + syncHeaderTheme(); + }, 100); + + window.addEventListener("hashchange", () => nextTick(handleTabHash)); + + htmlClassObserver = new MutationObserver(syncHeaderTheme); + htmlClassObserver.observe(document.documentElement, { + attributes: true, + attributeFilter: ["class"], + }); + }); + + onUnmounted(() => { + htmlClassObserver?.disconnect(); + zoom?.detach(); + }); + + watch( + () => route.path, + () => { + nextTick(() => { + initZoom(); + handleTabHash(); + setupTabHashUpdates(); + scheduleHeaderSync(); + }); + }, + ); + + options.setup?.(); + }, + }; +} diff --git a/packages/theme/src/layout/Layout.vue b/packages/theme/src/layout/Layout.vue new file mode 100644 index 00000000..d23a5cae --- /dev/null +++ b/packages/theme/src/layout/Layout.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/packages/theme/src/layout/default-layout.ts b/packages/theme/src/layout/default-layout.ts new file mode 100644 index 00000000..41b63b86 --- /dev/null +++ b/packages/theme/src/layout/default-layout.ts @@ -0,0 +1,10 @@ +import { defineComponent, h } from "vue"; +import Layout from "./doc-layout.vue"; +import { slotsToChildren } from "./slots"; + +export default defineComponent({ + name: "PlaneVoidzeroDefaultLayout", + setup(_, { slots }) { + return () => h(Layout, null, slotsToChildren(slots)); + }, +}); diff --git a/packages/theme/src/layout/doc-layout.vue b/packages/theme/src/layout/doc-layout.vue new file mode 100644 index 00000000..2ff8c45b --- /dev/null +++ b/packages/theme/src/layout/doc-layout.vue @@ -0,0 +1,179 @@ + + + + + + + diff --git a/packages/theme/src/layout/header.ts b/packages/theme/src/layout/header.ts new file mode 100644 index 00000000..600b3d49 --- /dev/null +++ b/packages/theme/src/layout/header.ts @@ -0,0 +1,10 @@ +import { defineComponent, h } from "vue"; +import Header from "../components/PlaneHeader.vue"; +import { slotsToChildren } from "./slots"; + +export default defineComponent({ + name: "PlaneVoidzeroHeader", + setup(_, { slots }) { + return () => h(Header, null, slotsToChildren(slots)); + }, +}); diff --git a/packages/theme/src/layout/slots.ts b/packages/theme/src/layout/slots.ts new file mode 100644 index 00000000..eb223fb9 --- /dev/null +++ b/packages/theme/src/layout/slots.ts @@ -0,0 +1,13 @@ +import type { Slots, VNode } from "vue"; + +/** Normalize Vue 3 slots for use with `h(Component, props, children)` */ +export function slotsToChildren(slots: Slots): Record VNode[]> { + const children: Record VNode[]> = {}; + for (const name of Object.keys(slots)) { + const slot = slots[name]; + if (slot) { + children[name] = () => slot() as VNode[]; + } + } + return children; +} diff --git a/packages/theme/src/layout/top-banner.ts b/packages/theme/src/layout/top-banner.ts new file mode 100644 index 00000000..02492af3 --- /dev/null +++ b/packages/theme/src/layout/top-banner.ts @@ -0,0 +1,9 @@ +import { defineComponent, h } from "vue"; +import Banner from "@voidzero-dev/vitepress-theme/src/components/oss/TopBanner.vue"; + +export default defineComponent({ + name: "PlaneVoidzeroTopBanner", + setup() { + return () => h(Banner); + }, +}); diff --git a/packages/theme/src/options.ts b/packages/theme/src/options.ts new file mode 100644 index 00000000..e33b624a --- /dev/null +++ b/packages/theme/src/options.ts @@ -0,0 +1,30 @@ +import type { Component, InjectionKey } from "vue"; +import type { EnhanceAppContext } from "vitepress"; + +/** Per-site branding + hooks for the shared Plane docs theme. */ +export interface PlaneThemeOptions { + brand: { + /** Logo shown on light backgrounds (dark mark). */ + logoOnLight: string; + /** Logo shown on dark backgrounds (light mark). */ + logoOnDark: string; + /** Alt text for the logo. Default: "Plane". */ + logoAlt?: string; + /** Wordmark next to the logo in the mobile menu, e.g. "Plane Docs". */ + menuTitle: string; + /** Footer background image (consumed by the VoidZero footer). */ + footerBg: string; + /** Monochrome icon (consumed by the VoidZero top banner). */ + monoIcon: string; + }; + /** Render the cookie-consent banner. Default: true. */ + cookieConsent?: boolean; + /** Extra globally-registered components (site-specific markdown components). */ + components?: Record; + /** Extra `enhanceApp` work, run after the shared setup. */ + enhanceApp?: (ctx: EnhanceAppContext) => void; + /** Extra client-side `setup()` work, run inside the shared theme's `setup()`. */ + setup?: () => void; +} + +export const planeOptionsKey: InjectionKey = Symbol.for("plane-theme-options"); diff --git a/packages/theme/src/types/shims.d.ts b/packages/theme/src/types/shims.d.ts new file mode 100644 index 00000000..87efbe0e --- /dev/null +++ b/packages/theme/src/types/shims.d.ts @@ -0,0 +1,40 @@ +/* + * Ambient module shims (this file must stay a script: no top-level import/export). + */ + +declare module "*.vue" { + import type { DefineComponent } from "vue"; + const component: DefineComponent; + export default component; +} + +/** Deep imports from @voidzero-dev/vitepress-theme (the package does not ship .vue types) */ +declare module "@voidzero-dev/vitepress-theme/src/components/vitepress-default/Layout.vue" { + import type { DefineComponent } from "vue"; + const component: DefineComponent; + export default component; +} + +declare module "@voidzero-dev/vitepress-theme/src/components/oss/Header.vue" { + import type { DefineComponent } from "vue"; + const component: DefineComponent; + export default component; +} + +declare module "@voidzero-dev/vitepress-theme/src/components/oss/TopBanner.vue" { + import type { DefineComponent } from "vue"; + const component: DefineComponent; + export default component; +} + +declare module "@voidzero-dev/vitepress-theme/src/types/theme-context" { + import type { InjectionKey } from "vue"; + export interface ThemeContext { + logoDark: string; + logoLight: string; + logoAlt: string; + footerBg: string; + monoIcon: string; + } + export const themeContextKey: InjectionKey; +} diff --git a/packages/theme/src/types/vitepress-augment.d.ts b/packages/theme/src/types/vitepress-augment.d.ts new file mode 100644 index 00000000..48231188 --- /dev/null +++ b/packages/theme/src/types/vitepress-augment.d.ts @@ -0,0 +1,20 @@ +/* + * VitePress config augmentations used by the shared theme (module file on purpose). + */ +import type {} from "vitepress"; + +declare module "vitepress" { + namespace DefaultTheme { + interface Config { + /** VoidZero theme variant (both Plane sites use "voidzero"). */ + variant?: "voidzero" | "viteplus" | "vite" | "vitest" | "rolldown" | "oxc"; + } + interface NavItemWithLink { + /** + * Render this nav item as a header button instead of a nav link: + * "primary" = filled (Sign in), "secondary" = outlined (link to the sibling docs site). + */ + planeButton?: "primary" | "secondary"; + } + } +} diff --git a/packages/theme/src/types/voidzero-theme.ts b/packages/theme/src/types/voidzero-theme.ts new file mode 100644 index 00000000..a77d72c7 --- /dev/null +++ b/packages/theme/src/types/voidzero-theme.ts @@ -0,0 +1,14 @@ +import type { Component } from "vue"; +import type { Theme } from "vitepress"; + +export { + themeContextKey, + type ThemeContext, +} from "@voidzero-dev/vitepress-theme/src/types/theme-context"; + +export const VPHomeHero = {} as Component; +export const VPHomeFeatures = {} as Component; + +declare const VoidZeroTheme: Theme; +export { VoidZeroTheme }; +export default VoidZeroTheme; diff --git a/packages/theme/src/types/vp-theme-modules.d.ts b/packages/theme/src/types/vp-theme-modules.d.ts new file mode 100644 index 00000000..564d5e05 --- /dev/null +++ b/packages/theme/src/types/vp-theme-modules.d.ts @@ -0,0 +1,115 @@ +/** + * Explicit module declarations for VoidZero VitePress theme aliases. + * Wildcard patterns are unreliable in Vetur / some IDE resolvers; list each import path. + */ +import type { DefineComponent } from "vue"; + +type VueModule = DefineComponent; + +declare module "@vp-default/VPNavBarSearch.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPNavBarMenuLink.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPNavBarMenuGroup.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPNavBarAppearance.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPSwitchAppearance.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPNavBarSocialLinks.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPSocialLinks.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPBadge.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPBackdrop.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPContent.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPFooter.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPLocalNav.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPSidebar.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-default/VPSkipLink.vue" { + const component: VueModule; + export default component; +} + +declare module "@components/oss/TopBanner.vue" { + const component: VueModule; + export default component; +} + +declare module "@vp-composables/langs" { + export function useLangs(options?: { correspondingLink?: boolean }): { + localeLinks: { link: string; text: string }[]; + currentLang: { label?: string }; + }; +} + +declare module "@vp-composables/data" { + export function useData(): ReturnType; +} + +declare module "@vp-composables/layout" { + export const layoutInfoInjectionKey: symbol; + export function registerWatchers(options: { closeSidebar: () => void }): void; + export function useLayout(): { hasSidebar: import("vue").ComputedRef }; +} + +declare module "@vp-composables/sidebar" { + export function useSidebarControl(): { + isOpen: import("vue").Ref; + open: () => void; + close: () => void; + }; +} + +declare module "@vp-support/utils" { + export function normalizeLink(path: string): string; +} + +declare module "@vp-support/search-config" { + export function getSearchProvider(theme: unknown): "local" | "algolia" | undefined; +} diff --git a/packages/theme/tsconfig.json b/packages/theme/tsconfig.json new file mode 100644 index 00000000..e049bbdf --- /dev/null +++ b/packages/theme/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts", "src/**/*.vue"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c4ea8ffd..8d67e796 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,330 +4,342 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + default: + '@types/node': + specifier: ^25.9.5 + version: 25.9.5 + '@voidzero-dev/vitepress-theme': + specifier: ^4.8.4 + version: 4.8.4 + lucide-vue-next: + specifier: ^0.577.0 + version: 0.577.0 + medium-zoom: + specifier: ^1.1.0 + version: 1.1.0 + mermaid: + specifier: ^11.15.0 + version: 11.16.1 + typescript: + specifier: ^6.0.3 + version: 6.0.3 + vitepress: + specifier: 2.0.0-alpha.16 + version: 2.0.0-alpha.16 + vitepress-plugin-llms: + specifier: ^1.13.1 + version: 1.13.1 + vitepress-plugin-mermaid: + specifier: ^2.0.17 + version: 2.0.17 + vitepress-plugin-tabs: + specifier: ^0.8.0 + version: 0.8.0 + vue: + specifier: ^3.5.41 + version: 3.5.41 + overrides: - esbuild: ^0.25.0 + rollup: 4.59.0 + lodash-es: 4.18.1 + esbuild: 0.25.0 + dompurify: 3.4.11 vite: 6.4.3 - postcss: ^8.5.10 - js-yaml@<3.15.0: 3.15.0 + postcss: 8.5.15 + uuid: 11.1.1 + js-yaml: 3.15.0 importers: .: + devDependencies: + oxfmt: + specifier: ^0.36.0 + version: 0.36.0 + turbo: + specifier: ^2.10.10 + version: 2.10.10 + typescript: + specifier: 'catalog:' + version: 6.0.3 + + apps/developer-docs: dependencies: - '@tailwindcss/vite': - specifier: ^4.2.1 - version: 4.2.1(vite@6.4.3(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1)) + '@plane/docs-theme': + specifier: workspace:* + version: link:../../packages/theme + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 25.9.5 '@voidzero-dev/vitepress-theme': - specifier: ^4.8.4 - version: 4.8.4(focus-trap@7.8.0)(vite@6.4.3(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1))(vitepress@1.6.4(@algolia/client-search@5.49.2)(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(search-insights@2.17.3))(vue@3.5.30) - lucide-vue-next: - specifier: ^0.577.0 - version: 0.577.0(vue@3.5.30) - medium-zoom: - specifier: ^1.1.0 - version: 1.1.0 - tailwindcss: - specifier: ^4.2.1 - version: 4.2.1 + specifier: 'catalog:' + version: 4.8.4(focus-trap@7.8.0)(vite@6.4.3(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1))(vitepress@2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3))(vue@3.5.41(typescript@6.0.3)) + mermaid: + specifier: 'catalog:' + version: 11.16.1 + typescript: + specifier: 'catalog:' + version: 6.0.3 vitepress: - specifier: ^1.6.3 - version: 1.6.4(@algolia/client-search@5.49.2)(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(search-insights@2.17.3) + specifier: 'catalog:' + version: 2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3) + vitepress-plugin-llms: + specifier: 'catalog:' + version: 1.13.1 + vitepress-plugin-mermaid: + specifier: 'catalog:' + version: 2.0.17(mermaid@11.16.1)(vitepress@2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3)) vitepress-plugin-tabs: - specifier: ^0.8.0 - version: 0.8.0(vitepress@1.6.4(@algolia/client-search@5.49.2)(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(search-insights@2.17.3))(vue@3.5.30) + specifier: 'catalog:' + version: 0.8.0(vitepress@2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3))(vue@3.5.41(typescript@6.0.3)) vue: - specifier: ^3.5.13 - version: 3.5.30 + specifier: 'catalog:' + version: 3.5.41(typescript@6.0.3) + + apps/docs: + dependencies: + '@plane/docs-theme': + specifier: workspace:* + version: link:../../packages/theme devDependencies: '@types/node': - specifier: ^25.6.0 - version: 25.6.0 - oxfmt: - specifier: ^0.36.0 - version: 0.36.0 + specifier: 'catalog:' + version: 25.9.5 + '@voidzero-dev/vitepress-theme': + specifier: 'catalog:' + version: 4.8.4(focus-trap@7.8.0)(vite@6.4.3(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1))(vitepress@2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3))(vue@3.5.41(typescript@6.0.3)) + typescript: + specifier: 'catalog:' + version: 6.0.3 + vitepress: + specifier: 'catalog:' + version: 2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3) vitepress-plugin-llms: - specifier: ^1.13.1 + specifier: 'catalog:' version: 1.13.1 + vitepress-plugin-tabs: + specifier: 'catalog:' + version: 0.8.0(vitepress@2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3))(vue@3.5.41(typescript@6.0.3)) + vue: + specifier: 'catalog:' + version: 3.5.41(typescript@6.0.3) -packages: - - '@algolia/abtesting@1.15.2': - resolution: {integrity: sha512-rF7vRVE61E0QORw8e2NNdnttcl3jmFMWS9B4hhdga12COe+lMa26bQLfcBn/Nbp9/AF/8gXdaRCPsVns3CnjsA==} - engines: {node: '>= 14.0.0'} - - '@algolia/autocomplete-core@1.17.7': - resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==} - - '@algolia/autocomplete-plugin-algolia-insights@1.17.7': - resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==} - peerDependencies: - search-insights: '>= 1 < 3' - - '@algolia/autocomplete-preset-algolia@1.17.7': - resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==} - peerDependencies: - '@algolia/client-search': '>= 4.9.1 < 6' - algoliasearch: '>= 4.9.1 < 6' - - '@algolia/autocomplete-shared@1.17.7': - resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==} - peerDependencies: - '@algolia/client-search': '>= 4.9.1 < 6' - algoliasearch: '>= 4.9.1 < 6' - - '@algolia/client-abtesting@5.49.2': - resolution: {integrity: sha512-XyvKCm0RRmovMI/ChaAVjTwpZhXdbgt3iZofK914HeEHLqD1MUFFVLz7M0+Ou7F56UkHXwRbpHwb9xBDNopprQ==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-analytics@5.49.2': - resolution: {integrity: sha512-jq/3qvtmj3NijZlhq7A1B0Cl41GfaBpjJxcwukGsYds6aMSCWrEAJ9pUqw/C9B3hAmILYKl7Ljz3N9SFvekD3Q==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-common@5.49.2': - resolution: {integrity: sha512-bn0biLequn3epobCfjUqCxlIlurLr4RHu7RaE4trgN+RDcUq6HCVC3/yqq1hwbNYpVtulnTOJzcaxYlSr1fnuw==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-insights@5.49.2': - resolution: {integrity: sha512-z14wfFs1T3eeYbCArC8pvntAWsPo9f6hnUGoj8IoRUJTwgJiiySECkm8bmmV47/x0oGHfsVn3kBdjMX0yq0sNA==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-personalization@5.49.2': - resolution: {integrity: sha512-GpRf7yuuAX93+Qt0JGEJZwgtL0MFdjFO9n7dn8s2pA9mTjzl0Sc5+uTk1VPbIAuf7xhCP9Mve+URGb6J+EYxgA==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-query-suggestions@5.49.2': - resolution: {integrity: sha512-HZwApmNkp0DiAjZcLYdQLddcG4Agb88OkojiAHGgcm5DVXobT5uSZ9lmyrbw/tmQBJwgu2CNw4zTyXoIB7YbPA==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-search@5.49.2': - resolution: {integrity: sha512-y1IOpG6OSmTpGg/CT0YBb/EAhR2nsC18QWp9Jy8HO9iGySpcwaTvs5kHa17daP3BMTwWyaX9/1tDTDQshZzXdg==} - engines: {node: '>= 14.0.0'} - - '@algolia/ingestion@1.49.2': - resolution: {integrity: sha512-YYJRjaZ2bqk923HxE4um7j/Cm3/xoSkF2HC2ZweOF8cXL3sqnlndSUYmCaxHFjNPWLaSHk2IfssX6J/tdKTULw==} - engines: {node: '>= 14.0.0'} - - '@algolia/monitoring@1.49.2': - resolution: {integrity: sha512-9WgH+Dha39EQQyGKCHlGYnxW/7W19DIrEbCEbnzwAMpGAv1yTWCHMPXHxYa+LcL3eCp2V/5idD1zHNlIKmHRHg==} - engines: {node: '>= 14.0.0'} - - '@algolia/recommend@5.49.2': - resolution: {integrity: sha512-K7Gp5u+JtVYgaVpBxF5rGiM+Ia8SsMdcAJMTDV93rwh00DKNllC19o1g+PwrDjDvyXNrnTEbofzbTs2GLfFyKA==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-browser-xhr@5.49.2': - resolution: {integrity: sha512-3UhYCcWX6fbtN8ABcxZlhaQEwXFh3CsFtARyyadQShHMPe3mJV9Wel4FpJTa+seugRkbezFz0tt6aPTZSYTBuA==} - engines: {node: '>= 14.0.0'} + packages/theme: + dependencies: + '@voidzero-dev/vitepress-theme': + specifier: 'catalog:' + version: 4.8.4(focus-trap@7.8.0)(vite@6.4.3(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1))(vitepress@2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3))(vue@3.5.41(typescript@6.0.3)) + lucide-vue-next: + specifier: 'catalog:' + version: 0.577.0(vue@3.5.41(typescript@6.0.3)) + medium-zoom: + specifier: 'catalog:' + version: 1.1.0 + vitepress-plugin-tabs: + specifier: 'catalog:' + version: 0.8.0(vitepress@2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3))(vue@3.5.41(typescript@6.0.3)) + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 25.9.5 + typescript: + specifier: 'catalog:' + version: 6.0.3 + vitepress: + specifier: 'catalog:' + version: 2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3) + vue: + specifier: 'catalog:' + version: 3.5.41(typescript@6.0.3) - '@algolia/requester-fetch@5.49.2': - resolution: {integrity: sha512-G94VKSGbsr+WjsDDOBe5QDQ82QYgxvpxRGJfCHZBnYKYsy/jv9qGIDb93biza+LJWizQBUtDj7bZzp3QZyzhPQ==} - engines: {node: '>= 14.0.0'} +packages: - '@algolia/requester-node-http@5.49.2': - resolution: {integrity: sha512-UuihBGHafG/ENsrcTGAn5rsOffrCIRuHMOsD85fZGLEY92ate+BMTUqxz60dv5zerh8ZumN4bRm8eW2z9L11jA==} - engines: {node: '>= 14.0.0'} + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.0': - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} - '@docsearch/css@3.8.2': - resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} + '@braintree/sanitize-url@6.0.4': + resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==} + + '@braintree/sanitize-url@7.1.2': + resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} + + '@chevrotain/types@11.1.2': + resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} '@docsearch/css@4.6.2': resolution: {integrity: sha512-fH/cn8BjEEdM2nJdjNMHIvOVYupG6AIDtFVDgIZrNzdCSj4KXr9kd+hsehqsNGYjpUjObeKYKvgy/IwCb1jZYQ==} - '@docsearch/js@3.8.2': - resolution: {integrity: sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==} - '@docsearch/js@4.6.2': resolution: {integrity: sha512-qj1yoxl3y4GKoK7+VM6fq/rQqPnvUmg3IKzJ9x0VzN14QVzdB/SG/J6VfV1BWT5RcPUFxIcVwoY1fwHM2fSRRw==} - '@docsearch/react@3.8.2': - resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==} - peerDependencies: - '@types/react': '>= 16.8.0 < 19.0.0' - react: '>= 16.8.0 < 19.0.0' - react-dom: '>= 16.8.0 < 19.0.0' - search-insights: '>= 1 < 3' - peerDependenciesMeta: - '@types/react': - optional: true - react: - optional: true - react-dom: - optional: true - search-insights: - optional: true - '@docsearch/sidepanel-js@4.6.2': resolution: {integrity: sha512-Pni85AP/GwRj7fFg8cBJp0U04tzbueBvWSd3gysgnOsVnQVSZwSYncfErUScLE1CAtR+qocPDFjmYR9AMRNJtQ==} - '@esbuild/aix-ppc64@0.25.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + '@esbuild/android-arm@0.25.0': + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.12': - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + '@esbuild/android-x64@0.25.0': + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.12': - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + '@esbuild/darwin-arm64@0.25.0': + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.12': - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + '@esbuild/darwin-x64@0.25.0': + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + '@esbuild/freebsd-arm64@0.25.0': + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.12': - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + '@esbuild/freebsd-x64@0.25.0': + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + '@esbuild/linux-arm64@0.25.0': + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.12': - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + '@esbuild/linux-arm@0.25.0': + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.12': - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + '@esbuild/linux-ia32@0.25.0': + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.12': - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + '@esbuild/linux-loong64@0.25.0': + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.12': - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + '@esbuild/linux-mips64el@0.25.0': + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.12': - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + '@esbuild/linux-ppc64@0.25.0': + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + '@esbuild/linux-riscv64@0.25.0': + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + '@esbuild/linux-s390x@0.25.0': + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.12': - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + '@esbuild/linux-x64@0.25.0': + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.12': - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + '@esbuild/netbsd-arm64@0.25.0': + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + '@esbuild/netbsd-x64@0.25.0': + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.12': - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + '@esbuild/openbsd-arm64@0.25.0': + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + '@esbuild/openbsd-x64@0.25.0': + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.12': - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.25.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + '@esbuild/sunos-x64@0.25.0': + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.12': - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + '@esbuild/win32-arm64@0.25.0': + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.12': - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + '@esbuild/win32-ia32@0.25.0': + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + '@esbuild/win32-x64@0.25.0': + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -350,6 +362,9 @@ packages: '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + '@iconify/utils@3.1.4': + resolution: {integrity: sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==} + '@iconify/vue@5.0.0': resolution: {integrity: sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==} peerDependencies: @@ -377,6 +392,12 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@mermaid-js/mermaid-mindmap@9.3.0': + resolution: {integrity: sha512-IhtYSVBBRYviH1Ehu8gk69pMDF8DSRqXBRDMWrEfHoaMruHeaP2DXA3PBnuwsMaCdPQhlUUcy/7DBLAEIXvCAw==} + + '@mermaid-js/parser@1.2.0': + resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==} + '@oxfmt/binding-android-arm-eabi@0.36.0': resolution: {integrity: sha512-Z4yVHJWx/swHHjtr0dXrBZb6LxS+qNz1qdza222mWwPTUK4L790+5i3LTgjx3KYGBzcYpjaiZBw4vOx94dH7MQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -502,6 +523,9 @@ packages: '@rive-app/canvas-lite@2.37.3': resolution: {integrity: sha512-lw4M13Yu1VZSlys/4yW3O4IGMXqSsZCdwPTEkspR9PkphJW+WWjxT99F946eXAVV6aNg5gE3XuW3PQaoDciiYg==} + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@rollup/rollup-android-arm-eabi@4.59.0': resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} cpu: [arm] @@ -640,26 +664,26 @@ packages: cpu: [x64] os: [win32] - '@shikijs/core@2.5.0': - resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==} + '@shikijs/core@3.23.0': + resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} - '@shikijs/engine-javascript@2.5.0': - resolution: {integrity: sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==} + '@shikijs/engine-javascript@3.23.0': + resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==} - '@shikijs/engine-oniguruma@2.5.0': - resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==} + '@shikijs/engine-oniguruma@3.23.0': + resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} - '@shikijs/langs@2.5.0': - resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==} + '@shikijs/langs@3.23.0': + resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} - '@shikijs/themes@2.5.0': - resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==} + '@shikijs/themes@3.23.0': + resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} - '@shikijs/transformers@2.5.0': - resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==} + '@shikijs/transformers@3.23.0': + resolution: {integrity: sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ==} - '@shikijs/types@2.5.0': - resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==} + '@shikijs/types@3.23.0': + resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -774,12 +798,138 @@ packages: peerDependencies: vue: ^2.7.0 || ^3.0.0 + '@turbo/darwin-64@2.10.10': + resolution: {integrity: sha512-gFDD+wRP5hWxBRghGyEbjpbLOY7aIU/wvsnKdMM7odQcp/wHMrnI83p0FyxxMRZnFH9ZD+S59MvcpOC5b+nrCA==} + cpu: [x64] + os: [darwin] + + '@turbo/darwin-arm64@2.10.10': + resolution: {integrity: sha512-VZYsxZ6yjyDosUqtiroAVSXPLmx/qBxdHJgIxdMH9RyNmLdOLOWtJnYMnI4qckwCgQMK85G3fu94/xk5+iBCgw==} + cpu: [arm64] + os: [darwin] + + '@turbo/linux-64@2.10.10': + resolution: {integrity: sha512-lAvW+yEnmsCKMEIwNugjozawvYytHKPhU0kfLBizu83MIs8OUb9KobYvkZ56L5akSM6K7+gBFLEIfQkaceh90g==} + cpu: [x64] + os: [android, linux] + + '@turbo/linux-arm64@2.10.10': + resolution: {integrity: sha512-MSJ+NkRTd79Z9+YEZpUV9VOWVOOigFhE+v/ETNYJEuTJp3r00y9YgFvDXrmM+DP8Kal6tk3U6xSugD2/Ojh+Jg==} + cpu: [arm64] + os: [android, linux] + + '@turbo/windows-64@2.10.10': + resolution: {integrity: sha512-ycWpXDkUfnDFDY9d+4Qna/UZotDB0wj+s9agrlmNt0Q7a3XHORhK8GPKJdzgzeutXu9EW5P/jyabTEHlohuDXw==} + cpu: [x64] + os: [win32] + + '@turbo/windows-arm64@2.10.10': + resolution: {integrity: sha512-PMk6zQN0csUFklLe+1hz/5G9uU1YmV0cEIey2R/bSeA6o69qcBTlN4A3jOqkgenOO5dOpMHmq2sEUZo8r1+Ssg==} + cpu: [arm64] + os: [win32] + + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.1': + resolution: {integrity: sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.4': + resolution: {integrity: sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + '@types/debug@4.1.13': resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -798,8 +948,11 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@25.6.0': - resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} + '@types/node@25.9.5': + resolution: {integrity: sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -811,9 +964,12 @@ packages: resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} deprecated: Potential CWE-502 - Update to 1.3.1 or higher - '@vitejs/plugin-vue@5.2.4': - resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} - engines: {node: ^18.0.0 || >=20.0.0} + '@upsetjs/venn.js@2.0.0': + resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} + + '@vitejs/plugin-vue@6.0.8': + resolution: {integrity: sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: 6.4.3 vue: ^3.2.25 @@ -824,93 +980,47 @@ packages: vitepress: ^2.0.0-alpha.16 vue: ^3.5.0 - '@vue/compiler-core@3.5.30': - resolution: {integrity: sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==} + '@vue/compiler-core@3.5.41': + resolution: {integrity: sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==} - '@vue/compiler-dom@3.5.30': - resolution: {integrity: sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==} + '@vue/compiler-dom@3.5.41': + resolution: {integrity: sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==} - '@vue/compiler-sfc@3.5.30': - resolution: {integrity: sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==} + '@vue/compiler-sfc@3.5.41': + resolution: {integrity: sha512-XJhip7R2wy6vX3knCxdZN4KracFaZUef58s1KYewqluedHIJaPIVfXoYT7MF1F8nCvv6k8bWWxDC8opMkg1VTQ==} - '@vue/compiler-ssr@3.5.30': - resolution: {integrity: sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==} + '@vue/compiler-ssr@3.5.41': + resolution: {integrity: sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A==} - '@vue/devtools-api@7.7.9': - resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} + '@vue/devtools-api@8.2.1': + resolution: {integrity: sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==} - '@vue/devtools-kit@7.7.9': - resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} + '@vue/devtools-kit@8.2.1': + resolution: {integrity: sha512-FIGIuq3AWReEpbAHY/cRGeHDfI0qOb8OCQ3YjbEAX04uaxIDbGc9rhkbVcG7rnfHPXE3RsU5KrWOu9V/okd8AQ==} - '@vue/devtools-shared@7.7.9': - resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} + '@vue/devtools-shared@8.2.1': + resolution: {integrity: sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==} - '@vue/reactivity@3.5.30': - resolution: {integrity: sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==} + '@vue/reactivity@3.5.41': + resolution: {integrity: sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA==} - '@vue/runtime-core@3.5.30': - resolution: {integrity: sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==} + '@vue/runtime-core@3.5.41': + resolution: {integrity: sha512-Vcry58hiAKwGen9Z1jUZE0feFsNArPCMOImYI8el48A9Idf6DuQYD0U05zZIF2Iad1hGhPSvcbBbAOhNr55fhg==} - '@vue/runtime-dom@3.5.30': - resolution: {integrity: sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==} + '@vue/runtime-dom@3.5.41': + resolution: {integrity: sha512-3vVBahVBS9+U6cmXBLyb8nE6/yYo4J/CGI9eVFs3KiMc0YHuudwKyShTD65jtJy/L9PUUxNAFu4cj4LiJ0UFbw==} - '@vue/server-renderer@3.5.30': - resolution: {integrity: sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==} - peerDependencies: - vue: 3.5.30 - - '@vue/shared@3.5.30': - resolution: {integrity: sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==} + '@vue/server-renderer@3.5.41': + resolution: {integrity: sha512-n6hx/pNFfbD6SuyeuMVkvqox8bwf/ET9JlA/kAz/imw8sw++wkqKe2mHX5KutjPpbKE4Z56yTHszoOjGMI9igQ==} - '@vueuse/core@12.8.2': - resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==} + '@vue/shared@3.5.41': + resolution: {integrity: sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==} '@vueuse/core@14.2.1': resolution: {integrity: sha512-3vwDzV+GDUNpdegRY6kzpLm4Igptq+GA0QkJ3W61Iv27YWwW/ufSlOfgQIpN6FZRMG0mkaz4gglJRtq5SeJyIQ==} peerDependencies: vue: ^3.5.0 - '@vueuse/integrations@12.8.2': - resolution: {integrity: sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==} - peerDependencies: - async-validator: ^4 - axios: ^1 - change-case: ^5 - drauu: ^0.4 - focus-trap: ^7 - fuse.js: ^7 - idb-keyval: ^6 - jwt-decode: ^4 - nprogress: ^0.2 - qrcode: ^1.5 - sortablejs: ^1 - universal-cookie: ^7 - peerDependenciesMeta: - async-validator: - optional: true - axios: - optional: true - change-case: - optional: true - drauu: - optional: true - focus-trap: - optional: true - fuse.js: - optional: true - idb-keyval: - optional: true - jwt-decode: - optional: true - nprogress: - optional: true - qrcode: - optional: true - sortablejs: - optional: true - universal-cookie: - optional: true - '@vueuse/integrations@14.2.1': resolution: {integrity: sha512-2LIUpBi/67PoXJGqSDQUF0pgQWpNHh7beiA+KG2AbybcNm+pTGWT6oPGlBgUoDWmYwfeQqM/uzOHqcILpKL7nA==} peerDependencies: @@ -953,24 +1063,14 @@ packages: universal-cookie: optional: true - '@vueuse/metadata@12.8.2': - resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==} - '@vueuse/metadata@14.2.1': resolution: {integrity: sha512-1ButlVtj5Sb/HDtIy1HFr1VqCP4G6Ypqt5MAo0lCgjokrk2mvQKsK2uuy0vqu/Ks+sHfuHo0B9Y9jn9xKdjZsw==} - '@vueuse/shared@12.8.2': - resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==} - '@vueuse/shared@14.2.1': resolution: {integrity: sha512-shTJncjV9JTI4oVNyF1FQonetYAiTBd+Qj7cY89SWbXSkx7gyhrgtEdF2ZAVWS1S3SHlaROO6F2IesJxQEkZBw==} peerDependencies: vue: ^3.5.0 - algoliasearch@5.49.2: - resolution: {integrity: sha512-1K0wtDaRONwfhL4h8bbJ9qTjmY6rhGgRvvagXkMBsAOMNr+3Q2SffHECh9DIuNVrMA1JwA0zCwhyepgBZVakng==} - engines: {node: '>= 14.0.0'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1029,9 +1129,19 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - copy-anything@4.0.5: - resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} - engines: {node: '>=18'} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + cose-base@1.0.3: + resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} + + cose-base@2.2.0: + resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} @@ -1041,6 +1151,165 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + cytoscape-cose-bilkent@4.1.0: + resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape-fcose@2.2.0: + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.34.1: + resolution: {integrity: sha512-Lr0RvH9H75y9ar8h9Toy6u4lxRSCcxUq+hHcQ26sVWo6BnaQp1gwEZOYqwuYTZhyW7npyKnNLP8oJ2p1/3OZ7g==} + engines: {node: '>=0.10'} + + d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + + dagre-d3-es@7.0.14: + resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} + + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -1056,6 +1325,9 @@ packages: defu@6.1.7: resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + delaunator@5.1.0: + resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1067,8 +1339,8 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - emoji-regex-xs@1.0.0: - resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} + dompurify@3.4.11: + resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1085,8 +1357,11 @@ packages: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} - esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + es-toolkit@1.50.0: + resolution: {integrity: sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==} + + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} engines: {node: '>=18'} hasBin: true @@ -1148,6 +1423,9 @@ packages: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} engines: {node: '>=6.0'} + hachure-fill@0.5.2: + resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + hast-util-to-html@9.0.5: resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} @@ -1160,6 +1438,20 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + is-extendable@0.1.1: resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} engines: {node: '>=0.10.0'} @@ -1172,10 +1464,6 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - is-what@5.5.0: - resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} - engines: {node: '>=18'} - jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -1184,10 +1472,23 @@ packages: resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==} hasBin: true + katex@0.16.47: + resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} + hasBin: true + + khroma@2.1.0: + resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} + layout-base@1.0.2: + resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} + + layout-base@2.0.1: + resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + lightningcss-android-arm64@1.31.1: resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} engines: {node: '>= 12.0.0'} @@ -1265,6 +1566,9 @@ packages: linkify-it@5.0.1: resolution: {integrity: sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==} + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -1288,6 +1592,11 @@ packages: resolution: {integrity: sha512-MqIQVVkz+uGEHi3TsHx/czcxxCbRIL7sv5K5DnYw/tI+apY54IbPefV/cmgxp6LoJSEx/TqcHdLs/298afG5QQ==} engines: {node: '>=6'} + marked@16.4.2: + resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} + engines: {node: '>= 20'} + hasBin: true + mdast-util-from-markdown@2.0.3: resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} @@ -1312,6 +1621,9 @@ packages: medium-zoom@1.1.0: resolution: {integrity: sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==} + mermaid@11.16.1: + resolution: {integrity: sha512-TQsq6u22fAn3rek5VOubrhKPo1g5hwC3FXUN9hiyupTckcYiGuuKGkNQrKYwGJkXUxZdojwRG46gsSCFZMDp4g==} + micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -1389,41 +1701,56 @@ packages: minisearch@7.2.0: resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==} - mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + non-layered-tidy-tree-layout@2.0.2: + resolution: {integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==} + ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} - oniguruma-to-es@3.1.1: - resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==} + oniguruma-parser@0.12.2: + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} + + oniguruma-to-es@4.3.6: + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} oxfmt@0.36.0: resolution: {integrity: sha512-/ejJ+KoSW6J9bcNT9a9UtJSJNWhJ3yOLSBLbkoFHJs/8CZjmaZVZAJe4YgO1KMJlKpNQasrn/G9JQUEZI3p0EQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + + path-data-parser@0.1.0: + resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - perfect-debounce@1.0.0: - resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + perfect-debounce@2.1.0: + resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} + points-on-curve@0.2.0: + resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} + + points-on-path@0.2.1: + resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + postcss-selector-parser@6.0.10: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} engines: {node: '>=4'} @@ -1432,9 +1759,6 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} - preact@10.29.0: - resolution: {integrity: sha512-wSAGyk2bYR1c7t3SZ3jHcM6xy0lcBcDel6lODcs9ME6Th++Dx2KU+6D3HD8wMMKGA8Wpw7OMd3/4RGzYRpzwRg==} - pretty-bytes@7.1.0: resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==} engines: {node: '>=20'} @@ -1476,23 +1800,29 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + robust-predicates@3.0.3: + resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} rollup@4.59.0: resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - search-insights@2.17.3: - resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} + roughjs@4.6.6: + resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} section-matter@1.0.0: resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} engines: {node: '>=4'} - shiki@2.5.0: - resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==} + shiki@3.23.0: + resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==} source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} @@ -1501,10 +1831,6 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - speakingurl@14.0.1: - resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} - engines: {node: '>=0.10.0'} - sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -1523,12 +1849,11 @@ packages: resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} engines: {node: '>=0.10.0'} - superjson@2.2.6: - resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} - engines: {node: '>=16'} + stylis@4.4.0: + resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} - tabbable@6.4.0: - resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + tabbable@6.5.0: + resolution: {integrity: sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==} tailwindcss@4.2.1: resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} @@ -1537,8 +1862,12 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} - tinyglobby@0.2.16: - resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} tinypool@2.1.0: @@ -1554,14 +1883,27 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + ts-dedent@2.3.0: + resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} + engines: {node: '>=6.10'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + turbo@2.10.10: + resolution: {integrity: sha512-/90KTW+USzvYOPmafRZHVKLBsHXQ5810Ao/HdtJYAqguIhZ+XruS6eIUjqJUDtrSxaZYynNFht68qckGKAOWTA==} + hasBin: true + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - undici-types@7.19.2: - resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -1587,6 +1929,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@11.1.1: + resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} + hasBin: true + vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} @@ -1637,21 +1983,30 @@ packages: resolution: {integrity: sha512-m+rxyghF5INi8hBw0huFPx6+VvaX1tDGvw1H7FdXowaZJ3dcRY5ShgbmK1AQlmeOFMdd16H8WarhSHLPXF/2OA==} engines: {node: '>=18'} + vitepress-plugin-mermaid@2.0.17: + resolution: {integrity: sha512-IUzYpwf61GC6k0XzfmAmNrLvMi9TRrVRMsUyCA8KNXhg/mQ1VqWnO0/tBVPiX5UoKF1mDUwqn5QV4qAJl6JnUg==} + peerDependencies: + mermaid: 10 || 11 + vitepress: ^1.0.0 || ^1.0.0-alpha + vitepress-plugin-tabs@0.8.0: resolution: {integrity: sha512-86FWHAuS9XCyTMDpMd5MELwp3bQyITDf/+IdZ20+iYbB8TIR8yoCp08PkDHxi4WWSVsCZ3n1uUIC7YCVhMsI3A==} peerDependencies: vitepress: ^1.0.0 vue: ^3.5.0 - vitepress@1.6.4: - resolution: {integrity: sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==} + vitepress@2.0.0-alpha.16: + resolution: {integrity: sha512-w1nwsefDVIsje7BZr2tsKxkZutDGjG0YoQ2yxO7+a9tvYVqfljYbwj5LMYkPy8Tb7YbPwa22HtIhk62jbrvuEQ==} hasBin: true peerDependencies: markdown-it-mathjax3: ^4 - postcss: ^8.5.10 + oxc-minify: '*' + postcss: 8.5.15 peerDependenciesMeta: markdown-it-mathjax3: optional: true + oxc-minify: + optional: true postcss: optional: true @@ -1666,8 +2021,8 @@ packages: '@vue/composition-api': optional: true - vue@3.5.30: - resolution: {integrity: sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==} + vue@3.5.41: + resolution: {integrity: sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -1695,237 +2050,110 @@ packages: snapshots: - '@algolia/abtesting@1.15.2': - dependencies: - '@algolia/client-common': 5.49.2 - '@algolia/requester-browser-xhr': 5.49.2 - '@algolia/requester-fetch': 5.49.2 - '@algolia/requester-node-http': 5.49.2 - - '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.49.2)(algoliasearch@5.49.2)(search-insights@2.17.3)': - dependencies: - '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.49.2)(algoliasearch@5.49.2)(search-insights@2.17.3) - '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.49.2)(algoliasearch@5.49.2) - transitivePeerDependencies: - - '@algolia/client-search' - - algoliasearch - - search-insights - - '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.49.2)(algoliasearch@5.49.2)(search-insights@2.17.3)': - dependencies: - '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.49.2)(algoliasearch@5.49.2) - search-insights: 2.17.3 - transitivePeerDependencies: - - '@algolia/client-search' - - algoliasearch - - '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.49.2)(algoliasearch@5.49.2)': - dependencies: - '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.49.2)(algoliasearch@5.49.2) - '@algolia/client-search': 5.49.2 - algoliasearch: 5.49.2 - - '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.49.2)(algoliasearch@5.49.2)': - dependencies: - '@algolia/client-search': 5.49.2 - algoliasearch: 5.49.2 - - '@algolia/client-abtesting@5.49.2': - dependencies: - '@algolia/client-common': 5.49.2 - '@algolia/requester-browser-xhr': 5.49.2 - '@algolia/requester-fetch': 5.49.2 - '@algolia/requester-node-http': 5.49.2 - - '@algolia/client-analytics@5.49.2': - dependencies: - '@algolia/client-common': 5.49.2 - '@algolia/requester-browser-xhr': 5.49.2 - '@algolia/requester-fetch': 5.49.2 - '@algolia/requester-node-http': 5.49.2 - - '@algolia/client-common@5.49.2': {} - - '@algolia/client-insights@5.49.2': - dependencies: - '@algolia/client-common': 5.49.2 - '@algolia/requester-browser-xhr': 5.49.2 - '@algolia/requester-fetch': 5.49.2 - '@algolia/requester-node-http': 5.49.2 - - '@algolia/client-personalization@5.49.2': - dependencies: - '@algolia/client-common': 5.49.2 - '@algolia/requester-browser-xhr': 5.49.2 - '@algolia/requester-fetch': 5.49.2 - '@algolia/requester-node-http': 5.49.2 - - '@algolia/client-query-suggestions@5.49.2': - dependencies: - '@algolia/client-common': 5.49.2 - '@algolia/requester-browser-xhr': 5.49.2 - '@algolia/requester-fetch': 5.49.2 - '@algolia/requester-node-http': 5.49.2 - - '@algolia/client-search@5.49.2': + '@antfu/install-pkg@1.1.0': dependencies: - '@algolia/client-common': 5.49.2 - '@algolia/requester-browser-xhr': 5.49.2 - '@algolia/requester-fetch': 5.49.2 - '@algolia/requester-node-http': 5.49.2 + package-manager-detector: 1.8.0 + tinyexec: 1.3.0 - '@algolia/ingestion@1.49.2': - dependencies: - '@algolia/client-common': 5.49.2 - '@algolia/requester-browser-xhr': 5.49.2 - '@algolia/requester-fetch': 5.49.2 - '@algolia/requester-node-http': 5.49.2 - - '@algolia/monitoring@1.49.2': - dependencies: - '@algolia/client-common': 5.49.2 - '@algolia/requester-browser-xhr': 5.49.2 - '@algolia/requester-fetch': 5.49.2 - '@algolia/requester-node-http': 5.49.2 - - '@algolia/recommend@5.49.2': - dependencies: - '@algolia/client-common': 5.49.2 - '@algolia/requester-browser-xhr': 5.49.2 - '@algolia/requester-fetch': 5.49.2 - '@algolia/requester-node-http': 5.49.2 + '@babel/helper-string-parser@7.29.7': {} - '@algolia/requester-browser-xhr@5.49.2': - dependencies: - '@algolia/client-common': 5.49.2 + '@babel/helper-validator-identifier@7.29.7': {} - '@algolia/requester-fetch@5.49.2': + '@babel/parser@7.29.8': dependencies: - '@algolia/client-common': 5.49.2 + '@babel/types': 7.29.8 - '@algolia/requester-node-http@5.49.2': + '@babel/types@7.29.8': dependencies: - '@algolia/client-common': 5.49.2 - - '@babel/helper-string-parser@7.27.1': {} - - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 - '@babel/parser@7.29.0': - dependencies: - '@babel/types': 7.29.0 + '@braintree/sanitize-url@6.0.4': + optional: true - '@babel/types@7.29.0': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + '@braintree/sanitize-url@7.1.2': {} - '@docsearch/css@3.8.2': {} + '@chevrotain/types@11.1.2': {} '@docsearch/css@4.6.2': {} - '@docsearch/js@3.8.2(@algolia/client-search@5.49.2)(search-insights@2.17.3)': - dependencies: - '@docsearch/react': 3.8.2(@algolia/client-search@5.49.2)(search-insights@2.17.3) - preact: 10.29.0 - transitivePeerDependencies: - - '@algolia/client-search' - - '@types/react' - - react - - react-dom - - search-insights - '@docsearch/js@4.6.2': {} - '@docsearch/react@3.8.2(@algolia/client-search@5.49.2)(search-insights@2.17.3)': - dependencies: - '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.49.2)(algoliasearch@5.49.2)(search-insights@2.17.3) - '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.49.2)(algoliasearch@5.49.2) - '@docsearch/css': 3.8.2 - algoliasearch: 5.49.2 - optionalDependencies: - search-insights: 2.17.3 - transitivePeerDependencies: - - '@algolia/client-search' - '@docsearch/sidepanel-js@4.6.2': {} - '@esbuild/aix-ppc64@0.25.12': - optional: true - - '@esbuild/android-arm64@0.25.12': + '@esbuild/aix-ppc64@0.25.0': optional: true - '@esbuild/android-arm@0.25.12': + '@esbuild/android-arm64@0.25.0': optional: true - '@esbuild/android-x64@0.25.12': + '@esbuild/android-arm@0.25.0': optional: true - '@esbuild/darwin-arm64@0.25.12': + '@esbuild/android-x64@0.25.0': optional: true - '@esbuild/darwin-x64@0.25.12': + '@esbuild/darwin-arm64@0.25.0': optional: true - '@esbuild/freebsd-arm64@0.25.12': + '@esbuild/darwin-x64@0.25.0': optional: true - '@esbuild/freebsd-x64@0.25.12': + '@esbuild/freebsd-arm64@0.25.0': optional: true - '@esbuild/linux-arm64@0.25.12': + '@esbuild/freebsd-x64@0.25.0': optional: true - '@esbuild/linux-arm@0.25.12': + '@esbuild/linux-arm64@0.25.0': optional: true - '@esbuild/linux-ia32@0.25.12': + '@esbuild/linux-arm@0.25.0': optional: true - '@esbuild/linux-loong64@0.25.12': + '@esbuild/linux-ia32@0.25.0': optional: true - '@esbuild/linux-mips64el@0.25.12': + '@esbuild/linux-loong64@0.25.0': optional: true - '@esbuild/linux-ppc64@0.25.12': + '@esbuild/linux-mips64el@0.25.0': optional: true - '@esbuild/linux-riscv64@0.25.12': + '@esbuild/linux-ppc64@0.25.0': optional: true - '@esbuild/linux-s390x@0.25.12': + '@esbuild/linux-riscv64@0.25.0': optional: true - '@esbuild/linux-x64@0.25.12': + '@esbuild/linux-s390x@0.25.0': optional: true - '@esbuild/netbsd-arm64@0.25.12': + '@esbuild/linux-x64@0.25.0': optional: true - '@esbuild/netbsd-x64@0.25.12': + '@esbuild/netbsd-arm64@0.25.0': optional: true - '@esbuild/openbsd-arm64@0.25.12': + '@esbuild/netbsd-x64@0.25.0': optional: true - '@esbuild/openbsd-x64@0.25.12': + '@esbuild/openbsd-arm64@0.25.0': optional: true - '@esbuild/openharmony-arm64@0.25.12': + '@esbuild/openbsd-x64@0.25.0': optional: true - '@esbuild/sunos-x64@0.25.12': + '@esbuild/sunos-x64@0.25.0': optional: true - '@esbuild/win32-arm64@0.25.12': + '@esbuild/win32-arm64@0.25.0': optional: true - '@esbuild/win32-ia32@0.25.12': + '@esbuild/win32-ia32@0.25.0': optional: true - '@esbuild/win32-x64@0.25.12': + '@esbuild/win32-x64@0.25.0': optional: true '@floating-ui/core@1.7.5': @@ -1939,11 +2167,11 @@ snapshots: '@floating-ui/utils@0.2.11': {} - '@floating-ui/vue@1.1.11(vue@3.5.30)': + '@floating-ui/vue@1.1.11(vue@3.5.41(typescript@6.0.3))': dependencies: '@floating-ui/dom': 1.7.6 '@floating-ui/utils': 0.2.11 - vue-demi: 0.14.10(vue@3.5.30) + vue-demi: 0.14.10(vue@3.5.41(typescript@6.0.3)) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -1954,10 +2182,16 @@ snapshots: '@iconify/types@2.0.0': {} - '@iconify/vue@5.0.0(vue@3.5.30)': + '@iconify/utils@3.1.4': dependencies: + '@antfu/install-pkg': 1.1.0 '@iconify/types': 2.0.0 - vue: 3.5.30 + import-meta-resolve: 4.2.0 + + '@iconify/vue@5.0.0(vue@3.5.41(typescript@6.0.3))': + dependencies: + '@iconify/types': 2.0.0 + vue: 3.5.41(typescript@6.0.3) '@internationalized/date@3.12.1': dependencies: @@ -1986,6 +2220,21 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@mermaid-js/mermaid-mindmap@9.3.0': + dependencies: + '@braintree/sanitize-url': 6.0.4 + cytoscape: 3.34.1 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.34.1) + cytoscape-fcose: 2.2.0(cytoscape@3.34.1) + d3: 7.9.0 + khroma: 2.1.0 + non-layered-tidy-tree-layout: 2.0.2 + optional: true + + '@mermaid-js/parser@1.2.0': + dependencies: + '@chevrotain/types': 11.1.2 + '@oxfmt/binding-android-arm-eabi@0.36.0': optional: true @@ -2045,6 +2294,8 @@ snapshots: '@rive-app/canvas-lite@2.37.3': {} + '@rolldown/pluginutils@1.0.1': {} + '@rollup/rollup-android-arm-eabi@4.59.0': optional: true @@ -2120,40 +2371,38 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true - '@shikijs/core@2.5.0': + '@shikijs/core@3.23.0': dependencies: - '@shikijs/engine-javascript': 2.5.0 - '@shikijs/engine-oniguruma': 2.5.0 - '@shikijs/types': 2.5.0 + '@shikijs/types': 3.23.0 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 hast-util-to-html: 9.0.5 - '@shikijs/engine-javascript@2.5.0': + '@shikijs/engine-javascript@3.23.0': dependencies: - '@shikijs/types': 2.5.0 + '@shikijs/types': 3.23.0 '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 3.1.1 + oniguruma-to-es: 4.3.6 - '@shikijs/engine-oniguruma@2.5.0': + '@shikijs/engine-oniguruma@3.23.0': dependencies: - '@shikijs/types': 2.5.0 + '@shikijs/types': 3.23.0 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@2.5.0': + '@shikijs/langs@3.23.0': dependencies: - '@shikijs/types': 2.5.0 + '@shikijs/types': 3.23.0 - '@shikijs/themes@2.5.0': + '@shikijs/themes@3.23.0': dependencies: - '@shikijs/types': 2.5.0 + '@shikijs/types': 3.23.0 - '@shikijs/transformers@2.5.0': + '@shikijs/transformers@3.23.0': dependencies: - '@shikijs/core': 2.5.0 - '@shikijs/types': 2.5.0 + '@shikijs/core': 3.23.0 + '@shikijs/types': 3.23.0 - '@shikijs/types@2.5.0': + '@shikijs/types@3.23.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -2230,19 +2479,154 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 4.2.1 - '@tailwindcss/vite@4.2.1(vite@6.4.3(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1))': + '@tailwindcss/vite@4.2.1(vite@6.4.3(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1))': dependencies: '@tailwindcss/node': 4.2.1 '@tailwindcss/oxide': 4.2.1 tailwindcss: 4.2.1 - vite: 6.4.3(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1) + vite: 6.4.3(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1) '@tanstack/virtual-core@3.14.0': {} - '@tanstack/vue-virtual@3.13.24(vue@3.5.30)': + '@tanstack/vue-virtual@3.13.24(vue@3.5.41(typescript@6.0.3))': dependencies: '@tanstack/virtual-core': 3.14.0 - vue: 3.5.30 + vue: 3.5.41(typescript@6.0.3) + + '@turbo/darwin-64@2.10.10': + optional: true + + '@turbo/darwin-arm64@2.10.10': + optional: true + + '@turbo/linux-64@2.10.10': + optional: true + + '@turbo/linux-arm64@2.10.10': + optional: true + + '@turbo/windows-64@2.10.10': + optional: true + + '@turbo/windows-arm64@2.10.10': + optional: true + + '@types/d3-array@3.2.2': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.1': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.4': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.1 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 '@types/debug@4.1.13': dependencies: @@ -2250,6 +2634,8 @@ snapshots: '@types/estree@1.0.8': {} + '@types/geojson@7946.0.16': {} + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 @@ -2269,9 +2655,12 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@25.6.0': + '@types/node@25.9.5': dependencies: - undici-types: 7.19.2 + undici-types: 7.24.6 + + '@types/trusted-types@2.0.7': + optional: true '@types/unist@3.0.3': {} @@ -2279,30 +2668,36 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-vue@5.2.4(vite@6.4.3(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1))(vue@3.5.30)': + '@upsetjs/venn.js@2.0.0': + optionalDependencies: + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + '@vitejs/plugin-vue@6.0.8(vite@6.4.3(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1))(vue@3.5.41(typescript@6.0.3))': dependencies: - vite: 6.4.3(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1) - vue: 3.5.30 + '@rolldown/pluginutils': 1.0.1 + vite: 6.4.3(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1) + vue: 3.5.41(typescript@6.0.3) - '@voidzero-dev/vitepress-theme@4.8.4(focus-trap@7.8.0)(vite@6.4.3(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1))(vitepress@1.6.4(@algolia/client-search@5.49.2)(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(search-insights@2.17.3))(vue@3.5.30)': + '@voidzero-dev/vitepress-theme@4.8.4(focus-trap@7.8.0)(vite@6.4.3(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1))(vitepress@2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3))(vue@3.5.41(typescript@6.0.3))': dependencies: '@docsearch/css': 4.6.2 '@docsearch/js': 4.6.2 '@docsearch/sidepanel-js': 4.6.2 - '@iconify/vue': 5.0.0(vue@3.5.30) + '@iconify/vue': 5.0.0(vue@3.5.41(typescript@6.0.3)) '@rive-app/canvas-lite': 2.37.3 '@tailwindcss/typography': 0.5.19(tailwindcss@4.2.1) - '@tailwindcss/vite': 4.2.1(vite@6.4.3(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1)) - '@vue/shared': 3.5.30 - '@vueuse/core': 14.2.1(vue@3.5.30) - '@vueuse/integrations': 14.2.1(focus-trap@7.8.0)(vue@3.5.30) - '@vueuse/shared': 14.2.1(vue@3.5.30) + '@tailwindcss/vite': 4.2.1(vite@6.4.3(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)) + '@vue/shared': 3.5.41 + '@vueuse/core': 14.2.1(vue@3.5.41(typescript@6.0.3)) + '@vueuse/integrations': 14.2.1(focus-trap@7.8.0)(vue@3.5.41(typescript@6.0.3)) + '@vueuse/shared': 14.2.1(vue@3.5.41(typescript@6.0.3)) mark.js: 8.11.1 minisearch: 7.2.0 - reka-ui: 2.9.6(vue@3.5.30) + reka-ui: 2.9.6(vue@3.5.41(typescript@6.0.3)) tailwindcss: 4.2.1 - vitepress: 1.6.4(@algolia/client-search@5.49.2)(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(search-insights@2.17.3) - vue: 3.5.30 + vitepress: 2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3) + vue: 3.5.41(typescript@6.0.3) transitivePeerDependencies: - '@vue/composition-api' - async-validator @@ -2319,142 +2714,93 @@ snapshots: - universal-cookie - vite - '@vue/compiler-core@3.5.30': + '@vue/compiler-core@3.5.41': dependencies: - '@babel/parser': 7.29.0 - '@vue/shared': 3.5.30 + '@babel/parser': 7.29.8 + '@vue/shared': 3.5.41 entities: 7.0.1 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.30': + '@vue/compiler-dom@3.5.41': dependencies: - '@vue/compiler-core': 3.5.30 - '@vue/shared': 3.5.30 + '@vue/compiler-core': 3.5.41 + '@vue/shared': 3.5.41 - '@vue/compiler-sfc@3.5.30': + '@vue/compiler-sfc@3.5.41': dependencies: - '@babel/parser': 7.29.0 - '@vue/compiler-core': 3.5.30 - '@vue/compiler-dom': 3.5.30 - '@vue/compiler-ssr': 3.5.30 - '@vue/shared': 3.5.30 + '@babel/parser': 7.29.8 + '@vue/compiler-core': 3.5.41 + '@vue/compiler-dom': 3.5.41 + '@vue/compiler-ssr': 3.5.41 + '@vue/shared': 3.5.41 estree-walker: 2.0.2 magic-string: 0.30.21 postcss: 8.5.15 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.30': + '@vue/compiler-ssr@3.5.41': dependencies: - '@vue/compiler-dom': 3.5.30 - '@vue/shared': 3.5.30 + '@vue/compiler-dom': 3.5.41 + '@vue/shared': 3.5.41 - '@vue/devtools-api@7.7.9': + '@vue/devtools-api@8.2.1': dependencies: - '@vue/devtools-kit': 7.7.9 + '@vue/devtools-kit': 8.2.1 - '@vue/devtools-kit@7.7.9': + '@vue/devtools-kit@8.2.1': dependencies: - '@vue/devtools-shared': 7.7.9 + '@vue/devtools-shared': 8.2.1 birpc: 2.9.0 hookable: 5.5.3 - mitt: 3.0.1 - perfect-debounce: 1.0.0 - speakingurl: 14.0.1 - superjson: 2.2.6 + perfect-debounce: 2.1.0 - '@vue/devtools-shared@7.7.9': - dependencies: - rfdc: 1.4.1 + '@vue/devtools-shared@8.2.1': {} - '@vue/reactivity@3.5.30': + '@vue/reactivity@3.5.41': dependencies: - '@vue/shared': 3.5.30 + '@vue/shared': 3.5.41 - '@vue/runtime-core@3.5.30': + '@vue/runtime-core@3.5.41': dependencies: - '@vue/reactivity': 3.5.30 - '@vue/shared': 3.5.30 + '@vue/reactivity': 3.5.41 + '@vue/shared': 3.5.41 - '@vue/runtime-dom@3.5.30': + '@vue/runtime-dom@3.5.41': dependencies: - '@vue/reactivity': 3.5.30 - '@vue/runtime-core': 3.5.30 - '@vue/shared': 3.5.30 + '@vue/reactivity': 3.5.41 + '@vue/runtime-core': 3.5.41 + '@vue/shared': 3.5.41 csstype: 3.2.3 - '@vue/server-renderer@3.5.30(vue@3.5.30)': + '@vue/server-renderer@3.5.41': dependencies: - '@vue/compiler-ssr': 3.5.30 - '@vue/shared': 3.5.30 - vue: 3.5.30 + '@vue/compiler-ssr': 3.5.41 + '@vue/runtime-dom': 3.5.41 + '@vue/shared': 3.5.41 - '@vue/shared@3.5.30': {} - - '@vueuse/core@12.8.2': - dependencies: - '@types/web-bluetooth': 0.0.21 - '@vueuse/metadata': 12.8.2 - '@vueuse/shared': 12.8.2 - vue: 3.5.30 - transitivePeerDependencies: - - typescript + '@vue/shared@3.5.41': {} - '@vueuse/core@14.2.1(vue@3.5.30)': + '@vueuse/core@14.2.1(vue@3.5.41(typescript@6.0.3))': dependencies: '@types/web-bluetooth': 0.0.21 '@vueuse/metadata': 14.2.1 - '@vueuse/shared': 14.2.1(vue@3.5.30) - vue: 3.5.30 - - '@vueuse/integrations@12.8.2(focus-trap@7.8.0)': - dependencies: - '@vueuse/core': 12.8.2 - '@vueuse/shared': 12.8.2 - vue: 3.5.30 - optionalDependencies: - focus-trap: 7.8.0 - transitivePeerDependencies: - - typescript + '@vueuse/shared': 14.2.1(vue@3.5.41(typescript@6.0.3)) + vue: 3.5.41(typescript@6.0.3) - '@vueuse/integrations@14.2.1(focus-trap@7.8.0)(vue@3.5.30)': + '@vueuse/integrations@14.2.1(focus-trap@7.8.0)(vue@3.5.41(typescript@6.0.3))': dependencies: - '@vueuse/core': 14.2.1(vue@3.5.30) - '@vueuse/shared': 14.2.1(vue@3.5.30) - vue: 3.5.30 + '@vueuse/core': 14.2.1(vue@3.5.41(typescript@6.0.3)) + '@vueuse/shared': 14.2.1(vue@3.5.41(typescript@6.0.3)) + vue: 3.5.41(typescript@6.0.3) optionalDependencies: focus-trap: 7.8.0 - '@vueuse/metadata@12.8.2': {} - '@vueuse/metadata@14.2.1': {} - '@vueuse/shared@12.8.2': - dependencies: - vue: 3.5.30 - transitivePeerDependencies: - - typescript - - '@vueuse/shared@14.2.1(vue@3.5.30)': - dependencies: - vue: 3.5.30 - - algoliasearch@5.49.2: + '@vueuse/shared@14.2.1(vue@3.5.41(typescript@6.0.3))': dependencies: - '@algolia/abtesting': 1.15.2 - '@algolia/client-abtesting': 5.49.2 - '@algolia/client-analytics': 5.49.2 - '@algolia/client-common': 5.49.2 - '@algolia/client-insights': 5.49.2 - '@algolia/client-personalization': 5.49.2 - '@algolia/client-query-suggestions': 5.49.2 - '@algolia/client-search': 5.49.2 - '@algolia/ingestion': 1.49.2 - '@algolia/monitoring': 1.49.2 - '@algolia/recommend': 5.49.2 - '@algolia/requester-browser-xhr': 5.49.2 - '@algolia/requester-fetch': 5.49.2 - '@algolia/requester-node-http': 5.49.2 + vue: 3.5.41(typescript@6.0.3) ansi-regex@5.0.1: {} @@ -2504,14 +2850,208 @@ snapshots: comma-separated-tokens@2.0.3: {} - copy-anything@4.0.5: + commander@7.2.0: {} + + commander@8.3.0: {} + + cose-base@1.0.3: + dependencies: + layout-base: 1.0.2 + + cose-base@2.2.0: dependencies: - is-what: 5.5.0 + layout-base: 2.0.1 cssesc@3.0.0: {} csstype@3.2.3: {} + cytoscape-cose-bilkent@4.1.0(cytoscape@3.34.1): + dependencies: + cose-base: 1.0.3 + cytoscape: 3.34.1 + + cytoscape-fcose@2.2.0(cytoscape@3.34.1): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.34.1 + + cytoscape@3.34.1: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.1.0 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.2: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.2 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + dagre-d3-es@7.0.14: + dependencies: + d3: 7.9.0 + lodash-es: 4.18.1 + + dayjs@1.11.21: {} + debug@4.4.3: dependencies: ms: 2.1.3 @@ -2522,6 +3062,10 @@ snapshots: defu@6.1.7: {} + delaunator@5.1.0: + dependencies: + robust-predicates: 3.0.3 + dequal@2.0.3: {} detect-libc@2.1.2: {} @@ -2530,7 +3074,9 @@ snapshots: dependencies: dequal: 2.0.3 - emoji-regex-xs@1.0.0: {} + dompurify@3.4.11: + optionalDependencies: + '@types/trusted-types': 2.0.7 emoji-regex@8.0.0: {} @@ -2543,34 +3089,35 @@ snapshots: entities@7.0.1: {} - esbuild@0.25.12: + es-toolkit@1.50.0: {} + + esbuild@0.25.0: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.12 - '@esbuild/android-arm': 0.25.12 - '@esbuild/android-arm64': 0.25.12 - '@esbuild/android-x64': 0.25.12 - '@esbuild/darwin-arm64': 0.25.12 - '@esbuild/darwin-x64': 0.25.12 - '@esbuild/freebsd-arm64': 0.25.12 - '@esbuild/freebsd-x64': 0.25.12 - '@esbuild/linux-arm': 0.25.12 - '@esbuild/linux-arm64': 0.25.12 - '@esbuild/linux-ia32': 0.25.12 - '@esbuild/linux-loong64': 0.25.12 - '@esbuild/linux-mips64el': 0.25.12 - '@esbuild/linux-ppc64': 0.25.12 - '@esbuild/linux-riscv64': 0.25.12 - '@esbuild/linux-s390x': 0.25.12 - '@esbuild/linux-x64': 0.25.12 - '@esbuild/netbsd-arm64': 0.25.12 - '@esbuild/netbsd-x64': 0.25.12 - '@esbuild/openbsd-arm64': 0.25.12 - '@esbuild/openbsd-x64': 0.25.12 - '@esbuild/openharmony-arm64': 0.25.12 - '@esbuild/sunos-x64': 0.25.12 - '@esbuild/win32-arm64': 0.25.12 - '@esbuild/win32-ia32': 0.25.12 - '@esbuild/win32-x64': 0.25.12 + '@esbuild/aix-ppc64': 0.25.0 + '@esbuild/android-arm': 0.25.0 + '@esbuild/android-arm64': 0.25.0 + '@esbuild/android-x64': 0.25.0 + '@esbuild/darwin-arm64': 0.25.0 + '@esbuild/darwin-x64': 0.25.0 + '@esbuild/freebsd-arm64': 0.25.0 + '@esbuild/freebsd-x64': 0.25.0 + '@esbuild/linux-arm': 0.25.0 + '@esbuild/linux-arm64': 0.25.0 + '@esbuild/linux-ia32': 0.25.0 + '@esbuild/linux-loong64': 0.25.0 + '@esbuild/linux-mips64el': 0.25.0 + '@esbuild/linux-ppc64': 0.25.0 + '@esbuild/linux-riscv64': 0.25.0 + '@esbuild/linux-s390x': 0.25.0 + '@esbuild/linux-x64': 0.25.0 + '@esbuild/netbsd-arm64': 0.25.0 + '@esbuild/netbsd-x64': 0.25.0 + '@esbuild/openbsd-arm64': 0.25.0 + '@esbuild/openbsd-x64': 0.25.0 + '@esbuild/sunos-x64': 0.25.0 + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 escalade@3.2.0: {} @@ -2590,13 +3137,13 @@ snapshots: dependencies: format: 0.2.2 - fdir@6.5.0(picomatch@4.0.4): + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: - picomatch: 4.0.4 + picomatch: 4.0.5 focus-trap@7.8.0: dependencies: - tabbable: 6.4.0 + tabbable: 6.5.0 format@0.2.2: {} @@ -2614,6 +3161,8 @@ snapshots: section-matter: 1.0.0 strip-bom-string: 1.0.0 + hachure-fill@0.5.2: {} + hast-util-to-html@9.0.5: dependencies: '@types/hast': 3.0.4 @@ -2636,14 +3185,22 @@ snapshots: html-void-elements@3.0.0: {} + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + import-meta-resolve@4.2.0: {} + + internmap@1.0.1: {} + + internmap@2.0.3: {} + is-extendable@0.1.1: {} is-fullwidth-code-point@3.0.0: {} is-plain-obj@4.1.0: {} - is-what@5.5.0: {} - jiti@2.6.1: {} js-yaml@3.15.0: @@ -2651,8 +3208,18 @@ snapshots: argparse: 1.0.10 esprima: 4.0.1 + katex@0.16.47: + dependencies: + commander: 8.3.0 + + khroma@2.1.0: {} + kind-of@6.0.3: {} + layout-base@1.0.2: {} + + layout-base@2.0.1: {} + lightningcss-android-arm64@1.31.1: optional: true @@ -2706,11 +3273,13 @@ snapshots: dependencies: uc.micro: 2.1.0 + lodash-es@4.18.1: {} + longest-streak@3.1.0: {} - lucide-vue-next@0.577.0(vue@3.5.30): + lucide-vue-next@0.577.0(vue@3.5.41(typescript@6.0.3)): dependencies: - vue: 3.5.30 + vue: 3.5.41(typescript@6.0.3) magic-string@0.30.21: dependencies: @@ -2729,6 +3298,8 @@ snapshots: markdown-title@1.0.2: {} + marked@16.4.2: {} + mdast-util-from-markdown@2.0.3: dependencies: '@types/mdast': 4.0.4 @@ -2794,6 +3365,30 @@ snapshots: medium-zoom@1.1.0: {} + mermaid@11.16.1: + dependencies: + '@braintree/sanitize-url': 7.1.2 + '@iconify/utils': 3.1.4 + '@mermaid-js/parser': 1.2.0 + '@types/d3': 7.4.3 + '@upsetjs/venn.js': 2.0.0 + cytoscape: 3.34.1 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.34.1) + cytoscape-fcose: 2.2.0(cytoscape@3.34.1) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.14 + dayjs: 1.11.21 + dompurify: 3.4.11 + es-toolkit: 1.50.0 + katex: 0.16.47 + khroma: 2.1.0 + marked: 16.4.2 + roughjs: 4.6.6 + stylis: 4.4.0 + ts-dedent: 2.3.0 + uuid: 11.1.1 + micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.3.0 @@ -2944,17 +3539,20 @@ snapshots: minisearch@7.2.0: {} - mitt@3.0.1: {} - ms@2.1.3: {} - nanoid@3.3.12: {} + nanoid@3.3.18: {} + + non-layered-tidy-tree-layout@2.0.2: + optional: true ohash@2.0.11: {} - oniguruma-to-es@3.1.1: + oniguruma-parser@0.12.2: {} + + oniguruma-to-es@4.3.6: dependencies: - emoji-regex-xs: 1.0.0 + oniguruma-parser: 0.12.2 regex: 6.1.0 regex-recursion: 6.0.2 @@ -2982,13 +3580,24 @@ snapshots: '@oxfmt/binding-win32-ia32-msvc': 0.36.0 '@oxfmt/binding-win32-x64-msvc': 0.36.0 + package-manager-detector@1.8.0: {} + + path-data-parser@0.1.0: {} + path-to-regexp@6.3.0: {} - perfect-debounce@1.0.0: {} + perfect-debounce@2.1.0: {} picocolors@1.1.1: {} - picomatch@4.0.4: {} + picomatch@4.0.5: {} + + points-on-curve@0.2.0: {} + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 postcss-selector-parser@6.0.10: dependencies: @@ -2997,12 +3606,10 @@ snapshots: postcss@8.5.15: dependencies: - nanoid: 3.3.12 + nanoid: 3.3.18 picocolors: 1.1.1 source-map-js: 1.2.1 - preact@10.29.0: {} - pretty-bytes@7.1.0: {} property-information@7.1.0: {} @@ -3019,19 +3626,19 @@ snapshots: dependencies: regex-utilities: 2.3.0 - reka-ui@2.9.6(vue@3.5.30): + reka-ui@2.9.6(vue@3.5.41(typescript@6.0.3)): dependencies: '@floating-ui/dom': 1.7.6 - '@floating-ui/vue': 1.1.11(vue@3.5.30) + '@floating-ui/vue': 1.1.11(vue@3.5.41(typescript@6.0.3)) '@internationalized/date': 3.12.1 '@internationalized/number': 3.6.6 - '@tanstack/vue-virtual': 3.13.24(vue@3.5.30) - '@vueuse/core': 14.2.1(vue@3.5.30) - '@vueuse/shared': 14.2.1(vue@3.5.30) + '@tanstack/vue-virtual': 3.13.24(vue@3.5.41(typescript@6.0.3)) + '@vueuse/core': 14.2.1(vue@3.5.41(typescript@6.0.3)) + '@vueuse/shared': 14.2.1(vue@3.5.41(typescript@6.0.3)) aria-hidden: 1.2.6 defu: 6.1.7 ohash: 2.0.11 - vue: 3.5.30 + vue: 3.5.41(typescript@6.0.3) transitivePeerDependencies: - '@vue/composition-api' @@ -3070,7 +3677,7 @@ snapshots: require-directory@2.1.1: {} - rfdc@1.4.1: {} + robust-predicates@3.0.3: {} rollup@4.59.0: dependencies: @@ -3103,21 +3710,30 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.59.0 fsevents: 2.3.3 - search-insights@2.17.3: {} + roughjs@4.6.6: + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + + rw@1.3.3: {} + + safer-buffer@2.1.2: {} section-matter@1.0.0: dependencies: extend-shallow: 2.0.1 kind-of: 6.0.3 - shiki@2.5.0: + shiki@3.23.0: dependencies: - '@shikijs/core': 2.5.0 - '@shikijs/engine-javascript': 2.5.0 - '@shikijs/engine-oniguruma': 2.5.0 - '@shikijs/langs': 2.5.0 - '@shikijs/themes': 2.5.0 - '@shikijs/types': 2.5.0 + '@shikijs/core': 3.23.0 + '@shikijs/engine-javascript': 3.23.0 + '@shikijs/engine-oniguruma': 3.23.0 + '@shikijs/langs': 3.23.0 + '@shikijs/themes': 3.23.0 + '@shikijs/types': 3.23.0 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -3125,8 +3741,6 @@ snapshots: space-separated-tokens@2.0.2: {} - speakingurl@14.0.1: {} - sprintf-js@1.0.3: {} string-width@4.2.3: @@ -3146,20 +3760,20 @@ snapshots: strip-bom-string@1.0.0: {} - superjson@2.2.6: - dependencies: - copy-anything: 4.0.5 + stylis@4.4.0: {} - tabbable@6.4.0: {} + tabbable@6.5.0: {} tailwindcss@4.2.1: {} tapable@2.3.0: {} - tinyglobby@0.2.16: + tinyexec@1.3.0: {} + + tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 tinypool@2.1.0: {} @@ -3169,11 +3783,24 @@ snapshots: trough@2.2.0: {} + ts-dedent@2.3.0: {} + tslib@2.8.1: {} + turbo@2.10.10: + optionalDependencies: + '@turbo/darwin-64': 2.10.10 + '@turbo/darwin-arm64': 2.10.10 + '@turbo/linux-64': 2.10.10 + '@turbo/linux-arm64': 2.10.10 + '@turbo/windows-64': 2.10.10 + '@turbo/windows-arm64': 2.10.10 + + typescript@6.0.3: {} + uc.micro@2.1.0: {} - undici-types@7.19.2: {} + undici-types@7.24.6: {} unified@11.0.5: dependencies: @@ -3216,6 +3843,8 @@ snapshots: util-deprecate@1.0.2: {} + uuid@11.1.1: {} + vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 @@ -3226,16 +3855,16 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@6.4.3(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1): + vite@6.4.3(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1): dependencies: - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + esbuild: 0.25.0 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 postcss: 8.5.15 rollup: 4.59.0 - tinyglobby: 0.2.16 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.5 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.31.1 @@ -3259,37 +3888,43 @@ snapshots: transitivePeerDependencies: - supports-color - vitepress-plugin-tabs@0.8.0(vitepress@1.6.4(@algolia/client-search@5.49.2)(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(search-insights@2.17.3))(vue@3.5.30): + vitepress-plugin-mermaid@2.0.17(mermaid@11.16.1)(vitepress@2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3)): + dependencies: + mermaid: 11.16.1 + vitepress: 2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3) + optionalDependencies: + '@mermaid-js/mermaid-mindmap': 9.3.0 + + vitepress-plugin-tabs@0.8.0(vitepress@2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3))(vue@3.5.41(typescript@6.0.3)): dependencies: - vitepress: 1.6.4(@algolia/client-search@5.49.2)(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(search-insights@2.17.3) - vue: 3.5.30 + vitepress: 2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3) + vue: 3.5.41(typescript@6.0.3) - vitepress@1.6.4(@algolia/client-search@5.49.2)(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(search-insights@2.17.3): + vitepress@2.0.0-alpha.16(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1)(postcss@8.5.15)(typescript@6.0.3): dependencies: - '@docsearch/css': 3.8.2 - '@docsearch/js': 3.8.2(@algolia/client-search@5.49.2)(search-insights@2.17.3) + '@docsearch/css': 4.6.2 + '@docsearch/js': 4.6.2 + '@docsearch/sidepanel-js': 4.6.2 '@iconify-json/simple-icons': 1.2.73 - '@shikijs/core': 2.5.0 - '@shikijs/transformers': 2.5.0 - '@shikijs/types': 2.5.0 + '@shikijs/core': 3.23.0 + '@shikijs/transformers': 3.23.0 + '@shikijs/types': 3.23.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.2.4(vite@6.4.3(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1))(vue@3.5.30) - '@vue/devtools-api': 7.7.9 - '@vue/shared': 3.5.30 - '@vueuse/core': 12.8.2 - '@vueuse/integrations': 12.8.2(focus-trap@7.8.0) + '@vitejs/plugin-vue': 6.0.8(vite@6.4.3(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1))(vue@3.5.41(typescript@6.0.3)) + '@vue/devtools-api': 8.2.1 + '@vue/shared': 3.5.41 + '@vueuse/core': 14.2.1(vue@3.5.41(typescript@6.0.3)) + '@vueuse/integrations': 14.2.1(focus-trap@7.8.0)(vue@3.5.41(typescript@6.0.3)) focus-trap: 7.8.0 mark.js: 8.11.1 minisearch: 7.2.0 - shiki: 2.5.0 - vite: 6.4.3(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.31.1) - vue: 3.5.30 + shiki: 3.23.0 + vite: 6.4.3(@types/node@25.9.5)(jiti@2.6.1)(lightningcss@1.31.1) + vue: 3.5.41(typescript@6.0.3) optionalDependencies: postcss: 8.5.15 transitivePeerDependencies: - - '@algolia/client-search' - '@types/node' - - '@types/react' - async-validator - axios - change-case @@ -3302,11 +3937,8 @@ snapshots: - lightningcss - nprogress - qrcode - - react - - react-dom - sass - sass-embedded - - search-insights - sortablejs - stylus - sugarss @@ -3316,17 +3948,19 @@ snapshots: - universal-cookie - yaml - vue-demi@0.14.10(vue@3.5.30): + vue-demi@0.14.10(vue@3.5.41(typescript@6.0.3)): dependencies: - vue: 3.5.30 + vue: 3.5.41(typescript@6.0.3) - vue@3.5.30: + vue@3.5.41(typescript@6.0.3): dependencies: - '@vue/compiler-dom': 3.5.30 - '@vue/compiler-sfc': 3.5.30 - '@vue/runtime-dom': 3.5.30 - '@vue/server-renderer': 3.5.30(vue@3.5.30) - '@vue/shared': 3.5.30 + '@vue/compiler-dom': 3.5.41 + '@vue/compiler-sfc': 3.5.41 + '@vue/runtime-dom': 3.5.41 + '@vue/server-renderer': 3.5.41 + '@vue/shared': 3.5.41 + optionalDependencies: + typescript: 6.0.3 wrap-ansi@7.0.0: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4142cb87..9f6d2b28 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,10 +1,45 @@ -allowBuilds: - esbuild: true - vue-demi: true -ignoredBuiltDependencies: - - esbuild +# pnpm workspace configuration (also holds the settings that used to live in +# package.json's "pnpm" field — pnpm reads overrides / peerDependencyRules / +# allowBuilds from here). + +packages: + - apps/* + - packages/* + +# Single source of truth for dependency versions shared by the apps and the theme +# package. Reference them with "catalog:" so every workspace package resolves the +# exact same copy (a second copy of vue / vitepress / @voidzero-dev/vitepress-theme +# would break the theme). +catalog: + "@types/node": ^25.9.5 + "@voidzero-dev/vitepress-theme": ^4.8.4 + lucide-vue-next: ^0.577.0 + medium-zoom: ^1.1.0 + mermaid: ^11.15.0 + typescript: ^6.0.3 + vitepress: 2.0.0-alpha.16 + vitepress-plugin-llms: ^1.13.1 + vitepress-plugin-mermaid: ^2.0.17 + vitepress-plugin-tabs: ^0.8.0 + vue: ^3.5.41 + overrides: - esbuild: ^0.25.0 + # Toolchain pins for VitePress 2 alpha compatibility. + rollup: 4.59.0 + lodash-es: 4.18.1 + esbuild: 0.25.0 + dompurify: 3.4.11 vite: 6.4.3 - postcss: ^8.5.10 - js-yaml@<3.15.0: 3.15.0 + postcss: 8.5.15 + uuid: 11.1.1 + js-yaml: 3.15.0 + +peerDependencyRules: + allowedVersions: + vitepress: "2" + +# Build scripts left disabled (neither was ever approved, and both sites build +# fine without them). Flip to true to allow. +allowBuilds: + esbuild: false + vue-demi: false diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 00000000..2d3bc081 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "jsx": "preserve", + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "skipLibCheck": true, + "ignoreDeprecations": "6.0", + "allowArbitraryExtensions": true, + "noEmit": true, + "lib": ["ESNext", "DOM"], + "types": ["vitepress/client", "node", "vue"], + "paths": { + "@voidzero-dev/vitepress-theme": ["./packages/theme/src/types/voidzero-theme.ts"], + "@vp-default/*": [ + "${configDir}/node_modules/@voidzero-dev/vitepress-theme/src/components/vitepress-default/*" + ], + "@vp-composables/*": [ + "${configDir}/node_modules/@voidzero-dev/vitepress-theme/src/composables/vitepress-default/*" + ], + "@vp-support/*": [ + "${configDir}/node_modules/@voidzero-dev/vitepress-theme/src/support/vitepress-default/*" + ], + "@components/*": ["${configDir}/node_modules/@voidzero-dev/vitepress-theme/src/components/*"] + } + } +} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 3d7b5300..00000000 --- a/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "node", - "types": ["node", "vitepress/client"], - "jsx": "preserve", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true - }, - "include": ["docs/.vitepress/**/*.ts", "docs/.vitepress/**/*.vue", "docs/.vitepress/**/*.d.ts"], - "exclude": ["node_modules"] -} diff --git a/turbo.json b/turbo.json new file mode 100644 index 00000000..5e219b35 --- /dev/null +++ b/turbo.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://turborepo.dev/schema.json", + "ui": "stream", + "globalDependencies": ["pnpm-workspace.yaml", "tsconfig.base.json"], + "tasks": { + "build": { + "dependsOn": ["^build"], + "inputs": ["$TURBO_DEFAULT$", ".env"], + "outputs": ["docs/.vitepress/dist/**"], + "env": ["VITE_*"] + }, + "check:types": { + "dependsOn": ["^check:types"], + "outputs": [] + }, + "dev": { + "cache": false, + "persistent": true, + "env": ["VITE_*"] + }, + "preview": { + "dependsOn": ["build"], + "cache": false, + "persistent": true, + "env": ["VITE_*"] + } + } +}