Developer docs

REST API

Plain JSON over HTTPS for everything the MCP server can do. Every route below is also an MCP tool of the same name: one implementation, one scope map, one rate limit, reachable from curl, a cron job or an automation platform.

Conventions

ItemRule
Base URLhttps://app.streamagent.io/api/v1
AuthenticationAuthorization: Bearer sa_live_… with an API key, or a sat_ assistant token. See Authentication.
ContentJSON in, JSON out. Send Content-Type: application/json on writes.
IdsUUID strings, exactly as the list routes return them. Never construct one.
VersioningThe version is in the path. This version only gains fields and routes; a breaking change would be a new path.
Rate limitsThe same per-credential ceilings as the MCP server, with Retry-After on 429.
Lead privacyLead fields are masked exactly as on the MCP server when the workspace keeps masking on.
curl "https://app.streamagent.io/api/v1/leads?status=new&limit=20" \
  -H "Authorization: Bearer sa_live_…"
The newest twenty leads still marked new.
curl -X POST https://app.streamagent.io/api/v1/routes \
  -H "Authorization: Bearer sa_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: onboarding-route-2026-09" \
  -d '{"entry_video":"Welcome tour","name":"Onboarding"}'
Create a route from a video title, safely retryable.

Pagination

List routes take limit (default 50, at most 100) and cursor. The response carries a pagination block with total, limit, has_more and next_cursor; pass next_cursor back as cursor until it is null. Cursors are opaque and only valid for the same route and filters.

{
  "leads": [ … ],
  "pagination": { "total": 143, "limit": 50, "has_more": true, "next_cursor": "eyJvIjo1MH0" },
  "pii_redacted": true
}
A first page of leads.

Idempotent writes

Send an Idempotency-Key header (any string up to 255 characters, unique per operation) on a write and the first response is stored for 24 hours. A retry with the same key and the same request gets that response back with Idempotency-Replayed: true; the same key with a different request is a 409 conflict. Keys are private to the credential that sent them.

Errors

Every non-success response is { "error": { "code", "message", "details"? } }. The message is written to be shown to a developer and lists every problem it found, so one fix reaches a correct call.

CodeHTTPMeaning
unauthorized401No credential, or one that is malformed, revoked or expired. The response carries a WWW-Authenticate header.
forbidden403The credential is valid but lacks the scope the route needs; details.required_scope names it.
plan_limit403The workspace plan does not include this capability; details.entitlement names it.
not_found404No route at that path, or the object id is not in this workspace.
method_not_allowed405The path exists but not for that verb. The Allow header lists the verbs that work.
conflict409An Idempotency-Key was reused with a different request, or the first request with it is still running.
invalid_request400A parameter or body field is missing, unknown or the wrong shape; the message lists every problem at once.
rate_limited429Over the per-credential ceiling. Retry after the number of seconds in Retry-After.
internal500Something failed on our side. The details are logged; retry once, then contact support.
  • Retry 429 after the header value and 500 once; never retry 400, 401, 403 or 409 unchanged.
  • A 404 on an id means the object is not in this workspace, which is also what a wrong workspace key looks like.

Machine-readable contract

The full OpenAPI document, generated from the same source as this page, is at /api/v1/openapi.json. Point a client generator or an assistant at it.

Routes

Grouped as on the MCP server page. Required parameters are marked with *. Path segments in braces are ids from the list routes; GET routes take parameters in the query string, other verbs in a JSON body.

Videos

Method and pathWhat it doesScopeQuery or body fields
GET /videosBrowse your library with status and view counts.videos:readstatus, folder_id, tags, search, sort, limit, cursor
GET /videos/{video_id}Full detail and stats for one video.videos:read
POST /videos/compareRank videos on real performance, not raw views.videos:readvideo_ids*
GET /videos/{video_id}/key-momentsThe notable moments detected in a video.videos:read
PATCH /videos/{video_id}Rename, describe, retag, or archive a video (write).videos:writetitle, description, tags, archived
POST /videosPull a video file in from a public link; it processes in the background (write).videos:writesource_url*, title, folder_id, tags
POST /videos/uploadsCreate the video and get a one-time URL to send the file to (write).videos:writefile_name*, file_size*, title, folder_id, tags

Analytics

