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:
- Resolve every present assignee; choose one owner for a multiply assigned Plane work item.
- Review missing or unmapped creators.
- Review custom types, custom states, and custom priorities.
- 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.