Skip to content

API reference

One REST API over everything the product does: upload a recording, read what the AI concluded, share it, export it, search across it, and receive the reports your own visitors file. JSON in, JSON out, cursor pagination, and RFC 7807 problem responses.

Authentication

Create a key under Dashboard → Developers (Pro and above). Keys are shown once, hashed at rest, scoped to the workspace that created them, and prefixed sr_live_. Send it as a bearer token on every request:

curl https://api.scenerecap.com/v1/recordings \
  -H "Authorization: Bearer sr_live_…"
The key is shown once, at creation. It is stored only as a hash, so a lost key cannot be recovered: revoke it and issue another. Treat it like a password: it is a workspace-wide credential, not a per-user one.

Conventions

Base URLhttps://api.scenerecap.com
VersioningPath-prefixed /v1. Additive changes ship without a version bump; removals do not.
PaginationCursor. Pass limit (1 to 100, default 30) and cursor (the ISO timestamp from nextCursor). A null nextCursor means the last page.
WorkspaceA key is pinned to the workspace it was created in. Leave out workspace / workspaceId and a request uses that workspace; naming any other workspace returns 404.
Rate limit300 requests per minute per client IP address, across all endpoints. Over that you get 429; retry after a short backoff.
ErrorsRFC 7807 application/problem+json with status, title and detail. A 402 means a plan limit, not a payment failure.
TimestampsISO 8601, UTC. Timeline offsets are seconds from the start of the recording.

Endpoints

Recordings

The core object: media, AI outputs, chapters, transcript and playback URLs.
GET/v1/recordings
List recordings, newest first.Filters: workspace, folder, q. Cursor pagination.
GET/v1/recordings/:id
One recording with chapters, transcript, AI outputs and signed playback URLs.
POST/v1/recordings/preflight
Ask whether the plan would accept a recording, before you upload anything.Body: mode, durationSec, optional workspaceId. The same checks as create (recording count, length, mode, monthly analyses): 200, or 402 naming the limit.
POST/v1/recordings
Create a recording from a completed upload.Accepts eventLogUploadId and redactionUploadId sidecars. durationSec is the length after any cuts in the redaction manifest. For a run of your own agent, send sourceMeta.source "agent" (see Agent runs below).
PATCH/v1/recordings/:id
Rename, or move to a folder.To change the AI mode, use reanalyze.
DELETE/v1/recordings/:id
Delete a recording.Cascades to storage: originals and derived assets both go.
POST/v1/recordings/:id/reanalyze
Run another AI mode over the same media.
POST/v1/recordings/:id/retry
Retry a failed pipeline run.When only the AI pass failed, only the analysis re-runs; otherwise the video is re-processed from the original.
GET/v1/recordings/:id/events
The interaction event log used as evidence.
POST/v1/recordings/:id/voiceover
Generate a narrated variant.Metered in voiceover minutes.
POST/v1/recordings/:id/repro
Re-drive a bug report headlessly and report whether it reproduces. See the Repro Agent docs.Pro+ and metered. Verified origins only, and not available for widget reports without a click trail. Optional runSessionId runs it signed in (admins only).
POST/v1/recordings/:id/perform
Ask the agent to re-perform this recording's flow and record a fresh take. See the Takes docs.Pro+ and metered from the same agent-run allowance. Verified origins only; needs an interaction event log.
GET/v1/recordings/:id/takes
List your take drafts for a recording (drafts are private to the requesting user).
POST/v1/takes/:id/approve
Approve a finished take into a real recording; the result is permanently labelled as performed by the agent.
DELETE/v1/takes/:id
Discard a draft take. Its stored video is deleted immediately; approved takes are refused.
PATCH/v1/outputs/:id
Correct an AI output in place.

Uploads

Multipart, presigned, and the same path the extension and the web uploader use.
POST/v1/uploads
Open a session; returns presigned part URLs.kind: media | eventlog | redaction_manifest
POST/v1/uploads/:id/complete
Finish the upload with the ETag of every part.

Shares and comments

Five access levels, from public to email-restricted, with timestamped comments.
GET/v1/recordings/:id/shares
List share links for a recording.
POST/v1/recordings/:id/shares
Create a share link.
DELETE/v1/shares/:id
Revoke a share link.
POST/v1/shares/:slug/comments
Comment on a shared recording, anchored to a timestamp.
POST/v1/shares/:slug/view
Record a view.Fires the share_viewed lifecycle signal once.
POST/v1/shares/:slug/report
Report a share for abuse.
POST/v1/shares/:slug/resolve
Exchange a password or email grant for access.

