REST API
Everything the dashboard does goes through the REST API — it is the same surface you script against.
Base paths
Section titled “Base paths”- API root:
/api/v1 - Workspace-scoped resources:
/api/v1/w/{workspace_id}/…— conversations, contacts, knowledge, agents, tours, reports, settings. Every request is checked against your membership (or API key) in that workspace.
Authentication
Section titled “Authentication”Users: POST /api/v1/auth/login with email/password returns an access token in the
body (valid 15 minutes, send as Authorization: Bearer …) and sets a rotating, httponly
refresh cookie (30 days). POST /api/v1/auth/refresh mints a new access token; refresh
tokens are single-use with reuse detection.
API keys: create under Settings → API keys — keys look like sk_stept_…, are
workspace-bound, shown once, and sent as Authorization: Bearer sk_stept_…. Scopes:
read (all read endpoints), write (read + conversations, contacts, knowledge, tours
mutations), admin (everything except workspace deletion).
Pagination
Section titled “Pagination”- Feeds (conversations, messages) use cursors:
{ "items": […], "next_cursor": "…" | null }— passcursor=to continue. - Lists (documents, runs, admin tables) use offsets:
{ "items": […], "total": 123, "limit": 25, "offset": 0 }.
Errors
Section titled “Errors”Every error is one envelope with the matching HTTP status:
{ "error": { "code": "not_found", "message": "Source not found", "details": {} } }Codes include bad_request (400), unauthorized (401), forbidden (403), not_found
(404), conflict (409), validation_failed (422, with per-field details) and
rate_limited (429).
Rate limits
Section titled “Rate limits”Abuse limits apply per route (for example login, signup, widget boot and public portal
endpoints); exceeding one returns 429 with the rate_limited code. Back off and retry.
OpenAPI
Section titled “OpenAPI”In development the interactive docs are at /api/v1/docs (spec:
/api/v1/openapi.json). In production they are off by default; set
STEPT_EXPOSE_API_DOCS=true to serve them.
Worked example: crawl your docs site
Section titled “Worked example: crawl your docs site”API=https://app.stepped.aiAUTH="Authorization: Bearer $TOKEN" # login access token or sk_stept_… keyWS=<workspace_id>
# 1. Create a crawl source (does not sync yet)SRC=$(curl -s -X POST $API/api/v1/w/$WS/knowledge/sources \ -H "$AUTH" -H 'Content-Type: application/json' -d '{ "type": "crawl", "name": "Docs site", "config": { "base_url": "https://docs.example.com/guides", "max_pages": 100, "include_patterns": ["/guides/*"], "refresh_minutes": 1440 } }' | jq -r .id)
# 2. Trigger the sync (returns immediately with status "syncing")curl -s -X POST $API/api/v1/w/$WS/knowledge/sources/$SRC/sync -H "$AUTH"
# 3. Poll until status is back to "idle" (or "error")curl -s $API/api/v1/w/$WS/knowledge/sources/$SRC -H "$AUTH" | jq '.status, .document_count'
# 4. Search what was indexedcurl -s -X POST $API/api/v1/w/$WS/knowledge/search \ -H "$AUTH" -H 'Content-Type: application/json' \ -d '{"query": "how do refunds work", "k": 5}' | jq '.results[].title'