Skip to content

Workspaces API ​

Internal Application API

These are internal SPA routes (cookie/session authenticated, used by the Studio app), not a versioned public contract — they may change between releases. For external integrations, use the Public API.

Workspaces are the top-level billing entity in Studio. Each user has a primary workspace created at signup, and can create or be invited to secondary workspaces.

List Workspaces ​

List all workspaces the current user belongs to.

GET /api/workspaces

Response ​

json
[
  {
    "id": "uuid",
    "name": "My Workspace",
    "slug": "my-workspace",
    "type": "primary",
    "plan": "starter",
    "role": "owner",
    "github_installation_id": 12345
  }
]

Create Workspace ​

Create a new secondary workspace.

POST /api/workspaces

Request Body ​

FieldTypeRequiredDescription
namestringYesWorkspace display name
slugstringYesURL slug (lowercase, alphanumeric, hyphens)

Response ​

json
{
  "id": "uuid",
  "name": "Team Workspace",
  "slug": "team-workspace",
  "type": "secondary"
}

Get Workspace ​

Get workspace details including billing information.

GET /api/workspaces/:workspaceId

Response ​

json
{
  "id": "uuid",
  "name": "My Workspace",
  "slug": "my-workspace",
  "type": "primary",
  "plan": "starter",
  "github_installation_id": 12345,
  "owner_id": "uuid"
}

Auth ​

Requires workspace membership.


Update Workspace ​

Update workspace name or settings.

PATCH /api/workspaces/:workspaceId

Request Body ​

FieldTypeRequiredDescription
namestringNoNew workspace name

Auth ​

Requires workspace owner or admin role.


Delete Workspace ​

Delete a workspace and all its projects.

DELETE /api/workspaces/:workspaceId

Auth ​

Requires workspace owner role. Primary workspaces cannot be deleted (delete the user account instead).


Transfer Ownership ​

Transfer workspace ownership to another member.

POST /api/workspaces/:workspaceId/transfer-owner

Request Body ​

FieldTypeRequiredDescription
newOwnerIdstringYesUser ID of the new owner

Auth ​

Requires current workspace owner role.


Members ​

List Members ​

GET /api/workspaces/:workspaceId/members

Response ​

json
[
  {
    "id": "uuid",
    "user_id": "uuid",
    "role": "owner",
    "invited_email": "[email protected]",
    "accepted_at": "2026-01-01T00:00:00Z",
    "display_name": "Jane Doe",
    "avatar_url": "https://..."
  }
]

Invite Member ​

POST /api/workspaces/:workspaceId/members
FieldTypeRequiredDescription
emailstringYesEmail address to invite
role'admin' | 'member'YesWorkspace role

Auth ​

Requires workspace owner or admin role.

Update Member Role ​

PATCH /api/workspaces/:workspaceId/members/:memberId
FieldTypeRequiredDescription
role'admin' | 'member'YesNew role

Remove Member ​

DELETE /api/workspaces/:workspaceId/members/:memberId

Resend Invitation ​

POST /api/workspaces/:workspaceId/members/:memberId/resend

AI Keys (BYOA) ​

Manage Bring Your Own API key credentials per workspace.

List AI Keys ​

GET /api/workspaces/:workspaceId/ai-keys

Response ​

json
[
  {
    "id": "uuid",
    "provider": "anthropic",
    "key_hint": "sk-ant-...7x2q",
    "created_at": "2026-01-01T00:00:00Z"
  }
]

Add/Update AI Key ​

POST /api/workspaces/:workspaceId/ai-keys
FieldTypeRequiredDescription
providerstringYesAI provider name (e.g., anthropic)
apiKeystringYesAPI key (encrypted at rest)

Delete AI Key ​

DELETE /api/workspaces/:workspaceId/ai-keys/:keyId

Enterprise Feature

AI key management routes through the enterprise bridge. If ee/ is not loaded, these endpoints return 403.


MCP Cloud Keys ​

Manage MCP Cloud API keys (crn_mcp_ Bearer keys) that grant programmatic access to the remote MCP endpoint. The plaintext key is shown once at creation and never stored -- the server only keeps a SHA-256 hash plus the prefix.

List MCP Cloud Keys ​

GET /api/workspaces/:workspaceId/mcp-cloud-keys

Optionally narrow to one project with ?projectId=.

Response ​

