Working with Plans

Copy-pasteable examples for the calls you will reach for most.

These examples use curl against the hosted service. Each request carries your API token; the examples assume you have put it in a shell variable:

export TOKEN=lwt_your_token_here

Paths are relative to https://liveplanner.app/api. For the exact request and response shapes of every field, see the OpenAPI reference at /api/openapi.json.

List your plans

A plan list returns the plans in your personal library (plans in a team library are not included), as metadata only, not the tab contents:

curl https://liveplanner.app/api/projects \
  -H "Authorization: Bearer $TOKEN"
{
  "projects": [{ "id": "4cd7082b-…", "name": "Sprint 14", "folderId": null, "shareable": false }]
}

Read a plan and a tab

Fetching one plan returns its metadata and a list of tab summaries, but not what each tab holds:

curl https://liveplanner.app/api/projects/$DOC \
  -H "Authorization: Bearer $TOKEN"

To see every tab of a plan at a glance, ask for its overview:

curl "https://liveplanner.app/api/projects/$DOC?view=overview" \
  -H "Authorization: Bearer $TOKEN"
doc 4cd7082b-… "Sprint 14" · 3 tabs · edited just now
  tab 8bf8 "Board" · 1 element: 1 box · kind=board · rev 1
  tab 359e "Requests" · 1 element: 1 box · kind=board · rev 1
  tab e1b2 "Flow" · 7 elements: 7 boxes · kind=dashboard · rev 1

To get the contents of one tab, request it by id. This keeps listing a big plan cheap:

curl https://liveplanner.app/api/projects/$DOC/tabs/$TAB \
  -H "Authorization: Bearer $TOKEN"
{
  "tab": {
    "id": "8bf83102-…",
    "name": "Board",
    "kind": "board",
    "rev": 1,
    "elements": [/* the board, with its columns and settings */]
  }
}

A tab's elements are what fill it: the board on a Board tab, the sheet on a Sheet tab, the metrics and charts on a Dashboard tab. The cards a board shows are not on the tab; they are items, read with GET /projects/$DOC/plan and GET /projects/$DOC/items (see Plan Items, Sheets and More).

Read a tab as text

The element JSON carries every setting. When you want what a tab means, which is usually what a script or an AI model needs, ask for a view instead. The outline gives one line per element:

curl "https://liveplanner.app/api/projects/$DOC/tabs/$TAB?view=outline" \
  -H "Authorization: Bearer $TOKEN"
tab 8bf8 "Board" · 1 element: 1 box · kind=board · rev 1
plan-board 0933 columns=Backlog|To Do|In Progress|Review|Done

Each element is named by a short ref: its id when that is short and readable, otherwise the first few characters of it. Anywhere the API takes an element, a ref, any unique start of the id or the full id all work. The first line ends with the tab's revision.

Other views answer other questions:

  • view=show&ref=0933 prints one element with every stored field.
  • view=comments shows the open comment threads in full; add all=1 for resolved ones too.
  • view=find&q=roadmap lists the elements whose text contains roadmap.

Add budget=2000 to fit any view to about 2,000 tokens. The view then leaves out detail in a fixed order, and its last line says what it left out and how to see it. Add json=1 for the same view as JSON.

Create a plan

POST /projects creates a plan. You supply the id, a name, and optionally the tabs to seed it with. The simplest way to fill a tab is a template, by its kind from GET /templates (such as kanban, project-planner, bug-triage or budget-planner). Ids are shared across all of liveplanner, not just your account, so use a fresh UUID for the plan, each tab and each folder; an id someone else already holds is refused or swapped for a new one:

DOC=$(uuidgen) TAB=$(uuidgen) FOLDER=$(uuidgen)

curl -X POST https://liveplanner.app/api/projects \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "'"$DOC"'",
    "name": "Sprint 14",
    "tabs": [{ "id": "'"$TAB"'", "name": "Board", "template": "kanban" }]
  }'

A template that builds several tabs (the Kanban template makes a Board, a Requests board and a Flow dashboard) adds them all after the first, each with an id of its own; the answer lists them. A tab with neither a template nor elements is a blank tab, which opens on Start Planning in the editor.

Update a tab

