HTTP API

Use Grain’s supported HTTPS API for workspace SQL, registered commands, API tokens and Front Door shell/file access.


The supported developer-facing HTTP API is served from https://api.rungrain.com. Download or import the canonical OpenAPI 3.1 specification before generating a client. https://rungrain.com/api/openapi.json is the website origin and is not the API endpoint.

This is an HTTP/REST surface. It is separate from MCP, which is a local stdio server using MCP JSON-RPC and discovers its tool schemas through tools/list.

What the public API supports

CapabilityOperations
Manage workspace API tokensCreate, list and revoke mat_ tokens
Query workspace dataRun parameterized or raw SQLite SQL against the workspace database
Discover and execute app commandsList enabled commands, fetch a command JSON Schema, execute a batch of 1–10 calls
Reach an app runtime from an external agentCreate/list/revoke mfd_ Front Door tokens, inspect capabilities, execute argv, upload and download files

Artifact creation, checkout synchronization, push, history, promotion and sharing are CLI/MCP workflows. The public HTTP specification does not expose every route registered by the application: control-plane UI, auth, billing, organization, content-sync, sandbox and admin routes remain internal or transport-specific.

The operation IDs in the specification are:

Operation IDMethod and path
createWorkspaceApiToken, listWorkspaceApiTokensPOST/GET /api/workspaces/{workspaceId}/api-tokens
revokeWorkspaceApiTokenDELETE /api/workspaces/{workspaceId}/api-tokens/{tokenId}
listWorkspaceCommandsGET /api/workspaces/{workspaceId}/commands
getWorkspaceCommandSchemaGET /api/workspaces/{workspaceId}/commands/{commandId}/schema
executeWorkspaceCommandsPOST /api/workspaces/{workspaceId}/commands/execute
runWorkspaceSqlPOST /api/workspaces/{workspaceId}/sql
createWorkspaceFrontDoorToken, listWorkspaceFrontDoorTokensPOST/GET /api/workspaces/{workspaceId}/frontdoor-tokens
revokeWorkspaceFrontDoorTokenDELETE /api/workspaces/{workspaceId}/frontdoor-tokens/{tokenId}
getWorkspaceFrontDoorCapabilitiesGET /api/workspaces/{workspaceId}/frontdoor/capabilities
execWorkspaceFrontDoorShellPOST /api/workspaces/{workspaceId}/frontdoor/exec
uploadWorkspaceFrontDoorFile, downloadWorkspaceFrontDoorFilePUT/GET /api/workspaces/{workspaceId}/frontdoor/files

Authentication

Every API request except the OpenAPI document needs an Authorization header appropriate to its route:

CredentialPrefixUseHow it is obtained
Firebase ID tokenno Grain prefixFirst-party token management and user-authorized SQL/command/Front Door capability callsA signed-in Grain application or first-party client; the public docs do not define a third-party token-minting flow
Workspace API tokenmat_Scoped SQL and command discovery/execution for one workspaceAn existing first-party/admin token-management flow; the raw value is returned once
Front Door tokenmfd_Runtime capability, shell and file transfer callsGrain desktop Settings → Shell Access → Create for an owner or builder, or a first-party token-management flow

The CLI session token (mca_) is for the desktop/CLI control-plane session and is not a public API credential. Do not put any raw token in source control, browser code, URLs, command history or support messages. Rotate by revoking the token and creating a replacement.

Start with a supported credential

After installing Grain and running grain login, open the workspace in the Grain desktop app and choose Settings. General and API Instructions show the workspace’s Grain ID and API base URL. You can also read the selected ID with grain --json status; use the ID returned for the workspace, not a display name.

For a runtime shell or file-transfer integration, owners and builders can choose Settings → Shell Access → Create, choose a name and expiration, and copy the one-time agent handoff snippet. It exports GRAIN_FRONTDOOR_URL, GRAIN_FRONTDOOR_TOKEN, and the derived exec/files endpoints. The token is shown once and may be revoked from the same section. Check Settings → Agent Capabilities before relying on a runtime feature: static or otherwise unsupported workspaces cannot use Front Door.

