coda-apiCoda API integration with managed OAuth. Manage docs, pages, tables, rows, and formulas. Use this skill when users want to read, create, update, or delete Coda docs, pages, tables, or rows. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).
Install via ClawdBot CLI:
clawdbot install byungkyu/coda-apiAccess the Coda API with managed OAuth authentication. Manage docs, pages, tables, rows, formulas, and controls with full CRUD operations.
# List your docs
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/coda/apis/v1/docs')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
https://gateway.maton.ai/coda/apis/v1/{resource}
Replace {resource} with the actual Coda API endpoint path. The gateway proxies requests to coda.io/apis/v1 and automatically injects your OAuth token.
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Manage your Coda OAuth connections at https://ctrl.maton.ai.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=coda&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'coda'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "f46d34b1-3735-478a-a0d7-54115a16cd46",
"status": "ACTIVE",
"creation_time": "2026-02-12T01:38:10.500238Z",
"last_updated_time": "2026-02-12T01:38:33.545353Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "coda",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple Coda connections, specify which one to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/coda/apis/v1/docs')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', 'f46d34b1-3735-478a-a0d7-54115a16cd46')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If omitted, the gateway uses the default (oldest) active connection.
GET /coda/apis/v1/whoami
Returns information about the authenticated user.
GET /coda/apis/v1/docs
Query parameters:
isOwner - Show only owned docs (true/false)query - Search querysourceDoc - Filter by source doc IDisStarred - Show only starred docsinGallery - Show only gallery docsworkspaceId - Filter by workspacefolderId - Filter by folderlimit - Page size (default: 25, max: 200)pageToken - Pagination tokenPOST /coda/apis/v1/docs
Content-Type: application/json
{
"title": "My New Doc",
"sourceDoc": "optional-source-doc-id",
"timezone": "America/Los_Angeles",
"folderId": "optional-folder-id"
}
GET /coda/apis/v1/docs/{docId}
DELETE /coda/apis/v1/docs/{docId}
GET /coda/apis/v1/docs/{docId}/pages
Query parameters:
limit - Page sizepageToken - Pagination tokenPOST /coda/apis/v1/docs/{docId}/pages
Content-Type: application/json
{
"name": "New Page",
"subtitle": "Optional subtitle",
"parentPageId": "optional-parent-page-id"
}
GET /coda/apis/v1/docs/{docId}/pages/{pageIdOrName}
PUT /coda/apis/v1/docs/{docId}/pages/{pageIdOrName}
Content-Type: application/json
{
"name": "Updated Page Name",
"subtitle": "Updated subtitle"
}
DELETE /coda/apis/v1/docs/{docId}/pages/{pageIdOrName}
GET /coda/apis/v1/docs/{docId}/tables
Query parameters:
limit - Page sizepageToken - Pagination tokensortBy - Sort by fieldtableTypes - Filter by table typeGET /coda/apis/v1/docs/{docId}/tables/{tableIdOrName}
GET /coda/apis/v1/docs/{docId}/tables/{tableIdOrName}/columns
Query parameters:
limit - Page sizepageToken - Pagination tokenGET /coda/apis/v1/docs/{docId}/tables/{tableIdOrName}/columns/{columnIdOrName}
GET /coda/apis/v1/docs/{docId}/tables/{tableIdOrName}/rows
Query parameters:
query - Filter rows by search queryuseColumnNames - Use column names instead of IDs in response (true/false)valueFormat - Value format (simple, simpleWithArrays, rich)sortBy - Sort by columnlimit - Page sizepageToken - Pagination tokenGET /coda/apis/v1/docs/{docId}/tables/{tableIdOrName}/rows/{rowIdOrName}
Query parameters:
useColumnNames - Use column names instead of IDsvalueFormat - Value formatPOST /coda/apis/v1/docs/{docId}/tables/{tableIdOrName}/rows
Content-Type: application/json
{
"rows": [
{
"cells": [
{"column": "Column Name", "value": "Cell Value"},
{"column": "Another Column", "value": 123}
]
}
],
"keyColumns": ["Column Name"]
}
keyColumns for upsert behavior (update if exists, insert if not)PUT /coda/apis/v1/docs/{docId}/tables/{tableIdOrName}/rows/{rowIdOrName}
Content-Type: application/json
{
"row": {
"cells": [
{"column": "Column Name", "value": "Updated Value"}
]
}
}
DELETE /coda/apis/v1/docs/{docId}/tables/{tableIdOrName}/rows/{rowIdOrName}
GET /coda/apis/v1/docs/{docId}/formulas
GET /coda/apis/v1/docs/{docId}/formulas/{formulaIdOrName}
GET /coda/apis/v1/docs/{docId}/controls
GET /coda/apis/v1/docs/{docId}/controls/{controlIdOrName}
GET /coda/apis/v1/docs/{docId}/acl/metadata
GET /coda/apis/v1/docs/{docId}/acl/permissions
POST /coda/apis/v1/docs/{docId}/acl/permissions
Content-Type: application/json
{
"access": "readonly",
"principal": {
"type": "email",
"email": "user@example.com"
}
}
Access values: readonly, write, comment
DELETE /coda/apis/v1/docs/{docId}/acl/permissions/{permissionId}
GET /coda/apis/v1/categories
GET /coda/apis/v1/resolveBrowserLink?url={encodedUrl}
Converts a Coda browser URL to API resource information.
GET /coda/apis/v1/mutationStatus/{requestId}
Check the status of an asynchronous mutation operation.
GET /coda/apis/v1/analytics/docs
Query parameters:
isPublished - Filter by published statussinceDate - Start date (YYYY-MM-DD)untilDate - End date (YYYY-MM-DD)limit - Page sizepageToken - Pagination tokenGET /coda/apis/v1/analytics/packs
GET /coda/apis/v1/analytics/updated
Coda uses cursor-based pagination with pageToken:
GET /coda/apis/v1/docs?limit=25
Response includes nextPageToken when more results exist:
{
"items": [...],
"href": "https://coda.io/apis/v1/docs?pageToken=...",
"nextPageToken": "eyJsaW1..."
}
Use the nextPageToken value as pageToken in subsequent requests.
Create, update, and delete operations return HTTP 202 with a requestId:
{
"id": "canvas-abc123",
"requestId": "mutate:9f038510-be42-4d16-bccf-3468d38efd57"
}
Check mutation status:
GET /coda/apis/v1/mutationStatus/mutate:9f038510-be42-4d16-bccf-3468d38efd57
Response:
{
"completed": true
}
Mutations are generally processed within a few seconds.
const response = await fetch(
'https://gateway.maton.ai/coda/apis/v1/docs?limit=10',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
console.log(data.items);
import os
import requests
response = requests.get(
'https://gateway.maton.ai/coda/apis/v1/docs',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
params={'limit': 10}
)
data = response.json()
for doc in data['items']:
print(f"{doc['name']}: {doc['id']}")
import os
import requests
headers = {'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
base_url = 'https://gateway.maton.ai/coda/apis/v1'
# Create doc
doc_response = requests.post(
f'{base_url}/docs',
headers=headers,
json={'title': 'My New Doc'}
)
doc = doc_response.json()
print(f"Created doc: {doc['id']}")
# Create page
page_response = requests.post(
f'{base_url}/docs/{doc["id"]}/pages',
headers=headers,
json={'name': 'First Page', 'subtitle': 'Created via API'}
)
page = page_response.json()
print(f"Created page: {page['id']}")
import os
import requests
headers = {'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
response = requests.post(
'https://gateway.maton.ai/coda/apis/v1/docs/{docId}/tables/{tableId}/rows',
headers=headers,
json={
'rows': [
{
'cells': [
{'column': 'Name', 'value': 'John Doe'},
{'column': 'Email', 'value': 'john@example.com'}
]
}
]
}
)
result = response.json()
print(f"Request ID: {result['requestId']}")
s0ekj2vV-vcanvas-curl -g when URLs contain brackets to disable glob parsingjq, environment variables may not expand correctly. Use Python examples instead.| Operation | Limit |
|-----------|-------|
| 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 |
| Reading analytics | 100 requests per 6 seconds |
| Status | Meaning |
|--------|---------|
| 400 | Missing Coda connection or invalid request |
| 401 | Invalid or missing Maton API key |
| 404 | Resource not found |
| 409 | Doc not yet accessible (just created) |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from Coda API |
MATON_API_KEY environment variable is set:echo $MATON_API_KEY
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
coda. For example:https://gateway.maton.ai/coda/apis/v1/docshttps://gateway.maton.ai/apis/v1/docsAI Usage Analysis
Analysis is being generated⦠refresh in a few seconds.
Use the mcporter CLI to list, configure, auth, and call MCP servers/tools directly (HTTP or stdio), including ad-hoc servers, config edits, and CLI/type generation.
Connect to 100+ APIs (Google Workspace, Microsoft 365, GitHub, Notion, Slack, Airtable, HubSpot, etc.) with managed OAuth. Use this skill when users want to...
Build, debug, and deploy websites using HTML, CSS, JavaScript, and modern frameworks following production best practices.
YouTube Data API integration with managed OAuth. Search videos, manage playlists, access channel data, and interact with comments. Use this skill when users want to interact with YouTube. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).
Scaffold, test, document, and debug REST and GraphQL APIs. Use when the user needs to create API endpoints, write integration tests, generate OpenAPI specs, test with curl, mock APIs, or troubleshoot HTTP issues.
Search for jobs across LinkedIn, Indeed, Glassdoor, ZipRecruiter, Google Jobs, Bayt, Naukri, and BDJobs using the JobSpy MCP server.