json
{
  "keys": [
    {
      "id": "uuid",
      "name": "Production key",
      "key_prefix": "crn_mcp_ab12",
      "project_id": "uuid",
      "allowed_tools": ["list_models", "get_content"],
      "media_enabled": false,
      "rate_limit_per_minute": 60,
      "monthly_call_limit": null,
      "last_used_at": "2026-01-01T00:00:00Z",
      "created_at": "2026-01-01T00:00:00Z",
      "calls_this_month": 42
    }
  ]
}

Plaintext keys are never returned here. An empty allowed_tools array means all tools are permitted. (The row also carries created_by and revoked_at, though revoked keys are filtered out of this list.)

Auth ​

Requires workspace membership (owner, admin, or member).


Create MCP Cloud Key ​

POST /api/workspaces/:workspaceId/mcp-cloud-keys

Request Body ​

FieldTypeRequiredDescription
projectIdstringYesProject the key is scoped to
namestringYesDisplay name
allowedToolsstring[]NoWhitelist of MCP tool names (empty = all tools). Unknown names are rejected.
mediaEnabledbooleanNoAllow media tools (default false)
rateLimitPerMinutenumberNo1-600 requests per minute (default 60)
monthlyCallLimitnumber | nullNoMonthly call cap (null = unlimited)

Response ​

json
{
  "id": "uuid",
  "key": "crn_mcp_ab12...full-plaintext",
  "keyPrefix": "crn_mcp_ab12",
  "name": "Production key",
  "projectId": "uuid",
  "allowedTools": [],
  "mediaEnabled": false,
  "rateLimitPerMinute": 60,
  "monthlyCallLimit": null,
  "createdAt": "2026-01-01T00:00:00Z"
}

WARNING

The plaintext key is returned exactly once. Store it securely -- the server only keeps its hash.

Auth ​

Requires workspace owner or admin role. Gated by the api.mcp_cloud plan feature and the api.mcp_keys plan limit.

Error Codes ​

StatusCondition
400Missing name/projectId, unknown tool in allowedTools, rateLimitPerMinute out of range, or invalid monthlyCallLimit
403Plan does not include api.mcp_cloud, or the api.mcp_keys key limit is reached
404Workspace or project not found

Revoke MCP Cloud Key ​

DELETE /api/workspaces/:workspaceId/mcp-cloud-keys/:keyId

Sets revoked_at; the key is immediately rejected by the MCP endpoint. The row is retained for audit.

Response ​

json
{ "revoked": true }

Auth ​

Requires workspace owner or admin role.


Connected Apps ​

Active OAuth grants for the remote MCP connector surface -- the client applications (e.g. Claude) a user has connected to this workspace. See the Connected Apps guide for the end-user flow.

Managed deployments only

These endpoints back the remote-MCP OAuth server, which only runs on the managed auth pair. On the self-hosted (Supabase) pair, GET returns { "enabled": false, "endpoint": null, "grants": [] } and DELETE returns 404.

List Connected Apps ​

GET /api/workspaces/:workspaceId/connected-apps

Owners and admins see every grant in the workspace; members see only their own.

Response ​

json
{
  "enabled": true,
  "endpoint": "https://studio.contentrain.io/api/mcp/remote",
  "grants": [
    {
      "grantId": "uuid",
      "clientHost": "claude.ai",
      "clientName": "Claude",
      "logoUri": "https://...",
      "projectId": "uuid",
      "projectRepo": "org/my-website",
      "scopes": ["content:read", "content:write", "project:metadata"],
      "createdAt": "2026-01-01T00:00:00Z",
      "lastUsedAt": "2026-01-02T00:00:00Z",
      "callsThisMonth": 128,
      "mine": true
    }
  ]
}

mine indicates whether the grant belongs to the requesting user.

Auth ​

Requires workspace membership (owner, admin, or member).


Revoke Connected App ​

DELETE /api/workspaces/:workspaceId/connected-apps/:grantId

Revokes the grant, kills the refresh-token family, and deletes live access tokens. The connected client's next refresh fails with invalid_grant and re-runs the auth flow.

Response ​

json
{ "revoked": true }

Auth ​

Owners and admins may revoke any grant; members may revoke only their own (otherwise 403). Returns 404 on the non-managed pair or when the grant does not exist.


Usage & Overage ​

Get Usage ​

Return metered usage for the current billing period (calendar month).

GET /api/workspaces/:workspaceId/usage

Response ​