Method and pathWhat it doesScopeQuery parameters
GET /videos/{video_id}/analyticsViews, engagement, and conversion for a video.analytics:readfrom_date, to_date
GET /videos/{video_id}/retentionWhere viewers stay and where they drop off.analytics:read
GET /changesWhat changed across your workspace lately.analytics:readdays
GET /analytics/summaryThe workspace numbers for a range, each with its prior-period change.analytics:readrange
GET /analytics/dailyA per-day series between two dates.analytics:readfrom, to

Outcomes

Method and pathWhat it doesScopeQuery parameters
GET /outcomesThe revenue ledger: purchases, booked calls and refunds with value.outcomes:readfrom_date, to_date, kind, status, lead_id, video_id, limit, cursor

Provisioning

Method and pathWhat it doesScopeQuery or body fields
GET /webhook-endpointsEvery endpoint with its events and whether it is active.workspace:manage
POST /webhook-endpointsRegister a URL and get its signing secret once (write).workspace:managename*, url*, events
PATCH /webhook-endpoints/{endpoint_id}Rename, re-point, change events, pause or rotate the secret (write).workspace:managename, url, events, active, rotate_secret
DELETE /webhook-endpoints/{endpoint_id}Remove an endpoint and its history (write).workspace:manage
POST /webhook-endpoints/{endpoint_id}/testDeliver the synthetic test event and report the result (write).workspace:manage
GET /webhook-endpoints/{endpoint_id}/deliveriesWhat was sent to an endpoint and how it went.workspace:managestatus, limit, cursor
GET /api-keysActive keys by name, prefix and tier.workspace:manage
POST /api-keysMint a key of equal or lower access and receive it once (write).workspace:managename*, scope
DELETE /api-keys/{key_id}Stop a key immediately (write).workspace:manage

Leads

Method and pathWhat it doesScopeQuery or body fields
GET /leadsYour captured leads, most engaged first.leads:readvideo_id, status, source, from_date, to_date, search, sort, limit, cursor
GET /leads/{lead_id}One lead with full activity history.leads:read
GET /leads/{lead_id}/journeyA lead’s complete timeline across your videos.leads:read
PATCH /leads/{lead_id}Set status, tags, or append a note (write).leads:writestatus, tags, notes, email, name, phone
PUT /leadsAdd a lead by email, or enrich the one that already exists (write).leads:writeemail*, first_name, last_name, name, phone, status, tags, notes, custom_fields, source

Transcripts

Method and pathWhat it doesScopeQuery parameters
GET /transcripts/searchFind where a topic is spoken across your library.transcripts:readq*, video_id, limit
GET /videos/{video_id}/transcriptThe full transcript for a video.transcripts:readoffset, max_chars

Routes

Method and pathWhat it doesScopeQuery or body fields
GET /routesYour interactive routes.routes:readvideo_id, status, limit, cursor
GET /routes/{route_id}The structure of one route.routes:read
PATCH /routes/{route_id}Rename, pause, or archive a route (write).routes:writename, status
POST /routesStart a new interactive route (write).routes:writeentry_video*, name
POST /routes/{route_id}/stepsAdd a video step to a route (write).routes:writevideo*
POST /routes/{route_id}/branchesWire the choices between steps (write).routes:writequestion, branches*

Scheduling

Method and pathWhat it doesScopeQuery or body fields
GET /bookingsUpcoming and past calls booked through your videos.leads:readscope, limit, cursor
GET /scheduling/overviewYour calendar shape at a glance.workspace:read
GET /scheduling/availabilityWhy a slot is (or isn’t) offered.workspace:readevent_type
POST /bookingsBook a call and capture the lead (write).leads:writeevent_type_id*, start*, invitee_email*, invitee_name, invitee_phone, invitee_timezone

Channel

Method and pathWhat it doesScopeBody fields
GET /channelWhat’s live on your public channel.workspace:read
PUT /channel/items/{id}Put a video or route on the channel, or take it off (write).channel:writekind*, visible*
PUT /channel/featuredChoose what leads the channel page (write).channel:writevideo_id, route_id
PATCH /channelUpdate the channel headline and intro line (write).channel:writeheadline, dek

Workspace

Method and pathWhat it doesScopeQuery or body fields
GET /workspacePlan, usage, and the guidance you wrote for assistants.workspace:read
GET /tagsThe tags in use across your content.workspace:read
POST /tags/renameRename or merge a tag everywhere it’s used (write).videos:writetag*, new_tag*, applies_to
GET /searchOne search across videos, transcripts, routes, and leads.workspace:readq*

Not in this version

Deleting a video or a lead stays a signed-in action; archive is the reversible path and is on PATCH /videos/{video_id}.