Announcing CIMD support for MCP Client registration
Learn more

Coda MCP vs Coda API for AI Agents

Hrishikesh Premkumar
Founding Architect

Coda, renamed Superhuman Docs in July 2026, now hosts an official MCP server that Claude and ChatGPT connect to with one click, and it also has a REST API that has been stable since 2020. The MCP server can build a table from scratch; the API cannot. The API can sync only the rows that changed since your last run; the MCP server has no equivalent. And the two paths authenticate users in completely different ways. This guide compares what each lets an agent do, the credential each one puts you on, and how to run either across many customers in production.

TL;DR

  • Coda, renamed Superhuman Docs on July 8, 2026, ships an official hosted MCP server at https://docs.superhuman.com/apis/mcp. It went into public beta on April 1, 2026 and opened to everyone with the rename. Its 32 tools build and edit docs: create tables and columns, configure views and charts, add comments, evaluate formulas, and delete docs, pages, tables, and rows.
  • The Coda REST API v1 covers a different surface. It reads and writes rows, pages, formulas, controls, folders, and sharing settings, supports incremental row sync with syncToken, page export to Markdown or HTML, button pushes, and webhook-invoked automations. It cannot create a table or a column, and every write is asynchronous: a 202 with a requestId you confirm through mutationStatus.
  • The auth models diverge. The MCP server supports OAuth 2 with full or limited (workspace and folder) access, plus personal tokens with an MCP restriction type. The REST API takes only a Bearer API token, which can be restricted to read, write, or both, and to a single doc or table. There is no OAuth path for the REST API.
  • MCP is the faster path for a person asking Claude or Cursor to restructure a doc or summarize a tracker. The API is the right path for headless agents, deterministic syncs, and anything that has to live within per-user limits of 10 writes per 6 seconds and 5 doc content writes per 10 seconds.
  • Scalekit's coda connector exposes 37 API tools (26 read, 11 write, 0 destructive) and stores each user's Coda API token in the token vault, so the MCP vs API decision doesn't change your auth infrastructure.

The decision your Coda agent forces first

Your agent needs to work inside your customers' Coda docs: read the launch tracker, update the status of 40 rows after standup, append meeting notes to the right page, or press the button that files a Jira ticket from a row. Coda now hosts an official MCP server that Claude and ChatGPT connect to with one click, and it also has a REST API that has been stable since 2020. The MCP server can build a table from scratch; the API can't. The API can sync only the rows that changed since your last run; the MCP server has no equivalent. And the two paths authenticate users in completely different ways. Here's how to pick.

What Coda MCP and Coda API actually are

Coda MCP

The Superhuman Docs MCP server is a remote Model Context Protocol (MCP) server maintained by Superhuman (formerly Coda, part of the company formerly called Grammarly). It was announced in public beta on April 1, 2026, gained charts, comment resolution, and richer view configuration in a June 4 update, and was opened to all users when Coda became Superhuman Docs. The server URL is https://docs.superhuman.com/apis/mcp; existing connections to the legacy coda.io endpoint keep working.

The tools reference lists 32 tools, including search, document_create, document_outline, page_create, table_create, table_rows_read, table_rows_manage, table_columns_manage, table_view_manage, content_read, content_modify, comment_add, formula_execute, and control_manage, along with delete tools for documents, pages, tables, rows, columns, views, comments, and controls. The same page warns that tool names and parameters may change and tells agent builders to discover tools at runtime with listTools rather than hardcoding names.

Clients connect with OAuth 2 (Claude, ChatGPT, Cursor) or with a personal access token whose restriction type is set to MCP (Codex, Copilot Studio, Google Antigravity). The connection guide is the official setup reference.

Coda API

The Coda API v1 is a REST API at https://coda.io/apis/v1. It covers docs, folders, pages, tables, columns, rows, formulas, controls, sharing and permissions, publishing, analytics, page content export, and a whoami endpoint. Row operations include list, get, insert, upsert by key columns, update, and delete. Two action endpoints matter for agents: pushing a button column on a row, and triggering a doc automation whose trigger is "Webhook invoked."

Authentication is a Bearer API token created under API settings on the user's Coda account page. A token can do everything its creator can do unless it is restricted to read only, write only, or read and write, and optionally to one doc or one table. The API is free on both free and paid plans, though creating a doc requires the token owner to be a Doc Maker.

What your agent can actually do

