CaptureBeam
API reference

CaptureBeam API

POST a YAML, poll the job, get an MP4. Bearer-authed, JSON in / JSON out.

Base URL

https://capturebeam.com/api/v1

Authentication

Every request needs an API key. Create one in your dashboard; the key is shown once and stored hashed. Send it as a Bearer token:

Authorization: Bearer cb_live_xxxxxxxxxxxxx

x-api-key: cb_live_… is also accepted for clients that don't play nicely with Authorization.

Endpoints

POST/v1/renders

Submit a demo for rendering. Returns immediately with a job ID and a link to edit the demo in the dashboard.

Three body shapes are accepted:

  • application/json{ "yaml": "title: ...", "name"?: "..." }
  • application/json{ "projectId": "pj_..." }
  • text/yaml — raw YAML body

Raw-YAML submissions auto-create a Project so the user can tweak the demo and re-render from the dashboard. The response includes editUrl pointing at that project.

Example

curl -X POST https://capturebeam.com/api/v1/renders \
  -H "Authorization: Bearer cb_live_xxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"projectId": "pj_a1b2c3d4"}'

# 202 Accepted
{
  "id": "rj_a1b2c3d4e5f6",
  "status": "pending",
  "projectId": "pj_a1b2c3d4",
  "editUrl": "https://capturebeam.com/dashboard/projects/pj_a1b2c3d4",
  "statusUrl": "https://capturebeam.com/api/v1/renders/rj_a1b2c3d4e5f6",
  "docsVersion": "1.2.0"
}
GET/v1/renders/{id}

Poll a job's status. Poll every 2-3 seconds; jobs typically take 20-40 seconds.

Example

curl https://capturebeam.com/api/v1/renders/rj_a1b2c3d4e5f6 \
  -H "Authorization: Bearer cb_live_xxxxxxxxxxxxx"

# 200 OK — running, with live progress
{
  "id": "rj_…",
  "status": "running",
  "videoUrl": null,
  "manifestUrl": null,
  "steps": null,
  "error": null,
  "durationMs": null,
  "createdAt": "2026-01-15T12:00:00Z",
  "projectId": "pj_…",
  "editUrl": "https://capturebeam.com/dashboard/projects/pj_…",
  "progress": { "current": 3, "total": 8, "stepType": "click" },
  "docsVersion": "1.2.0"
}

# 200 OK — succeeded
{
  "id": "rj_…",
  "status": "succeeded",
  "videoUrl": "https://media.capturebeam.com/render-….mp4",
  "manifestUrl": "https://media.capturebeam.com/manifest-….json",
  "durationMs": 22400,
  "steps": [
    { "index": 0, "type": "goto", "status": "ok", "durationMs": 1240 },
    { "index": 1, "type": "click", "status": "ok",
      "resolvedSelector": "getByRole(button, name=\"Sign in\")",
      "durationMs": 380 }
  ],
  "createdAt": "2026-01-15T12:00:00Z",
  "projectId": "pj_…",
  "editUrl": "https://capturebeam.com/dashboard/projects/pj_…",
  "progress": null,
  "docsVersion": "1.2.0"
}

# 200 OK — succeeded with one step skipped
{
  "id": "rj_…", "status": "succeeded",
  "videoUrl": "https://…",
  "steps": [
    { "index": 0, "type": "goto", "status": "ok" },
    { "index": 1, "type": "click", "status": "failed",
      "error": "Could not resolve target { role: \"button\", name: \"Buy\" } …" }
  ]
}

# 200 OK — failed
{
  "id": "rj_…",
  "status": "failed",
  "error": "Render timed out after 600000ms (last phase: remotion-render)…",
  "steps": null
}

videoUrl and manifestUrl are signed URLs valid for 24 hours. steps is the compact per-step summary — index, type, status (ok/skipped/failed), optional error, optional resolved selector. progress is non-null while status === "running" so dashboards can show “Step 3 of 8 · click”.

POST/v1/probe

Discover interactive elements on a URL. Use to author valid targets before submitting a render.

Returns the page's buttons, links, inputs, and ARIA-tagged elements with their accessible names — exactly the shapes you can use as target values.

curl -X POST https://capturebeam.com/api/v1/probe \
  -H "Authorization: Bearer cb_live_xxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://app.example.com"}'

# 200 OK
{
  "url": "https://app.example.com",
  "status": 200,
  "title": "Example",
  "elements": [
    { "role": "button", "name": "Sign in",
      "target": { "role": "button", "name": "Sign in" } },
    { "role": "textbox", "name": "Email",
      "target": { "role": "textbox", "name": "Email" } }
  ],
  "truncated": false
}
GET/v1/schema

JSON Schema for demo.yaml plus a quick-reference summary. Use to validate client-side or to pin against the live contract.

Public — no auth required. Includes docsVersion and a changelog so cached agent skills can detect when the contract has changed.

Status codes

CodeMeaning
202Render queued. Poll the job ID.
200Job state retrieved.
400Missing or malformed body. Check yaml / projectId.
401Missing or invalid API key.
402No active subscription. Upgrade.
404Project not found, or doesn't belong to your account.
429Concurrency limit reached.
500Server error. Retry safe — the job stays in pending.

Render-and-wait

Most CI integrations want a single blocking call. This shell pattern submits and polls until done:

#!/usr/bin/env bash
set -euo pipefail
KEY=${CAPTUREBEAM_KEY:?}
BASE=https://capturebeam.com

JOB_ID=$(curl -sS -X POST $BASE/api/v1/renders \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"projectId":"pj_a1b2c3d4"}' | jq -r .id)

while :; do
  RES=$(curl -sS $BASE/api/v1/renders/$JOB_ID \
    -H "Authorization: Bearer $KEY")
  STATUS=$(echo "$RES" | jq -r .status)
  case "$STATUS" in
    succeeded) echo "$RES" | jq -r .videoUrl; exit 0 ;;
    failed)    echo "$RES" | jq -r .error >&2; exit 1 ;;
    *)         sleep 3 ;;
  esac
done

Limits

  • Concurrency: 3 simultaneous renders per account. The 4th returns 429 — back off and retry.
  • Polling: no rate limit on the status endpoint. Two- to three-second intervals are typical.

Versioning

Every API response includes a docsVersion field (semver). Compare against the version your client was written against. PATCH bumps are clarifications; MINOR adds new optional fields; MAJOR may change semantics. Read the changelog on GET /v1/schema to see what changed.