Developers

The public API.

One versioned REST surface for workspaces, runs, conversations, and visualizations, with a generated TypeScript client.

Base URL https://api.wsb.studioVersion v1Contract contracts/api/v1/wsb-api.openapi.json

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_conflict when 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
null on 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_invalid

Scopes 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.