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:
vid_videosong_songcode_code buildres_researchspace_spacedeck_presentationPolling 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 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. One exception to "result on complete": a space job also carries its result while processing as soon as its first tier has landed (stage: preview: a room's walkable quick room, or a one-photo space's 360 preview), so you can show the room before the final tier finishes.
Results by type
{
"videoUrl": "https://www.opencharts.com/api/canvas-video/...",
"thumbnailUrl": "https://...",
"aspectRatio": "16:9",
"modelTier": "fast",
"durationSeconds": 8
}{
"title": "Neon Skyline",
"songId": "6870f4...",
"versions": [
{
"id": "a",
"audioUrl": "https://...",
"coverImageUrl": "https://...",
"durationSeconds": 172,
"engine": "Theo Symphony"
}
]
}{ "codeProjectId": "6870f5...", "name": "Landing page", "framework": "html" }{
"content": "# Report...",
"sources": [ { "url": "https://...", "title": "..." } ],
"searchCount": 14,
"readCount": 9,
"totalTokens": 48213
}{
"projectId": "6870f6...",
"name": "Q4 Board Deck",
"kind": "create",
"slideCount": 11,
"slidesTotal": 12,
"partial": true,
"failedSlides": 1,
"openUrl": "https://www.opencharts.com/presentations/6870f6..."
}A deck is complete once its slides are built. partial: true means some slides did not build: the deck keeps a placeholder for each, and opening it offers to generate the missing ones. A rewrite (kind: regenerate) keeps the existing slides until the new ones are ready.
{
"stage": "space",
"imagined": true,
"source": "photos",
"photoCount": 4,
"panoramaUrl": "https://www.opencharts.com/api/canvas-video/...",
"spaceUrl": "https://www.opencharts.com/api/canvas-video/...",
"spaceFullUrl": "https://www.opencharts.com/api/canvas-video/...",
"format": "spz",
"metricScale": 1.38,
"floorOffsetM": 1.351,
"colliderUrl": "https://www.opencharts.com/api/canvas-video/...",
"dimensions": null,
"engine": "Theo Real World 1.1"
}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.