Developer Guide
Public API
Create AI video sequences programmatically — one-shot create, poll for status, with HAL links, ?wait long-polling, and an OpenAPI 3.1 spec
OpenStory exposes a small public HTTP API so agents and scripts can create video sequences without driving the UI. It is self-describing: an agent can learn the whole surface from two unauthenticated endpoints and never has to read this page.
Discovery
GET /api/v1— the API root: an instructions narrative, the create request JSON Schema, and a HAL_linkscatalog of available actions.GET /api/v1/openapi.json— the full OpenAPI 3.1 specification, generated from the same schema the API validates against.
Both are unauthenticated so tooling can discover the API before a key is wired
up. https://openstory.so/llms.txt also points here.
Authentication
Every endpoint except discovery requires an API key. Create one in the dashboard under Settings → Developer. Send it as either header:
Authorization: Bearer <key>
x-api-key: <key>
Keys are team-scoped — a key only ever sees its own team's sequences — and rate
limited to 10 requests/second; exceeding it returns 429 with a Retry-After
header.
Create a sequence
POST /api/v1/sequences turns a script into a video sequence. Generation is
asynchronous: the call returns 202 immediately with the created sequence id(s)
and a status URL to poll.
curl -X POST https://openstory.so/api/v1/sequences \
-H "Authorization: Bearer $OPENSTORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"script": "A lighthouse keeper befriends a stranded whale.",
"title": "Sea Tale",
"style": "Cinematic Noir",
"targetSeconds": 30,
"motion": true,
"music": true,
"characters": ["Old Tom the keeper", { "name": "The whale", "isHuman": false }],
"locations": ["Stormy lighthouse"]
}'
The body is ergonomic: reference a style, cast member, or location by id or name,
or pass an inline object to create a new one. enhance (auto | always |
off) controls script expansion; motion and music toggle video and score.
See GET /api/v1 or the OpenAPI spec for the full request schema.
Response (202):
{
"sequences": [
{
"id": "seq_…",
"status": "draft",
"workflowRunId": "…",
"statusUrl": "/api/v1/sequences/seq_…",
"_links": {
"self": { "href": "…", "method": "GET" },
"poll": { "href": "…", "method": "GET", "templated": true }
}
}
],
"enhancedScript": "…",
"_links": { "self": { "…": "…" } }
}
Poll for status
GET /api/v1/sequences/{id} returns a database-derived status document: overall
status, per-frame image/video status and URLs, music, poster, and ready/failed
counts. Because it is derived from the database it is always correct, even if you
reconnect later.
A terminal completed status can still carry counts.videosFailed > 0 — a
failed frame does not fail the whole run — so check the counts to confirm an
end-to-end success.
Long-polling with ?wait
Agents often have no sleep tool, so every pollable endpoint accepts
?wait=<duration> (60s, 30, 2m, 1500ms; capped at 90s). The server holds
the request open and returns the moment the sequence changes or reaches a
terminal state:
curl "https://openstory.so/api/v1/sequences/seq_…?wait=60s" \
-H "x-api-key: $OPENSTORY_API_KEY"
The response carries X-Wait-Changed (did it advance?) and X-Wait-Done (is it
terminal?) headers. On POST, ?wait additionally embeds each new sequence's
first progress snapshot, with waitChanged/waitDone flags per sequence. A
malformed wait value is rejected with 400 rather than silently downgrading to
a non-blocking request.
Conventions
- HAL links. Every response includes a
_linksmap of the actions available from that resource, each stating itsmethod. Follow links rather than hardcoding paths. - Errors are always JSON:
{ "error": { "code", "message", "details"? } }with the matching HTTP status — never an HTML page or redirect.