Skip to content

Project files ​

plane init and plane clone create a project folder like this:

text
myproj/
├── plane.yaml               # project config
├── workspace.yaml           # snapshot of workspace features, written by init
├── schema/
│   ├── features.yaml
│   ├── types.yaml
│   ├── states.yaml
│   ├── workflows.yaml
│   ├── labels.yaml
│   ├── members.yaml
│   ├── workitem_templates.yaml
│   └── page_templates.yaml
├── work/
│   ├── workitems.yaml
│   ├── cycles.yaml
│   ├── modules.yaml
│   ├── milestones.yaml
│   └── pages.yaml
├── .plane/
│   └── state.json           # sync state, git-ignored
└── .gitignore               # ignores .plane/

Commit the YAML. Don't commit .plane/. After a fresh git clone of your config repo, run plane schema import to reconnect local names to their remote IDs before you push.

plane.yaml ​

yaml
type: project
workspace: myteam
connection: connection-1 # optional: which saved connection to use
project:
  key: MYPROJ # max 10 characters, A-Z and 0-9
  name: Myproj
  description: ""
  network: public # public | private
  timezone: UTC # IANA name
  uuid: <written after the first push>
defaults:
  type: Story
api_url: https://api.plane.so # optional; set for self-hosted
schema:
  extends: ../shared/schema # optional: inherit another schema folder
template: default # template used at init or upgrade
KeyMeaning
workspaceRequired. Workspace slug.
connectionOptional connection ID from plane auth list-connections. This is what selects credentials for push, pull, diff and the other project commands. See Which connection a command uses.
project.keyRequired. The Plane project identifier, used as the prefix of work item IDs such as MYPROJ-12. plane init upper-cases it, strips other characters, and rejects keys longer than 10 characters.
project.name, description, network, timezoneProject metadata, pushed by plane schema push.
project.uuidFilled in once the project exists in Plane. Don't set it by hand.
api_urlPlane server base URL. Defaults to https://api.plane.so. Left at the default, the connection's server URL is used.
defaults.typeRead but not used in v0.5.2. A work item with no type is sent as task.
schema.extendsRelative path to a parent schema folder to inherit from.
templateThe template source, stored as typed. init records default; plane upgrade rewrites it.

Schema files ​

Schema files describe the project's structure. Files can only reference names defined elsewhere in the schema: workflows list states from states.yaml, and work items use type, state and label names from the schema.

schema/features.yaml ​

Turns project features on or off. Changes apply on the next plane schema push.

yaml
features:
  cycles: true
  modules: true
  pages: true
  views: true
  intakes: false
  epics: false
  work_item_types: false
  workflows: false
  parallel_cycles: false
  manually_start_end_cycles: false
  project_updates: false
  time_tracking: false

The values above are the built-in template's defaults. features.yaml also gates data: with cycles: false, modules: false or pages: false, that section isn't pushed even if its file in work/ has content.

schema/states.yaml ​

Each key is a state name. Every state belongs to one of five groups: backlog, unstarted, started, completed or cancelled.

yaml
states:
  Backlog:
    group: backlog
    color: "#60646C"
    allow_issue_creation: true
    is_default: true
  In Progress:
    group: started
    color: "#F59E0B"
  Done:
    group: completed
    color: "#46A758"
  Cancelled:
    group: cancelled
    color: "#9AA4BC"
FieldMeaning
groupState group. Defaults to unstarted.
colorHex color.
allow_issue_creationWhether work items can be created directly in this state. Default true.
is_defaultThe state new work items get. Set it on one state only.

The built-in template ships Icebox, Backlog, Ready, In Progress, In Review, Testing, Done and Cancelled. Don't delete a state that a workflow uses.

schema/labels.yaml ​

A list of labels. id is written back after the first push.

yaml
labels:
  - name: Frontend
    color: "#3b82f6"
  - name: Backend
    color: "#10b981"

The built-in template ships Frontend, Backend, Infrastructure, Documentation and Urgent.

schema/workflows.yaml ​

Workflows restrict which state changes are allowed for given work item types, and can require approval for a change. They need workflows: true in features.yaml. Plane creates a default workflow itself when the feature is on, so define only your own workflows here.

Each key under states is a state from states.yaml. A state's flow_type is transition (move freely) or approval (needs sign-off), and to lists the states it can move to. Use {} for a target with no extra settings, and for a state with no outgoing transitions.

yaml
workflows:
  Feature Workflow:
    description: Feature development with review
    is_active: true
    work_item_types:
      - Feature
    states:
      Backlog:
        flow_type: transition
        to:
          - In Progress: {}
      In Progress:
        flow_type: approval
        to:
          - Done:
              approvers: [lead@example.com]
              required_approvals: 1
              rejection_state: Backlog
      Done: {}
FieldMeaning
descriptionFree text.
is_activeWhether Plane enforces the workflow. Default false.
work_item_typesType names the workflow applies to.
states.<State>flow_type and to for that state, or {}.
approversEmails of the members who can approve. Approval flows only.
required_approvalsHow many approvals are needed. Empty means every listed approver.
rejection_stateWhere the work item goes if the change is rejected.
conditions.pre, .postRules run before and after the transition. plane pull writes these from Plane.

This is the form plane pull writes. The older form, a list of state names plus a separate transitions: map of - to: <State> entries with a per-entry type, is still read.

schema/types.yaml ​

Custom work item types, and custom properties on Plane's own types (Bug, Feature, Task, Chore, Improvement). Custom types need work_item_types: true in features.yaml.

