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/workspacesAll 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_hereRate 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.
| Header | Description |
|---|---|
| X-Credits-Remaining | Credits remaining in your current period |
| X-Credits-Cost | Credits 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
| Status | Meaning |
|---|---|
| 400 | Bad request, invalid parameters or validation error |
| 401 | Unauthenticated, missing or invalid credentials |
| 403 | Forbidden, insufficient permissions or wrong scope |
| 404 | Resource not found |
| 409 | Conflict, remote state changed since your base_state (sync only) |
| 413 | Payload too large, split the request into smaller batches |
| 429 | Quota / rate limit exceeded |
| 500 | Internal server error |
Common error codes
| Code | When you'll see it |
|---|---|
| unauthenticated | Missing/invalid Authorization header |
| invalid_api_key | Key doesn't match a record (typo or revoked) |
| key_expired | Key past its expires_at |
| forbidden | Authenticated but missing required scope/role |
| forbidden_principal | Workspace API keys can't perform user-only operations (e.g., create or delete workspaces) |
| validation_error | Request body or per-field validation failed (details.field, details.limit, details.actual) |
| not_found | Workspace, file, or resource doesn't exist |
| payload_too_large | Request body, ZIP, or per-field value exceeds the size cap |
| rate_limited | Per-window request rate cap |
| limit_exceeded | Workspace would exceed file/storage limits |
| workspace_limit | Per-flavor workspace cap reached (details.scope = 'indexed' | 'sandbox') |
| indexed_token_limit | Per-tier indexing token budget reached (details.scope = 'workspace' | 'account'; details.workspaceId for convert-to-sandbox) |
| conflict | OCC conflict on /files/sync, pull first, then retry |
| quota_exceeded | Per-tier credit/quota window exceeded |
| internal_error | Server-side problem; safe to retry with backoff |
Workspaces
/v1/workspacesList 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/v1/workspacesCreate a new workspace. Requires a user session.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | ✓ | Workspace name |
| description | string | , | Optional description |
| workspace_type | string | , | '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_public | boolean | , | 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/v1/workspaces/:idGet 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": "..."
}/v1/workspaces/:idUpdate workspace settings. Requires admin role. workspace_type is set at create time and not editable here - use POST /convert-to-sandbox instead.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | , | New workspace name |
| description | string | , | New description |
| is_public | boolean | , | Visibility toggle |
| github_repo | string | null | , | owner/repo or null to unlink |
| github_branch | string | null | , | Branch to track |
| settings | object | , | Arbitrary workspace settings (max 16 KB JSON) |
| api_access_policy | object | , | Access policy for workspace API keys (max 16 KB JSON) |
Response · 200
{
"id": "...", "name": "Renamed Project", /* full row, like GET */ ...
}/v1/workspaces/:idDelete a workspace permanently. Requires owner role.
Response · 200
{ "success": true }/v1/workspaces/:id/convert-to-sandboxDemote 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-sandboxFiles
Sync files to and from your workspace index. Files written here are immediately available to MCP search and AI assistants.
- 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_limitwith 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, nonode_modules/.git
/v1/workspaces/:id/filesList 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/v1/workspaces/:id/files/:pathGet 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"
}/v1/workspaces/:id/files/stateReturn 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..." }
}
}/v1/workspaces/:id/files/pullStream a ZIP of requested files back to the caller. Used by `starspace pull`.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| requested_files | string[] | ✓ | File paths to fetch (relative) |
Response · 200
Content-Type: application/zip
<binary zip stream>/v1/workspaces/:id/files/:pathUpsert a single file. Supports raw body (text/plain, application/octet-stream) or JSON ({content: string}). Raw body is preferred for efficiency.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| content | string | ✓ | File 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/v1/workspaces/:id/files/:pathDelete a single file from the workspace index.
Response · 200
{ "deleted": true, "file_path": "src/main.ts" }/v1/workspaces/:id/files/syncBatch 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
| Name | Type | Required | Description |
|---|---|---|---|
| files | array | ✓ | Array of { path, content } objects (JSON mode) |
| delete_missing | boolean | , | 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/syncSources
Sources are the data feeds for your workspace. Supported kinds:
/v1/workspaces/:id/sourcesList 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": "..."
}
]
}/v1/workspaces/:id/sourcesAdd a new source to the workspace. Requires admin role.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | ✓ | Source name |
| kind | string | ✓ | Source kind (e.g. github_repo) |
| mount_path | string | ✓ | Path prefix in workspace |
| config | object | , | 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/v1/workspaces/:id/sources/:sourceIdGet source details.
Response · 200
{
"id": "uuid", "workspace_id": "uuid", "name": "...",
"kind": "github_repo", "mount_path": "/backend",
"config": { ... },
"created_at": "...", "updated_at": "..."
}/v1/workspaces/:id/sources/:sourceIdUpdate a source. Requires admin role.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | , | New source name |
| mount_path | string | , | New mount path |
| config | object | , | Kind-specific config (max 8 KB JSON) |
Response · 200
/* Full source row, like GET /sources/:sourceId *//v1/workspaces/:id/sources/:sourceIdRemove a source. Requires admin role.
Response · 200
{ "success": true }API Keys
/v1/workspaces/:id/api-keysList 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 }
}
]
}/v1/workspaces/:id/api-keysCreate a new API key. Returns the raw key once, store it securely.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | , | Key name (default: "API Key") |
| expires_at | string | , | 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-keysraw_key is only returned once at creation time. Store it securely./v1/workspaces/:id/api-keys/:keyIdGet 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": { ... }
}/v1/workspaces/:id/api-keys/:keyIdUpdate API key name or policy.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | , | New key name |
| metadata | object | , | Key policy (rate limits, scopes; max 16 KB JSON) |
Response · 200
/* Full key metadata, like GET /api-keys/:keyId *//v1/workspaces/:id/api-keys/:keyId/revokeRevoke 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": {}
}/v1/workspaces/:id/api-keys/:keyIdDelete an API key permanently.
Response · 200
{ "success": true }API Key Policy Fields
| Field | Type | Description |
|---|---|---|
| requests_per_minute | number | Max requests per minute |
| requests_per_hour | number | Max requests per hour |
| requests_per_day | number | Max requests per day |
| max_concurrent_requests | number | Max concurrent requests |
| allowed_scopes | string[] | Scopes this key can authorize |
OAuth Clients
/v1/workspaces/:id/oauth-clientsList 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": "..."
}
]
}/v1/workspaces/:id/oauth-clientsCreate a new OAuth client for third-party integrations.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | ✓ | Client name |
| redirect_uris | string[] | ✓ | Allowed redirect URIs |
| description | string | , | Client description |
| allowed_scopes | string[] | , | Scopes the client may request |
| is_confidential | boolean | , | 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": "..."
}/v1/workspaces/:id/oauth-clients/:clientIdGet 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": "..."
}/v1/workspaces/:id/oauth-clients/:clientIdUpdate an OAuth client.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | , | New name |
| description | string | , | New description |
| redirect_uris | string[] | , | New allowed redirect URIs |
| allowed_scopes | string[] | , | New allowed scopes |
Response · 200
/* Full client row, like GET /oauth-clients/:clientId *//v1/workspaces/:id/oauth-clients/:clientIdDelete an OAuth client.
Response · 200
{ "success": true }/v1/workspaces/:id/oauth-clients/:clientId/rotate-secretRotate 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
/v1/workspaces/:id/oauth-tokensList 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": {}
}
]
}/v1/workspaces/:id/oauth-tokens/:tokenId/revokeRevoke 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
/v1/workspaces/:id/requestsList 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>/requestsInput Limits
Body and per-field caps. Requests over the limit are rejected - never silently truncated.
Per-endpoint body cap (Content-Length)
| Endpoint | Cap |
|---|---|
| Most workspace/source/key/oauth POST · PATCH | 1 MB |
| POST /v1/workspaces/:id/files/sync · PUT /files/:path | 60 MB |
| POST /ai-chat | 1 MB |
| POST /ingest-document | 8 MB |
Per-field caps
| Field | Limit |
|---|---|
| workspaces.name | 200 chars |
| workspaces.description | 2000 bytes |
| workspaces.github_repo · github_branch | 200 chars |
| workspaces.settings · api_access_policy | 16 KB JSON each |
| workspace_sources.name | 200 chars |
| workspace_sources.mount_path | 1024 chars |
| workspace_sources.config | 8 KB JSON |
| workspace_api_keys.name | 200 chars |
| workspace_api_keys.policy | 16 KB JSON |
| ingest-document.content | 8 MB base64 |
| ai-chat.contents (serialized) | 256 KB |
See Also
- MCP Server Reference, Connect AI assistants to your workspace