json
{
  "billingPeriod": "2026-07",
  "categories": [
    {
      "key": "ai_messages",
      "limitKey": "ai.messages_per_month",
      "name": "AI Messages",
      "current": 1234,
      "limit": 5000,
      "overageEnabled": false,
      "overageUnits": 0,
      "overageUnitPrice": 0,
      "overageAmount": 0,
      "unit": "messages",
      "percentage": 25
    }
  ],
  "totalOverageAmount": 0,
  "projectedOverageAmount": 0
}

Categories are returned in this order: ai_messages, form_submissions, cdn_bandwidth, media_storage, api_messages, mcp_calls. A limit of -1 means unlimited.

Simple Format ​

The CLI-friendly format keys each category by camelCase name. Each value is a { current, limit, percentage } object:

GET /api/workspaces/:workspaceId/usage?format=simple
json
{
  "aiMessages": { "current": 1234, "limit": 5000, "percentage": 25 },
  "formSubmissions": { "current": 12, "limit": 1000, "percentage": 1 },
  "cdnBandwidthGb": { "current": 0.5, "limit": 50, "percentage": 1 },
  "mediaStorageGb": { "current": 0.2, "limit": 10, "percentage": 2 },
  "apiMessages": { "current": 0, "limit": 1000, "percentage": 0 },
  "mcpCalls": { "current": 8, "limit": 10000, "percentage": 0 }
}

Auth ​

Requires workspace owner or admin role.


Get Overage Settings ​

Return overage preferences, plan limits, and per-category overage pricing.

GET /api/workspaces/:workspaceId/overage-settings

Response ​

json
{
  "overageSettings": { "ai_messages": true },
  "categories": [
    {
      "limitKey": "ai.messages_per_month",
      "settingsKey": "ai_messages",
      "unit": "per message",
      "unitPrice": 0.03,
      "planLimit": 1500,
      "enabled": true
    }
  ],
  "canEnableOverage": true
}

canEnableOverage is true only when the workspace has a saved payment method, an active subscription (trialing / active / past_due), and a non-free plan.

Auth ​

Requires workspace owner or admin role.


Update Overage Settings ​

Toggle overage billing per category. Submitted keys are merged with existing settings.

PATCH /api/workspaces/:workspaceId/overage-settings

Request Body ​

Any subset of the boolean category toggles:

FieldTypeDescription
ai_messagesbooleanEnable overage for AI messages
api_messagesbooleanEnable overage for API messages
cdn_bandwidthbooleanEnable overage for CDN bandwidth
form_submissionsbooleanEnable overage for form submissions
media_storagebooleanEnable overage for media storage
mcp_callsbooleanEnable overage for MCP Cloud calls

Response ​

json
{ "overageSettings": { "ai_messages": true, "cdn_bandwidth": false } }

Auth ​

Requires workspace owner or admin role.

Error Codes ​

StatusCondition
400Unknown settings key, non-boolean value, or no fields provided
402No active paid subscription (or no saved payment method)
403Free plan cannot enable overages

See the Billing guide for how overage pricing is applied.


Billing ​

Billing actions live under /api/billing/ (they are not scoped under /api/workspaces/ in the path) but take workspaceId in the request body. They route to the active payment provider (Polar by default, Stripe as fallback).

Create Checkout Session ​

Create a hosted checkout session to subscribe a workspace to a paid plan.

POST /api/billing/checkout

Request Body ​

FieldTypeRequiredDescription
workspaceIdstringYesTarget workspace
plan'starter' | 'pro'YesPlan to subscribe to

Response ​

json
{ "url": "https://checkout.provider.com/..." }

Redirect the browser to url. A free trial is granted only if the workspace has never consumed one.

Auth ​

Requires workspace owner or admin role. Rate limited to 1 request per 30 seconds per workspace.

Error Codes ​

StatusCondition
400Missing workspaceId/plan, or invalid plan
403Not a workspace owner/admin
409Workspace already has an active subscription
429Rate limit exceeded
502 / 503Payment provider unavailable

Create Portal Session ​

Create a customer portal session so the user can manage their subscription (payment method, cancel, plan change).

POST /api/billing/portal

Request Body ​

FieldTypeRequiredDescription
workspaceIdstringYesTarget workspace

Response ​

json
{ "url": "https://billing.provider.com/..." }

Auth ​

Requires workspace owner or admin role.

Error Codes ​

StatusCondition
400Missing workspaceId, or the workspace has no payment customer yet
403Not a workspace owner/admin
502 / 503Payment provider unavailable

Released under the AGPL-3.0 License.