Skip to content

Plane#

Plane Cloud supports native work-item CSV import plus read-only sync authenticated by either a personal access token (PAT) or a Plane bot OAuth installation. Every import method uses the shared reviewable provider-import flow and stops at a TeamBoost review session; it does not write back to Plane.

Capabilities#

Capability Plane behavior Status
CSV_IMPORT Parse a native Plane Cloud work-item export into projected TeamBoost task drafts Built
SYNC Pull visible projects and work items from one Plane Cloud workspace using PAT or bot OAuth Built
USER_IMPORT Pull workspace members and referenced actors into the proposed mapping during API sync Built (sync-coupled)
WEBHOOK Verify Plane webhook callbacks Not built

PAT credential#

Plane uses one encrypted JSON credential per TeamBoost organization:

{
  "apiKey": "<Plane Cloud personal access token>",
  "workspaceSlug": "<Plane workspace slug>"
}

The object must contain exactly those two keys. The API key must use Plane's plane_api_ prefix, and the workspace slug may contain letters, numbers, _, and -. Store and verify the credential through the generic endpoints:

PUT  /api/v1/providers/plane/credentials?organization_id=<id>
POST /api/v1/providers/plane/credentials/verify?organization_id=<id>

The complete JSON value is encrypted at rest and is never returned. Verification makes two bounded, read-only probes: one project page and the workspace member directory. Requests stay on Plane Cloud's fixed https://api.plane.so/api/v1/ origin and send the PAT only in X-API-Key.

Start a reviewable API import with:

POST /api/v1/providers/plane/imports/sync?organization_id=<id>

The sync cursor-paginates every visible project, then reads that project's work-items, states, labels, and work-item types. It also reads the workspace member directory. The client uses only GET, uses Plane's current work-items resource, bounds pagination and retries, and fails loudly on repeated cursors, unexpected response shapes, or inaccessible credentials. It does not create, modify, archive, or delete Plane data.

USER_IMPORT is sync-coupled: non-bot workspace members and stable non-bot actors referenced by work items become the session's proposed user mappings. Members or expanded actors whose Plane is_bot field is explicitly true are excluded. A bot that created or was assigned a work item remains visible in the lossless source context but is not proposed as a TeamBoost user. There is no standalone user-import route.

Bot OAuth installation#

Plane's recommended bot-token flow is available through the generic OAuth endpoints. Configure one Plane app in the service environment:

PLANE_CLIENT_ID=<Plane app client id>
PLANE_CLIENT_SECRET=<Plane app client secret>
PLANE_REDIRECT_URI=https://<integration-host>/api/v1/providers/plane/oauth/callback
PLANE_SCOPES=projects:read projects.work_items:read projects.states:read projects.labels:read projects.work_item_types:read workspaces.members:read

The scope set is intentionally fixed to the six read permissions the current sync uses. Missing settings, write scopes, extra scopes, or a malformed redirect URI fail with oauth_not_configured.

Start the install under an organization-scoped user token:

GET /api/v1/providers/plane/oauth/authorize?organization_id=<id>

The response contains Plane's fixed app-consent URL with a signed, ten-minute state. The Setup URL frontend must redirect the installer to that URL. Plane then redirects to PLANE_REDIRECT_URI with app_installation_id, an unused bot flow code when supplied by Plane, and the signed state.

The generic callback accepts either the direct redirect:

GET /api/v1/providers/plane/oauth/callback?state=<state>&app_installation_id=<id>&code=<unused-code>

or an authenticated frontend relay:

{
  "state": "<signed-state>",
  "callbackParameters": {
    "app_installation_id": "<Plane installation id>"
  }
}

Plane callbacks must contain exactly one bounded app_installation_id; duplicate, missing, reserved, oversized, non-ASCII, or unexpected callback parameters are rejected before token exchange. targetAccountId is not accepted because the approved Plane installation determines the workspace.

The adapter exchanges the installation id only at https://api.plane.so/auth/o/token/, using HTTP Basic client authentication and grant_type=client_credentials, then resolves the exact workspace through Plane's fixed app-installation endpoint. It validates Bearer token type, expiry, granted scopes, active installation status, workspace id, and workspace slug. The access token, expiry, workspace binding, and installation refresh handle are stored as one encrypted OAUTH_TOKEN credential. No fake refresh token is created.

