Developers · REST API v1.0.0
AutoReel API reference
Build reels, run every model and check your credits from your own product. Plain HTTPS and JSON, scoped Bearer keys, and signed webhooks the moment work finishes. Included with Pro and Agency.
On this page
Every endpoint lives under this URL. Requests and responses are JSON; send Content-Type: application/json with a body. Timestamps are ISO 8601 in UTC and ids are UUIDs (models use their slug).
https://autoreel.app/apiCreate a key in your workspace's Settings → API keys (Pro and Agency plans, owners and admins). Send it as a Bearer token on every request. Keys start with ar_live_ and are shown once: we only keep a hash. Keep them on your server, never in a browser or mobile app.
curl https://autoreel.app/api/v1/credits \
-H "Authorization: Bearer $AUTOREEL_API_KEY"A missing, revoked or expired key gets 401. A workspace whose plan doesn't include the API gets 403 ENTITLEMENT_REQUIRED, and a key without the endpoint's scope gets 403 FORBIDDEN.
Each key has scopes. Give a key only what its integration needs; scopes ending in :write on reels and generations spend credits.
| Scope | Allows |
|---|---|
reels:read | Read reels and their exports |
reels:write | Build reels (spends credits) |
generations:read | Read generations |
generations:write | Start generations (spends credits) |
models:read | List models and their parameters |
credits:read | Read the credit balance |
files:write | Upload files to use in generations and reels |
webhooks:write | Manage webhook endpoints |
Limits apply per key, per minute. Every response says where you stand with RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds). Over the limit you get 429 RATE_LIMITED with a Retry-After header: wait that many seconds.
| Plan | API and webhooks | Requests a minute, per key |
|---|---|---|
| Free | Not included | — |
| Creator | Not included | — |
| Pro | Included | 60 |
| Agency | Included | 300 |
Failed requests answer with an HTTP status and a JSON body with a stable code to branch on and a message you can show to people. Invalid input adds fieldErrors, keyed by the field's path.
{
"error": {
"code": "VALIDATION",
"message": "Check the request body.",
"fieldErrors": {
"build.script": [
"Too small: expected string to have >=10 characters"
]
}
}
}| Code | HTTP | Meaning |
|---|---|---|
UNAUTHENTICATED | 401 | No key, or the key is wrong, revoked or expired. |
FORBIDDEN | 403 | The key lacks the endpoint's scope. |
NOT_FOUND | 404 | No such resource in this workspace. |
VALIDATION | 422 | The body or query is invalid (or isn't JSON). See fieldErrors. |
CONFLICT | 409 | An Idempotency-Key was reused for a different request, or is still in flight. |
RATE_LIMITED | 429 | Too many requests for this key. Wait Retry-After seconds. |
INSUFFICIENT_CREDITS | 402 | Not enough credits for the work. Top up or wait for the next grant. |
ENTITLEMENT_REQUIRED | 403 | The plan doesn't include this (the API, a premium model, 1080p…). |
PAYMENT_FAILED | 402 | A payment the request depends on didn't go through. |
CONTENT_BLOCKED | 422 | The prompt or media broke the content policy. |
PROVIDER_ERROR | 502 | The AI provider failed. Retrying usually works. |
PROVIDER_UNAVAILABLE | 503 | The AI provider is down for now. Retry with backoff. |
NOT_CONFIGURED | 503 | That part of AutoReel isn't set up on this deployment. |
INTERNAL | 500 | Our fault. Retry; if it keeps happening, send us the request id. |
POST /v1/generations and POST /v1/reels spend credits, so retry them safely: send a unique Idempotency-Key (up to 255 characters, a UUID is ideal). Repeating the same request with the same key within 24 hours replays the first response, marked Idempotent-Replayed: true, instead of doing the work twice. The same key with a different body gets 409 CONFLICT. Failed requests free their key, so you can retry them with it.
curl -X POST https://autoreel.app/api/v1/reels \
-H "Authorization: Bearer $AUTOREEL_API_KEY" \
-H "Idempotency-Key: 4f1d2c3b-5a6e-4f70-8b9c-0d1e2f3a4b5c" \
-H "Content-Type: application/json" \
-d @reel.jsonEvery response carries an x-request-id header. Log it with failures and include it when you contact support: it points us straight at the request.
Lists come as { "data": [...] }. GET /v1/reels is paged, newest first: pass limit (up to 30) and, for the next page, the nextCursor of the previous one as cursor. A null nextCursor means you have everything.
let cursor = null;
do {
const url = new URL("https://autoreel.app/api/v1/reels");
url.searchParams.set("limit", "30");
if (cursor) url.searchParams.set("cursor", cursor);
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}` },
});
const page = await response.json();
for (const reel of page.data) console.log(reel.id, reel.status);
cursor = page.nextCursor;
} while (cursor);/v1/modelsModels the workspace can run, with their parameter schemas. available is false for models above the plan's tier.
- Scope
models:read- Success
- 200 OK
Parameters
| Parameter | Type | Description |
|---|---|---|
categoryquery · optional | string | Filter by category, e.g. text_to_video.at most 40 characters |
Response 200
dataarray of ModelrequiredShow 10 fields
idstringrequiredUse as
modelwhen creating a generation.namestringrequireddescriptionstring | nullrequiredcategorystringrequiredoutputKindstringrequiredOne of
videoimageaudiotextjsontierstringrequiredOne of
standardproavailablebooleanrequiredFalse when the workspace's plan doesn't include the model's tier.
expectedDurationSecondsintegerrequiredinputSchemaobjectrequiredJSON Schema of
params.defaultsobjectrequired
Examples
curl https://autoreel.app/api/v1/models \
-H "Authorization: Bearer $AUTOREEL_API_KEY"const response = await fetch("https://autoreel.app/api/v1/models", {
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/models/{id}One model by its id (slug).
- Scope
models:read- Success
- 200 OK
Parameters
| Parameter | Type | Description |
|---|---|---|
idpath · required | string | Model id (slug). |
Response 200
idstringrequiredUse as
modelwhen creating a generation.namestringrequireddescriptionstring | nullrequiredcategorystringrequiredoutputKindstringrequiredOne of
videoimageaudiotextjsontierstringrequiredOne of
standardproavailablebooleanrequiredFalse when the workspace's plan doesn't include the model's tier.
expectedDurationSecondsintegerrequiredinputSchemaobjectrequiredJSON Schema of
params.defaultsobjectrequired
Examples
curl https://autoreel.app/api/v1/models/google%2Fveo3.1%2Ftext-to-video \
-H "Authorization: Bearer $AUTOREEL_API_KEY"const response = await fetch("https://autoreel.app/api/v1/models/google%2Fveo3.1%2Ftext-to-video", {
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/generationsRuns one model. The credits it costs are reserved now and charged when it finishes (returned if it fails). Poll the generation or subscribe to generation.completed.
- Scope
generations:write- Success
- 202 Accepted
- Retries
- Safe with
Idempotency-Key
Parameters
| Parameter | Type | Description |
|---|---|---|
Idempotency-Keyheader · optional | string | Repeat a request safely: the same key replays the first response for 24 hours.at most 255 characters |
Request body
modelstringrequiredModel id (its slug), from
GET /v1/models.1–200 characters
paramsobjectrequiredThe model's parameters (its
inputSchema). Files areasset:<file id>fromPOST /v1/files.
Response 202
idstring · uuidrequiredstatusstringrequiredOne of
queuedsubmittingprocessingcompletedfailedcanceledtimed_outprogressintegerrequired0–100
modelstring | nullrequiredestimatedCreditsintegerrequiredcreditsChargedintegerrequirederrorobject | nullrequiredShow 2 fields
codestringrequiredmessagestringrequired
outputsarray of FileOutputrequiredShow 8 fields
fileIdstring · uuidrequiredkindstringrequiredOne of
videoimageaudiomimeTypestringrequiredurlstring | nullrequiredDownload link, valid for an hour.
widthinteger | nullrequiredheightinteger | nullrequireddurationMsinteger | nullrequiredsizeBytesinteger | nullrequired
createdAtstring · date-timerequiredcompletedAtstring · date-time | nullrequired
Examples
curl -X POST https://autoreel.app/api/v1/generations \
-H "Authorization: Bearer $AUTOREEL_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"model": "google/veo3.1/text-to-video",
"params": {
"prompt": "A barista pours latte art in slow motion, warm morning light",
"duration": 5
}
}'const response = await fetch("https://autoreel.app/api/v1/generations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
"model": "google/veo3.1/text-to-video",
"params": {
"prompt": "A barista pours latte art in slow motion, warm morning light",
"duration": 5
}
}),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/generations/{id}Status, progress, cost and output files (download links last an hour).
- Scope
generations:read- Success
- 200 OK
Parameters
| Parameter | Type | Description |
|---|---|---|
idpath · required | string | Generation id. |
Response 200
idstring · uuidrequiredstatusstringrequiredOne of
queuedsubmittingprocessingcompletedfailedcanceledtimed_outprogressintegerrequired0–100
modelstring | nullrequiredestimatedCreditsintegerrequiredcreditsChargedintegerrequirederrorobject | nullrequiredShow 2 fields
codestringrequiredmessagestringrequired
outputsarray of FileOutputrequiredShow 8 fields
fileIdstring · uuidrequiredkindstringrequiredOne of
videoimageaudiomimeTypestringrequiredurlstring | nullrequiredDownload link, valid for an hour.
widthinteger | nullrequiredheightinteger | nullrequireddurationMsinteger | nullrequiredsizeBytesinteger | nullrequired
createdAtstring · date-timerequiredcompletedAtstring · date-time | nullrequired
Examples
curl https://autoreel.app/api/v1/generations/6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90 \
-H "Authorization: Bearer $AUTOREEL_API_KEY"const response = await fetch("https://autoreel.app/api/v1/generations/6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90", {
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/reelsNewest first, 30 per page.
- Scope
reels:read- Success
- 200 OK
Parameters
| Parameter | Type | Description |
|---|---|---|
statusquery · optional | string | anybuildingreadyrenderedfailed |
limitquery · optional | integer | 1–30 |
cursorquery · optional | string | nextCursor from the previous page.at most 200 characters |
Response 200
dataarray of ReelrequiredShow 10 fields
idstring · uuidrequiredtitlestringrequiredtypestringrequiredstatusstringrequiredOne of
draftgeneratingreadyrenderingrenderedfailedarchivedaspectRatiostringrequireddurationMsintegerrequiredbuildobject | nullrequiredShow 4 fields
generationIdstring · uuidrequiredstatusstringrequiredprogressnumberrequirederrorstring | nullrequired
latestExportobject | nullrequiredShow 5 fields
idstring · uuidrequiredstatusstringrequiredOne of
queuedrenderingcompletedfailedcanceledresolutionstringrequiredurlstring | nullrequiredMP4 download link, valid for an hour.
posterUrlstring | nullrequired
editorUrlstringrequiredWhere a person can edit the reel in AutoReel.
createdAtstring · date-timerequired
nextCursorstring | nullrequired
Examples
curl https://autoreel.app/api/v1/reels \
-H "Authorization: Bearer $AUTOREEL_API_KEY"const response = await fetch("https://autoreel.app/api/v1/reels", {
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/reelsBuilds a UGC, slideshow or hook + demo reel from the same settings as the studio. Its steps run as generations and spend credits. When status is ready, export it.
- Scope
reels:write- Success
- 202 Accepted
- Retries
- Safe with
Idempotency-Key
Parameters
| Parameter | Type | Description |
|---|---|---|
Idempotency-Keyheader · optional | string | Repeat a request safely: the same key replays the first response for 24 hours.at most 255 characters |
Request body
buildone of 3 shapesrequiredWhat to build: the same settings as the studio's UGC, slideshow or hook + demo pages.
Show 3 shapes
Exactly one of:
type: "ugc"titlestringrequired1–200 characters
aspectRatiostringrequiredOne of
9:161:14:516:9musicobject | nullrequiredShow 2 fields
assetIdstring · uuidrequiredvolumenumberrequired0–1
brandKitIdstring · uuid | nulloptionaltype"ugc"requiredcreatorAssetIdstring · uuidrequiredscriptstringrequired10–3000 characters
voiceobjectrequiredShow 2 fields
modelSlugstringrequired1–200 characters
paramsobjectrequired
avatarobjectrequiredShow 2 fields
modelSlugstringrequired1–200 characters
paramsobjectrequired
productobject | nullrequiredShow 2 fields
assetIdstring · uuidrequiredtextstringrequired1–120 characters
captionsobjectrequiredShow 3 fields
enabledbooleanrequiredstyleIdstringrequiredOne of
bold-popkaraokeboxedminimalheadlinepositionstringrequiredOne of
topmiddlebottom
type: "slideshow"titlestringrequired1–200 characters
aspectRatiostringrequiredOne of
9:161:14:516:9musicobject | nullrequiredShow 2 fields
assetIdstring · uuidrequiredvolumenumberrequired0–1
brandKitIdstring · uuid | nulloptionaltype"slideshow"requiredhookstringrequired1–200 characters
slidesarray of objectrequired1–12 items
Show 2 fields
headingstringrequired1–160 characters
bodystringrequiredat most 400 characters
imagesarray of string · uuid | nullrequired1–13 items
imageModelobject | nullrequiredShow 2 fields
modelSlugstringrequired1–200 characters
paramsobjectrequired
imageStylestringrequiredat most 400 characters
textStylestringrequiredOne of
titlesubtitlelabeloutlinehighlightslideDurationMsintegerrequired1500–8000
transitionstringrequiredOne of
nonefadeslidewipe
type: "hook_demo"titlestringrequired1–200 characters
aspectRatiostringrequiredOne of
9:161:14:516:9musicobject | nullrequiredShow 2 fields
assetIdstring · uuidrequiredvolumenumberrequired0–1
brandKitIdstring · uuid | nulloptionaltype"hook_demo"requiredhookobjectrequiredShow 5 fields
textstringrequired1–200 characters
videoPromptstringrequired3–2000 characters
videoobjectrequiredShow 2 fields
modelSlugstringrequired1–200 characters
paramsobjectrequired
imageAssetIdstring · uuid | nullrequiredvoiceobject | nullrequiredShow 2 fields
modelSlugstringrequired1–200 characters
paramsobjectrequired
demoobjectrequiredShow 3 fields
assetIdstring · uuidrequiredtrimStartMsintegerrequiredat least 0
durationMsinteger | nullrequired1000–60000
textStylestringrequiredOne of
titlesubtitlelabeloutlinehighlight
templateIdstring · uuidoptionalThe template the reel follows (optional).
Response 202
idstring · uuidrequiredtitlestringrequiredtypestringrequiredstatusstringrequiredOne of
draftgeneratingreadyrenderingrenderedfailedarchivedaspectRatiostringrequireddurationMsintegerrequiredbuildobject | nullrequiredShow 4 fields
generationIdstring · uuidrequiredstatusstringrequiredprogressnumberrequirederrorstring | nullrequired
latestExportobject | nullrequiredShow 5 fields
idstring · uuidrequiredstatusstringrequiredOne of
queuedrenderingcompletedfailedcanceledresolutionstringrequiredurlstring | nullrequiredMP4 download link, valid for an hour.
posterUrlstring | nullrequired
editorUrlstringrequiredWhere a person can edit the reel in AutoReel.
createdAtstring · date-timerequired
Examples
curl -X POST https://autoreel.app/api/v1/reels \
-H "Authorization: Bearer $AUTOREEL_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"build": {
"title": "3 morning habits that changed my skin",
"aspectRatio": "9:16",
"music": {
"assetId": "6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90",
"volume": 0.5
},
"brandKitId": "6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90",
"type": "ugc",
"creatorAssetId": "6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90",
"script": "Stop scrolling. These three habits fixed my skin in two weeks, and the second one is free.",
"voice": {
"modelSlug": "minimax/speech-2.8-hd",
"params": {}
},
"avatar": {
"modelSlug": "wavespeed-ai/infinitetalk",
"params": {}
},
"product": {
"assetId": "6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90",
"text": "Glow serum"
},
"captions": {
"enabled": true,
"styleId": "bold-pop",
"position": "top"
}
},
"templateId": "6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90"
}'const response = await fetch("https://autoreel.app/api/v1/reels", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
"build": {
"title": "3 morning habits that changed my skin",
"aspectRatio": "9:16",
"music": {
"assetId": "6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90",
"volume": 0.5
},
"brandKitId": "6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90",
"type": "ugc",
"creatorAssetId": "6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90",
"script": "Stop scrolling. These three habits fixed my skin in two weeks, and the second one is free.",
"voice": {
"modelSlug": "minimax/speech-2.8-hd",
"params": {}
},
"avatar": {
"modelSlug": "wavespeed-ai/infinitetalk",
"params": {}
},
"product": {
"assetId": "6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90",
"text": "Glow serum"
},
"captions": {
"enabled": true,
"styleId": "bold-pop",
"position": "top"
}
},
"templateId": "6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90"
}),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/reels/{id}Build progress and the newest export (its MP4 link lasts an hour).
- Scope
reels:read- Success
- 200 OK
Parameters
| Parameter | Type | Description |
|---|---|---|
idpath · required | string | Reel id. |
Response 200
idstring · uuidrequiredtitlestringrequiredtypestringrequiredstatusstringrequiredOne of
draftgeneratingreadyrenderingrenderedfailedarchivedaspectRatiostringrequireddurationMsintegerrequiredbuildobject | nullrequiredShow 4 fields
generationIdstring · uuidrequiredstatusstringrequiredprogressnumberrequirederrorstring | nullrequired
latestExportobject | nullrequiredShow 5 fields
idstring · uuidrequiredstatusstringrequiredOne of
queuedrenderingcompletedfailedcanceledresolutionstringrequiredurlstring | nullrequiredMP4 download link, valid for an hour.
posterUrlstring | nullrequired
editorUrlstringrequiredWhere a person can edit the reel in AutoReel.
createdAtstring · date-timerequired
Examples
curl https://autoreel.app/api/v1/reels/6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90 \
-H "Authorization: Bearer $AUTOREEL_API_KEY"const response = await fetch("https://autoreel.app/api/v1/reels/6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90", {
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/reels/{id}/exportRenders the reel's current version to MP4. Exports are free; Free plans export 720p with a watermark. Subscribe to reel.exported or poll the reel.
- Scope
reels:write- Success
- 202 Accepted
Parameters
| Parameter | Type | Description |
|---|---|---|
idpath · required | string | Reel id. |
Request body
resolutionstringrequired1080p needs a paid plan.
One of
720p1080p
Response 202
idstring · uuidrequiredtitlestringrequiredtypestringrequiredstatusstringrequiredOne of
draftgeneratingreadyrenderingrenderedfailedarchivedaspectRatiostringrequireddurationMsintegerrequiredbuildobject | nullrequiredShow 4 fields
generationIdstring · uuidrequiredstatusstringrequiredprogressnumberrequirederrorstring | nullrequired
latestExportobject | nullrequiredShow 5 fields
idstring · uuidrequiredstatusstringrequiredOne of
queuedrenderingcompletedfailedcanceledresolutionstringrequiredurlstring | nullrequiredMP4 download link, valid for an hour.
posterUrlstring | nullrequired
editorUrlstringrequiredWhere a person can edit the reel in AutoReel.
createdAtstring · date-timerequired
Examples
curl -X POST https://autoreel.app/api/v1/reels/6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90/export \
-H "Authorization: Bearer $AUTOREEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"resolution": "720p"
}'const response = await fetch("https://autoreel.app/api/v1/reels/6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90/export", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"resolution": "720p"
}),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/creditsSpendable credits (subscription first, then purchased).
- Scope
credits:read- Success
- 200 OK
Response 200
balanceintegerrequiredsubscriptionCreditsintegerrequiredpurchasedCreditsintegerrequiredplanstringrequired
Examples
curl https://autoreel.app/api/v1/credits \
-H "Authorization: Bearer $AUTOREEL_API_KEY"const response = await fetch("https://autoreel.app/api/v1/credits", {
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/filesReturns a signed upload URL. PUT the bytes to it with uploadHeaders, then complete the file. Use ref as a media parameter.
- Scope
files:write- Success
- 201 Created
Request body
filenamestringrequired1–255 characters
contentTypestringrequirede.g.
image/png,video/mp4,audio/mpeg.3–100 characters
sizeBytesintegerrequiredmore than 0
Response 201
idstring · uuidrequiredstatusstringrequiredOne of
pendingreadyfailedkindstringrequiredOne of
videoimageaudiorefstringrequiredUse as a media parameter value:
asset:<id>.uploadUrlstring | nullrequiredPUT the bytes here, then complete the file.
uploadHeadersmap of string | nullrequiredHeaders to send with the PUT, exactly as given.
Examples
curl -X POST https://autoreel.app/api/v1/files \
-H "Authorization: Bearer $AUTOREEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filename": "product-shot.png",
"contentType": "image/png",
"sizeBytes": 1
}'const response = await fetch("https://autoreel.app/api/v1/files", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"filename": "product-shot.png",
"contentType": "image/png",
"sizeBytes": 1
}),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/files/{id}/completeChecks and measures the uploaded file.
- Scope
files:write- Success
- 200 OK
Parameters
| Parameter | Type | Description |
|---|---|---|
idpath · required | string | File id. |
Response 200
idstring · uuidrequiredstatusstringrequiredOne of
pendingreadyfailedkindstringrequiredOne of
videoimageaudiorefstringrequiredUse as a media parameter value:
asset:<id>.uploadUrlstring | nullrequiredPUT the bytes here, then complete the file.
uploadHeadersmap of string | nullrequiredHeaders to send with the PUT, exactly as given.
Examples
curl -X POST https://autoreel.app/api/v1/files/6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90/complete \
-H "Authorization: Bearer $AUTOREEL_API_KEY"const response = await fetch("https://autoreel.app/api/v1/files/6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90/complete", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/webhooksEndpoints and the events they receive.
- Scope
webhooks:write- Success
- 200 OK
Response 200
dataarray of WebhookrequiredShow 7 fields
idstring · uuidrequiredurlstringrequireddescriptionstring | nullrequiredeventsarray of stringrequiredenabledbooleanrequiredsecretstringoptionalSigning secret, returned once when the endpoint is created.
createdAtstring · date-timerequired
Examples
curl https://autoreel.app/api/v1/webhooks \
-H "Authorization: Bearer $AUTOREEL_API_KEY"const response = await fetch("https://autoreel.app/api/v1/webhooks", {
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/webhooksThe signing secret is returned once.
- Scope
webhooks:write- Success
- 201 Created
Request body
urlstring · urirequiredHTTPS endpoint that receives the events.
at most 2000 characters
eventsarray of stringrequired1–9 items
descriptionstringoptionalat most 200 characters
Response 201
idstring · uuidrequiredurlstringrequireddescriptionstring | nullrequiredeventsarray of stringrequiredenabledbooleanrequiredsecretstringoptionalSigning secret, returned once when the endpoint is created.
createdAtstring · date-timerequired
Examples
curl -X POST https://autoreel.app/api/v1/webhooks \
-H "Authorization: Bearer $AUTOREEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/autoreel",
"events": [
"generation.completed"
],
"description": "Production receiver"
}'const response = await fetch("https://autoreel.app/api/v1/webhooks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"url": "https://example.com/webhooks/autoreel",
"events": [
"generation.completed"
],
"description": "Production receiver"
}),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();/v1/webhooks/{id}Stops deliveries to it.
- Scope
webhooks:write- Success
- 200 OK
Parameters
| Parameter | Type | Description |
|---|---|---|
idpath · required | string | Endpoint id. |
Response 200
deletedtruerequired
Examples
curl -X DELETE https://autoreel.app/api/v1/webhooks/6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90 \
-H "Authorization: Bearer $AUTOREEL_API_KEY"const response = await fetch("https://autoreel.app/api/v1/webhooks/6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.AUTOREEL_API_KEY}`,
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const data = await response.json();Instead of polling, let AutoReel tell you when work finishes. Add an HTTPS endpoint in your workspace's Settings → Webhooks (or with POST /v1/webhooks), pick its events, and we POST a signed JSON event to it each time one happens.
Events
| Event | Sent when |
|---|---|
generation.completed | A generation finished |
generation.failed | A generation failed |
reel.ready | A reel finished building and is ready to edit |
reel.failed | A reel build failed |
reel.exported | A reel export (MP4) is ready |
post.published | A scheduled post went live |
post.failed | A scheduled post failed |
automation.run.completed | An automation run finished |
automation.run.failed | An automation run failed |
Payload
Every event has the same envelope. data holds the ids and status of what changed; fetch the resource for the rest.
idstring · uuidrequiredEvent id (use it to ignore repeats).
typestringrequiredcreatedAtstring · date-timerequireddataobjectrequiredIds and status of what changed; fetch the resource for details.
{
"id": "0b9e4c1a-7d2f-4e8b-9a6c-3f1e2d4c5b6a",
"type": "reel.exported",
"createdAt": "2026-01-15T09:30:00.000Z",
"data": {
"reelId": "6f1c2e8a-4b7d-4c1e-9a3f-2d5b8e7c1a90",
"exportId": "9d8c7b6a-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
"resolution": "1080p"
}
}Headers
AutoReel-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256>: verify it before trusting the body.AutoReel-EventThe event type, e.g.reel.exported.AutoReel-DeliveryThe delivery id (shown in the delivery log).User-AgentAutoReel-Webhooks/1.0.
Verifying signatures
Each endpoint has its own signing secret (whsec_…), shown once when you add the endpoint or rotate its secret. Compute the HMAC-SHA256 of ${t}.${rawBody} with it, compare the hex digest to v1 in constant time, and reject signatures more than 5 minutes old so captured requests can't be replayed. Use the raw body exactly as received: re-serialized JSON won't match.
import crypto from "node:crypto";
import express from "express";
const secret = process.env.AUTOREEL_WEBHOOK_SECRET; // whsec_…
/**
* True when the request was signed by AutoReel with your endpoint's secret
* and is less than five minutes old.
*/
function verifyAutoReelSignature(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(
String(header ?? "")
.split(",")
.map((part) => part.trim().split("=")),
);
const timestamp = Number(parts.t);
if (!Number.isInteger(timestamp)) return false;
// Reject old signatures so a captured request can't be replayed later.
if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest();
const received = Buffer.from(String(parts.v1 ?? ""), "hex");
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}
const app = express();
// Verify the raw body exactly as it was sent: parse the JSON only afterwards.
app.post("/webhooks/autoreel", express.raw({ type: "application/json" }), (req, res) => {
const rawBody = req.body.toString("utf8");
if (!verifyAutoReelSignature(rawBody, req.get("AutoReel-Signature"), secret)) {
return res.status(400).send("Invalid signature");
}
const event = JSON.parse(rawBody);
// event.type is e.g. "reel.exported"; skip event.id values you've already handled.
res.sendStatus(200); // Answer within 10 seconds; do slow work afterwards.
});
app.listen(3000);Retries and switching off
- Answer with any 2xx within 10 seconds. Do slow work after answering. Redirects aren't followed, so use the final URL.
- Anything else is retried up to 6 attempts in all, after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours (about 8½ hours).
- Events can arrive more than once or out of order: skip
ids you've handled and read the resource for its current state. - After 20 deliveries in a row fail for good, the endpoint is switched off and workspace admins are told. Fix it, then switch it back on in settings.
- Send test event in settings sends a
webhook.testevent once, without retries. Each delivery and its response is logged for 30 days.