REST API
Plain HTTP for the tools that speak it rather than MCP: Zapier, Make, n8n, a shell script, a scheduled job. 21 operations under https://koma.ac/api-preview/api/v1, each one of the MCP server's tools wearing an HTTP method, so what you can do here is exactly what an MCP client can do and the parameters are the same. The document that describes it is at /api/v1/openapi.json; import it into Zapier, Postman or an OpenAPI client.
How to call
- Base URL
https://koma.ac/api-preview/api/v1. JSON in, JSON out. - Authentication: a Koma API key or an OAuth access token as a bearer:
Authorization: Bearer koma_…. Create a key in Koma under Settings, then Connections. Authentication has the details. - Ids are UUIDs; get them from
GET /context, the list routes orGET /search. An id that belongs to someone else answers404. - Query parameters are typed by the schema:
limit=5,includeArchived=true, a list astaskStatus=todo,inProgress. - Errors are
{ "error": "one sentence", "code": "invalid | notFound | methodNotAllowed | unauthorized | rateLimited | toolError" }with400,401,404,405or429. A429carriesRetry-After; a405carriesAllow. - Rate limit: 60 requests a minute per key or token, counted before authentication, so a bad key is counted too.
- Status codes: a route that makes something answers
201(POST /lists,POST /items,POST /items/{id}/messages,POST /uploads); everything else200. Deletes are soft:GET /trashandPOST /trash/restorebring things back for about 30 days.
Adding a task
The one call most automations need: a line of text becomes a task on the Tasks list. POST /items takes one or many; listId is a list's id, or the word tasks for the Tasks list and notes for the Notes list. What you set yourself (dueAt, taskStatus) stays as sent; enrich: true lets the pass that runs a beat later tidy the wording and pick up the due date the text names.
curl -X POST "https://koma.ac/api-preview/api/v1/items" \
-H "Authorization: Bearer koma_…" -H "Content-Type: application/json" \
-d '{"listId": "tasks", "items": [{"title": "Call the dentist tomorrow at 10", "enrich": true}]}'
At a glance
- Context:
GET /context - Lists:
GET /lists,POST /lists,PATCH /lists/{id},DELETE /lists/{id} - Items:
GET /items,POST /items,PATCH /items,DELETE /items,POST /items/query,POST /items/move,GET /items/{id},POST /uploads - Chats:
GET /items/{id}/messages,POST /items/{id}/messages - Search:
GET /search - Trash:
GET /trash,POST /trash/restore - Automations:
GET /automations,GET /automations/{id}/runs,POST /automations/{id}/run
Context
GET /context
The workspace in one call · Read-only · MCP tool get_context
Use this at the start of a conversation that involves the user's Koma workspace, or when you need an overview of it. Returns, in one call: how Koma works; the user (name, timezone, today's date and local time); what they asked the AI to remember about them; their lists with ids, properties and item counts (the Tasks list and the Notes list named by id); overdue, due-today, in-progress and upcoming tasks; the newest unsorted tasks; recently touched notes; their automations with the last run; and the last few days of activity. Everything comes with ids for follow-up calls. It is an overview, not the full data: use search, list_items, query_items or get_item for depth. Pass focusListId to scope the task sections to one list.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
focusListId | string | no | Optional list id. Scopes the task sections to that list; the rest stays workspace-wide. |
Returns 200
about(string[])user(object)unavailable(string[])links(object): The roots of a row's links:<webOrigin>/artifacts/<id>is the item's page in Koma,<appScheme>://artifacts/<id>the same item in the Koma Mac app.
Example
curl -X GET "https://koma.ac/api-preview/api/v1/context" \
-H "Authorization: Bearer koma_…"
Lists
GET /lists
The lists with their properties and counts · Read-only · MCP tool list_lists
The user's lists: every live list with its id, name, icon, standing instructions, item count, layout and PROPERTIES with their stable ids — the ids create_items and update_items key values on. A list whose statusPropertyId is "status" and dueDatePropertyId is "dueDate" keeps a status and a due date on its items, which makes them tasks; the Tasks list and the Notes list are named by id. Use it to resolve a list's name to its id before writing to it, or to read a list's properties you have not seen yet. Pass suppressOptions: true when you only need names and counts; a select property can carry hundreds of options (a select value may be sent as its label).
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
suppressOptions | boolean | no | true replaces each select property's option list with an optionCount — much smaller payload. |
Returns 200
total(number)tasksListId(string | null)notesListId(string | null)lists(object[])
Example
curl -X GET "https://koma.ac/api-preview/api/v1/lists" \
-H "Authorization: Bearer koma_…"
POST /lists
Create a list · Adds data · MCP tool create_list
Create a list. template "tasks" creates a list for to-dos: its items keep a status (todo / inProgress / completed) and a due date, like the Tasks list. Otherwise properties shape it (a reading list with Author and Status, a CRM with Company and Email); omit both to start with no properties — every item has a title. Check list_lists or get_context first so an existing list is not duplicated. Returns the list with its id and its properties' stable ids, ready for create_items.
Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | The list's name, in the user's own words. |
icon | string | null | no | An emoji for the list. |
instructions | string | null | no | Standing rules for how entries should be handled, one rule per line. Max 500 characters — they ride in the prompt on every turn. |
template | "tasks" | null | no | "tasks" creates a list for to-dos that keeps a status and a due date. Omit for a list shaped by properties. |
properties | object[] | null | no | The list's own properties (3 to 6 is typical; a title exists on every item, so no title property). Ignored with template "tasks". |
titleLabel | string | null | no | What the title column is headed on this list — the word every item's name sits under: Name on a CRM, Book on a reading list. Omit for Title. Never a property: every item has a title. |
Returns 201
id(string)name(string)icon(string | null)instructions(string | null)titleLabel(string | null): The title column's name on this list (Name on a CRM, Book on a reading list); null reads as Title. An item's name is its title under any label.layout(string)role("tasks" | "notes" | "other"): What the list's items are, read from its properties: "tasks" when it keeps the status and the due date, "notes" when it has no properties, "other" when its properties are its own.properties(object[])statusPropertyId(string | null): "status" when the list keeps its items' status in the task columns.dueDatePropertyId(string | null): "dueDate" when the list keeps its items' due date in the task columns.itemCount(number): The list's open items: every live item but a repeating template, and never a completed one on a list that keeps its status in the task columns.isLifeAdmin(boolean)
Example
curl -X POST "https://koma.ac/api-preview/api/v1/lists" \
-H "Authorization: Bearer koma_…" \
-H "Content-Type: application/json" \
-d '{"name":"<name>"}'
PATCH /lists/{id}
Change a list's name, icon, instructions, layout or properties · Updates or removes existing data · MCP tool update_list
Change a list: its name, icon, standing instructions, layout, or its properties. instructions REPLACES the existing text (read it first with list_lists and carry forward what is still true; null clears it). addProperties adds properties (every one in this call), updateProperty renames, retypes or re-options ONE property, deletePropertyIds removes properties, columnOrder reorders them. A retype migrates the items' values; one that would clear or trim values answers a PREVIEW (preview: true, what would be lost) and writes nothing until the same call is repeated with acknowledgeDataLoss: true. The reserved status and due date properties can be renamed, never retyped or removed. The title column is renamed through titleLabel (it stays every item's title), never retyped, removed or hidden. Only the fields passed change.
Path
id: The list's id (from list_lists or get_context).
Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | null | no | New name. Omit, or pass null, to leave it unchanged. |
icon | string | null | no | New emoji; "" removes it. Omit, or pass null, to leave it unchanged. |
instructions | string | null | no | REPLACES the standing rules (max 500 characters); "" clears them. Omit, or pass null, to leave them unchanged. |
titleLabel | string | null | no | The title column's new name on this list (Name, Book, Client); "" resets it to Title. Omit, or pass null, to leave it unchanged. The column stays every item's title under any name. |
layout | "list" | "kanban" | "table" | null | no | How the list opens: 'list', 'kanban' (the board) or 'table'. |
columnOrder | string[] | null | no | Left-to-right property order: EVERY property exactly once, by property id. |
addProperties | object[] | null | no | Properties to ADD, every one in this call. Existing items leave a new property unset. |
updateProperty | object | null | no | ONE property to rename, retype or re-option. |
deletePropertyIds | string[] | null | no | Stable ids of the properties to remove, every one in this call. |
acknowledgeDataLoss | boolean | null | no | true ONLY on a repeat call, after a preview reported items that would clear or trim and you told the user what will be lost. |
Returns 200
id(string)name(string)icon(string | null)instructions(string | null)titleLabel(string | null): The title column's name on this list (Name on a CRM, Book on a reading list); null reads as Title. An item's name is its title under any label.layout(string)role("tasks" | "notes" | "other"): What the list's items are, read from its properties: "tasks" when it keeps the status and the due date, "notes" when it has no properties, "other" when its properties are its own.properties(object[])statusPropertyId(string | null): "status" when the list keeps its items' status in the task columns.dueDatePropertyId(string | null): "dueDate" when the list keeps its items' due date in the task columns.itemCount(number): The list's open items: every live item but a repeating template, and never a completed one on a list that keeps its status in the task columns.isLifeAdmin(boolean)preview(boolean)warning(string)
Example
curl -X PATCH "https://koma.ac/api-preview/api/v1/lists/<id>" \
-H "Authorization: Bearer koma_…" \
-H "Content-Type: application/json" \
-d '{}'
DELETE /lists/{id}
Delete a list into the trash · Updates or removes existing data · MCP tool delete_list
Delete a list by id. Deleting the list also deletes EVERY item on it. SOFT delete: the list and its items are recoverable for ~30 days via restore_deleted (type "list"), which brings the items deleted with it back too. When the list still holds items the call refuses unless confirmDeleteItems is true, so the blast radius is always named before it happens. The hub's own Tasks list and Life Admin cannot be deleted.
Path
id: The list's id (from list_lists).
Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
confirmDeleteItems | boolean | null | no | Required (true) when the list holds items: confirms deleting the list AND every item on it. Confirm with the user first. |
Returns 200
deleted(boolean)id(string)name(string)itemsDeleted(number)
Example
curl -X DELETE "https://koma.ac/api-preview/api/v1/lists/<id>" \
-H "Authorization: Bearer koma_…" \
-H "Content-Type: application/json" \
-d '{}'
Items
GET /items
List and filter items · Read-only · MCP tool list_items
List, filter and search items. Omit listId for the Tasks set — every item on a list that keeps a status and a due date (the Tasks list and every list made for tasks), ordered by due date then creation, completed tasks left out by default. Pass listId for one list's items, whatever its shape: a notes list's prose, a reading list's rows. Each row carries the item's title, body as Markdown, status and due date where its list keeps them, values keyed by property id, and its list. get_item reads one item in full with handles for editing, and is the call when the person asks to see, open or show one item; query_items filters, counts and sums by property. A read that repeats on a schedule passes changedSince, the nextChangedSince of its previous read: only what moved since comes back on the person's own lists, deletions included, one call per cycle instead of the whole list.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
listId | string | no | One list's items: a list id (from list_lists or get_context), or the word "tasks" (the Tasks list, the unsorted pool) or "notes" (the Notes list, made afresh when the user deleted theirs). Omit for the Tasks set. |
changedSince | string | no | An ISO 8601 instant (exclusive), or the nextChangedSince of the previous read verbatim. The changes feed for a worker that polls: every item of the list (or of the person's own lists that keep the task columns, trashed and archived ones included) whose record moved after it — edits, completions, archives, restores, deletions — oldest change first, the deleted ones as deleted tombstones, then hasMore and nextChangedSince to pass next time. Items shared with the person are not in the feed (list_items without changedSince reads them). query is refused beside it; the other filters and sort do not apply. |
ids | string[] | no | Specific item ids to fetch (the other filters still apply). |
query | string | no | Free-text search across title and body. |
taskStatus | "todo" | "inProgress" | "completed"[] | no | Filter to these statuses, on lists that keep one. Omit for the default active set (excludes completed). |
dueAfter | string | no | ISO 8601 instant — only items due on/after this (inclusive). |
dueBefore | string | no | ISO 8601 instant — only items due before this (exclusive). Today = dueAfter today 00:00 local, dueBefore tomorrow 00:00 local; overdue = dueBefore today 00:00 alone. |
createdAfter | string | no | ISO 8601 instant — only items created on/after this. |
createdBefore | string | no | ISO 8601 instant — only items created before this (exclusive). |
updatedAfter | string | no | ISO 8601 instant — only items last edited on/after this. A poll that wants deletions too uses changedSince instead. |
updatedBefore | string | no | ISO 8601 instant — only items last edited before this (exclusive). |
completedAfter | string | no | ISO 8601 instant — only items completed on/after this. Implies completed items unless taskStatus says otherwise. |
completedBefore | string | no | ISO 8601 instant — only items completed before this (exclusive). |
sort | "due" | "created" | "updated" | "completed" | no | Row order. Default 'due' (due date, then creation); a timestamp sort returns newest first. |
limit | integer | no | Max results (default 50). (1 to 200) |
includeImageUrls | boolean | no | true additionally returns a short-lived signed url per attachment so the images can be viewed. Off by default. |
Returns 200
count(number)listId(string | null)listName(string | null)listIcon(string | null): The list's own icon, an emoji, when it has one; null for the Tasks set.properties(object[]): The list's own properties, on a read of one list that has some.items(object[])deleted(object[]): With changedSince: the items deleted since, as tombstones.serverTime(string): With changedSince: the server's clock when this read began.hasMore(boolean): With changedSince: more changed thanlimit; continue withnextChangedSince.nextChangedSince(string): With changedSince: what to pass as the next call's changedSince, verbatim (an instant, or the last row's stamp and id while more is pending).links(object): The roots of a row's links:<webOrigin>/artifacts/<id>is the item's page in Koma,<appScheme>://artifacts/<id>the same item in the Koma Mac app.
Example
curl -X GET "https://koma.ac/api-preview/api/v1/items" \
-H "Authorization: Bearer koma_…"
POST /items
Create items on a list, many in one call · Adds data · MCP tool create_items
Create items on one list, many in one call (1 to 100): a to-do or a dictated list of them on the Tasks list, a note on the Notes list, rows of a reading list or a CRM. Each entry carries a title, a body as Markdown, and values for the list's own properties as {propertyId, value} pairs (ids or names from list_lists; a select value may be its label or option id, a multiSelect value an array of them; an unknown label becomes a new option). On a list that keeps a status and a due date an entry may set taskStatus and dueAt. The items are stored as written and indexed for search; no AI pass runs unless enrich is true, which on a tasks list derives the title and due date from the words. An entry's externalId (the caller's own id) makes re-sending safe: the same externalId returns the item already made, under existing. With matchOn, an entry whose named values match an existing item fills that item's empty values, body and images and overwrites nothing (changing a filled value is update_items' job); without it every entry is inserted. Images come as imageUrls (a Koma storage path from create_image_upload, or a public https image URL the server downloads); failures come back in imageFailures. One bad entry never fails the batch; it is reported under failed with its reason.
Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
listId | string | no | The list every item lands on: a list id (from list_lists or get_context), or the word "tasks" (the Tasks list, the unsorted pool) or "notes" (the Notes list, made afresh when the user deleted theirs). Omit for the Tasks list. |
items | object[] | yes | Every item to create, in one call. (up to 100 items) |
matchOn | string[] | null | no | Property ids or names that identify an existing item. When given, an entry whose listed values all match an existing item is not added again: the existing item gains the entry's values for its empty cells, its body if it had none, and its images, and keeps every cell it already had; matching is exact. Omit to always insert — no property is a natural key, so there is no default dedup. |
enrich | boolean | null | no | Run Koma's background AI pass on each item of a tasks list (title cleanup, due date and list suggestions). Default false: the items are stored as written. |
Returns 201
listId(string)listName(string)listIcon(string | null): The list's own icon, an emoji, when it has one.properties(object[]): The list's own properties, when it has some.items(object[])added(number): Items inserted by this call.links(object): The roots of a row's links:<webOrigin>/artifacts/<id>is the item's page in Koma,<appScheme>://artifacts/<id>the same item in the Koma Mac app.mergedIds(string[]): Existing items an entry was merged into through matchOn.existing(object[])failed(object[])unknownKeys(string[])imagesAttached(number)imageFailures(object[])
Example
curl -X POST "https://koma.ac/api-preview/api/v1/items" \
-H "Authorization: Bearer koma_…" \
-H "Content-Type: application/json" \
-d '{"items":[{}]}'
PATCH /items
Update items, many in one call · Updates or removes existing data · MCP tool update_items
Update items, many in one call, each with its own title, body edits and values; the status, due date, archive and favorite flags apply to every item in the call. Only the fields passed change; null leaves a field as it is. The body is edited with edits against the handles get_item returns in body — replace, insert, delete, setChecked, move, replaceText, or rewrite pinned to bodyVersion — applied in order, all or nothing; a refused call writes nothing on that item and returns the current body with fresh handles. The older way, body with the call's merge mode (append by default, prepend, insertAfter with anchor, replace with confirmReplace: true), still works; an item uses one or the other. values MERGE into the item (only the properties sent change; "" clears one; an unknown property is an error naming the valid ones). imageUrls add images (clearImages: true drops the old ones first). taskStatus and dueAt apply only on a list that keeps them ("none" clears the due date and its reminder). Items are written in order; a refusal stops the batch and names the items already written, so re-send only the rest. Returns each item, with body, bodyVersion and applied after a body write.
Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
items | object[] | yes | Every item to update, in one call. (up to 200 items) |
mode | "prepend" | "append" | "replace" | "insertAfter" | null | no | Legacy: how each item's body merges: 'append' (default, at the bottom), 'prepend' (at the top), 'insertAfter' (right after the line named in anchor), or 'replace' (swap the whole body — only when the user explicitly asked to rewrite it; requires confirmReplace: true). |
anchor | string | null | no | For mode 'insertAfter' only: a verbatim snippet of the existing line to insert after. Errors if not found. |
confirmReplace | boolean | null | no | Required (true) for mode 'replace': confirms the user explicitly asked to swap the WHOLE body, deleting everything in it. Never pass it on the user's behalf without that explicit ask. |
taskStatus | "todo" | "inProgress" | "completed" | null | no | todo, inProgress or completed, on a list that keeps a status; applies to every item in the call. Status transitions report progress. |
dueAt | string | null | no | ISO 8601 timestamp, on a list that keeps a due date; "none" clears it; applies to every item in the call. |
archived | boolean | null | no | true sets the item(s) aside (hidden from every list, kept and reversible); false brings them back. |
favorited | boolean | null | no | true pins the item(s) in the app's Favorites; false unpins them. |
Returns 200
updated(number)items(object[])imagesAttached(number)imageFailures(object[])links(object): The roots of a row's links:<webOrigin>/artifacts/<id>is the item's page in Koma,<appScheme>://artifacts/<id>the same item in the Koma Mac app.
Example
curl -X PATCH "https://koma.ac/api-preview/api/v1/items" \
-H "Authorization: Bearer koma_…" \
-H "Content-Type: application/json" \
-d '{"items":[{"itemId":"<itemId>"}]}'
DELETE /items
Delete items into the trash · Updates or removes existing data · MCP tool delete_items
Delete items by id, up to 50 per call. SOFT delete: every item stays recoverable for ~30 days via restore_deleted (type "item") before it is purged, and drops out of search. Returns the deleted items; ids that are not the user's own items are reported under refused and the rest still delete.
Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
itemIds | string[] | yes | The items' ids. Up to 50 per call. (up to 50 items) |
Returns 200
deleted(number)items(object[])refused(string[])
Example
curl -X DELETE "https://koma.ac/api-preview/api/v1/items" \
-H "Authorization: Bearer koma_…" \
-H "Content-Type: application/json" \
-d '{"itemIds":["<itemIds>"]}'
POST /items/query
Filter, sort, count or sum the items of one list · Read-only · MCP tool query_items
Filter, sort, count or sum the items of ONE list by its properties — exact numbers instead of eyeballing a dump: "how many are unread", "expenses in March", "the wines rated 5". where clauses AND-combine on a property by id or name (on a list that keeps them, status and dueDate are properties too); a select value may be its label. mode 'list' (default) returns the matching items, paged with offset and limit; 'count' how many match; 'sum' the total of sumPropertyId across the matches. Archived items are left out unless includeArchived: true; each item reports its archivedAt.
Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
listId | string | yes | The list to query: a list id (from list_lists or get_context), or the word "tasks" (the Tasks list, the unsorted pool) or "notes" (the Notes list, made afresh when the user deleted theirs). |
where | object[] | null | no | Filter clauses, AND-combined. Omit for every item. |
sort | object | null | no | Sort order. Omit for the list's default (newest first). |
mode | "list" | "count" | "sum" | null | no | 'list' (default) returns matching items; 'count' how many match; 'sum' totals sumPropertyId across matches. |
sumPropertyId | string | null | no | For mode 'sum': the numeric property to total (its stable id or name). |
createdAfter | string | null | no | ISO 8601 instant — items added on/after this. |
createdBefore | string | null | no | ISO 8601 instant — items added before this (exclusive). |
updatedAfter | string | null | no | ISO 8601 instant — items last edited on/after this. A read that repeats on a schedule passes the instant of its previous read here, or uses list_items' changedSince, which also carries deletions. |
updatedBefore | string | null | no | ISO 8601 instant — items last edited before this (exclusive). |
includeArchived | boolean | null | no | true also includes archived items (hidden from every list by default), so one can be brought back with update_items. |
limit | integer | null | no | mode 'list': max items per page (default 50). |
offset | integer | null | no | mode 'list': items to skip — page through a big list with offset + limit until hasMore is false. |
includeImageUrls | boolean | null | no | mode 'list': true additionally returns a short-lived signed url per attachment. |
Returns 200
mode("list" | "count" | "sum")listId(string)listName(string)listIcon(string | null): The list's own icon, an emoji, when it has one.count(number): How many items match.sum(number)sumPropertyId(string)properties(object[])items(object[])offset(number)hasMore(boolean)
Example
curl -X POST "https://koma.ac/api-preview/api/v1/items/query" \
-H "Authorization: Bearer koma_…" \
-H "Content-Type: application/json" \
-d '{"listId":"<listId>"}'
POST /items/move
Move items onto another list · Updates or removes existing data · MCP tool move_items
Move items onto another list: a task out to the Tasks list ("tasks"), a note onto the Notes list ("notes"), anything onto a list by id. Each item keeps its id, body, chat and history; its values follow their properties by id, then by name and type, select values by label. A value the destination list has no place for would be DROPPED: a first call that would drop values answers a preview (preview: true, droppedValues) and moves nothing; the same call with acknowledgeDataLoss: true then moves. The status and due date ride along unseen onto a list that keeps neither and show again on the way back. Items that are not the user's own are reported under refused, never moved.
Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
itemIds | string[] | yes | Ids of the items to move. (up to 100 items) |
listId | string | yes | The destination list: a list id (from list_lists or get_context), or the word "tasks" (the Tasks list, the unsorted pool) or "notes" (the Notes list, made afresh when the user deleted theirs). |
acknowledgeDataLoss | boolean | null | no | true ONLY on a repeat call, after a preview reported values the destination list has no place for and you told the user what will be lost. |
Returns 200
preview(boolean)listId(string)listName(string | null)items(object[])droppedValues(object[])refused(string[])
Example
curl -X POST "https://koma.ac/api-preview/api/v1/items/move" \
-H "Authorization: Bearer koma_…" \
-H "Content-Type: application/json" \
-d '{"itemIds":["<itemIds>"],"listId":"<listId>"}'
GET /items/{id}
One item with its body, values and list · Read-only · MCP tool get_item
Read one item in full by its id — the thing to read before working on it, the call when the person asks to see, open or show one item (search and list_items find it; this shows it), and the companion to search, which returns only the matched passage. Returns the item with body (the body as Markdown with a short handle in front of every block; update_items edits target those handles), bodyVersion (what a whole-body rewrite is pinned to), its status and due date where its list keeps them, values by property id with cells by property name beside them, its list, guidance (a line about working this item: its status, what the person wrote in its chat since an agent last posted, where the work goes) and, for an item a meeting was recorded into, the attributed transcript once its recording is done, with summary and taskCandidates.
Path
id: The item's id (from list_items, query_items, search or get_context).
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
includeImageUrls | boolean | no | true additionally returns a short-lived signed url per attachment so the images can be viewed. Off by default. |
Returns 200
id(string)title(string | null)body(string | null): The body as Markdown; with a short handle in front of every block on a read made for editing.bodyVersion(string): A fingerprint of the body as read; a whole-bodyrewritenames it asbaseVersion.taskStatus(string | null): On a list that keeps a status.dueAt(string | null): On a list that keeps a due date.hasDueTime(boolean | null)reminderAt(string | null)completedAt(string | null)values(object): The item's values keyed by property id; a select value is an option id.cells(object of string): The item's values by property NAME, as text, where its list has properties of its own.isSharedWithMe(boolean): Shared with the person by its owner;editablesays what update_items admits on it.editable("all" | "titleAndBody"): On a row shared with the person:titleAndBodyfor an item shared with them one by one (its status, due date, values and images are its owner's; update_items refuses them by name),allfor an item on a list shared with them (every field but the owner's favorite).listId(string | null)listName(string | null)listIcon(string | null): The list's own icon, an emoji, when it has one.createdAt(string | null)updatedAt(string | null)editedAt(string | null)archivedAt(string | null)recordingStatus(string)attachments(object[])bodyImages(object[]): The pictures the body shows (), whether or not they are attachments, with the size each was stored at (null when unknown); with includeImageUrls, a short-lived signed url on each.guidance(string): How to work this item right now: its status, what the person wrote in its chat since an agent last posted, where the work goes.label(string): For an item with no title: its first non-empty value, else its list's name.properties(object[]): The list's own properties, so a value can be written back by id.webUrl(string): The item's page in Koma, to open in a browser.appUrl(string): The item in the Koma Mac app: the app's own link scheme (yamo://artifacts/:id; yamo-staging:// against staging).transcript(string)recording(object)summary(object)taskCandidates(object[])
Example
curl -X GET "https://koma.ac/api-preview/api/v1/items/<id>" \
-H "Authorization: Bearer koma_…"
POST /uploads
Pre-signed slots for image bytes · Adds data · MCP tool create_image_upload
Get pre-signed URLs to upload images DIRECTLY into Koma storage — the reliable alternative to passing third-party links in imageUrls (those must stay fetchable until the server downloads them; short-lived signed links often expire first). For each slot: HTTP PUT the raw image bytes to uploadUrl with the matching Content-Type header (valid ~2 hours), then pass its bucketPath as an imageUrls entry in create_items / update_items. Keep images at or under 10 MB (maxBytes). Up to 10 slots per call.
Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
mimeType | "image/png" | "image/jpeg" | "image/heic" | "image/webp" | yes | The image type you will upload (all slots in one call share it). |
count | integer | no | How many upload slots (default 1). (1 to 10) |
Returns 201
uploads(object[])maxBytes(number)validForSeconds(number)
Example
curl -X POST "https://koma.ac/api-preview/api/v1/uploads" \
-H "Authorization: Bearer koma_…" \
-H "Content-Type: application/json" \
-d '{"mimeType":"image/png"}'
Chats
GET /items/{id}/messages
Recent messages in an item's chat · Read-only · MCP tool read_chat_messages
Read the recent messages in an item's chat: what the user said there, what Koma replied, and what any agent posted with post_chat_message. Pass since to read only what arrived after a moment you already saw; a read that repeats on a schedule passes the createdAt of the last message it saw every time. Empty when the item has no chat yet.
Path
id: The item's id.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
since | string | no | ISO 8601 instant. Only messages created after it are returned, oldest first: the createdAt of the last message you saw. |
limit | integer | no | Max messages (default 20). (1 to 50) |
Returns 200
sessionId(string | null)messages(object[])truncated(boolean)
Example
curl -X GET "https://koma.ac/api-preview/api/v1/items/<id>/messages" \
-H "Authorization: Bearer koma_…"
POST /items/{id}/messages
Post a message into an item's chat as an agent · Updates or removes existing data · MCP tool post_chat_message
Post a message into an item's chat as the calling agent: an action taken outside Koma with its link, or the one question the work is blocked on. The user sees it on every device as an unread reply on the item and as a line in their main conversation, and the agent cannot take it back, so a post per step, or a note that work is finished, reads as noise. Set needsInput when the work is blocked on their answer. The same messageKey posted twice lands once.
Path
id: The item's id.
Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
message | string | yes | The message, as Markdown. (up to 20000 characters) |
agentName | string | no | How the agent is named in the chat (default "Your agent"). (up to 60 characters) |
needsInput | boolean | no | True when the message is a question the work waits on. |
messageKey | string | no | Idempotency key: a retry with the same key updates the same message. (up to 120 characters) |
Returns 201
id(string)sessionId(string)createdAt(string)
Example
curl -X POST "https://koma.ac/api-preview/api/v1/items/<id>/messages" \
-H "Authorization: Bearer koma_…" \
-H "Content-Type: application/json" \
-d '{"message":"<message>"}'
Search
GET /search
Search items and lists · Read-only · MCP tool search
Search the user's Koma workspace — their items (tasks, notes, the rows of every list they keep) and their lists — by meaning or keyword (hybrid semantic + keyword retrieval). Use this when the user asks about their own information, plans or anything they may have saved, and before adding something that may already exist. Their wording often won't match their notes' wording; the semantic arm bridges that gap (and across languages). Returns the best-matching passage per item, each with its list; get_item returns an item in full. An empty result means nothing relevant is saved. Not for general web knowledge.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | yes | Natural-language query — what the user is trying to find, in their words. |
limit | integer | no | Max results (default 10). (1 to 20) |
after | string | no | Only items CREATED on/after this ISO 8601 timestamp. Use when scoping by when it was saved ("last week", "in March"). |
before | string | no | Only items CREATED before this ISO 8601 timestamp (exclusive). Pair with after to bound a window. |
Returns 200
count(number)results(object[])
Example
curl -X GET "https://koma.ac/api-preview/api/v1/search" \
-H "Authorization: Bearer koma_…"
Trash
GET /trash
What is in the trash · Read-only · MCP tool list_deleted
The trash: everything soft-deleted in the last ~30 days — items and lists — newest first, each with the moment it is purged forever (purgesAt). Anything listed here can be brought back with restore_deleted. Filter by type, or omit type for both.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
type | "item" | "list" | no | Only this kind. Omit for both. |
limit | integer | no | Max entries per kind (default 25). (1 to 100) |
Returns 200
count(number)items(object[])
Example
curl -X GET "https://koma.ac/api-preview/api/v1/trash" \
-H "Authorization: Bearer koma_…"
POST /trash/restore
Restore one item or list from the trash · Adds data · MCP tool restore_deleted
Bring back one soft-deleted item or list from the trash (see list_deleted). Restoring a list also brings back the items deleted with it; an item whose list is still in the trash can't come back alone (restore the list first). An item whose list is gone for good lands on the hub's default list for what it was (movedToDefaultList: true). Restores one thing per call. If it is already live the call succeeds with alreadyLive: true.
Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
type | "item" | "list" | yes | The entry's type, as list_deleted reported it. |
id | string | yes | The deleted item's or list's id. |
Returns 200
restored(boolean)alreadyLive(boolean)type(string)id(string)title(string | null)restoredItems(number)movedToDefaultList(boolean)
Example
curl -X POST "https://koma.ac/api-preview/api/v1/trash/restore" \
-H "Authorization: Bearer koma_…" \
-H "Content-Type: application/json" \
-d '{"type":"item","id":"<id>"}'
Automations
GET /automations
List automations · Read-only · MCP tool list_automations
List the user's automations — standing orders Koma runs on its own, on a schedule or on demand. Returns each one's id, name, what it does, status (active / paused / draft), when it runs, the next run, and how the last run ended. Use it when the user asks what runs on its own, when something runs next, or before reading or starting a run.
Returns 200
count(number)automations(object[])
Example
curl -X GET "https://koma.ac/api-preview/api/v1/automations" \
-H "Authorization: Bearer koma_…"
GET /automations/{id}/runs
Recent runs of an automation · Read-only · MCP tool get_automation_runs
Read one automation's recent runs, newest first: when each ran, how it ended, what it wrote (one line per action), the message it left for the user, and any error. Use it for "what did my morning brief find", "why did it fail", or to read the result of a run started with run_automation once it has finished.
Path
id: The automation's id (from list_automations or get_context).
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | no | How many runs, newest first (default 5, max 20). (1 to 20) |
Returns 200
automation(object)runs(object[])
Example
curl -X GET "https://koma.ac/api-preview/api/v1/automations/<id>/runs" \
-H "Authorization: Bearer koma_…"
POST /automations/{id}/run
Start an automation now · Updates or removes existing data · MCP tool run_automation
Start one of the user's automations now — the same as Run now on its page in Koma. Use it only when the user asks to run a specific automation. The run happens inside Koma with the user's connected apps and can take a minute; its result lands in the user's Koma chat, not in this conversation. Read the outcome afterwards with get_automation_runs. Refused for a draft, while the same automation is already running, or once the daily run limit is reached.
Path
id: The automation's id (from list_automations or get_context).
Returns 200
id(string)name(string)runId(string)status(string)
Example
curl -X POST "https://koma.ac/api-preview/api/v1/automations/<id>/run" \
-H "Authorization: Bearer koma_…"