# REST API

Plain HTTP for the tools that speak it rather than MCP: Zapier, Make, n8n, a shell script, a scheduled job. 30 operations under `https://api.roma.app/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`](https://api.roma.app/api/v1/openapi.json); import it into Zapier, Postman or an OpenAPI client.

## How to call

- **Base URL** `https://api.roma.app/api/v1`. JSON in, JSON out.
- **Authentication**: a Roma API key or an OAuth access token as a bearer: `Authorization: Bearer roma_…`. Create a key in Roma under Settings, then Connections. [Authentication](/developers/authentication#api-keys) has the details.
- **Ids** are UUIDs; get them from `GET /context`, the list routes or `GET /search`. An id that belongs to someone else answers `404`.
- **Query parameters** are typed by the schema: `limit=5`, `includeArchived=true`, a list as `taskStatus=todo,inProgress`.
- **Errors** are `{ "error": "one sentence", "code": "invalid | notFound | methodNotAllowed | unauthorized | rateLimited | toolError" }` with `400`, `401`, `404`, `405` or `429`. A `429` carries `Retry-After`; a `405` carries `Allow`.
- **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 /tasks`, `POST /quick-add`, `POST /projects`, `POST /notes`, `POST /collections`, `POST /collections/{id}/items`, `POST /uploads`); everything else `200`. Deletes are soft: `GET /trash` and `POST /trash/restore` bring things back for about 30 days.

## Quick add

The one call most automations need: a line of text becomes a task. The text is the task's title as it lands; the enrichment pass that runs a beat later tidies the wording and picks up the due date and the project the text names. `POST /tasks` runs the same pass, so a title is tidied either way; what you set yourself (`dueAt`, `projectId`, `priority`) stays as sent.

```bash
curl -X POST "https://api.roma.app/api/v1/quick-add" \
  -H "Authorization: Bearer roma_…" -H "Content-Type: application/json" \
  -d '{"text": "Call the dentist tomorrow at 10"}'
```

## At a glance

- **Context**: `GET /context`
- **Tasks**: `GET /tasks`, `POST /tasks`, `POST /quick-add`, `GET /tasks/{id}`, `PATCH /tasks/{id}`, `DELETE /tasks/{id}`
- **Projects**: `GET /projects`, `POST /projects`, `PATCH /projects/{id}`
- **Notes and search**: `GET /search`, `GET /notes`, `POST /notes`, `GET /notes/{id}`, `PATCH /notes/{id}`, `DELETE /notes/{id}`
- **Collections**: `GET /collections`, `POST /collections`, `PATCH /collections/{id}`, `DELETE /collections/{id}`, `GET /collections/{id}/items`, `POST /collections/{id}/items`, `PATCH /collections/{id}/items`, `DELETE /collections/{id}/items`, `POST /uploads`
- **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 Roma workspace, or when you need an overview of it. Returns, in one call: how Roma works; the user (name, timezone, today's date and local time); what they asked the AI to remember about them; their projects; overdue, due-today, in-progress and upcoming tasks; unsorted tasks; recently touched notes; their collections with columns; 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_tasks, get_note or get_collection_items for depth. Pass focusProjectId to scope the task sections to one project.

**Query parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `focusProjectId` | `string` | no | Optional project UUID. Scopes the task sections to that project; the rest stays workspace-wide. |

**Returns** `200`

- `about` (`string[]`)
- `user` (`object`)
- `unavailable` (`string[]`)

**Example**

```bash
curl -X GET "https://api.roma.app/api/v1/context" \
  -H "Authorization: Bearer roma_…"
```

## Tasks

### GET /tasks

**List and filter tasks** · `Read-only` · MCP tool `list_tasks`

List, filter, and search the user's tasks — the way to find work to pick up. Returns tasks ordered by due date, then priority. Scope to the whole workspace, unsorted tasks only, or a single project. By default completed tasks are excluded.

**Query parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `scope` | `string` | no | "all" (default) for everything, "inbox" for unsorted items only (no project — the value keeps its legacy name), or a project UUID for that project. |
| `query` | `string` | no | Free-text search across title and description. |
| `taskStatus` | `"todo" \| "inProgress" \| "completed"[]` | no | Filter to these statuses. Omit to use the default active set (excludes completed). |
| `dueAfter` | `string` | no | ISO 8601 instant — only tasks due on/after this (inclusive). |
| `dueBefore` | `string` | no | ISO 8601 instant — only tasks 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 tasks created on/after this. |
| `createdBefore` | `string` | no | ISO 8601 instant — only tasks created before this (exclusive). |
| `updatedAfter` | `string` | no | ISO 8601 instant — only tasks last changed on/after this. |
| `updatedBefore` | `string` | no | ISO 8601 instant — only tasks last changed before this (exclusive). |
| `completedAfter` | `string` | no | ISO 8601 instant — only tasks completed on/after this. Implies completed tasks unless taskStatus says otherwise. |
| `completedBefore` | `string` | no | ISO 8601 instant — only tasks completed before this (exclusive). |
| `sort` | `"due" \| "created" \| "updated" \| "completed"` | no | Row order. Default 'due' (due date, then priority); a timestamp sort returns newest first. |
| `limit` | `integer` | no | Max results (default 50). (1 to 200) |

**Returns** `200`

- `count` (`number`)
- `tasks` (`object[]`)

**Example**

```bash
curl -X GET "https://api.roma.app/api/v1/tasks" \
  -H "Authorization: Bearer roma_…"
```

### POST /tasks

**Create a task** · `Adds data` · MCP tool `create_task`

Create a task. Omit projectId to leave it unsorted (no project); set projectId to file it under a project. The task is enriched by AI in the background after creation, so enriched fields may appear on a subsequent read. Returns the created task.

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `title` | `string` | yes | Short, clear task title. |
| `description` | `string` | no | Optional longer details. |
| `projectId` | `string` | no | Project UUID. Omit to leave it unsorted (no project). |
| `priority` | `"low" \| "medium" \| "high"` | no |  |
| `taskStatus` | `"todo" \| "inProgress" \| "completed"` | no | Defaults to 'todo'. |
| `dueAt` | `string` | no | ISO 8601 due timestamp. |

**Returns** `201`

- `id` (`string`)
- `title` (`string | null`)
- `description` (`string | null`)
- `taskStatus` (`string | null`)
- `priority` (`string | null`)
- `dueAt` (`string | null`)
- `projectId` (`string | null`)
- `createdAt` (`string | null`)
- `updatedAt` (`string | null`)
- `editedAt` (`string | null`)
- `completedAt` (`string | null`)

**Example**

```bash
curl -X POST "https://api.roma.app/api/v1/tasks" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{"title":"<title>"}'
```

### POST /quick-add

**Create a task from one line of text** · `Adds data` · MCP tool `create_task`

Create a task. Omit projectId to leave it unsorted (no project); set projectId to file it under a project. The task is enriched by AI in the background after creation, so enriched fields may appear on a subsequent read. Returns the created task.

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `text` | `string` | yes | Short, clear task title. |
| `description` | `string` | no | Optional longer details. |
| `projectId` | `string` | no | Project UUID. Omit to leave it unsorted (no project). |
| `priority` | `"low" \| "medium" \| "high"` | no |  |
| `taskStatus` | `"todo" \| "inProgress" \| "completed"` | no | Defaults to 'todo'. |
| `dueAt` | `string` | no | ISO 8601 due timestamp. |

**Returns** `201`

- `id` (`string`)
- `title` (`string | null`)
- `description` (`string | null`)
- `taskStatus` (`string | null`)
- `priority` (`string | null`)
- `dueAt` (`string | null`)
- `projectId` (`string | null`)
- `createdAt` (`string | null`)
- `updatedAt` (`string | null`)
- `editedAt` (`string | null`)
- `completedAt` (`string | null`)

**Example**

```bash
curl -X POST "https://api.roma.app/api/v1/quick-add" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{"text":"<text>"}'
```

### GET /tasks/{id}

**One task with its subtasks and project** · `Read-only` · MCP tool `get_task_context`

Fetch one task with everything needed to start working on it: the full task row (description, status, priority, due date) and the project it belongs to. The equivalent of opening an issue.

**Path**

- `id`: The task's UUID.

**Returns** `200`

- `task` (`object`)
- `project` (`object | null`)

**Example**

```bash
curl -X GET "https://api.roma.app/api/v1/tasks/<id>" \
  -H "Authorization: Bearer roma_…"
```

### PATCH /tasks/{id}

**Update a task** · `Updates or removes existing data` · MCP tool `update_task`

Update fields on an existing task — title, body, status, priority, due date, or which project it is in (projectId; null moves it out to unsorted). Use status transitions to report progress (e.g. todo → inProgress → completed). Only the fields you pass are changed. The body merge mode controls how `description` combines with the existing body — adding text appends/prepends; it never replaces unless you explicitly ask it to.

**Path**

- `id`: The task's UUID.

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `title` | `string` | no |  |
| `description` | `string \| null` | no | Body text to merge in per `mode`. Omit to leave the body unchanged; null to clear it. |
| `mode` | `"prepend" \| "append" \| "replace" \| "insertAfter"` | no | How `description` 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; never to add content; requires confirmReplace: true). |
| `confirmReplace` | `boolean` | 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. |
| `anchor` | `string` | no | For mode 'insertAfter' only: a verbatim snippet of the existing line to insert after. Errors if not found. |
| `taskStatus` | `"todo" \| "inProgress" \| "completed"` | no |  |
| `priority` | `"low" \| "medium" \| "high" \| null` | no |  |
| `dueAt` | `string \| null` | no | ISO 8601 timestamp, or null to clear. |
| `projectId` | `string \| null` | no | Move the task: a project UUID (from list_projects or get_context) files it there; null moves it out to unsorted. Omit to leave it where it is. |

**Returns** `200`

- `id` (`string`)
- `title` (`string | null`)
- `description` (`string | null`)
- `taskStatus` (`string | null`)
- `priority` (`string | null`)
- `dueAt` (`string | null`)
- `projectId` (`string | null`)
- `createdAt` (`string | null`)
- `updatedAt` (`string | null`)
- `editedAt` (`string | null`)
- `completedAt` (`string | null`)

**Example**

```bash
curl -X PATCH "https://api.roma.app/api/v1/tasks/<id>" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{}'
```

### DELETE /tasks/{id}

**Delete a task into the trash** · `Updates or removes existing data` · MCP tool `delete_task`

Delete a task (or subtask) by id. This is a SOFT delete — the task is recoverable for ~30 days before it is purged. Deleting a task also removes it from search. Deletes one task per call so each delete is confirmed individually.

**Path**

- `id`: The task's UUID.

**Returns** `200`

- `deleted` (`boolean`)
- `id` (`string`)
- `title` (`string | null`)

**Example**

```bash
curl -X DELETE "https://api.roma.app/api/v1/tasks/<id>" \
  -H "Authorization: Bearer roma_…"
```

## Projects

### GET /projects

**List projects** · `Read-only` · MCP tool `list_projects`

List all of the user's projects. Use this to resolve a project name to its UUID before creating or filing a task, or to understand the workspace structure.

**Returns** `200`

- `count` (`number`)
- `projects` (`object[]`)

**Example**

```bash
curl -X GET "https://api.roma.app/api/v1/projects" \
  -H "Authorization: Bearer roma_…"
```

### POST /projects

**Create a project** · `Adds data` · MCP tool `create_project`

Create a project — a folder that groups the user's tasks and notes. Use it when the user wants a new project, or to give work a home that has none; check list_projects or get_context first so an existing project is not duplicated. Returns the project with its id, ready for create_task / update_task projectId.

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | `string` | yes | Project name, in the user's own words. |
| `emoji` | `string` | no | Optional single emoji for the project. |

**Returns** `201`

- `id` (`string`)
- `name` (`string | null`)
- `emoji` (`string | null`)

**Example**

```bash
curl -X POST "https://api.roma.app/api/v1/projects" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{"name":"<name>"}'
```

### PATCH /projects/{id}

**Rename a project or set its emoji** · `Updates or removes existing data` · MCP tool `update_project`

Rename a project or change its emoji. Only the fields you pass change; emoji null removes it. Does not move or change the project's tasks.

**Path**

- `id`: The project's UUID (from list_projects or get_context).

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | `string` | no | New name. |
| `emoji` | `string \| null` | no | New emoji, or null to remove it. |

**Returns** `200`

- `id` (`string`)
- `name` (`string | null`)
- `emoji` (`string | null`)

**Example**

```bash
curl -X PATCH "https://api.roma.app/api/v1/projects/<id>" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{}'
```

## Notes and search

### GET /search

**Search notes, tasks and list rows** · `Read-only` · MCP tool `search`

Search the user's Roma workspace — their notes, tasks, typed lists and the rows inside them — 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; get_note returns a note's full text. 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**

```bash
curl -X GET "https://api.roma.app/api/v1/search" \
  -H "Authorization: Bearer roma_…"
```

### GET /notes

**List notes** · `Read-only` · MCP tool `list_notes`

List the user's notes, newest first. Scope to the whole workspace, unsorted notes only, or a single project. Returns a snippet of each body; use get_note for the full text. This is browse-by-container — use search to find by topic.

**Query parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `scope` | `string` | no | "all" (default) for everything, "inbox" for unsorted items only (no project — the value keeps its legacy name), or a project UUID for that project. |
| `createdAfter` | `string` | no | ISO 8601 instant — only notes created on/after this. |
| `createdBefore` | `string` | no | ISO 8601 instant — only notes created before this (exclusive). |
| `updatedAfter` | `string` | no | ISO 8601 instant — only notes last changed on/after this. |
| `updatedBefore` | `string` | no | ISO 8601 instant — only notes last changed before this (exclusive). |
| `sort` | `"created" \| "updated"` | no | Row order, newest first on that stamp. Default created. |
| `limit` | `integer` | no | Max results (default 50). (1 to 200) |

**Returns** `200`

- `count` (`number`)
- `docs` (`object[]`)

**Example**

```bash
curl -X GET "https://api.roma.app/api/v1/notes" \
  -H "Authorization: Bearer roma_…"
```

### POST /notes

**Create a note** · `Adds data` · MCP tool `create_note`

Capture a note for the user. Omit projectId to leave it unsorted (no project); set projectId to file it under a project. The body is stored as authored within the editor's supported Markdown subset (unsupported constructs degrade to plain text) — no AI cleanup or reclassification runs — and indexed for search. Returns the created note.

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `content` | `string` | yes | The note body, as Markdown (headings `#`, lists, task lists `- [ ]`, bold, links — the editor's supported subset). Rendered to rich text; author real Markdown, not literal markers. |
| `title` | `string` | no | Short title. Omit to let the body speak for itself. |
| `projectId` | `string` | no | Project UUID. Omit to leave it unsorted (no project). |

**Returns** `201`

- `id` (`string`)
- `title` (`string | null`)
- `content` (`string`)
- `projectId` (`string | null`)
- `createdAt` (`string | null`)

**Example**

```bash
curl -X POST "https://api.roma.app/api/v1/notes" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{"content":"<content>"}'
```

### GET /notes/{id}

**One note as Markdown** · `Read-only` · MCP tool `get_note`

Read one note in full by its id — the companion to search: search returns the matched passage, this pulls the whole document when you need more than the snippet. Returns the note's title and full body as Markdown, and, for a note a meeting was recorded into, the attributed transcript once its recording is done.

**Path**

- `id`: The note's UUID (from a search result).

**Returns** `200`

- `id` (`string`)
- `title` (`string`)
- `content` (`string`)
- `projectId` (`string | null`)
- `createdAt` (`string | null`)
- `transcript` (`string`)
- `recording` (`object`)

**Example**

```bash
curl -X GET "https://api.roma.app/api/v1/notes/<id>" \
  -H "Authorization: Bearer roma_…"
```

### PATCH /notes/{id}

**Update a note** · `Updates or removes existing data` · MCP tool `update_note`

Edit an existing note — its title, body, or which project it's in. Only the fields you pass change. The body merge mode controls how `content` combines with the existing body. Re-indexes for search automatically.

**Path**

- `id`: The note's UUID.

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `title` | `string` | no | New title. Omit to leave unchanged. |
| `content` | `string` | no | Markdown body to merge in (headings, lists, bold, links). Omit for a title/project-only edit. |
| `mode` | `"prepend" \| "append" \| "replace" \| "insertAfter"` | no | How `content` merges: 'append' (default, at the bottom), 'prepend' (at the top), 'replace' (swap the whole body — only when the user explicitly asked to rewrite it; never to add content; requires confirmReplace: true), or 'insertAfter' (place it right after the line named in `anchor`, matching that line's formatting). |
| `confirmReplace` | `boolean` | 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. |
| `anchor` | `string` | no | For mode 'insertAfter' only: a verbatim snippet of the existing line to insert after. Errors if not found. |
| `projectId` | `string` | no | Project UUID to move the note into, or "inbox" to clear its project (unsorted — the value keeps its legacy name). Omit to leave it where it is. |

**Returns** `200`

- `id` (`string`)
- `title` (`string | null`)
- `content` (`string`)
- `projectId` (`string | null`)

**Example**

```bash
curl -X PATCH "https://api.roma.app/api/v1/notes/<id>" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{}'
```

### DELETE /notes/{id}

**Delete a note into the trash** · `Updates or removes existing data` · MCP tool `delete_note`

Delete a note by id. This is a SOFT delete — the note is recoverable for ~30 days before it is purged. Deleting a note also removes it from search. Deletes one note per call so each delete is confirmed individually.

**Path**

- `id`: The note's UUID.

**Returns** `200`

- `deleted` (`boolean`)
- `id` (`string`)
- `title` (`string | null`)

**Example**

```bash
curl -X DELETE "https://api.roma.app/api/v1/notes/<id>" \
  -H "Authorization: Bearer roma_…"
```

## Collections

### GET /collections

**List collections with their columns** · `Read-only` · MCP tool `list_collections`

List the user's collections (typed lists — a reading list, a watchlist, an expenses tracker). Returns each list's id, name, standing instructions, row count, and its COLUMNS with their stable ids — the ids add_collection_items and update_collection_items take. Use it before writing to a collection whose columns you have not read yet. Pass suppressOptions: true when you only need names/counts — select columns can carry hundreds of options (you don't need them to write: a select value may be sent as its label).

**Query parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `suppressOptions` | `boolean` | no | true replaces each select column's option list with an optionCount — much smaller payload. |

**Returns** `200`

- `count` (`number`)
- `collections` (`object[]`)

**Example**

```bash
curl -X GET "https://api.roma.app/api/v1/collections" \
  -H "Authorization: Bearer roma_…"
```

### POST /collections

**Create a collection** · `Adds data` · MCP tool `create_collection`

Create a typed list with its columns. Every column is equal — declare the ones the list actually needs, including a name column if rows have names (a list of measurements or dates may have none). Returns the created list WITH the stable column ids to use when adding rows.

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | `string` | yes | The list's name. |
| `instructions` | `string` | no | Standing rules for how entries should be handled, one rule per line. Max 500 characters — they ride in the prompt on every turn. (up to 500 characters) |
| `properties` | `object[]` | no | The columns. Omit to start with one default text column the user can rename, retype or delete. |

**Returns** `201`

- `id` (`string`)
- `name` (`string`)
- `columns` (`object[]`)

**Example**

```bash
curl -X POST "https://api.roma.app/api/v1/collections" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{"name":"<name>"}'
```

### PATCH /collections/{id}

**Rename a collection or replace its instructions** · `Updates or removes existing data` · MCP tool `update_collection`

Rename a collection or edit its standing instructions. `instructions` REPLACES the existing text — read the current value first (list_collections) and carry forward anything still true; null clears them. Column (schema) editing is not available over MCP — new select options mint automatically when adding or updating rows.

**Path**

- `id`: The collection's UUID (from list_collections).

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | `string` | no | New list name. |
| `instructions` | `string \| null` | no | REPLACES the standing rules (max 500 characters — they ride the prompt every turn); null clears them. |

**Returns** `200`

- `id` (`string`)
- `name` (`string`)
- `instructions` (`string | null`)

**Example**

```bash
curl -X PATCH "https://api.roma.app/api/v1/collections/<id>" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{}'
```

### DELETE /collections/{id}

**Delete a collection into the trash** · `Updates or removes existing data` · MCP tool `delete_collection`

Delete a collection (typed list) by id. Deleting the list also deletes ALL its rows. SOFT delete — the list and its rows are recoverable for ~30 days via restore_deleted (type "collection"), which brings the rows deleted with it back too. When the list still holds rows the call refuses unless confirmDeleteItems is true, so the blast radius is always named before it happens.

**Path**

- `id`: The collection's UUID (from list_collections).

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `confirmDeleteItems` | `boolean` | no | Required (true) when the list holds rows: confirms deleting the list AND every row in it. Confirm with the user first. |

**Returns** `200`

- `deleted` (`boolean`)
- `id` (`string`)
- `name` (`string`)
- `itemsDeleted` (`number`)

**Example**

```bash
curl -X DELETE "https://api.roma.app/api/v1/collections/<id>" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{}'
```

### GET /collections/{id}/items

**Rows of a collection, paged** · `Read-only` · MCP tool `get_collection_items`

Read the rows of one collection. Values are keyed by COLUMN ID (see list_collections); a select column's value is an option id, a multiSelect column's an array of option ids. Each row also lists its image attachments (so you can verify an import's images actually landed); pass includeImageUrls: true for short-lived viewable links. Paginated: the result carries totalCount and hasMore — keep calling with offset until hasMore is false to read a large list completely. Archived rows are left out unless includeArchived: true. Use to check what a list already holds before adding to it.

**Path**

- `id`: The collection's UUID (from list_collections).

**Query parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `includeArchived` | `boolean` | no | true also returns archived rows (hidden from every list by default) so one can be brought back with update_collection_items; each row then says whether it is archived. |
| `limit` | `integer` | no | Max rows per page (default 50, newest first). (1 to 200) |
| `offset` | `integer` | no | Rows to skip — page through a big list with offset + limit until hasMore is false. (0 to 9007199254740991) |
| `includeImageUrls` | `boolean` | no | true additionally returns a short-lived signed `url` per attachment so the images can be viewed. Off by default. |
| `createdAfter` | `string` | no | ISO 8601 instant — only rows added on/after this. |
| `createdBefore` | `string` | no | ISO 8601 instant — only rows added before this (exclusive). |
| `updatedAfter` | `string` | no | ISO 8601 instant — only rows last changed on/after this. |
| `updatedBefore` | `string` | no | ISO 8601 instant — only rows last changed before this (exclusive). |

**Returns** `200`

- `count` (`number`)
- `totalCount` (`number`)
- `offset` (`number`)
- `hasMore` (`boolean`)
- `items` (`object[]`)

**Example**

```bash
curl -X GET "https://api.roma.app/api/v1/collections/<id>/items" \
  -H "Authorization: Bearer roma_…"
```

### POST /collections/{id}/items

**Add rows to a collection** · `Adds data` · MCP tool `add_collection_items`

Add rows to a collection, many in one call (the import path). Values are keyed by column id or name; a select value may be its label or option id, a multiSelect value is an ARRAY of them, and unknown labels become new options. Keys that match no column come back in `unknownKeys`. Images that could not be stored come back in `imageFailures` with the reason; create_image_upload is the reliable path for images. Every row is inserted unless `matchOn` names columns to match on; a matched row keeps every filled cell and only gains what it lacks (empty cells, notes, images), so nothing is overwritten. To change a filled cell use update_collection_items (re-adding without matchOn creates a duplicate).

**Path**

- `id`: The collection's UUID (from list_collections).

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `matchOn` | `string[]` | no | Column ids or names that identify an existing row. When given, a row whose listed cells all match an existing row is not added again: the existing row gains the incoming row's empty-cell values, notes and images and keeps every cell it already had; matching is exact. Omit to always insert — no column is a natural key, so there is no default dedup. |
| `items` | `object[]` | yes | Every row to add, in one call. (up to 200 items) |

**Returns** `201`

- `added` (`number`)
- `itemIds` (`string[]`)
- `unknownKeys` (`string[]`)
- `imagesAttached` (`number`)
- `imageFailures` (`object[]`)

**Example**

```bash
curl -X POST "https://api.roma.app/api/v1/collections/<id>/items" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{"items":[{}]}'
```

### PATCH /collections/{id}/items

**Correct rows of a collection** · `Updates or removes existing data` · MCP tool `update_collection_items`

Update existing rows of ONE collection, many in one call; pass only what changes. `values` MERGE into the row (only the keys sent change; "" clears a cell), `body` replaces the notes, `imageUrls` add images (clearImages: true drops the old ones first), `archived` sets a row aside or brings it back. A key matching no column is an error; rows earlier in the batch stay updated and are named, so re-send only the rest. Read row ids and current values with get_collection_items first.

**Path**

- `id`: The collection's UUID (from list_collections).

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `items` | `object[]` | yes | Every row to update, in one call. (up to 200 items) |

**Returns** `200`

- `updated` (`number`)
- `itemIds` (`string[]`)
- `imagesAttached` (`number`)
- `imageFailures` (`object[]`)

**Example**

```bash
curl -X PATCH "https://api.roma.app/api/v1/collections/<id>/items" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"itemId":"<itemId>"}]}'
```

### DELETE /collections/{id}/items

**Delete rows into the trash** · `Updates or removes existing data` · MCP tool `delete_collection_items`

Delete rows from collections by id — up to 50 per call. SOFT delete: every row stays recoverable for ~30 days via restore_deleted (type "collectionItem") before it is purged, and drops out of search. Returns a per-id result — ids that don't resolve are reported and the rest still delete.

**Path**

- `id`

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `itemIds` | `string[]` | yes | The rows' UUIDs (from get_collection_items). Up to 50 per call. (up to 50 items) |

**Returns** `200`

- `deleted` (`number`)
- `results` (`object[]`)

**Example**

```bash
curl -X DELETE "https://api.roma.app/api/v1/collections/<id>/items" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{"itemIds":["<itemIds>"]}'
```

### POST /uploads

**Pre-signed slots for image bytes** · `Adds data` · MCP tool `create_image_upload`

Get pre-signed URLs to upload images DIRECTLY into Roma 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 add_collection_items / update_collection_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**

```bash
curl -X POST "https://api.roma.app/api/v1/uploads" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{"mimeType":"image/png"}'
```

## Trash

### GET /trash

**What is in the trash** · `Read-only` · MCP tool `list_deleted`

The trash: everything soft-deleted in the last ~30 days — tasks, notes, collections, and collection rows — 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 all kinds.

**Query parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `type` | `"task" \| "note" \| "collection" \| "collectionItem"` | no | Only this kind. Omit for all kinds. |
| `limit` | `integer` | no | Max entries per kind (default 25). (1 to 100) |

**Returns** `200`

- `count` (`number`)
- `items` (`object[]`)

**Example**

```bash
curl -X GET "https://api.roma.app/api/v1/trash" \
  -H "Authorization: Bearer roma_…"
```

### POST /trash/restore

**Restore one item from the trash** · `Adds data` · MCP tool `restore_deleted`

Bring back one soft-deleted item from the trash (see list_deleted) — a task, note, collection, or collection row. Restoring a collection also brings back the rows deleted with it; a row whose list is still deleted can't come back alone (restore the collection instead). A restored task whose project was deleted meanwhile lands in the inbox (movedToInbox: true). Restores one item per call. If the item is already live the call succeeds with alreadyLive: true.

**Body** (JSON)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `type` | `"task" \| "note" \| "collection" \| "collectionItem"` | yes | The entry's type, as list_deleted reported it. |
| `id` | `string` | yes | The deleted item's UUID. |

**Returns** `200`

- `restored` (`boolean`)
- `alreadyLive` (`boolean`)
- `type` (`string`)
- `id` (`string`)
- `title` (`string | null`)
- `restoredItems` (`number`)
- `movedToInbox` (`boolean`)

**Example**

```bash
curl -X POST "https://api.roma.app/api/v1/trash/restore" \
  -H "Authorization: Bearer roma_…" \
  -H "Content-Type: application/json" \
  -d '{"type":"task","id":"<id>"}'
```

## Automations

### GET /automations

**List automations** · `Read-only` · MCP tool `list_automations`

List the user's automations — standing orders Roma 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**

```bash
curl -X GET "https://api.roma.app/api/v1/automations" \
  -H "Authorization: Bearer roma_…"
```

### 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**

```bash
curl -X GET "https://api.roma.app/api/v1/automations/<id>/runs" \
  -H "Authorization: Bearer roma_…"
```

### 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 Roma. Use it only when the user asks to run a specific automation. The run happens inside Roma with the user's connected apps and can take a minute; its result lands in the user's Roma 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**

```bash
curl -X POST "https://api.roma.app/api/v1/automations/<id>/run" \
  -H "Authorization: Bearer roma_…"
```