Capability
Coda MCP
Coda API
Search across docs, pages, tables, and rows
Yes
Limited: Doc list filter and single-column row filter
Read rows with filters and pagination
Yes: Max 100 rows per call
Yes
Insert, upsert, or update rows
Yes: Up to 100 rows per add
Yes: Insert, upsert by key columns, update
Incremental sync of changed rows
No
Yes: Sync token from a prior full listing
Create a table or add columns
Yes
No: No table or column creation in v1
Configure views, filters, and charts
Yes
No
Read and write page content
Yes
Yes: Page content list, page update with append, prepend, or replace
Export a page to Markdown or HTML
Limited: Returns content, no file export
Yes
Evaluate an arbitrary formula
Yes
Limited: Reads named formula values only
Push a row button or trigger an automation
No
Yes
Comments
Yes
No
Inspect sharing and permissions
No
Yes
Delete docs, pages, tables, or rows
Yes: Delete tools for each
Limited: API deletes docs and rows; the Scalekit connector exposes no destructive tools

The pattern is clear. The MCP server is a doc builder: it shapes structure, views, and content the way a person in the editor would. The API is a data plane: it moves rows in and out reliably, tracks what changed, and fires the actions a doc author already defined. Neither is a superset of the other.

What the MCP path makes easier

Building a doc. "Turn these meeting notes into a tracker with Owner, Status, and Due date columns, grouped by Status" is one conversational turn on the MCP path, because table_create, table_columns_manage, and table_view_manage exist. On the API path, that request is impossible without a human creating the table first.

The MCP server also handles name resolution that the API pushes onto you. name_match turns "the Q4 roadmap table" into a table ID, and search spans docs, pages, and rows at once. On the API, the agent chains coda_docs_list, coda_tables_list, and coda_columns_list before it can write a single cell, and Coda discourages addressing tables and columns by name because users rename them.

Writing the schema is the hard part. Not the API call. Superhuman wrote these schemas and keeps them current; the tradeoff is that they may change during the server's development, so anything you hardcode against them can break without a version bump.

The auth path each one puts you on

Coda MCP: OAuth with folder-level limits, or a typed personal token

OAuth 2 is the default for Claude, ChatGPT, and Cursor. The scope is always read and write, but during consent the user picks Full access or Limited access, and Limited access restricts the connection to specific workspaces or folders. That folder boundary is the most useful least-privilege control on either path, because it bounds which docs the agent can touch, not just which verbs it can use.

The alternative is a personal access token with its restriction type set to MCP, which can be read only, write only, or read and write. The token type matters: a token created for the REST API returns 401 against the MCP endpoint, so the two paths need separate credentials even for the same user.

Plan and role shape what the credential can do. Doc Makers on paid plans get every tool. Doc Makers on free plans get 30 requests per week, capped at 60 per month. Editors get the same allowance and read tools only. A B2B agent whose customers include free-plan users will hit that ceiling in the first afternoon.

Coda API: a Bearer token per user, and nothing else

The REST API has no OAuth flow. Each user generates an API token in their account settings and hands it to your app. By default that token can do anything the user can do, across every doc they can open, until they delete it. A token can't be viewed again after creation.

Restrictions are the mitigation, and they are good ones. A token restricted to read only on /docs/AbCDeFGH/tables/grid-pqRst-U can read one table and nothing else. For an agent that only syncs a single tracker, ask users for exactly that. The cost is onboarding friction: users must create the right token, with the right restriction, for the right doc, by hand. whoami returns a scoped boolean and the tokenName, so your app can at least verify whether a pasted token is restricted before storing it.

What the user can't do, the agent can't do. On Coda, an unrestricted token makes that the only boundary there is.

Both paths still need per-user isolation

In a multi-tenant B2B agent, MCP gives you a credential per user and the direct API gives you a credential per user. In neither case does the path itself solve storage, rotation, or revocation; those are infrastructure problems regardless of which path you choose.

Coda sharpens the API side. A long-lived Bearer token has no refresh cycle that would surface a revoked grant. If a user deletes the token in Coda, your agent finds out on the next call with a 401, mid-task. Until then, it keeps working for as long as it sits in your database.

Recommended reading: OAuth vs API keys for AI agents

What you own in production

On the MCP path

Superhuman hosts the server, owns the tool schemas, and runs the OAuth authorization server. You own the client side: storing each user's OAuth tokens or personal MCP tokens, handling consent that a user narrowed to two folders when the agent needs a third, and handling the free-plan request allowance when it runs out.

