coda

$npx mdskill add vm0-ai/vm0-skills/coda

Manage Coda documents, tables, and workspaces via API.

  • Enables agents to create, read, update, and delete Coda content.
  • Integrates with Coda API using Bearer token authentication.
  • Executes requests when users reference Coda terms or share links.
  • Returns structured data including doc metadata and workspace details.

SKILL.md

.github/skills/codaView on GitHub ↗
---
name: coda
description: Coda API for docs, tables, rows, and pages. Use when user mentions "Coda", "coda.io", shares a Coda doc link, "Coda table", "Coda doc", or asks to read/write a Coda workspace.
---

## Troubleshooting

If requests fail, run `zero doctor check-connector --env-name CODA_TOKEN` or `zero doctor check-connector --url https://coda.io/apis/v1/whoami --method GET`

## Base URL

```
https://coda.io/apis/v1
```

## Authentication

All requests use a Bearer token:

```
Authorization: Bearer $CODA_TOKEN
```

## Core APIs

### Verify Token

```bash
curl -s "https://coda.io/apis/v1/whoami" --header "Authorization: Bearer $CODA_TOKEN" | jq '{name, loginId, type, tokenName, scoped, workspace: .workspace.name}'
```

Returns the user/token info. Use this to confirm the connector is working before running other calls.

Docs: https://coda.io/developers/apis/v1#operation/whoami

### List Docs

```bash
curl -s "https://coda.io/apis/v1/docs?limit=25" --header "Authorization: Bearer $CODA_TOKEN" | jq '.items[] | {id, name, href, browserLink, ownerName}'
```

Useful query parameters:

- `isOwner=true` — only docs you own
- `query=<search-text>` — name search
- `workspaceId=<workspace-id>` — filter by workspace
- `limit=<1-100>` — page size (default 25)
- `pageToken=<token>` — follow `.nextPageToken` for pagination

Docs: https://coda.io/developers/apis/v1#operation/listDocs

### Create a Doc

Write to `/tmp/coda_doc.json`:

```json
{
  "title": "Project Tracker",
  "timezone": "America/Los_Angeles"
}
```

```bash
curl -s -X POST "https://coda.io/apis/v1/docs" --header "Authorization: Bearer $CODA_TOKEN" --header "Content-Type: application/json" -d @/tmp/coda_doc.json | jq '{id, name, browserLink}'
```

Optional fields:

- `sourceDoc` — copy an existing doc by ID
- `folderId` — place the doc in a specific folder (defaults to your private folder)

Docs: https://coda.io/developers/apis/v1#operation/createDoc

### Get a Doc

Replace `<your-doc-id>` with your actual doc ID (the 10-character code after `_d` in a Coda URL, e.g. `abc123xyz0`):

```bash
curl -s "https://coda.io/apis/v1/docs/<your-doc-id>" --header "Authorization: Bearer $CODA_TOKEN" | jq '{id, name, browserLink, workspace: .workspace.name, ownerName, updatedAt}'
```

Docs: https://coda.io/developers/apis/v1#operation/getDoc

### List Pages in a Doc

Replace `<your-doc-id>` with your actual doc ID:

```bash
curl -s "https://coda.io/apis/v1/docs/<your-doc-id>/pages?limit=50" --header "Authorization: Bearer $CODA_TOKEN" | jq '.items[] | {id, name, browserLink, contentType, isHidden}'
```

Docs: https://coda.io/developers/apis/v1#operation/listPages

### Get a Page

Replace `<your-doc-id>` and `<your-page-id>` with actual IDs:

```bash
curl -s "https://coda.io/apis/v1/docs/<your-doc-id>/pages/<your-page-id>" --header "Authorization: Bearer $CODA_TOKEN" | jq '{id, name, subtitle, contentType, parent: .parent.name}'
```

Docs: https://coda.io/developers/apis/v1#operation/getPage

### Export Page Content

Page content export is a two-step async flow: request export, then poll for the signed download URL.

Write to `/tmp/coda_export.json`:

```json
{
  "outputFormat": "markdown"
}
```

`outputFormat` accepts `html` or `markdown`.

Step 1 — request export. Replace `<your-doc-id>` and `<your-page-id>`:

```bash
curl -s -X POST "https://coda.io/apis/v1/docs/<your-doc-id>/pages/<your-page-id>/export" --header "Authorization: Bearer $CODA_TOKEN" --header "Content-Type: application/json" -d @/tmp/coda_export.json | jq '{id, status, href}'
```

Step 2 — poll until `status` is `complete`, then download. Replace `<your-request-id>`:

```bash
curl -s "https://coda.io/apis/v1/docs/<your-doc-id>/pages/<your-page-id>/export/<your-request-id>" --header "Authorization: Bearer $CODA_TOKEN" | jq '{status, downloadLink}'
```

When `status: "complete"`, fetch the content from `downloadLink` (no auth header needed — it is a pre-signed URL):

```bash
curl -s "<download-link-from-previous-response>"
```

Docs: https://coda.io/developers/apis/v1#tag/Pages/operation/beginContentExport

### List Tables in a Doc

