Developer docs

Authentication

Two kinds of credential reach the API and the MCP server: an API key you create for your own code, and a short-lived token an assistant obtains when a workspace owner connects it. Both are scoped to one workspace, and both work on either transport.

API keys

Create a key under Settings → API keys. Give it a name and pick a tier; the tier fixes what the key may do for its whole life. The full secret is shown once, at creation. Revoke a key from the same pane; revocation is immediate. A workspace can hold 10 active keys; one key per tool is the habit that keeps a revoke from breaking anything else.

Keys start with sa_live_ (older keys starting with sk_live_ still work). Send one as a bearer token on every request. The old ?api_key= query parameter is no longer accepted and returns an error that says so.

curl https://app.streamagent.io/api/mcp \
  -H "Authorization: Bearer sa_live_…" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
List the tools your key can call.
curl https://app.streamagent.io/api/v1/workspace \
  -H "Authorization: Bearer sa_live_…"
The same key on the REST API.

Using a credential on the REST API

Every route under https://app.streamagent.io/api/v1 takes the same bearer header and checks the same scope as the MCP tool of the same name; a key that can list leads over MCP can GET /leads, and a key that cannot, cannot. Errors come back as { "error": { "code", "message" } } with the HTTP status the code implies, lists page with limit and cursor, and writes accept an Idempotency-Key. The full conventions and the route table are on the REST API page.

Key tiers and what they expand to

A tier is a fixed set of scopes. The authorizer checks the scope a tool requires against this set on every call.

TierValueScopes
Readreadvideos:read, leads:read, analytics:read, transcripts:read, routes:read, workspace:read, outcomes:read
Read & writereadwritevideos:read, videos:write, leads:read, leads:write, analytics:read, transcripts:read, routes:read, routes:write, workspace:read, outcomes:read
Adminadminvideos:read, videos:write, leads:read, leads:write, analytics:read, transcripts:read, routes:read, routes:write, workspace:read, outcomes:read, channel:write, workspace:manage, actions:direct

Scopes

Assistants connected through sign-in request scopes individually and the workspace owner approves the exact set on a consent screen. Keys get their scopes from the tier above.

ScopeGrantsCovers
videos:readView your videosLibrary, key moments, comparisons, comments, questions & Shorts
videos:writeAdd and update your videosAdd or generate videos, titles, descriptions, tags & archive
leads:readView your leadsProfiles, scores & full journeys
leads:writeAdd and update your leadsAdd leads, status, tags, notes & details
analytics:readView your analyticsViews, retention, route results, traffic, pixels, ads & goals
transcripts:readSearch your video transcriptsFull transcripts & moment search
routes:readView your routesFlows, steps & branches, Smart Rotations & landing pages
routes:writeCreate and edit your routesBuild flows, steps & branches
workspace:readView your workspace settingsPlan, usage, tags, brand voice, team, settings, domains & integrations
channel:writeCurate your main page and collectionsCollections, what they hold, and your main page
outcomes:readView your revenue outcomesPurchases, booked calls & refunds with value, and revenue sources
workspace:manageManage webhooks and API keysEndpoints, secrets, delivery log & keys
actions:directAct without your approvalBook calls, archive or pause, generate clips, curate collections, rename tags & edit Studio projects at once instead of suggesting them

Some writes wait for approval: book_call, archiving with update_video, pausing or archiving with update_route, generate_video, the channel curation tools, rename_tag and edit_studio_project. Without actions:direct a call to any of them is saved as a suggestion the workspace owner applies in the app (Settings → Changes → Suggestions): the tool returns proposed: true with the proposal_id, and over REST the status is 202. Nothing is applied until a member clicks Apply. actions:direct is never issued by default -- a sign-in connection must request it and the owner approves it on the consent screen; among keys only the Admin tier carries it.

Every write a credential makes is listed for the owner under Settings → Changes, labelled "Connected app", with Undo wherever the change can be reversed.

One key across every client

An Enterprise agency can create a key from its own workspace that reaches every client workspace under it: tick Every client workspace when creating it under Settings → API keys (or pass agency_wide: true to POST /api-keys with an Admin key from the agency workspace). The key keeps its tier; what changes is which workspace a request acts on. Name the client on each request with the X-StreamAgent-Workspace header, as its id or its handle (the {handle}.streamagent.io part). Without the header the key acts on the agency workspace itself.

curl https://app.streamagent.io/api/v1/leads \
  -H "Authorization: Bearer sa_live_…" \
  -H "X-StreamAgent-Workspace: acme-realty"
The same key, acting on one client.

GET /workspaces (the list_workspaces tool) returns the agency and every client with the ids and handles the header accepts. A header naming a workspace outside the agency, or sent with a key that is not agency-wide, is refused with 403 before anything is read. Every call is logged under the workspace it acted on, so each client's Connected apps feed shows what the agency's key did there. The MCP server takes the same header on its HTTP requests, so an assistant configured with one key can be pointed at one client.

Assistant connections

Claude, ChatGPT and Perplexity connect by sign-in rather than by key: the owner approves a consent screen and the assistant receives a token prefixed sat_. The steps per assistant are on the MCP server page. Connected assistants, what each may do, and a revoke action live under Settings → Connected apps.

Rate limits

Every credential gets 600 calls per minute and 10,000 per hour by default; Enterprise workspaces get 2,000 per minute and 50,000 per hour. A workspace or a single key can carry its own ceiling; the effective limit is the key's, else the workspace's, else the plan's, else the default.

When you exceed it the response is 429 with a Retry-After header in seconds. Traffic without a valid credential is limited separately by client address, so a burst of failed sign-ins never counts against your key.

Lead privacy

Lead contact details are masked before they leave the workspace unless the owner turns masking off: names, emails, phone numbers and addresses come back with the first character and asterisks. The masking is applied identically to every lead tool.

Errors

The MCP server speaks JSON-RPC and the REST API speaks plain JSON, but authentication failures look the same on both: the HTTP status below, and a WWW-Authenticate header your client can read. On REST the body is { "error": { "code": "unauthorized" } } or "forbidden"; on MCP it is the JSON-RPC error shown.

SituationHTTPError
No credential, or sent as a query parameter401-32001 with a message saying to use the Authorization: Bearer header
Malformed or unknown credential401-32001 "Invalid token format"
Revoked key or expired token401-32001
Tool needs a scope the credential lacks403error names the missing scope
Rate limit exceeded429Retry-After header, seconds until the window resets
  • Retry 429 after the header value, not before.
  • Treat 401 as terminal for that credential: rotate the key or reconnect the assistant.