There is no general API-key issuance page in the current public desktop settings flow. The API Instructions section supplies values and examples but does not mint a mat_ token. SQL and registered-command examples below therefore require a mat_ token that an authorized first-party or administrator has already issued. The Firebase ID token example is for first-party clients that already own a signed-in Firebase session; this repository does not promise a supported way for a new third-party integration to obtain one. Do not try to substitute a CLI mca_ token or a Front Door mfd_ token for a mat_ token.

Set the non-secret values once before using the examples below. Replace the workspace placeholder with the Grain ID shown in Settings → General or Settings → API Instructions:

export API_ORIGIN=https://api.rungrain.com
export WORKSPACE_ID=<workspace-id>

Create a workspace API token

For a first-party/admin client that already has a Firebase ID token, create a workspace API token as follows. role defaults to reader; the available role presets are reader, writer, schema_writer, command_runner and custom. A custom role needs explicit scopes. Omit expiresAt for the default 30-day lifetime, or send null for a token with no expiry; a timestamp must be in the future and no more than 90 days ahead.

export FIREBASE_ID_TOKEN=<short-lived-firebase-id-token>

curl --fail-with-body -sS "$API_ORIGIN/api/workspaces/$WORKSPACE_ID/api-tokens" \
  -H "Authorization: Bearer $FIREBASE_ID_TOKEN" \
  -H 'Content-Type: application/json' \
  --data '{"name":"report-reader","role":"reader","expiresAt":null}'

The response is { "token": <metadata>, "rawToken": "mat_..." }. Store rawToken immediately; list and revoke operations never return it again. The token is bound to WORKSPACE_ID, not an entire organization.

Workspace SQL

POST /api/workspaces/{workspaceId}/sql accepts sql, with optional accessMode, mode, params and maxRows. In the current released app templates, this SQL targets the SQLite database at /project/workspace/data.db; it is not the Grain control-plane Postgres database:

export GRAIN_API_TOKEN=<mat-token>

curl --fail-with-body -sS "$API_ORIGIN/api/workspaces/$WORKSPACE_ID/sql" \
  -H "Authorization: Bearer $GRAIN_API_TOKEN" \
  -H 'Content-Type: application/json' \
  --data '{"accessMode":"data:read","mode":"all","sql":"SELECT id, title FROM todos ORDER BY id LIMIT ?","params":[20],"maxRows":20}'

The defaults are accessMode: data:read and mode: all. Modes are all (columns and rows), get (one row), run (mutation metadata) and exec (SQL execution metadata). Use data:read, data:write or schema:write deliberately; the service does not turn arbitrary SQL into a safe query. The current empty, nextjs and react-vite templates all use this workspace-local SQLite file. Grain’s own control-plane uses Postgres internally, but that database is not the app database and is not exposed by this public API.

Read results can encode large values as { "kind": "bigint", "value": "..." }, { "kind": "unsafeInteger", "value": "..." }, { "kind": "blob", "bytes": 123 } or { "kind": "truncatedText", "value": "...", "bytes": 123 }. Check truncated before treating a bulk result as complete.

Registered commands

Discover a workspace’s enabled command IDs and safety descriptions before executing them:

curl --fail-with-body -sS "$API_ORIGIN/api/workspaces/$WORKSPACE_ID/commands" \
  -H "Authorization: Bearer $GRAIN_API_TOKEN"

curl --fail-with-body -sS "$API_ORIGIN/api/workspaces/$WORKSPACE_ID/commands/<command-id>/schema" \
  -H "Authorization: Bearer $GRAIN_API_TOKEN"

Execute one to ten command calls in order. arguments defaults to {} when omitted:

curl --fail-with-body -sS "$API_ORIGIN/api/workspaces/$WORKSPACE_ID/commands/execute" \
  -H "Authorization: Bearer $GRAIN_API_TOKEN" \
  -H 'Content-Type: application/json' \
  --data '{"commands":[{"commandId":"todo.list","arguments":{}}]}'

The outer response contains batchId and results. Each result is either { "ok": true, "runId": "...", "result": ... } or { "ok": false, "error": { "code": "...", "message": "..." } }, with command timestamps. Inspect every result, not just the HTTP status or outer JSON.

Front Door runtime access