Replace `<your-doc-id>`:

```bash
curl -s "https://coda.io/apis/v1/docs/<your-doc-id>/tables?limit=50" --header "Authorization: Bearer $CODA_TOKEN" | jq '.items[] | {id, name, tableType, rowCount, browserLink}'
```

Use `tableType=table` to exclude views, or `tableType=view` to list only views.

Docs: https://coda.io/developers/apis/v1#operation/listTables

### Get a Table

Replace `<your-doc-id>` and `<your-table-id>` (the table ID from the previous call, or the table name):

```bash
curl -s "https://coda.io/apis/v1/docs/<your-doc-id>/tables/<your-table-id>" --header "Authorization: Bearer $CODA_TOKEN" | jq '{id, name, rowCount, displayColumn: .displayColumn.name}'
```

Docs: https://coda.io/developers/apis/v1#operation/getTable

### List Columns

Replace `<your-doc-id>` and `<your-table-id>`:

```bash
curl -s "https://coda.io/apis/v1/docs/<your-doc-id>/tables/<your-table-id>/columns" --header "Authorization: Bearer $CODA_TOKEN" | jq '.items[] | {id, name, display, format: .format.type}'
```

Docs: https://coda.io/developers/apis/v1#operation/listColumns

### List Rows

Replace `<your-doc-id>` and `<your-table-id>`:

```bash
curl -s "https://coda.io/apis/v1/docs/<your-doc-id>/tables/<your-table-id>/rows?useColumnNames=true&limit=100" --header "Authorization: Bearer $CODA_TOKEN" | jq '.items[] | {id, name, values}'
```

Key query parameters:

- `useColumnNames=true` — return column names as keys in `values` (default keys are column IDs)
- `valueFormat=simpleWithArrays` — easier-to-read values (`simple`, `simpleWithArrays`, or `rich`)
- `query=<column-id-or-name>:"<value>"` — server-side filter, e.g. `query=Status:"Done"`
- `sortBy=natural` — preserve table's natural sort order
- `limit=<1-500>` and `pageToken=<token>` for pagination

Docs: https://coda.io/developers/apis/v1#operation/listRows

### Get a Single Row

Replace `<your-doc-id>`, `<your-table-id>`, and `<your-row-id>`:

```bash
curl -s "https://coda.io/apis/v1/docs/<your-doc-id>/tables/<your-table-id>/rows/<your-row-id>?useColumnNames=true" --header "Authorization: Bearer $CODA_TOKEN" | jq '{id, name, values}'
```

Docs: https://coda.io/developers/apis/v1#operation/getRow

### Insert / Upsert Rows

Rows use the `cells` array with `column` set to either the column ID or column name.

Write to `/tmp/coda_rows.json`:

```json
{
  "rows": [
    {
      "cells": [
        {"column": "Name", "value": "Design review"},
        {"column": "Status", "value": "In Progress"},
        {"column": "Due", "value": "2026-05-01"}
      ]
    },
    {
      "cells": [
        {"column": "Name", "value": "Launch plan"},
        {"column": "Status", "value": "To Do"}
      ]
    }
  ],
  "keyColumns": ["Name"]
}
```

- Omit `keyColumns` to always insert new rows.
- Include `keyColumns` (array of column IDs/names) to upsert: rows matching on those columns are updated instead of inserted.

Replace `<your-doc-id>` and `<your-table-id>`:

```bash
curl -s -X POST "https://coda.io/apis/v1/docs/<your-doc-id>/tables/<your-table-id>/rows?disableParsing=false" --header "Authorization: Bearer $CODA_TOKEN" --header "Content-Type: application/json" -d @/tmp/coda_rows.json | jq '{requestId, addedRowIds}'
```

Returns `202 Accepted` with a `requestId`. Writes are asynchronous — poll `/apis/v1/docs/{docId}/mutationStatus/{requestId}` for completion if you need to block on it.

Docs: https://coda.io/developers/apis/v1#operation/upsertRows

### Update a Row

Write to `/tmp/coda_row_update.json`:

```json
{
  "row": {
    "cells": [
      {"column": "Status", "value": "Done"}
    ]
  }
}
```

Replace `<your-doc-id>`, `<your-table-id>`, and `<your-row-id>`:

```bash
curl -s -X PUT "https://coda.io/apis/v1/docs/<your-doc-id>/tables/<your-table-id>/rows/<your-row-id>" --header "Authorization: Bearer $CODA_TOKEN" --header "Content-Type: application/json" -d @/tmp/coda_row_update.json | jq '{id, requestId}'
```

Docs: https://coda.io/developers/apis/v1#operation/updateRow

### Delete Rows

Write to `/tmp/coda_delete.json`:

```json
{
  "rowIds": ["i-aBcDeFg", "i-HiJkLmN"]
}
```

Replace `<your-doc-id>` and `<your-table-id>`:

```bash
curl -s -X DELETE "https://coda.io/apis/v1/docs/<your-doc-id>/tables/<your-table-id>/rows" --header "Authorization: Bearer $CODA_TOKEN" --header "Content-Type: application/json" -d @/tmp/coda_delete.json | jq '{requestId, rowIds}'
```

