Starspace logoStarspace
    /Docs

    Workspace Management API

    REST API for creating and managing workspaces, sources, API keys, and OAuth clients programmatically.

    Base URL

    https://api.starspace.run/functions/v1/workspace-management/v1/workspaces

    All endpoint paths below are relative to this base.

    Authentication

    Two methods are supported:

    Session JWT

    Pass your logged-in session JWT in the Authorization header.

    Authorization: Bearer <your-jwt>

    Workspace API Key

    Use an ak_ prefixed key via the Authorization header or x-api-key header.

    Authorization: Bearer ak_your_key_here
    # or
    x-api-key: ak_your_key_here

    Rate Limiting & Quotas

    Each request is metered through a credit-based quota system. Responses include quota headers. Requests are also bounded by per-endpoint body caps and per-field length caps - oversized requests get a 413 payload_too_large or 400 validation_error with the offending field and limit in details. See the input-size table at the bottom of this page.

    HeaderDescription
    X-Credits-RemainingCredits remaining in your current period
    X-Credits-CostCredits consumed by this request

    Error Format

    Error responses return a JSON object with an error object and a standard HTTP status code. The code field is stable and machine-readable; the message is human-readable; the optional hint tells you what to do next.

    {
      "error": {
        "code": "not_found",
        "message": "Workspace not found",
        "hint": "Verify the workspace ID matches an existing workspace.",
        "details": { /* optional, varies by error */ }
      }
    }

    A small number of legacy/admin endpoints still return the older { "error": "string" } shape. Clients should accept both, read error.message if error is an object, otherwise read error directly.

    HTTP statuses

    StatusMeaning
    400Bad request, invalid parameters or validation error
    401Unauthenticated, missing or invalid credentials
    403Forbidden, insufficient permissions or wrong scope
    404Resource not found
    409Conflict, remote state changed since your base_state (sync only)
    413Payload too large, split the request into smaller batches
    429Quota / rate limit exceeded
    500Internal server error

    Common error codes

    CodeWhen you'll see it
    unauthenticatedMissing/invalid Authorization header
    invalid_api_keyKey doesn't match a record (typo or revoked)
    key_expiredKey past its expires_at
    forbiddenAuthenticated but missing required scope/role
    forbidden_principalWorkspace API keys can't perform user-only operations (e.g., create or delete workspaces)
    validation_errorRequest body or per-field validation failed (details.field, details.limit, details.actual)
    not_foundWorkspace, file, or resource doesn't exist
    payload_too_largeRequest body, ZIP, or per-field value exceeds the size cap
    rate_limitedPer-window request rate cap
    limit_exceededWorkspace would exceed file/storage limits
    workspace_limitPer-flavor workspace cap reached (details.scope = 'indexed' | 'sandbox')
    indexed_token_limitPer-tier indexing token budget reached (details.scope = 'workspace' | 'account'; details.workspaceId for convert-to-sandbox)
    conflictOCC conflict on /files/sync, pull first, then retry
    quota_exceededPer-tier credit/quota window exceeded
    internal_errorServer-side problem; safe to retry with backoff

    Workspaces

    GET/v1/workspaces

    List all workspaces you are a member of. Requires a user session (not API key).

    Response · 200

    {
      "items": [
        {
          "id": "92d087e3-75dd-4a2c-...",
          "name": "My Project",
          "description": null,
          "owner_id": "uuid",
          "workspace_type": "indexed",
          "is_public": false,
          "github_repo": null,
          "github_branch": null,
          "settings": null,
          "sync_version": null,
          "api_access_policy": {},
          "created_at": "2026-05-15T10:00:00Z",
          "updated_at": "2026-05-15T10:00:00Z"
        }
      ]
    }
    curl -H "Authorization: Bearer <jwt>" \
      https://api.starspace.run/functions/v1/workspace-management/v1/workspaces
    POST/v1/workspaces

    Create a new workspace. Requires a user session.

    Parameters

    NameTypeRequiredDescription
    namestringWorkspace name
    descriptionstring, Optional description
    workspace_typestring, 'indexed' (default) runs the embedding pipeline; 'sandbox' shares the same virtual FS but skips indexing - no ask/search, no Gemini cost. Set at create time, demote-only via /convert-to-sandbox.
    is_publicboolean, Default false. Public workspaces are visible to anyone with the link.

    Response · 201

    {
      "id": "92d087e3-75dd-4a2c-...",
      "name": "My Project",
      "description": "Code analysis workspace",
      "owner_id": "uuid",
      "workspace_type": "indexed",
      "is_public": false,
      "github_repo": null,
      "github_branch": null,
      "settings": null,
      "sync_version": null,
      "api_access_policy": {},
      "created_at": "2026-05-15T10:00:00Z",
      "updated_at": "2026-05-15T10:00:00Z"
    }
    # Indexed workspace (default)
    curl -X POST \
      -H "Authorization: Bearer <jwt>" \
      -H "Content-Type: application/json" \
      -d '{"name": "My Project", "description": "Code analysis workspace"}' \
      https://api.starspace.run/functions/v1/workspace-management/v1/workspaces
    
    # Sandbox workspace (no embeddings, just files + shell)
    curl -X POST \
      -H "Authorization: Bearer <jwt>" \
      -H "Content-Type: application/json" \
      -d '{"name": "Agent Shell", "workspace_type": "sandbox"}' \
      https://api.starspace.run/functions/v1/workspace-management/v1/workspaces
    GET/v1/workspaces/:id

    Get workspace details, including workspace_type. Requires viewer role or higher.

    Response · 200

    {
      "id": "...", "name": "...", "description": "...", "owner_id": "...",
      "workspace_type": "indexed",
      "is_public": false,
      "github_repo": null, "github_branch": null,
      "settings": null, "sync_version": null,
      "api_access_policy": {},
      "created_at": "...", "updated_at": "..."
    }
    PATCH/v1/workspaces/:id

    Update workspace settings. Requires admin role. workspace_type is set at create time and not editable here - use POST /convert-to-sandbox instead.

    Parameters

    NameTypeRequiredDescription
    namestring, New workspace name
    descriptionstring, New description
    is_publicboolean, Visibility toggle
    github_repostring | null, owner/repo or null to unlink
    github_branchstring | null, Branch to track
    settingsobject, Arbitrary workspace settings (max 16 KB JSON)
    api_access_policyobject, Access policy for workspace API keys (max 16 KB JSON)

    Response · 200

    {
      "id": "...", "name": "Renamed Project", /* full row, like GET */ ...
    }
    DELETE/v1/workspaces/:id

    Delete a workspace permanently. Requires owner role.

    Response · 200

    { "success": true }
    POST/v1/workspaces/:id/convert-to-sandbox

    Demote an Indexed workspace to Sandbox. Drops every chunk + embedding for the workspace in one transaction, flips workspace_type to 'sandbox', releases its indexing-token budget back to the account. Files in workspace_file_index are preserved. Requires admin role. Promotion back to Indexed is not supported in v1.

    Response · 200

    {
      "id": "...", "name": "...",
      "workspace_type": "sandbox",
      /* rest of the workspace row, unchanged */
      "updated_at": "..."
    }
    curl -X POST \
      -H "Authorization: Bearer ak_your_key_here" \
      https://api.starspace.run/functions/v1/workspace-management/v1/workspaces/<id>/convert-to-sandbox

    Files

    Sync files to and from your workspace index. Files written here are immediately available to MCP search and AI assistants.

    Limits & Validation
    • Per-file size: governed by your tier (Free: 5 MB, Hobby: 10 MB, Pro: 25 MB)
    • Storage quota: governed by your tier (Free: 100 MB, Hobby: 1 GB, Pro: 10 GB)
    • Indexing token budget (per workspace / per account aggregate): Free 500K / 2M, Hobby 5M / 25M, Pro 50M / 250M. Excess writes return indexed_token_limit with two actions: upgrade or convert to Sandbox.
    • Sandbox workspaces: 5,000 files / workspace (no embedding cost, just sanity)
    • Batch sync: max 500 files or 50 MB per request
    • Path rules: no .., no absolute paths, no node_modules/.git
    GET/v1/workspaces/:id/files

    List all indexed files in the workspace (path, size, hash, timestamp). Supports ?prefix=src/ and ?limit=N (default 1000, max 10000).

    Response · 200

    {
      "items": [
        {
          "file_path": "src/main.ts",
          "size_bytes": 1240,
          "content_hash": "sha256:...",
          "updated_at": "2026-05-15T10:00:00Z"
        }
      ]
    }
    curl -H "Authorization: Bearer ak_your_key_here" \
      https://api.starspace.run/functions/v1/workspace-management/v1/workspaces/<id>/files
    GET/v1/workspaces/:id/files/:path

    Get a single file's content and metadata.

    Response · 200

    {
      "file_path": "src/main.ts",
      "content": "console.log('hello')",
      "size_bytes": 20,
      "content_hash": "sha256:...",
      "updated_at": "2026-05-15T10:00:00Z"
    }
    GET/v1/workspaces/:id/files/state

    Return the current state hashmap of every file in the workspace - { files: { '<path>': { hash: 'sha256:...' } } }. Used by the CLI to compute a local-vs-remote diff before push.

    Response · 200

    {
      "workspace_id": "...",
      "sync_version": "2026-05-15T10:00:00Z",
      "files": {
        "src/main.ts": { "hash": "sha256:abc..." },
        "src/utils.ts": { "hash": "sha256:def..." }
      }
    }
    POST/v1/workspaces/:id/files/pull

    Stream a ZIP of requested files back to the caller. Used by `starspace pull`.

    Parameters

    NameTypeRequiredDescription
    requested_filesstring[]File paths to fetch (relative)

    Response · 200

    Content-Type: application/zip
    
    <binary zip stream>
    PUT/v1/workspaces/:id/files/:path

    Upsert a single file. Supports raw body (text/plain, application/octet-stream) or JSON ({content: string}). Raw body is preferred for efficiency.

    Parameters

    NameTypeRequiredDescription
    contentstringFile content (JSON mode only)

    Response · 200

    {
      "file_path": "src/main.ts",
      "size_bytes": 1240,
      "content_hash": "sha256:...",
      "updated_at": "2026-05-15T10:00:00Z"
    }
    # Raw body (preferred)
    curl -X PUT \
      -H "Authorization: Bearer ak_your_key_here" \
      -H "Content-Type: text/plain" \
      --data-binary @src/main.ts \
      https://api.starspace.run/functions/v1/workspace-management/v1/workspaces/<id>/files/src/main.ts
    
    # JSON body (legacy)
    curl -X PUT \
      -H "Authorization: Bearer ak_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{"content": "console.log(\"hello\")"}' \
      https://api.starspace.run/functions/v1/workspace-management/v1/workspaces/<id>/files/src/main.ts
    DELETE/v1/workspaces/:id/files/:path

    Delete a single file from the workspace index.

    Response · 200

    { "deleted": true, "file_path": "src/main.ts" }
    POST/v1/workspaces/:id/files/sync

    Batch sync a file manifest. Supports ZIP upload (application/zip) or JSON. With delete_missing/X-Delete-Missing, the cloud index will exactly match your local state.

    Parameters

    NameTypeRequiredDescription
    filesarrayArray of { path, content } objects (JSON mode)
    delete_missingboolean, If true, deletes cloud files not in the list (JSON mode, default false). Can also be passed via the X-Delete-Missing header for ZIP uploads.

    Response · 200

    {
      "upserted": 142,
      "deleted": 3
    }
    # ZIP upload (preferred for large syncs)
    zip -r /tmp/sync.zip src/
    curl -X POST \
      -H "Authorization: Bearer ak_your_key_here" \
      -H "Content-Type: application/zip" \
      -H "X-Delete-Missing: true" \
      --data-binary @/tmp/sync.zip \
      https://api.starspace.run/functions/v1/workspace-management/v1/workspaces/<id>/files/sync
    
    # JSON body (legacy)
    curl -X POST \
      -H "Authorization: Bearer ak_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{
        "files": [
          {"path": "src/main.ts", "content": "console.log(\"hello\")"},
          {"path": "src/utils.ts", "content": "export const add = (a, b) => a + b;"}
        ],
        "delete_missing": true
      }' \
      https://api.starspace.run/functions/v1/workspace-management/v1/workspaces/<id>/files/sync

    Sources

    Sources are the data feeds for your workspace. Supported kinds:

    github_repoupload_bundlepaper_collectionweb_collectionnote_set
    GET/v1/workspaces/:id/sources

    List all sources in a workspace.

    Response · 200

    {
      "items": [
        {
          "id": "uuid",
          "workspace_id": "uuid",
          "name": "Backend Repo",
          "kind": "github_repo",
          "mount_path": "/backend",
          "config": { "owner": "org", "repo": "backend", "branch": "main" },
          "created_at": "...", "updated_at": "..."
        }
      ]
    }
    POST/v1/workspaces/:id/sources

    Add a new source to the workspace. Requires admin role.

    Parameters

    NameTypeRequiredDescription
    namestringSource name
    kindstringSource kind (e.g. github_repo)
    mount_pathstringPath prefix in workspace
    configobject, Kind-specific configuration

    Response · 201

    {
      "id": "uuid",
      "workspace_id": "uuid",
      "name": "Backend Repo",
      "kind": "github_repo",
      "mount_path": "/backend",
      "config": { "owner": "org", "repo": "backend", "branch": "main" },
      "created_at": "...", "updated_at": "..."
    }
    curl -X POST \
      -H "Authorization: Bearer ak_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Backend Repo",
        "kind": "github_repo",
        "mount_path": "/backend",
        "config": {"repo": "org/backend", "branch": "main"}
      }' \
      https://api.starspace.run/functions/v1/workspace-management/v1/workspaces/<id>/sources
    GET/v1/workspaces/:id/sources/:sourceId

    Get source details.

    Response · 200

    {
      "id": "uuid", "workspace_id": "uuid", "name": "...",
      "kind": "github_repo", "mount_path": "/backend",
      "config": { ... },
      "created_at": "...", "updated_at": "..."
    }
    PATCH/v1/workspaces/:id/sources/:sourceId

    Update a source. Requires admin role.

    Parameters

    NameTypeRequiredDescription
    namestring, New source name
    mount_pathstring, New mount path
    configobject, Kind-specific config (max 8 KB JSON)

    Response · 200

    /* Full source row, like GET /sources/:sourceId */
    DELETE/v1/workspaces/:id/sources/:sourceId

    Remove a source. Requires admin role.

    Response · 200

    { "success": true }

    API Keys

    GET/v1/workspaces/:id/api-keys

    List all API keys for the workspace. Requires admin role. The raw key value is NEVER returned here - only at create time.

    Response · 200

    {
      "items": [
        {
          "id": "uuid",
          "name": "CI Pipeline Key",
          "key_prefix": "ak_a1b2c3",
          "status": "active",
          "expires_at": null,
          "last_used_at": "2026-05-15T10:00:00Z",
          "created_at": "2026-05-10T08:00:00Z",
          "metadata": { "requests_per_minute": 60 }
        }
      ]
    }
    POST/v1/workspaces/:id/api-keys

    Create a new API key. Returns the raw key once, store it securely.

    Parameters

    NameTypeRequiredDescription
    namestring, Key name (default: "API Key")
    expires_atstring, ISO 8601 expiration date

    Response · 201

    {
      "id": "uuid",
      "name": "CI Pipeline Key",
      "key_prefix": "ak_a1b2c3",
      "raw_key": "ak_a1b2c3_FULL_TOKEN_SHOWN_ONCE",
      "status": "active",
      "expires_at": null,
      "created_at": "2026-05-15T10:00:00Z",
      "metadata": {}
    }
    curl -X POST \
      -H "Authorization: Bearer <jwt>" \
      -H "Content-Type: application/json" \
      -d '{"name": "CI Pipeline Key"}' \
      https://api.starspace.run/functions/v1/workspace-management/v1/workspaces/<id>/api-keys
    Important: The raw_key is only returned once at creation time. Store it securely.
    GET/v1/workspaces/:id/api-keys/:keyId

    Get API key metadata (not the raw key).

    Response · 200

    {
      "id": "uuid", "name": "...", "key_prefix": "ak_a1b2c3",
      "status": "active", "expires_at": null,
      "last_used_at": "...", "created_at": "...",
      "metadata": { ... }
    }
    PATCH/v1/workspaces/:id/api-keys/:keyId

    Update API key name or policy.

    Parameters

    NameTypeRequiredDescription
    namestring, New key name
    metadataobject, Key policy (rate limits, scopes; max 16 KB JSON)

    Response · 200

    /* Full key metadata, like GET /api-keys/:keyId */
    POST/v1/workspaces/:id/api-keys/:keyId/revoke

    Revoke an API key immediately. The key will return 401 invalid_api_key on next use.

    Response · 200

    {
      "id": "uuid", "name": "...", "key_prefix": "ak_a1b2c3",
      "status": "revoked",
      "expires_at": null, "created_at": "...", "metadata": {}
    }
    DELETE/v1/workspaces/:id/api-keys/:keyId

    Delete an API key permanently.

    Response · 200

    { "success": true }

    API Key Policy Fields

    FieldTypeDescription
    requests_per_minutenumberMax requests per minute
    requests_per_hournumberMax requests per hour
    requests_per_daynumberMax requests per day
    max_concurrent_requestsnumberMax concurrent requests
    allowed_scopesstring[]Scopes this key can authorize

    OAuth Clients

    GET/v1/workspaces/:id/oauth-clients

    List all OAuth clients. Requires admin role.

    Response · 200

    {
      "items": [
        {
          "client_id": "...",
          "name": "Cursor IDE",
          "description": null,
          "redirect_uris": ["http://localhost:9999/callback"],
          "allowed_scopes": ["workspace:read", "workspace:write"],
          "token_endpoint_auth_method": "client_secret_basic",
          "is_confidential": true,
          "status": "active",
          "metadata": {},
          "created_at": "...", "updated_at": "..."
        }
      ]
    }
    POST/v1/workspaces/:id/oauth-clients

    Create a new OAuth client for third-party integrations.

    Parameters

    NameTypeRequiredDescription
    namestringClient name
    redirect_urisstring[]Allowed redirect URIs
    descriptionstring, Client description
    allowed_scopesstring[], Scopes the client may request
    is_confidentialboolean, Default true - requires client_secret on token endpoint

    Response · 201

    {
      "client_id": "...",
      "client_secret": "shown_once_at_create",
      "name": "...", "description": null,
      "redirect_uris": [...],
      "allowed_scopes": [...],
      "token_endpoint_auth_method": "client_secret_basic",
      "is_confidential": true,
      "status": "active",
      "metadata": {},
      "created_at": "...", "updated_at": "..."
    }
    GET/v1/workspaces/:id/oauth-clients/:clientId

    Get OAuth client details. The client_secret is NOT included - use /rotate-secret to retrieve a new one.

    Response · 200

    {
      "client_id": "...", "name": "...", "description": null,
      "redirect_uris": [...], "allowed_scopes": [...],
      "token_endpoint_auth_method": "client_secret_basic",
      "is_confidential": true,
      "status": "active",
      "metadata": {},
      "created_at": "...", "updated_at": "..."
    }
    PATCH/v1/workspaces/:id/oauth-clients/:clientId

    Update an OAuth client.

    Parameters

    NameTypeRequiredDescription
    namestring, New name
    descriptionstring, New description
    redirect_urisstring[], New allowed redirect URIs
    allowed_scopesstring[], New allowed scopes

    Response · 200

    /* Full client row, like GET /oauth-clients/:clientId */
    DELETE/v1/workspaces/:id/oauth-clients/:clientId

    Delete an OAuth client.

    Response · 200

    { "success": true }
    POST/v1/workspaces/:id/oauth-clients/:clientId/rotate-secret

    Rotate the client secret. The new secret is returned once - existing tokens issued under the old secret continue to work but the old secret cannot be used at the token endpoint again.

    Response · 200

    {
      "client_id": "...",
      "client_secret": "new_secret_shown_once",
      "rotated_at": "..."
    }

    OAuth Tokens

    GET/v1/workspaces/:id/oauth-tokens

    List active OAuth access and refresh tokens. Includes both workspace-bound tokens (workspace_id matches) AND multi-workspace tokens whose granted_workspace_ids array contains this workspace. Multi-workspace tokens carry workspace_id=null and granted_workspace_ids=[uuid, ...].

    Response · 200

    {
      "items": [
        {
          "id": "uuid",
          "oauth_client_id": "...",
          "workspace_id": "uuid | null",
          "granted_workspace_ids": ["uuid", "..."],
          "token_prefix": "at_a1b2c3",
          "scope": "workspace:read workspace:write",
          "expires_at": "...",
          "revoked_at": null,
          "last_used_at": "...",
          "created_at": "...",
          "metadata": {}
        }
      ]
    }
    POST/v1/workspaces/:id/oauth-tokens/:tokenId/revoke

    Revoke an OAuth token. For multi-workspace tokens, revocation is global - the token can no longer act on any of its granted workspaces.

    Response · 200

    {
      "id": "uuid", "token_prefix": "at_a1b2c3",
      "revoked_at": "2026-05-15T10:00:00Z",
      "revoked_related_access_token": true
    }

    Request Logs

    GET/v1/workspaces/:id/requests

    List management API request logs with a 24-hour summary. Requires admin role.

    Response · 200

    {
      "summary_24h": {
        "total_requests": 142,
        "by_api_kind": { "files_sync": 12, "files_get": 130 },
        "by_status": { "200": 138, "402": 1, "429": 3 },
        "credits_consumed": 138
      },
      "items": [
        {
          "id": "uuid",
          "request_path": "/v1/workspaces/.../files",
          "request_method": "GET",
          "api_kind": "files_get",
          "response_status": 200,
          "credits_cost": 1,
          "created_at": "...",
          "completed_at": "..."
        }
      ]
    }
    curl -H "Authorization: Bearer ak_your_key_here" \
      https://api.starspace.run/functions/v1/workspace-management/v1/workspaces/<id>/requests

    Input Limits

    Body and per-field caps. Requests over the limit are rejected - never silently truncated.

    Per-endpoint body cap (Content-Length)

    EndpointCap
    Most workspace/source/key/oauth POST · PATCH1 MB
    POST /v1/workspaces/:id/files/sync · PUT /files/:path60 MB
    POST /ai-chat1 MB
    POST /ingest-document8 MB

    Per-field caps

    FieldLimit
    workspaces.name200 chars
    workspaces.description2000 bytes
    workspaces.github_repo · github_branch200 chars
    workspaces.settings · api_access_policy16 KB JSON each
    workspace_sources.name200 chars
    workspace_sources.mount_path1024 chars
    workspace_sources.config8 KB JSON
    workspace_api_keys.name200 chars
    workspace_api_keys.policy16 KB JSON
    ingest-document.content8 MB base64
    ai-chat.contents (serialized)256 KB

    See Also