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.