Overview
v2.0.0. The team-workspace HTTP API. Authenticate with a Bearer API key minted in Settings → API keys. All operations are scoped to the org the key belongs to.
Base URL
https://switchy.build/apiLocal dev runs at http://localhost:3000/api. The full machine-readable spec is at /api/openapi.json (OpenAPI 3.1.0).
Authentication
Every request needs a Bearer token. Mint one in Settings → API keys and pass it in the Authorization header:
Authorization: Bearer sk_live_...Keys are scoped to one org. To call the API for a different org, mint a key while that org is active.
Response envelope
Every response, success or failure, follows the same shape:
// Success
{
"success": true,
"data": { ... },
"metadata": { "timestamp": "2026-04-19T12:34:56.789Z" }
}
// Failure
{
"success": false,
"error": { "code": "VALIDATION_ERROR", "message": "...", "details": { ... } },
"metadata": { "timestamp": "2026-04-19T12:34:56.789Z" }
}Branch on error.code — the codes are stable. The HTTP status mirrors the code: 4xx for caller errors, 5xx for ours.
Error codes
| Code | HTTP | When |
|---|---|---|
AUTH_ERROR | 401 | Missing or invalid bearer token. |
FORBIDDEN | 403 | Authenticated but the caller is not allowed (wrong role, not a Space member, wrong org). |
NOT_FOUND | 404 | The resource does not exist or is invisible to the caller. |
VALIDATION_ERROR | 400 | Body or query failed zod validation. Per-field details in error.details.errors. |
CONFLICT | 409 | Duplicate slug, race, or invalid state transition. |
RATE_LIMIT | 429 | Per-key rate limit hit. Retry after the value in the Retry-After header. |
INTERNAL_ERROR | 500 | Unexpected server error. requestId is logged on our side. |
Idempotency
POST endpoints accept Idempotency-Key (any opaque UUID you generate). The first request wins; identical follow-ups within 24h replay the original response. We strongly recommend setting it on every write.
Rate limits
- Reads: 60 / minute / key.
- Writes: 20 / minute / key.
- MCP tool calls (
tools/call): governed by per-tool quotas inside the MCP server, not the HTTP API.
Hitting the limit returns 429 with a Retry-After header. There's no soft burst — pace your client.
Pagination
List endpoints take page (1-based) and limit (1–100, default 20). Cursor-based endpoints use before (a resource id) instead. Both styles return data.total when known.
Resource reference
- Spaces — Shared chat rooms inside an org. Internally still called "projects" in URLs (see migration guide).
- Sessions — Conversation threads inside a Space.
- Messages — Posts within a session — human or AGENT-authored.
- Memory — Embedded notes with PRIVATE / SPACE / ORG visibility.
- Org — Members + invitations.
- Billing — Plan, credits, top-ups, Stripe checkout.
- MCP — External MCP integrations bound to Spaces.
- Keys — API keys minted for use from SDKs and external MCP clients.
SDKs
Both SDKs wrap this API one-to-one and add convenience: typed responses, async iterators for paginated lists, retry on 429.
- JS / TS:
npm install @switchy-ai/sdk - Python:
pip install switchy-sdk
The new 0.3.x namespaces drop in this wave. See the migration guide for the side-by-side mapping.
Webhooks
The only incoming webhook is the Stripe one at /api/webhooks/stripe; you don't configure it. Org-event webhooks (member added, invitation accepted, etc.) ship in a later wave.