You also own drift. The tools reference says names and parameters may change. An agent that discovers tools at runtime survives that; a deterministic workflow that calls table_rows_manage with a fixed argument shape may not. And the MCP server is session-bound: it has no triggers or schedules, and it does nothing until a client calls it.

On the API path

You own the asynchronous write model. Every insert, upsert, update, page change, button push, and automation trigger returns 202 Accepted with a requestId. The change usually lands within a few seconds, but it can fail after you received the 202. An agent that writes a row and immediately reads it back may see stale data; one that reports "updated" without checking mutationStatus may be wrong. Edits made in the Coda UI also take a few seconds to appear in the API.

You own rate limits, which are per user and shared across all docs: reading data, 100 requests per 6 seconds; writing data, 10 requests per 6 seconds; writing doc content, 5 requests per 10 seconds; listing docs, 4 requests per 6 seconds. A 429 means back off and retry. Batch row writes into one upsert rather than one call per row, or a 40-row standup update becomes four 6-second windows of throttling.

You own ID hygiene. Doc IDs come from the URL segment after _d, table IDs look like grid-pqRst-U, columns like c-tuVwxYz, rows like i-tuVwxYz. Names work but break silently when someone renames a column, so a production agent resolves names to IDs once and stores the IDs.

Schema drift and versioning

The REST API is versioned as v1, and Coda commits to a three-month deprecation notice for removals. That stability is the reason Superhuman's own staff recommend the API over the MCP server for deterministic scripts. The MCP server moves faster: four months after the beta opened it had gained charts, view layouts, and conditional formatting. Faster features, less predictable contracts.

When to use MCP, when to use the API

Use Coda MCP when

  • A product manager asks Claude to turn a page of interview notes into a structured feedback table with a Theme column and a grouped view, which requires table_create and table_view_manage.
  • A user in Cursor or Claude Code wants their agent to read the engineering roadmap doc and append a status section, interactively, with consent limited to the Engineering folder.
  • A team wants comment-aware workflows: summarize open comment threads on a spec and resolve the ones that were addressed.
  • You are prototyping a Coda assistant and want to learn which questions users actually ask before you commit to API tools, and your users are Doc Makers on paid plans.

Use the Coda API when

  • The agent runs headless: a nightly job uses syncToken to pull only the tracker rows that changed and posts a digest to Slack.
  • The workflow is a deterministic pipeline, such as upserting CRM deals into a Coda table keyed on Deal ID, where idempotency and mutationStatus checks matter.
  • The agent should fire logic the doc author already built: pressing a "File bug" button column with coda_row_button_push, or invoking a webhook automation with coda_automation_trigger and a JSON payload.
  • You need a credential scoped to one table, read only, so a compromised token exposes one tracker rather than a user's whole workspace.

The credential problem that exists on both paths

Both paths give you a Coda credential per user. Neither gives you a vault. A multi-tenant agent with 80 customers and 6 users each holds 480 Coda credentials. On the API path, each is a long-lived Bearer token that grants everything the user can do unless they restricted it, has no refresh cycle, and can be deleted by the user at any time without notice to you. On the MCP path, each is an OAuth grant or a typed personal token, with its own expiry and refresh behavior.

Every one of those must be encrypted at rest, kept out of logs and out of LLM context, mapped to exactly one user and one customer, and destroyed when the user disconnects or the customer churns. The tempting shortcut is one admin's token for the whole customer. That turns every user's agent into the admin, collapses your audit trail to one identity, and gives one leaked string access to every doc that admin can open.

Scalekit's coda connector collects each user's Coda API token through a hosted page, stores it in the token vault, and injects it on every call, so the MCP vs API decision doesn't change your auth infrastructure. Your agent passes an identifier; the token never enters the agent runtime or the LLM context.

Recommended reading: why a token vault matters for AI agent workflows

How to connect a Coda agent with Scalekit

Scalekit's coda connector is an API-path connector with 37 tools: 26 read and 11 write. It has no destructive tools, so an agent built on it cannot delete a doc, page, or row through a prebuilt tool. Users authenticate with their Coda API token, entered once on a Scalekit-hosted page and stored as that user's connected account.

Prerequisites and the connection name

In the Scalekit dashboard, go to AgentKit > Connections and create a Coda connection. There is no OAuth client to register, because Coda's API uses per-user tokens. The connection name you choose is the connection_name your code passes, and the string must match the dashboard exactly. This is the most common integration error.

