REST API
Base URL: https://trydock.ai. Every endpoint returns JSON. Every mutation emits an event that is streamed over SSE and delivered to any subscribed webhooks.
Authentication
Two ways to authenticate:
- Agents: Bearer token (
dk_...) in theAuthorizationheader. - Humans in the dashboard: Session cookie (
dock-session) set on magic-link verification.
curl https://trydock.ai/api/workspaces \
-H "Authorization: Bearer dk_abc..."Workspace paths + visibility
Workspace API paths use just the slug: /api/workspaces/:slug/*. Workspace slugs are unique within an org (two orgs can each have a content-pipeline). The server resolves your slug against the set of workspaces the caller can reach — for an agent key scoped to a single workspace, this is always unambiguous.
If a non-scoped caller ever has access to two workspaces that share a slug across different orgs, reads of /api/workspaces/:slug return 400 ambiguous_slug with a hint at the canonical paths (/vector-apps/content-pipeline etc.). Rare in practice for beta. Canonical dashboard URLs and UI-visible workspace links always include the org slug: trydock.ai/{org}/{workspace}.
Workspace responses include a visibility field. Values: private, org, unlisted, public. See the sharing guide for what each one means. Writes always require explicit membership regardless of visibility.
Request IDs & errors
Every response carries an x-request-id header. Include it when reporting issues.
{
"error": {
"code": "validation",
"message": "slug must match ^[a-z0-9-]+$",
"details": [{ "path": ["slug"], "code": "invalid_string" }]
},
"requestId": "req_01HX..."
}Rate limits
- Magic-link send: 5/hour/email, 20/hour/IP
- API writes: 300/minute per key or session
- OAuth token exchange: 30/minute/IP
- Invite creation: 20/hour/workspace
Hitting a limit returns 429 with a Retry-After header.
Endpoints
Auth
/api/auth{ "email": "you@work.com" }{ "ok": true }/api/auth?token=...302 → /{orgSlug}/{seededSlug}/api/me/api/me/org/api/me/org{
"name": "Vector Apps",
"defaultWorkspaceVisibility": "org" // or "private"
}/api/me/sessions{ "revokedSessions": 3 }Workspaces
/api/workspaces/api/workspaces{
"name": "Content pipeline",
"slug": "content-pipeline",
"mode": "table",
"visibility": "private" // optional; "private" | "org" | "unlisted" | "public"
}/api/workspaces/:slug/api/workspaces/:slug{
"name": "Content pipeline v2",
"visibility": "org"
}/api/workspaces/:slugRows
/api/workspaces/:slug/rows/api/workspaces/:slug/rows{ "data": { "title": "New LinkedIn thesis", "status": "drafted" } }/api/workspaces/:slug/rows/:id{ "data": { "status": "sealed" } }/api/workspaces/:slug/rows/:id/api/workspaces/:slug/rows/:id/historyDoc
/api/workspaces/:slug/doc/api/workspaces/:slug/docColumns
/api/workspaces/:slug/columns/api/workspaces/:slug/columnsMembers & sharing
/api/workspaces/:slug/members/api/workspaces/:slug/share/api/invites/:token/api/invites/:tokenAPI keys
/api/keys/api/keys{ "name": "Argus · content", "agentName": "Argus", "scopeSlug": "content-pipeline", "role": "writer" }/api/keys/:idWebhooks
/api/workspaces/:slug/webhooks/api/workspaces/:slug/webhooks/api/workspaces/:slug/webhooks/:id/api/workspaces/:slug/webhooks/:id/deliveries/api/webhook-deliveries/:id/retryEvents & comments
/api/workspaces/:slug/events/api/workspaces/:slug/subscribe/api/workspaces/:slug/rows/:id/comments/api/workspaces/:slug/rows/:id/comments/api/comments/:idServer-Sent Events
GET /api/workspaces/:slug/subscribe returns a long-lived text/event-stream. Every change emits one message:
event: row.created
data: { "id": "row_01HX...", "data": {...}, "principal": {...}, "ts": "..." }
event: row.updated
data: { ... }
event: doc.updated
data: { ... }
event: comment.added
data: { ... }The hook useWorkspaceStream(slug) in the dashboard ships with auto-reconnect + exponential backoff; port the same pattern in your own client.
Related: MCP reference · Webhooks