A tab's elements change through changesets, but most of a plan lives elsewhere: the cards on its boards, the cells of its Sheets, the questions of its forms. Each has its own endpoints, shown here with curl. The examples assume your API token is in $TOKEN and a plan id in $DOC; paths are relative to https://liveplanner.app/api, and /api/openapi.json has every field.
Every write here reaches anyone with the plan open straight away, credited to you.
Plan Items
A card on a Plan board is an item, kept in the plan's item store rather than on a tab, so one card can show on several boards. A board files its cards by status: each column has one, such as in-progress~k3f2. Read the boards, their statuses and the card types first:
curl https://liveplanner.app/api/projects/$DOC/plan \
-H "Authorization: Bearer $TOKEN"
{
"boards": [
{
"tabId": "tab-1",
"tabName": "Sprint",
"elementId": "b1",
"title": "Sprint 12",
"kind": "board",
"types": null,
"columns": [
{ "status": "todo~a1b2", "name": "To Do" },
{ "status": "doing~c3d4", "name": "Doing", "wipLimit": 3 }
]
}
],
"statuses": [
{ "status": "todo~a1b2", "name": "To Do" },
{ "status": "doing~c3d4", "name": "Doing" }
],
"types": [
{ "id": "task", "label": "Task", "fields": ["title", "description", "status", "assignee", "…"] }
],
"itemTypesRev": 3
}
GET /projects/$DOC/items lists the items themselves with the store's revision ({ "items": [...], "rev": 41 }). Each item has an id, a type, a key (the number people say, #12, never reused) and its fields.
Make Cards
POST /projects/$DOC/items makes one card: a type, its fields (a title at least, up to 500 characters) and an optional place, the status to file it under and the card to sit after or before:
curl -X POST https://liveplanner.app/api/projects/$DOC/items \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"type": "task",
"fields": { "title": "Fix login", "priority": "high", "labels": ["auth"], "due": "2026-11-02", "estimate": 3 },
"place": { "status": "todo~a1b2" }
}'
It answers 201 with { "item": …, "rev": 42 }. POST /projects/$DOC/items/bulk makes up to 200 in one write, in order: { "items": [ { "type": "task", "fields": { "title": "One" } }, … ] }. A plan holds up to 2,000 items.
The fields every card understands are title, description, status, assignee (a person { "id", "name", "color" }), priority (urgent, high, medium or low), color, labels (up to 12), estimate (0 to 999), start and due (YYYY-MM-DD), checklist, archived and flagged. A card type's custom fields go under their ids, such as f-severity. votes and comments are written only by voting and by the comment endpoints.
Change and Move Cards
# Change one card: set some fields, clear others, optionally change its type
curl -X POST https://liveplanner.app/api/projects/$DOC/items/$ITEM \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "set": { "priority": "urgent" }, "clear": ["due"] }'
# Move it to another column, after another card, and assign it in the same write
curl -X POST https://liveplanner.app/api/projects/$DOC/items/$ITEM/move \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "status": "doing~c3d4", "after": "'"$OTHER"'", "set": { "assignee": { "id": "u1", "name": "Sam", "color": "#2563eb" } } }'
# Change up to 200 cards at once: each is checked first, so one refusal changes nothing
curl -X POST https://liveplanner.app/api/projects/$DOC/items/patches \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "items": [ { "id": "'"$A"'", "set": { "flagged": true } }, { "id": "'"$B"'", "clear": ["labels"] } ] }'
A move takes a status and after or before (after wins; neither puts the card at the end of the column). Its set and clear reach only the fields a swimlane can change (assignee, priority, labels, estimate, start, due, a custom field, or archived). A change never clears a card's title. DELETE /projects/$DOC/items/$ITEM deletes one card.
POST /projects/$DOC/items/tally adds a session vote's dots to up to 200 cards, { "items": [ { "id": "…", "votes": { "<voter id>": 2 } } ] }, as the editor does when a vote ends. A card deleted meanwhile is skipped.
Refusals are 400 with a code such as title_required, field_value_invalid, type_invalid, place_invalid or status_excluded (the card's type leaves that status out), 404 item_not_found, 409 item_busy (try again) or 413 items_full.
Card Types
A plan's card types are stored whole. To change them, send the full catalogue back with the revision you read (itemTypesRev from /plan):
curl -X PUT https://liveplanner.app/api/projects/$DOC/item-types \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"expectedRev": 3,
"itemTypes": {
"version": 1,
"types": [
{ "id": "bug", "label": "Bug", "newTitle": "New bug", "glyph": "task", "color": "#dc2626",
"fields": ["title", "description", "status", "assignee", "priority", "f-severity", "comments"],
"custom": [{ "id": "f-severity", "label": "Severity", "kind": "choice", "options": ["S1", "S2", "S3"] }] }
]
}
}'
Up to 32 types. A custom field's kind is text, longtext, number, currency, date, checkbox, link, email, choice, multichoice, rating or card, and its id starts f-. "itemTypes": null goes back to the default types (Project, Task, Note, Idea, Action). If someone changed the types since you read them, the answer is 409 item_types_stale with the stored catalogue and revision, to apply your change to again. This needs edit access to the whole plan.
The CLI (item add, item set, item move, type ls) and the MCP server's change_items and change_card_types do all of this by column and type names, which is easier for an agent than status ids.
Sheets
GET /projects/$DOC/sheets returns every Sheet with its cells (?tabId= for one tab's, ?ids= for named ones). A Sheet addresses rows and columns by id, not by letter, so inserting a row never breaks a reference: its layout lists the row ids top to bottom and the column ids left to right, and A1 is the first column id with the first row id.
{
"sheets": [
{
"id": "k7Qm2xPaR4tZ",
"tabId": "tab-1",
"title": "Costs",
"rev": 7,
"layout": { "rows": ["r1x", "r2y", "…"], "cols": ["c1p", "c2q", "…"] },
"cells": [
{ "r": "r1x", "c": "c1p", "i": { "s": "Item" } },
{ "r": "r1x", "c": "c2q", "i": { "s": "Cost" } }
]
}
]
}
Change cells with a write. Each cell change names its row and column ids and an input: { "n": 1200 } for a number, { "s": "Hosting" } for text, { "b": true } for a checkbox value, or null to clear it. An f patch formats it ("b": true for bold, "nf": "currency", "bg" and "fc" for colours):
curl -X POST https://liveplanner.app/api/projects/$DOC/sheets/$SHEET/writes \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"write": {
"kind": "cells",
"cells": [
{ "r": "r2y", "c": "c1p", "i": { "s": "Hosting" } },
{ "r": "r2y", "c": "c2q", "i": { "n": 1200 }, "f": { "nf": "currency" } },
{ "r": "r1x", "c": "c2q", "f": { "b": true } }
]
}
}'
It answers with the write as it landed, the sheet's new rev and the stored state of every cell it touched. The other two writes are { "kind": "title", "title": "Costs 2027" } and { "kind": "layout", "changes": [ … ] }, whose changes insert, delete, move, size, hide, freeze, merge or filter rows and columns, such as { "k": "freeze", "rows": 1 }.
Formulas are stored with their references by row and column id, which is awkward to write by hand.
To write formulas, or anything by A1, use liveplanner sheet set "Budget" Costs B2 1200 =B2*12 or
the MCP server's change_sheet, which work out the ids for you.
A Sheet shows on a tab through a Sheet element, as a board does; start one on a new tab with the blank-spreadsheet template (or any spreadsheet template, such as budget-planner). liveplanner sheet add (or the MCP server's add_sheet) makes the sheet and places its element in one go. Over REST, POST /projects/$DOC/sheets with { "tabId": "…", "title": "Costs" } makes the sheet (titles are unique on a tab), and a changeset adds its element: { "op": "add", "element": { "type": "shape", "shape": "plan-sheet", "x": 0, "y": 0, "width": 960, "height": 560, "planSheet": { "sheetId": "…" } } }. See Sheets for what people see.
Forms
A Form tab's form is written whole. Read it from the tab (GET /projects/$DOC/tabs/$TAB, its form), change it, and send it back with the revision you read:
curl -X POST https://liveplanner.app/api/projects/$DOC/tabs/$TAB/form \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "form": { … }, "rev": 7 }'
This needs edit access to the tab. To publish the form, POST /projects/$DOC/tabs/$TAB/form-link makes its public link; PUT on the same path opens or closes it while keeping the link, and DELETE removes the link (publishing again makes a new one). The CLI's form commands and the MCP server's change_form and share_form make these calls by question name, which is easier than writing the form by hand.
A Tab as a Picture
curl https://liveplanner.app/api/projects/$DOC/tabs/$TAB/render.svg \
-H "Authorization: Bearer $TOKEN" -o overview.svg
The tab drawn by the same renderer the CLI's tab render and pull --svg use, as image/svg+xml. It is never cached, so it always shows the tab now. For an image to embed somewhere public, use a live image instead.
Copy a Plan
curl -X POST https://liveplanner.app/api/projects/$DOC/copy \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "name": "Sprint 14 (copy)" }'
Makes a copy in your own files and answers 201 with the new project. Leave out name for Copy of <name>. Anything you can read can be copied; share links are not copied with it.
Was this article helpful?