Bot tokens are refreshed within five minutes of expiry by repeating the same client-credentials exchange with the encrypted installation handle. Rotation must resolve to the same installation and workspace. Installing OAuth replaces the organization's prior Plane PAT; storing a PAT later replaces OAuth. There is only one active Plane credential per TeamBoost organization.

After installation, the existing credential verification and sync endpoints are unchanged. Plane REST calls send only Authorization: Bearer <bot-token>; PAT calls send only X-API-Key.

Export and upload#

Upload a native Plane Cloud work-item CSV export through:

POST /api/v1/providers/plane/imports/csv?organization_id=<id>

The current export contract requires these populated columns on every task: Identifier, Name, State Name, Priority, Created At, and Updated At. The observed native header also carries project and parent context, assignees, subscribers, descriptions, lifecycle dates, labels, cycles, modules, links, relations, comments, work-item type, worklogs, custom properties, customers, and initiatives.

The parser supports UTF-8 with or without a BOM, quoted multiline descriptions, and Plane's ISO-8601 UTC timestamps. Assignees, subscribers, labels, links, relations, comments, worklogs, customers, and initiatives must be JSON arrays when populated; custom properties must be a JSON object. Headers must be unique, and every row must match the header width. Invalid structures become row errors instead of being guessed.

Mapping behavior#

Plane value TeamBoost draft Behavior
Work-item UUID / CSV Identifier provider id The API UUID remains the provider id; the human key remains the identifier
Project identifier + API sequence_id identifier Combined as <PROJECT>-<sequence> when the API does not return a direct identifier
Name title Direct; titles shorter than two characters are blocking errors
Bug / Feature / Change / Improvement / Fix and supported editorial types type Mapped to the corresponding TeamBoost type; custom or missing values become OTHER with a note
API state group / known CSV state status Backlog/unstarted/started/completed/canceled groups map conservatively; Blocked maps to BLOCKED; unknown states remain unset with a note
none, low, medium, high, urgent priority Direct normalized mapping; custom values become UNKNOWN with a note
Description / description_stripped description Direct
Creator creatorId Resolved through the review mapping; a missing or unmapped creator is advisory and remains unset
One assignee ownerId Must resolve to a TeamBoost member or confirmation remains blocked
Multiple assignees no owner Confirmation remains blocked until one TeamBoost owner is chosen
Labels labels Names are preserved; API label ids resolve through the project label directory
Project name projectTitle Direct; Plane ids are not treated as TeamBoost project ids
Module name taskGroupTitle Direct, with a review note
Start / due dates plannedStartDate / plannedEndDate Direct UTC date semantics
Parent, cycle, estimate, relations, links, comments, worklogs, customers, initiatives descriptionJson.sourceRecord Preserved as reviewable provider context; no unsupported relationship is invented

Every CSV column and every field on a successfully normalized API work item is preserved in the session's lossless source record. The API record also keeps stable actor metadata used by the user-mapping and projection steps.

Review requirements#

Before confirming the session:

  1. Resolve every present assignee; choose one owner for a multiply assigned Plane work item.
  2. Review missing or unmapped creators.
  3. Review custom types, custom states, and custom priorities.
  4. Inspect module, cycle, estimate, parent, and relationship context preserved from Plane.

projectionErrors block confirmation. mappingNotes remain advisory.

Not built yet#

  • Plane webhook verification and event processing.
  • Standalone user-directory import.
  • Incremental sync state or per-project selection; sync currently reads all projects visible to the configured workspace credential.
  • Write-back to Plane.

Automated tests cover the bot exchange, callback and state rejection paths, encrypted replacement, near-expiry rotation, workspace mismatch refusal, and the same verification/sync route. A genuine Plane Cloud staged handshake also proved signed-state echo, installation/workspace binding, encrypted credential storage, token rotation, credential verification, and GET-only sync. A stable testing-deployment smoke remains required before release.