yaml
work_item_types:
  Bug:
    description: A defect or issue requiring a fix
    properties:
      - name: severity
        type: option
        required: true
        options:
          - minor
          - major
          - critical
      - name: is_regression
        type: boolean
  RFC:
    description: Design proposal requiring team feedback
    logo_props:
      icon: Activity
      background_color: "#6366f1"
    properties:
      - name: review_deadline
        type: datetime
FieldMeaning
descriptionFree text.
is_epicMark the type as an epic type. Needs epics: true in features.yaml.
logo_propsicon and background_color, or emoji.
properties[].nameProperty name. Work items set values under this name in properties.
properties[].typeProperty type, from the list below.
properties[].requiredWhether the property is required. Default false.
properties[].optionsChoices for an option property.
properties[].is_multiAllow several values for an option property. Default false.

Property types: text, number, decimal, date, datetime, option, boolean, url, email, file, member_picker, relation, release_picker and formula. string, enum and user are accepted as older names for text, option and member_picker. In v0.5.2, release_picker properties are read on pull but can't be pushed, and formula properties can't be pushed yet.

plane schema push never deletes a property that's missing from types.yaml unless you pass --prune.

schema/members.yaml ​

Project members, each with email and an optional role (guest, member or admin). plane init seeds it with the token owner as admin, and plane pull refreshes it.

schema/workitem_templates.yaml and page_templates.yaml ​

Project-level work item and page templates. They're written by plane pull and pushed by plane schema push, and need templates to be available on your Plane instance. The id field is written back by Plane; don't edit it.

Work files ​

work/workitems.yaml ​

Every *.yaml file in work/ except cycles.yaml, modules.yaml, milestones.yaml and pages.yaml is read as a work items file, in alphabetical order. That lets you split work items across files such as work/backend.yaml and work/frontend.yaml. The usual form is a workitems: list; a bare list or a single item is also accepted.

yaml
workitems:
  - id: MYPROJ-12
    title: Add rate limiting to the public API
    type: Task
    state: In Progress
    priority: high
    labels: [Backend]
    assignees: [dev@example.com]
    start_date: "2026-10-01"
    due_date: "2026-10-15"
    parent: MYPROJ-10
    dependencies:
      blocked_by: [MYPROJ-11]
    relations:
      relates_to: [MYPROJ-14]
    properties:
      severity: major
FieldNotes
titleRequired.
idOptional but strongly recommended. A stable tracking key such as MYPROJ-12. See Sync state.
descriptionFree text.
typeA type name such as Task, or {name: Task, id: <uuid>}. plane pull writes the second form.
stateA state name from states.yaml.
priorityurgent, high, medium, low or none, in any case.
labelsLabel names.
start_date, due_dateYYYY-MM-DD. A due date before the start date is an error.
parentParent work item ID. An item can't be its own parent, and parent loops are errors.
assigneesEmails or user UUIDs.
dependenciesMap of blocked_by, blocking, starts_before, starts_after, finishes_before and finishes_after to lists of work item IDs.
relationsMap of relates_to, duplicate, implements and implemented_by, plus your workspace's custom relation names, to lists of work item IDs.
propertiesCustom property values keyed by property name, checked against the item's type.

watchers and children are rejected by validation in v0.5.2 because push doesn't sync them. Flatten child items into top-level items with parent set.

Older files may use flat blocked_by, blocking, relates_to and duplicate_of keys. They're still read and mapped into dependencies and relations.

work/cycles.yaml and work/modules.yaml ​

yaml
cycles:
  - name: Sprint 1
    description: Core setup
    start_date: "2026-10-01"
    end_date: "2026-10-14"
yaml
modules:
  - name: Authentication
    description: Login, OAuth and sessions
    start_date: "2026-10-01"
    end_date: "2026-12-31"
    status: in-progress

Plane works out a cycle's status from its dates, so a status on a cycle is ignored. Module status is one of backlog, planned, in-progress, paused, completed or cancelled.

work/milestones.yaml ​

yaml
milestones:
  - name: Beta launch
    target_date: "2026-12-31"
    work_items:
      - MYPROJ-1
      - MYPROJ-5

Milestones need the Milestones feature turned on for the project in Plane.

work/pages.yaml ​

yaml
pages:
  - name: Architecture overview
    description_html: "<p>System architecture documentation.</p>"
    owner: alice@example.com
    access: public # public | private

Pages are create-only. A page without an id is created on push and its id is written back. A page that already has an id is skipped, because Plane has no update API for it; edit it in Plane instead. Removing a page from the file doesn't delete it.

Sync state ​

.plane/state.json connects your YAML to Plane. It maps names (types, states, labels, cycles, modules, milestones, pages, workflows and properties) to remote IDs, and stores each work item's tracking key with a hash of its content at the last sync.

ConceptHow it works
Tracking keyid:<your id> when the item has an id; otherwise hash:<content hash>. Editing the title of a hash-tracked item changes its key, which creates a duplicate.
ID write-backWhen a push creates items with no id, plane writes the Plane sequence ID into your YAML and updates the tracking key.
Change detectionA SHA-256 hash over title, description, type, state, priority, labels, assignees, dates, parent, dependencies, relations and properties. Unchanged items are skipped.
Additive pushPush creates and updates. Removing an item from YAML doesn't delete it in Plane.
Conflict checkBefore updating an item, push compares its current remote hash with the last-synced one. If someone changed it in Plane, you're asked before it's overwritten.
LockingState writes are atomic and locked within one process. Run one plane push per project at a time, including in CI.

To look inside the state or fix one entry, see plane state.