Core concepts

Jobs & polling

The unified async job model: type-prefixed ids, neutral statuses, and one poll endpoint.

Job ids

Every async endpoint returns a job with a type-prefixed id, and every job, regardless of pipeline, is polled at one dispatcher:

Prefixes
vid_
video
POST /videos
song_
song
POST /songs
code_
code build
POST /code-builds
res_
research
POST /research

Polling requires the read or ai scope (either works), so the key that created a job can always poll it. Research additionally keeps a legacy alias at GET /research/{jobId} that accepts raw or res_-prefixed ids and returns its original response shape; prefer GET /jobs/{id}.

Statuses

Statuses are provider-neutral: queued then processing then complete or failed. The envelope also carries the pipeline's rawStatus for fidelity and an optional progressMessage.

Polling

curl
curl https://www.opencharts.com/api/v1/jobs/vid_6870f3abc \
  -H "Authorization: Bearer $OPENCHARTS_API_KEY"

Completed jobs carry a result with stable, absolute asset URLs; failed jobs carry an error string. Poll every few seconds with backoff; renders can take minutes.

Results by type

video result
{
  "videoUrl": "https://www.opencharts.com/api/canvas-video/...",
  "thumbnailUrl": "https://...",
  "aspectRatio": "16:9",
  "modelTier": "fast",
  "durationSeconds": 8
}
song result
{
  "title": "Neon Skyline",
  "songId": "6870f4...",
  "versions": [
    {
      "id": "a",
      "audioUrl": "https://...",
      "coverImageUrl": "https://...",
      "durationSeconds": 172,
      "engine": "Theo Symphony"
    }
  ]
}
code build result
{ "codeProjectId": "6870f5...", "name": "Landing page", "framework": "html" }
research result
{
  "content": "# Report...",
  "sources": [ { "url": "https://...", "title": "..." } ],
  "searchCount": 14,
  "readCount": 9,
  "totalTokens": 48213
}

Prefer webhooks

Instead of polling, register a webhook for the matching *.completed / *.failed events and receive a signed delivery the moment a job finishes. See Webhooks.