Developers
The public API.
One versioned REST surface for workspaces, runs, conversations, and visualizations, with a generated TypeScript client.
Authentication
Send one credential, never both — a request presenting both is refused.
- Authorization
Bearer <token>: an API key secret (wsb_live_…/wsb_test_…) or an identity token.- Session cookie
- Browser-managed,
Secure,HttpOnly. Not available outside the browser. - X-WSB-CSRF
- Required alongside the session cookie on an unsafe request. Never sent with a bearer token.
curl https://api.wsb.studio/v1/me \
-H "Authorization: Bearer wsb_live_..."Idempotency
Every operation that creates or transitions a resource requires Idempotency-Key. Replay the same key and get the original response back; reuse it with a different body and the request is refused.
- Idempotency-Key
- 1–255 printable characters. No space, no comma.
- Scope
- Unique per workspace and operation, retained 24 hours or more.
- Conflict
409 idempotency_conflictwhen the same key arrives with a different request.
Pagination
Collections page by opaque cursor, newest first. Offset pages can’t hold still under a collection that’s being written to.
- cursor
- From the previous page’s
next_cursor. - limit
- 1–200. A narrower collection refuses above its own ceiling instead of clamping.
- next_cursor
nullon the last page.
Rate limits
Fixed window. The same four headers on every response.
- RateLimit-Limit
- Requests permitted per window.
- RateLimit-Remaining
- Requests left in the current window.
- RateLimit-Reset
- Seconds until the window resets. Always a delta.
- Retry-After
- Seconds to wait, on a
429.
Errors
One envelope, one closed set of codes. Branch on code, not on message.
{
"schema_version": "wsb.api.error.v1",
"error": {
"code": "rate_limited",
"message": "The request was rate limited.",
"request_id": "request.9f1c04a7b23d5e6081af72c3",
"details": { "retry_after_seconds": 30 }
}
}billing_unconfiguredbudget_exceededconflictcursor_invaliddata_coverage_internal_errordata_coverage_unavailableentitlement_requiredentitlement_unavailableforbiddenidempotency_conflictinternalinvalid_argumentinvalid_transitionlease_lostnot_foundprojection_failedquota_exceededquota_unavailablerate_limitedstale_projectiontemporarily_unavailableunauthenticatedverification_failedwebhook_signature_invalidScopes and API keys
A key carries one or more scopes. Its effective authority is the intersection of those scopes and its owner’s workspace role — a key can never exceed the person who issued it, and demoting that person narrows every key they hold.
- workspace.read
- Read a workspace and its resources.
- strategy.write
- Create and revise drafts and strategy versions.
- run.create
- Start a backtest run.
- run.cancel
- Cancel a run in progress.
- member.list
- List workspace members.
- member.manage
- Invite members and change roles.
- apikey.manage
- Issue, list, and revoke API keys.
Keys are issued, listed, and revoked under /v1/workspaces/{workspace_id}/api-keys. Revoking is a POST … /revoke, not a delete — a revoked key stays listed.
TypeScript client
The surface is one OpenAPI 3.1 document, contracts/api/v1/wsb-api.openapi.json. We generate our TypeScript client from it; generate yours with any OpenAPI 3.1 tool.
Every request gets an X-Request-ID, echoed as error.request_id on a failure. Include it when you write in.