Scripts and agents change a tab with a changeset: a short list of edit operations, applied together or not at all. Anyone with the plan open sees the change arrive straight away, it is credited to you, and it can be undone as one unit for 30 days. Changes people make in the editor at the same time are kept. This one changes the Kanban template's Flow dashboard (its tab id in $FLOW), using the refs its outline printed:

curl -X POST https://liveplanner.app/api/projects/$DOC/tabs/$FLOW/changesets \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "summary": "swap the calendar for a workload chart",
    "base": { "rev": 1 },
    "operations": [
      { "op": "test", "target": "0a78", "fields": { "planView": { "view": "calendar" } } },
      { "op": "set", "target": "0a78", "fields": { "planView": { "view": "workload" } } },
      { "op": "rm", "target": "82d4" }
    ]
  }'

test refuses the changeset unless the element still holds what you read, set changes only the fields you name, and rm removes an element (here a dashboard metric). The answer lists what changed, the changeset's id and the tab's new revision. Add ?dryRun=1 to see the answer without changing anything:

~ 0a78  planView (changed)
- 82d4  plan-view
rev 1→2 · cs_tbrfqcvm1w · revert: liveplanner changeset revert 4cd7082b-… cs_tbrfqcvm1w

The same operations read well as text, one a line, which is easier for a script or an agent to write. Send them as a string:

curl -X POST https://liveplanner.app/api/projects/$DOC/tabs/$FLOW/changesets \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "operations": "set 0a78 planView={\"view\":\"workload\"}\nrm 82d4" }'

Edit Operations Reference lists every operation and selector, and how to rebuild a whole tab.

To rebuild a whole tab, send "replace" instead of "operations", with a template, a dashboard, a new Form tab's form, or elements. A replace on a tab id the plan doesn't have yet creates that tab.

Every tab read returns the tab's rev. Send it back as "base": { "rev": 7 } and the changeset is refused with 409 changeset_conflict if an element it touches changed since you read it, so nothing you didn't see gets overwritten. An element somebody has selected in the editor is left alone too: the changeset is refused with 409 elements_held, naming who holds it.

To rename a tab, PUT its name: PUT /projects/$DOC/tabs/$TAB/name with { "name": "Board" }. The whole-tab PUT the editor saves with is not open to tokens; it answers 405 use_changesets.

GET /projects/$DOC/changesets lists the recent changesets, and POST /projects/$DOC/changesets/<id>/revert undoes one, leaving any element somebody changed since.

Cards, Sheet cells and forms have endpoints of their own: see Plan Items, Sheets and More.

Organise into folders

Create a folder (add "parentId" to make it inside another, or "teamId" for a team's), then create a plan straight into it by naming its id as folderId. Renaming and moving folders, and moving a plan you already have, happens in the Explorer.

curl -X POST https://liveplanner.app/api/folders \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "id": "'"$FOLDER"'", "name": "Sprints" }'

curl -X POST https://liveplanner.app/api/projects \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "id": "'"$(uuidgen)"'", "name": "Sprint 12", "folderId": "'"$FOLDER"'" }'

GET /api/folders lists your folders with their ids.

Delete and restore

DELETE /projects/$DOC moves the plan to the Trash, where it waits for 30 days, and answers 204. While it is there, reading or changing it answers 410 project_trashed. Add ?permanent=true to delete it for good straight away.

curl -X DELETE https://liveplanner.app/api/projects/$DOC \
  -H "Authorization: Bearer $TOKEN"

# What you can restore: your own Trash and every team's you belong to
curl https://liveplanner.app/api/trash \
  -H "Authorization: Bearer $TOKEN"

curl -X POST https://liveplanner.app/api/trash/$DOC/restore \
  -H "Authorization: Bearer $TOKEN"

DELETE /trash/$DOC deletes one trashed plan for good, and DELETE /trash empties your personal Trash (?team=<id> empties a team's). A read-only token can list the Trash but not restore or purge.

Share a plan

Create a share link with a role of view, participate or edit:

curl -X POST https://liveplanner.app/api/projects/$DOC/share \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "role": "view" }'

The response includes the share code to build a link from. See Sharing and Embeds for what the different roles allow.

Send the role explicitly. Omitting it does not mean "view": a body with no role mints an edit link, which is the app's own default. An unrecognised role is rejected outright rather than quietly falling back, so a typo fails loudly, but an absent one does not.

When a call fails, the status code and a small { "error": "..." } body tell you why. See Errors and Rate Limits.

Was this article helpful?