Exports and search

POST/v1/recordings/:id/exports
Queue an export.format: markdown | html | pdf | docx | gif | skill | agent_md, exactly these values. Plan-gated; poll for the signed URL.
GET/v1/recordings/:id/exports
Poll export status and collect the download URL.
GET/v1/recordings/:id/agent-export?kind=skill|agent_md
Read the newest agent export as text.Returns status absent | pending | ready | failed. For a skill it returns the SKILL.md text, the bundled screenshot paths and the zip's signed URL. POST the export first; this never renders.
GET/v1/search
Hybrid search: title, full-text and vector, fused with RRF.Results carry timestamps you can deep-link to with ?t=.

Reporting widget and Inbox

Your own visitors record bugs on your site; the reports arrive here already written up.
GET/v1/workspaces/:id/site-keys
List site keys.
POST/v1/workspaces/:id/site-keys
Create a site key.origins is an allowlist (up to 10); retentionDays is opt-in and null means keep forever.
PATCH/v1/site-keys/:id
Rename, change origins, set retention, activate or pause.
DELETE/v1/site-keys/:id
Revoke a site key.
GET/v1/workspaces/:id/reports
The Inbox. Duplicates are collapsed to one row with a clusterCount.Filter by status: new, triaged, closed.
PATCH/v1/reports/:id
Triage a report or attach the issue URL you filed.

Filing to your tracker

GET/v1/integrations
Which providers are configured, connected, or need reconnecting.
GET/v1/integrations/:provider/resources
Pickable targets: channels, repos, teams, projects, pages.
PATCH/v1/integrations/:provider
Set the destination for filed issues.
POST/v1/recordings/:id/create-issue
File the AI bug report to GitHub, Linear or Jira.
POST/v1/recordings/:id/notion-export
Export a tutorial or SOP into Notion.
DELETE/v1/integrations/:provider
Disconnect a provider.

Workspaces, folders and people

GET/v1/me
The caller, their workspaces and per-workspace flags.
POST/v1/workspaces
Create a workspace.
GET/v1/workspaces/:id/members
List members and roles.
PATCH/v1/workspaces/:id/members/:userId
Change a member's role.The last admin cannot be demoted.
DELETE/v1/workspaces/:id/members/:userId
Remove a member.The last admin cannot be removed.
GET/v1/workspaces/:id/invites
List pending invites.
POST/v1/workspaces/:id/invites
Invite by email.Tokens are bound to the invited address.
POST/v1/invites/:id/resend
Send a pending invite again.
DELETE/v1/invites/:id
Revoke a pending invite.
POST/v1/invites/accept
Accept an invite.
GET/v1/workspaces/:id/folders
List folders.
POST/v1/workspaces/:id/folders
Create a folder.
PATCH/v1/folders/:id
Rename a folder.
DELETE/v1/folders/:id
Delete a folder.Its recordings are kept, just no longer in a folder.
GET/v1/workspaces/:id/billing
Plan, seats and usage against the monthly allowance.subscription.cancelAt is set when the plan is scheduled to end.
GET/v1/workspaces/:id/billing/invoices
Invoice history with hosted and PDF links.

Checks

A recorded flow re-driven on a schedule or from CI. See the Checks docs for statuses and alerts.
POST/v1/checks
Turn a recording into a check.Body: sourceRecordingId, name, targetOrigin (a verified origin), schedule hourly | daily | weekly | manual, timezone, successInWords, alertChannels. Optional runSessionId (admins only). Pro+.
GET/v1/checks
List the workspace's checks with their latest status.
GET/v1/checks/:id
One check, its recent runs and any plan change waiting for approval.
PATCH/v1/checks/:id
Rename, reschedule, pause or resume, change alerts, or accept a proposed plan.Alert destinations need an admin.
DELETE/v1/checks/:id
Delete a check and the videos of its failed runs.Admins only.
POST/v1/checks/:id/runs
Run it now.Body: trigger manual | api | ci. Returns 202 with the run id; counts against the monthly check runs.
GET/v1/check-runs/:id
One run: passed, failed, blocked or error, the step it reached, and why.blocked (a sign-in page) and error (could not evaluate) never alert.
POST/v1/check-runs/:id/bug-report
Turn a failed run's video into a bug report recording.Labelled as performed by the agent, everywhere it appears.

Living tutorials