Copy SCALEKIT_ENVIRONMENT_URL, SCALEKIT_CLIENT_ID, and SCALEKIT_CLIENT_SECRET from Developers > API Credentials into .env. Tell your users to create a Coda API token under API settings on their Coda account page, restricted to the narrowest doc or table and access level your agent needs.

pip install scalekit-sdk-python python-dotenv langchain-openai

Authorize each user once

get_or_create_connected_account returns the user's connected account. If it is not ACTIVE, get_authorization_link returns a link to a Scalekit-hosted page. For an API token connector like Coda, that page asks the user to paste their token instead of running an OAuth consent flow. Your app never sees the token, and it never appears in chat or agent context.

import os from dotenv import load_dotenv from scalekit import ScalekitClient load_dotenv() scalekit_client = ScalekitClient( env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"], client_id=os.environ["SCALEKIT_CLIENT_ID"], client_secret=os.environ["SCALEKIT_CLIENT_SECRET"], ) actions = scalekit_client.actions CONNECTION_NAME = "coda" # must match the connection name in AgentKit > Connections identifier = "user_123" # your app's stable ID for this user account = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=identifier, ) if account.connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name=CONNECTION_NAME, identifier=identifier, ) print("Connect your Coda API token:", link.link) input("Press Enter after saving the token on the Scalekit page...") account = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=identifier, ) if account.connected_account.status != "ACTIVE": raise RuntimeError(f"Coda is {account.connected_account.status}, not ACTIVE") me = actions.execute_tool( tool_name="coda_current_user_get", tool_input={}, connection_name=CONNECTION_NAME, identifier=identifier, ) print(me.execution_id, me.data)

The coda_current_user_get call wraps Coda's whoami, so the response tells you whose token this is and whether it is scoped. Check it before the first real run.

Retrieve the Coda tools this user can call

This is not a catalog load. The agent asks for the tools this user's connected account is authorized to call, then narrows them to the five a tracker-review agent needs. coda_rows_upsert, coda_row_button_push, and coda_automation_trigger never reach the model, so it cannot rewrite rows or fire a doc's automations while answering a question about status.

tools = actions.langchain.get_tools( identifier=identifier, connection_names=[CONNECTION_NAME], tool_names=[ "coda_docs_list", "coda_tables_list", "coda_columns_list", "coda_rows_list", "coda_row_get", ], page_size=100, ) tool_map = {t.name: t for t in tools} print(sorted(tool_map))

Run the agent loop

The loop runs until the model stops requesting tools. Every tool call executes with this user's Coda token, injected by Scalekit. The doc ID comes from your app's config for this customer; it is the part of the doc URL after _d.

from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage from langchain_openai import ChatOpenAI doc_id = os.environ["CODA_DOC_ID"] llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [ SystemMessage( "You review Coda project trackers. Call coda_tables_list and coda_columns_list " "before reading rows so you use table and column IDs, not names." ), HumanMessage( f"In doc {doc_id}, find every row in the Launch Tracker table whose Status is " "Blocked. For each, give the owner, the due date, and the blocker note." ), ] while True: response = llm.invoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tc in response.tool_calls: tool = tool_map.get(tc["name"]) if tool: result = tool.invoke(tc["args"]) else: result = f"Unknown tool: {tc['name']}" messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))

Deterministic calls with execute_tool

For pipelines, skip the model. execute_tool runs one tool with a fixed input and returns data plus an execution_id you can log. This job pages through a table once, keeps the nextSyncToken Coda returns at the end of the full listing, and on later runs fetches only the rows that changed.

table_id = os.environ["CODA_TABLE_ID"] # for example grid-pqRst-U, from coda_tables_list def list_rows(sync_token=None): rows, page_token, next_sync = [], None, None while True: tool_input = { "doc_id": doc_id, "table_id_or_name": table_id, "limit": 100, "value_format": "simpleWithArrays", } if page_token: tool_input["page_token"] = page_token if sync_token: tool_input["sync_token"] = sync_token result = actions.execute_tool( tool_name="coda_rows_list", tool_input=tool_input, connection_name=CONNECTION_NAME, identifier=identifier, ) rows.extend(result.data.get("items", [])) page_token = result.data.get("nextPageToken") next_sync = result.data.get("nextSyncToken") or next_sync if not page_token: return rows, next_sync all_rows, sync_token = list_rows() print(f"Full listing: {len(all_rows)} rows") # Persist sync_token per user and table; the next scheduled run calls: changed_rows, sync_token = list_rows(sync_token=sync_token) print(f"Changed since last run: {len(changed_rows)} rows")