Docs: https://coda.io/developers/apis/v1#operation/deleteRows

### Check Mutation Status

Write operations are asynchronous and return a `requestId`. Poll this endpoint until `completed: true` before assuming a mutation is applied.

Replace `<your-doc-id>` and `<your-request-id>`:

```bash
curl -s "https://coda.io/apis/v1/docs/<your-doc-id>/mutationStatus/<your-request-id>" --header "Authorization: Bearer $CODA_TOKEN" | jq '{completed, warning}'
```

Docs: https://coda.io/developers/apis/v1#operation/getMutationStatus

### Resolve a Coda URL

Translate a browser URL (e.g. a doc link copied from the Coda UI) into its corresponding API resource (doc, page, table, column, or row) with IDs.

```bash
curl -s "https://coda.io/apis/v1/resolveBrowserLink?url=<url-encoded-coda-link>" --header "Authorization: Bearer $CODA_TOKEN" | jq '{type, resource: .resource.id, href: .resource.href}'
```

Docs: https://coda.io/developers/apis/v1#operation/resolveBrowserLink

## Finding IDs

- **Doc ID**: the 10-character code after `_d` in a Coda URL. `https://coda.io/d/My-Doc_dABC123xyz0/...` → doc ID is `ABC123xyz0`.
- **Page ID**: last segment after `#_` in a page URL, or use **List Pages** above.
- **Table / Column / Row IDs**: prefixed strings like `grid-...`, `c-...`, `i-...`. Use the list endpoints above, or **Resolve a Coda URL** to extract them from a browser link.

Alternative to IDs: most row/table endpoints also accept the human-readable name (e.g. `Tasks` instead of `grid-xyz`). Pass `useColumnNames=true` on row reads to return column names in the `values` object.

## Value Formats

When reading rows, the `valueFormat` query parameter controls how cell values are serialized:

| Value | Description |
|---|---|
| `simple` | Scalars as primitives; references rendered as display values |
| `simpleWithArrays` | Same as `simple` but multi-value cells stay as arrays (recommended) |
| `rich` | Full structured objects with formatting metadata — verbose |

When writing rows, wrap string/number/boolean values directly: `{"column": "Name", "value": "Foo"}`. For multi-select or lists, pass an array: `{"column": "Tags", "value": ["red", "blue"]}`. Date columns accept ISO-8601 strings.

## Pagination

List endpoints return a `nextPageToken` when there are more items:

```bash
# First request
curl -s "https://coda.io/apis/v1/docs?limit=50" --header "Authorization: Bearer $CODA_TOKEN" | jq '{items: (.items | length), nextPageToken}'

# Follow-up: replace <your-page-token> with the value above
curl -s "https://coda.io/apis/v1/docs?limit=50&pageToken=<your-page-token>" --header "Authorization: Bearer $CODA_TOKEN"
```

## Rate Limits

- Most endpoints: around 100 requests/minute per token
- Bulk writes: combine multiple rows into a single upsert request rather than one request per row
- 429 response → back off and retry with exponential delay

## API Reference

- API root: https://coda.io/developers/apis/v1
- API token settings: https://coda.io/account (Account Settings → API Settings)

More from vm0-ai/vm0-skills

SkillDescription
account-reconciliationPerform account reconciliations comparing general ledger balances against subledgers, bank statements, or external records. Use for bank reconciliation, GL-to-subledger reconciliation, intercompany reconciliation, balance sheet reconciliation, reconciling item analysis, outstanding item aging, or clearing open items.
agentphoneBuild AI phone agents with AgentPhone API. Use when the user wants to make phone calls, send/receive SMS, manage phone numbers, create voice agents, set up webhooks, or check usage — anything related to telephony, phone numbers, or voice AI.
ahrefsAhrefs SEO API for backlink and keyword analysis. Use when user mentions
amplitudeAmplitude product analytics API. Use when user mentions "Amplitude",
analysis-qaQuality-check a data analysis before sharing — verify joins, aggregations, denominators, time ranges, and metric definitions. Detect pitfalls like survivorship bias, average-of-averages, join explosion, timezone mismatches, incomplete periods, and selection bias. Includes documentation templates for reproducible analyses.
anthropic-managed-agentsAnthropic Managed Agents API for programmatically creating, running, and streaming AI agents on Anthropic's cloud infrastructure. Use when the user mentions "Managed Agents", "Anthropic agent sessions", or needs to create/run/stream an Anthropic agent with tool use (bash, git, web), attach GitHub repositories, or inject secrets via Vault. Do NOT use for standard Claude Messages API — use the Claude API skill instead.
apifyApify web scraping platform. Use when user mentions "scrape website",
asanaAsana API for tasks and projects. Use when user mentions "Asana", "asana.com",
atlassianAtlassian API for Confluence and Jira. Use when user mentions "Confluence
attioAttio REST API for AI-native CRM operations — manage companies, people, deals, and custom objects, plus notes, tasks, lists, and comments. Use when the user mentions "Attio", "CRM record", "create company", "add person", "list entry", "CRM note", or "CRM task".