Keep a tutorial in step with the product. Changes arrive as drafts a person approves. See the Living tutorials docs.
POST/v1/living-tutorials
Keep a ready tutorial up to date. Records the first baseline straight away.Body: sourceRecordingId, targetOrigin, schedule weekly | daily | deploy, sensitivity low | normal | high, noiseMasks, showStaleNote, slackChannel, runSessionId. Admins only. The webhook secret is returned once.
GET/v1/living-tutorials
List the workspace's living tutorials.
GET/v1/recordings/:id/living-tutorial
The living tutorial for a recording, if there is one.
GET/v1/living-tutorials/:id
One tutorial: versions, recent refreshes and the draft waiting for review.
PATCH/v1/living-tutorials/:id
Change the schedule, sensitivity, masks or origin, or turn it off.Admins only. Moving to another origin detaches its signed-in session.
DELETE/v1/living-tutorials/:id
Stop keeping it up to date. Published versions stay.Admins only.
POST/v1/living-tutorials/:id/refresh-now
Check it against the product now.Members can. Counts against the monthly tutorial refreshes.
POST/v1/living-tutorials/:id/refresh
The deploy webhook. Signed with the tutorial's secret, no API key.See the worked example below. At most 24 a day; an Idempotency-Key makes a retry free.
POST/v1/living-tutorials/:id/rotate-secret
Issue a new webhook secret. The old one stops working at once.Admins only.
GET/v1/living-tutorials/:id/refreshes/:refreshId
One refresh: verified, changed, failed or blocked, with the changelog.
POST/v1/living-tutorials/:id/refreshes/:refreshId/approve
Publish the draft as the next version. The share link serves it once it is ready.Admins only.
POST/v1/living-tutorials/:id/refreshes/:refreshId/discard
Throw the draft away.Admins only.
POST/v1/living-tutorials/:id/versions/:versionId/restore
Make an earlier version the live one again.Admins only.

Agent runs

Recordings of your own agent driving a browser, reviewed like any recording. The SDK makes these calls for you; see the Agent runs docs.
POST/v1/recordings
Create an agent run: the usual create, with sourceMeta.source "agent".sourceMeta: agentName (required), agentFramework, agentRunId, referenceRecordingId, metadata (up to 20 string pairs), outcome, note. The agent's own outcome is shown, never used as the verdict.
GET/v1/recordings/:id
Includes agentRun: the review (verdict, moments worth a second look) and the comparison with the reference recording.
POST/v1/recordings/:id/sign-off
Record a person's decision on the run, kept beside the machine verdict.Body: decision approved | rejected, optional comment. Emits agent_run.reviewed.

Signed-in sessions and verified sites

A test account's session for one site you control, so runs work behind a sign-in. Write-only: nothing returns a session. See the Signed-in sessions docs.
POST/v1/verified-origins
Add a site and get the token to serve at /.well-known/scenerecap-verification.txt.Admins only. A site already on one of your site keys counts without this.
POST/v1/verified-origins/:id/verify
Fetch the file and mark the site verified.https only, no redirects. Returns 409 with the reason when it does not match.
GET/v1/verified-origins
List the workspace's sites and whether each is verified or covered by a site key.Admins only.
DELETE/v1/verified-origins/:id
Remove a site. Sessions for it stop working unless a site key covers it.Admins only.
POST/v1/run-sessions
Add a session: origin, label, expiresInDays (1 to 30) and a Playwright storage state.Admins only, Pro+. Cut to that site, encrypted, and refused if it appears to contain a password. At most 50 per workspace.
GET/v1/run-sessions
List sessions: label, origin, cookie count, expiry and last use.Filter with ?origin=. Works with the feature off, so you can always see what we hold.
DELETE/v1/run-sessions/:id
Revoke a session. The row is deleted at once; runs that used it end as blocked.Admins only. Works with the feature off.

Keys and webhooks

GET/v1/workspaces/:id/api-keys
List API keys (prefix and last use only, never the key).
POST/v1/workspaces/:id/api-keys
Create a key.The only response that ever contains the secret.
DELETE/v1/api-keys/:id
Revoke a key immediately.
GET/v1/workspaces/:id/webhooks
List webhook endpoints.
POST/v1/workspaces/:id/webhooks
Register an endpoint and pick events.
PATCH/v1/webhooks/:id
Change the URL or events, or pause and resume with active.
GET/v1/webhooks/:id/deliveries
Every attempt, with status and response.
DELETE/v1/webhooks/:id
Remove an endpoint.

Worked example: upload and analyse

Three calls. Open a session, PUT the parts, then create the recording; the pipeline starts on its own. On a plan with limits, ask first, so a refusal costs one call and not an upload:

# 0. (optional) would the plan accept it? 200, or 402 naming the limit
curl -sX POST https://api.scenerecap.com/v1/recordings/preflight \
  -H "Authorization: Bearer $SR_KEY" -H "content-type: application/json" \
  -d '{"mode":"bug_report","durationSec":48}'

# 1. open a multipart session
curl -sX POST https://api.scenerecap.com/v1/uploads \
  -H "Authorization: Bearer $SR_KEY" -H "content-type: application/json" \
  -d '{"kind":"media","filename":"session.webm","contentType":"video/webm","sizeBytes":8123456}'
# -> { "uploadId": "…", "partSize": 8388608, "parts": [{ "n": 1, "url": "https://…" }] }

# 2. PUT each part to its presigned url, keeping the ETag each one returns
curl -sX PUT "$PART_URL" --data-binary @part-1.bin -D - | grep -i etag

# 3. complete, then create the recording
curl -sX POST https://api.scenerecap.com/v1/uploads/$UPLOAD_ID/complete \
  -H "Authorization: Bearer $SR_KEY" -H "content-type: application/json" \
  -d '{"parts":[{"n":1,"etag":"abc123"}]}'

curl -sX POST https://api.scenerecap.com/v1/recordings \
  -H "Authorization: Bearer $SR_KEY" -H "content-type: application/json" \
  -d '{"uploadId":"'$UPLOAD_ID'","title":"Checkout fails","mode":"bug_report"}'

Poll GET /v1/recordings/:id until status is ready, then read outputs for the bug report, tutorial, explanation or recap.

Worked example: triage the widget Inbox

Duplicates arrive already grouped, so one row can stand for many reports.

curl -s "https://api.scenerecap.com/v1/workspaces/$WS/reports?status=new" \
  -H "Authorization: Bearer $SR_KEY"
# -> reports[0].clusterCount === 5  (five visitors, one bug, one row)

# file it, then mark it triaged with the issue you created
curl -sX POST https://api.scenerecap.com/v1/recordings/$REC/create-issue \
  -H "Authorization: Bearer $SR_KEY" -H "content-type: application/json" \
  -d '{"provider":"linear"}'

curl -sX PATCH https://api.scenerecap.com/v1/reports/$REPORT \
  -H "Authorization: Bearer $SR_KEY" -H "content-type: application/json" \
  -d '{"status":"triaged","issueUrl":"https://linear.app/…"}'

Webhooks

Register a URL under Developers and choose your events: recording.ready, recording.shared, comment.created, check.failed, check.recovered, agent_run.reviewed. Each delivery is a JSON POST carrying X-SceneRecap-Event, X-SceneRecap-Delivery and X-SceneRecap-Signature: sha256=<hex>, an HMAC-SHA256 of the raw body using the secret shown once at creation.

Verify against the raw body, before any JSON parsing. A re-serialised body will not match the signature, and comparing with === instead of a timing-safe compare leaks the secret a byte at a time.
import crypto from "node:crypto";

export function verify(rawBody: Buffer, header: string, secret: string): boolean {
  const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(header);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Non-2xx responses are retried with exponential backoff up to three attempts in total (the first delivery plus two retries), and every attempt (status, response body, timing) is visible in the Deliveries panel.

Check alerts fire on a change only: check.failed when a passing check starts failing, and check.recovered when it passes again. A blocked or error run sends neither.

Worked example: refresh a tutorial after a deploy

Call the tutorial’s refresh URL from your deploy pipeline. It takes no API key: sign the request with the tutorial’s own secret, over <timestamp>.<body>, so a captured request cannot be replayed later. Requests more than five minutes old are refused, and every refusal is the same 401.

TS=$(date +%s)
BODY='{"ref":"'$GIT_SHA'"}'
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$TUTORIAL_SECRET" -hex | sed 's/^.* //')

curl -sX POST https://api.scenerecap.com/v1/living-tutorials/$TUTORIAL/refresh \
  -H "content-type: application/json" \
  -H "X-SceneRecap-Timestamp: $TS" \
  -H "X-SceneRecap-Signature: sha256=$SIG" \
  -H "Idempotency-Key: deploy-$GIT_SHA" \
  -d "$BODY"
# a retry with the same Idempotency-Key answers 200 with "replayed": true and costs nothing

Plan limits

Limits are enforced server-side and return 402 with a message naming the limit. API keys require Pro; the reporting widget is a paid add-on; export formats, AI analyses, agent runs (Repro Agent verdicts and Takes draw from one pool), check runs, tutorial refreshes each have their own monthly allowance. See pricing for the current numbers.