Writes are asynchronous. coda_rows_upsert with key_columns updates the row whose key matches and inserts it otherwise, so a retry after a timeout doesn't create duplicates. The response carries a requestId; coda_mutation_status_get confirms the change actually applied.

import time upsert = actions.execute_tool( tool_name="coda_rows_upsert", tool_input={ "doc_id": doc_id, "table_id_or_name": table_id, "key_columns": ["Ticket"], "rows": [ { "cells": [ {"column": "Ticket", "value": "ENG-4182"}, {"column": "Status", "value": "In review"}, {"column": "Owner", "value": "priya@acme.com"}, ] }, { "cells": [ {"column": "Ticket", "value": "ENG-4190"}, {"column": "Status", "value": "Blocked"}, {"column": "Owner", "value": "dev@acme.com"}, ] }, ], }, connection_name=CONNECTION_NAME, identifier=identifier, ) request_id = upsert.data["requestId"] print(upsert.execution_id, request_id) for _ in range(10): status = actions.execute_tool( tool_name="coda_mutation_status_get", tool_input={"request_id": request_id}, connection_name=CONNECTION_NAME, identifier=identifier, ) if status.data.get("completed"): print("Applied:", status.data) break time.sleep(2) else: raise RuntimeError(f"Coda write {request_id} not confirmed after 20 seconds")

Column names keep this example readable. In production, resolve them once with coda_columns_list and store the c- IDs, because a renamed column breaks a name-based write.

To run logic the doc already owns, trigger an automation whose trigger is set to "Webhook invoked." The rule ID comes from the automation's settings in the doc; the payload is whatever JSON the automation reads.

run = actions.execute_tool( tool_name="coda_automation_trigger", tool_input={ "doc_id": doc_id, "rule_id": os.environ["CODA_AUTOMATION_RULE_ID"], # for example grid-auto-b3Jmey6jBS "payload": {"ticket": "ENG-4190", "reason": "Blocked for 3 days"}, }, connection_name=CONNECTION_NAME, identifier=identifier, ) print(run.execution_id, run.data)

When a capability has no prebuilt tool, such as deleting a row, Scalekit's API proxy calls Coda's REST endpoint directly with the same user's vaulted token, so the destructive operation stays an explicit choice in your code rather than a tool the model can pick.

Multi-tool, multi-tenant Coda agents on a virtual MCP server

The coda connector has 37 tools. At roughly 200 tokens each, that is about 7,400 tokens of tool definitions before the agent reads a row, and a model choosing among coda_row_update, coda_rows_upsert, coda_row_button_push, and coda_automation_trigger will sometimes choose wrong. A digest agent needs three of them. A Scalekit virtual MCP server declares exactly which tools an agent role can see, and session tokens keep each run bound to one user's connected accounts. Docs: set up a virtual MCP server.

Create the server once per agent role

from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping vmcp = actions.mcp.create_config( name="coda-tracker-digest", description="Reads changed rows in a Coda tracker and posts a status digest to Slack", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name="coda", tools=[ "coda_tables_list", "coda_columns_list", "coda_rows_list", ], ), McpConfigConnectionToolMapping( connection_name="slack", tools=["slack_send_message"], ), ], ) print(vmcp.config.id, vmcp.config.mcp_server_url)

Mint a session token per user, per run

One server definition serves every user. Before each run, check that the user's accounts are active and mint a short-lived session token scoped to them. Any MCP client that speaks Streamable HTTP can connect; this example uses langchain-mcp-adapters (pip install langchain-mcp-adapters) and passes the token as a Bearer header.

import asyncio from datetime import timedelta from langchain_mcp_adapters.client import MultiServerMCPClient config_id = vmcp.config.id mcp_server_url = vmcp.config.mcp_server_url user_id = "user_123" accounts = actions.mcp.list_mcp_connected_accounts( config_id=config_id, identifier=user_id, include_auth_link=True, ) not_ready = [ a for a in accounts.connected_accounts if str(a.connected_account_status).upper() != "ACTIVE" ] for a in not_ready: print(f"{a.connection_name} is {a.connected_account_status}: {a.authentication_link}") if not_ready: raise RuntimeError("Connect the accounts above before starting this run") session_token = actions.mcp.create_session_token( mcp_config_id=config_id, identifier=user_id, expiry=timedelta(minutes=30), ).token async def load_tools(): client = MultiServerMCPClient( { "scalekit": { "transport": "streamable_http", "url": mcp_server_url, "headers": {"Authorization": f"Bearer {session_token}"}, } } ) return await client.get_tools() mcp_tools = asyncio.run(load_tools()) print([t.name for t in mcp_tools])