Front Door is a workspace-bound HTTPS bridge for external agents that need a runtime shell or direct file transfer. The supported self-service path is Settings → Shell Access → Create in the Grain desktop app. The app reveals a ready-to-copy environment snippet once; use the programmatic request below only from a first-party/admin client that already has a Firebase ID token:

curl --fail-with-body -sS "$API_ORIGIN/api/workspaces/$WORKSPACE_ID/frontdoor-tokens" \
  -H "Authorization: Bearer $FIREBASE_ID_TOKEN" \
  -H 'Content-Type: application/json' \
  --data '{"name":"build-agent","expiresAt":null}'

The response includes token metadata, one-time rawToken: "mfd_...", and an env object with GRAIN_FRONTDOOR_URL and GRAIN_FRONTDOOR_TOKEN. Use the returned URL/token; do not construct a runtime URL from a private preview or E2B address.

Inspect capabilities before using the bridge:

export GRAIN_FRONTDOOR_TOKEN=<mfd-token>
curl --fail-with-body -sS "$API_ORIGIN/api/workspaces/$WORKSPACE_ID/frontdoor/capabilities" \
  -H "Authorization: Bearer $GRAIN_FRONTDOOR_TOKEN"

Execute argv, not a shell string:

curl --fail-with-body -sS "$API_ORIGIN/api/workspaces/$WORKSPACE_ID/frontdoor/exec" \
  -H "Authorization: Bearer $GRAIN_FRONTDOOR_TOKEN" \
  -H 'Content-Type: application/json' \
  --data '{"argv":["node","-e","console.log(\"hello\")"],"cwd":"/project/workspace","timeoutMs":10000}'

The response contains runId, nullable exitCode, stdout/stderr, byte counts, timedOut and truncated. A user command that exits nonzero still returns HTTP 200; inspect exitCode and stderr. argv has at most 64 arguments, each at most 4,096 bytes; the timeout is 1–60 seconds and output is capped at 1 MiB.

Transfer a raw file with application/octet-stream and a relative path query:

SHA256=$(shasum -a 256 report.json | awk '{print $1}')

curl --fail-with-body -sS -X PUT \
  "$API_ORIGIN/api/workspaces/$WORKSPACE_ID/frontdoor/files?path=uploads/report.json" \
  -H "Authorization: Bearer $GRAIN_FRONTDOOR_TOKEN" \
  -H 'Content-Type: application/octet-stream' \
  -H "X-Grain-Content-SHA256: $SHA256" \
  --data-binary @report.json

curl --fail-with-body -sS \
  "$API_ORIGIN/api/workspaces/$WORKSPACE_ID/frontdoor/files?path=uploads/report.json" \
  -H "Authorization: Bearer $GRAIN_FRONTDOOR_TOKEN" \
  -o report-downloaded.json

Direct uploads are not resumable and are limited to 100 MiB. Paths must remain under /project/workspace; absolute paths, .., protected Grain paths, excluded directories and symlink escapes are rejected. Downloads return binary bytes with Content-Length and X-Grain-Workspace-Path; range requests are not supported. The checksum header is the lowercase SHA-256 of the exact bytes sent.

Errors, limits and verification

Errors use this shape for client-visible failures:

{
  "error": {
    "code": "invalid_request",
    "message": "Invalid request"
  }
}

Common statuses are 400 invalid input or SQL, 401 missing/expired/revoked/wrong credential, 403 insufficient workspace role/scope, 404 workspace or command not found, 413 oversized SQL/result/upload, 429 Front Door rate/concurrency limiting, 502 runtime/manifest infrastructure failure and 504 command/SQL timeout. The OpenAPI document is the authoritative response and schema reference.

The SQL service limits request bodies to 512 KiB and SQL text to 256 KiB, read traffic to 60 requests/minute and write/schema traffic to 20 requests/minute; SQL execution has a 120-second timeout, a 16 MiB output cap, a 1,000-row default and 100,000-row maximum for all mode, and 64 KiB cell text. Front Door allows two concurrent commands per token and 60 requests/minute; created Front Door tokens default to seven days and cannot exceed 90 days.

Use curl --fail-with-body so HTTP errors fail the shell command while preserving the JSON body. A 200 response is not enough for SQL/commands/Front Door: inspect the mode-specific response, command result, exit code and truncation fields.