The agent sees four tools, not 37. The endpoint is static; the identity is per-user. This also gives you an MCP endpoint backed by Coda's versioned REST API rather than a beta tool surface, with a credential that can be restricted to one table.

Observability: every Coda action tied to a user

Every execute_tool call returns an execution_id, and every execution is attributed to a connected account, which maps to one of your users and one customer. Coda adds its own reference: each write returns a requestId. Log both together, and when a customer asks which agent run changed 40 rows in their launch tracker or pressed the button that filed duplicate tickets, the answer is a lookup, not an investigation.

A shared token breaks that. If every user's agent runs on one admin's Coda token, Coda's doc history shows the admin making every edit, and your logs can only prove that the shared token did it. Shared credentials are a single-user solution. They do not survive a second user.

Recommended reading: audit trails for agent auth

Which one to build against

If your agent is a person's assistant inside Claude, ChatGPT, or Cursor, building tables, restructuring pages, and working through comments on request, use the Superhuman Docs MCP server: it can do things the API cannot, and its folder-limited OAuth is a sound boundary for interactive use. If your agent runs on a schedule, syncs rows, presses buttons, or triggers automations for many customers, build against the Coda API: you get syncToken, versioned contracts, and tokens restricted to one table. Many teams will end up with both, the MCP server for authoring and the API for operations. Either way, every user brings a Coda credential that has to be stored, scoped, and revoked, and that's what needs production-grade infrastructure.

FAQs

Does Coda have an official MCP server?

Yes. Superhuman Docs, formerly Coda, hosts it at https://docs.superhuman.com/apis/mcp. It went into public beta on April 1, 2026 and opened to everyone with the rename on July 8, 2026. Its 32 tools build and edit docs: create tables and columns, configure views and charts, add and resolve comments, evaluate formulas, and delete docs, pages, tables, and rows. Superhuman warns that tool names and parameters may change.

What can the Coda API do that the MCP server cannot?

Incremental row sync with a syncToken, page export to Markdown or HTML, pushing a button column on a row, triggering a webhook-invoked automation, and inspecting sharing and permissions. It cannot create a table or a column, and every write is asynchronous: a 202 with a requestId that you confirm through the mutation status endpoint.

How does Coda authentication differ between MCP and the API?

The MCP server supports OAuth 2 with Full or Limited access, where Limited restricts the connection to specific workspaces or folders, plus personal tokens with an MCP restriction type. The REST API takes only a Bearer API token, which can be restricted to read, write, or both and to a single doc or table. A token created for the REST API returns 401 against the MCP endpoint, so the two paths need separate credentials.

What are the Coda API rate limits?

Per user, shared across all docs: 100 read requests per 6 seconds, 10 write requests per 6 seconds, 5 doc content writes per 10 seconds, and 4 doc listings per 6 seconds. Batching row writes into one upsert matters, because a 40-row update sent one call at a time spans four throttling windows.

When should an agent use the Coda API instead of MCP?

When it runs headless on a schedule and pulls only changed rows with a syncToken, when it upserts CRM records keyed on a column with idempotency and mutation checks, when it should fire logic the doc author built through buttons and automations, or when it needs a credential scoped to one table so a compromised token exposes one tracker rather than a workspace.

How does Scalekit connect an AI agent to Coda?

The coda connector exposes 37 tools over the Coda API with no destructive tools. Each user pastes their Coda API token once on a Scalekit-hosted page, Scalekit stores it in the token vault and injects it on every call, coda_current_user_get reports whether the token is scoped, a virtual MCP server narrows the tools per agent role, and every execution returns an execution_id next to Coda's requestId.

Build your Coda agent with Scalekit

Browse the Scalekit Coda connector and the Coda connector docs with the full tool list. See Scalekit pricing, or talk to us.

No items found.
On this page
Share this article
Scalekit
Table-scoped tokens

Acquire enterprise customers with
‍zero upfront cost.

Every feature unlocked. No hidden fees.