Studio
Make and edit websites, documents, apps, images and video by talking to the same Studio director the product uses. Each message is a turn.
Talk to the Website Director — build or change a website#
/v1/studio/website/turnsstudio_website_turnOne message to Website Studio's director, the same turn the app runs. Without artifact_id it starts a new website: when the request is clear (or auto: true) the director writes the creation brief, the site is created, and its build is queued — the answer comes back in a few seconds with status: "building", the new artifact.id and a job. The site itself builds in the background in 2–5 minutes; poll GET /v1/websites/{id} (or GET /v1/turns/{id}) until status is completed, then public_url is where it lives once published.
If the director needs something first (what you sell, who it's for) it answers needs_input with questions; reply with another turn carrying the same conversation_id, or send auto: true to let it decide.
With artifact_id (a website's id) it changes that site: the Page Editor makes the edit (20–90 s) and a rebuild is queued (status: "building", 1–3 min). A question about the site is simply answered (completed).
Example: { "message": "Build a landing page for Harbor Dental, a family dentist in Portland. Goal: book a first cleaning.", "auto": true }
The answer (also what GET /v1/turns/{id} returns):
| field | |
|---|---|
id | The turn. Poll it with GET /v1/turns/{id}. |
status | completed · needs_input (answer questions/outline with another turn, same conversation_id or artifact_id) · building (still working — poll) · failed (see error). |
artifact | { id, type, name, url, public_url } — url opens it in Attria; public_url is set once it is public. |
conversation_id | The director conversation this turn belongs to. |
message | The director's own words. |
questions / outline | What it needs from you when status is needs_input. |
Send an Idempotency-Key header: a retry with the same key replays the turn instead of running (and charging) it twice. Once a turn has been claimed, a failure is answered as the turn with status: "failed" and an error; refusals before that (not found, no credits) are ordinary HTTP errors.
Slow: can take several minutes. Agents should send background: true and poll GET /v1/turns/{id} (tool studio_turn_get).
auto: true lets Attria decide instead of answering with status: "needs_input" (questions or an outline to confirm).
stream: true answers with Server-Sent Events; the last event is result (the same body a waiting call returns) or error.
background: true answers 202 Accepted at once with an id; poll GET /v1/turns/{id} (tool studio_turn_get) every 5–10 s until it finishes.
Send an Idempotency-Key header: a retry with the same key replays the first result instead of doing the work twice.
Headers
Idempotency-KeystringoptionalAny unique string (a UUID) per logical write. Retrying with the same key replays the original result.
Bodyapplication/json
messagestringrequiredWhat you would type to this Studio's director, in plain words. Answers to its questions go here too.
max length 3800artifact_idstring · uuidoptionalWork on an existing artifact of this Studio (from a previous answer's
artifact.idor a list endpoint). Omit to start a new one.conversation_idstring · uuidoptionalContinue a conversation: the
conversation_idof a previous answer. Needed to answer a website director's questions before the site exists; otherwiseartifact_idalready implies it.autobooleanoptional"Decide for me": the director settles anything unclear itself and makes the artifact now instead of asking. Default false — the director may answer
needs_inputwith questions or an outline, exactly as in the app.DefaultfalsestreambooleanoptionalAnswer as server-sent events:
turn,plan,status, thenresult(the same body a waited-for call returns).DefaultfalsebackgroundbooleanoptionalAnswer 202 as soon as the turn is claimed and keep working; poll
GET /v1/turns/{id}.Defaultfalse
Can fail with 400 · 401 · 402 · 404 · 409 · 429 · 500 — the shape is in Errors & limits.
curl -X POST "https://app.attria.io/api/v1/studio/website/turns" \
-H "Authorization: Bearer $ATTRIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"message": "Build a landing page for Harbor Dental, a family dentist in Portland. Goal: book a first cleaning.",
"auto": true
}'const res = await fetch("https://app.attria.io/api/v1/studio/website/turns", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
message: "Build a landing page for Harbor Dental, a family dentist in Portland. Goal: book a first cleaning.",
auto: true,
}),
});
const turn = await res.json();import os, uuid, requests
res = requests.post(
"https://app.attria.io/api/v1/studio/website/turns",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"message": "Build a landing page for Harbor Dental, a family dentist in Portland. Goal: book a first cleaning.",
"auto": True,
},
timeout=900, # a turn that waits can take minutes
)
turn = res.json(){
"id": "5b7e…",
"object": "studio.turn",
"status": "building",
"studio": "website",
"artifact": {
"id": "0c1d…",
"type": "website",
"name": "Harbor Dental",
"url": "https://app.attria.io/studio/websites/0c1d…",
"public_url": null
},
"conversation_id": "9f2a…",
"message": "The website is building. It usually takes 2–5 minutes.",
"job": {
"id": "77aa…",
"status": "queued",
"stage": "generate",
"error": null
},
"created_at": "2026-09-27T18:02:11Z",
"completed_at": "2026-09-27T18:02:14Z"
}Talk to the Documents director — write or change a deck, proposal or document#
/v1/studio/document/turnsstudio_document_turnOne message to Documents Studio's director — decks, proposals, résumés, invoices, one-pagers, reports — the same turn the app runs. Without artifact_id it opens a new document and writes it; with one it revises that document — a section, the look, or the whole thing. The director designs it and may add images. Usually 20–60 s, longer for long decks.
By default the director may first propose an outline or ask questions (status: "needs_input"). Reply "Build it" (or any go-ahead, or auto: true) to approve an outline, or answer the questions in another turn with the same artifact_id. auto: true skips all of that and writes immediately.
Put every fact, figure, name and date the document needs in message — the director uses only what you give it and the workspace's brand. Read the result with GET /v1/documents/{id}.
Example: { "message": "A 6-slide investor update for Q3: revenue $412k (+38% QoQ), 3 enterprise logos signed, runway 22 months, hiring 2 engineers.", "auto": true }
The answer (also what GET /v1/turns/{id} returns):
| field | |
|---|---|
id | The turn. Poll it with GET /v1/turns/{id}. |
status | completed · needs_input (answer questions/outline with another turn, same conversation_id or artifact_id) · building (still working — poll) · failed (see error). |
artifact | { id, type, name, url, public_url } — url opens it in Attria; public_url is set once it is public. |
conversation_id | The director conversation this turn belongs to. |
message | The director's own words. |
questions / outline | What it needs from you when status is needs_input. |
Send an Idempotency-Key header: a retry with the same key replays the turn instead of running (and charging) it twice. Once a turn has been claimed, a failure is answered as the turn with status: "failed" and an error; refusals before that (not found, no credits) are ordinary HTTP errors.
Slow: can take several minutes. Agents should send background: true and poll GET /v1/turns/{id} (tool studio_turn_get).
auto: true lets Attria decide instead of answering with status: "needs_input" (questions or an outline to confirm).
stream: true answers with Server-Sent Events; the last event is result (the same body a waiting call returns) or error.
background: true answers 202 Accepted at once with an id; poll GET /v1/turns/{id} (tool studio_turn_get) every 5–10 s until it finishes.
Send an Idempotency-Key header: a retry with the same key replays the first result instead of doing the work twice.
Headers
Idempotency-KeystringoptionalAny unique string (a UUID) per logical write. Retrying with the same key replays the original result.
Bodyapplication/json
messagestringrequiredWhat you would type to this Studio's director, in plain words. Answers to its questions go here too.
max length 18000artifact_idstring · uuidoptionalWork on an existing artifact of this Studio (from a previous answer's
artifact.idor a list endpoint). Omit to start a new one.conversation_idstring · uuidoptionalContinue a conversation: the
conversation_idof a previous answer. Needed to answer a website director's questions before the site exists; otherwiseartifact_idalready implies it.autobooleanoptional"Decide for me": the director settles anything unclear itself and makes the artifact now instead of asking. Default false — the director may answer
needs_inputwith questions or an outline, exactly as in the app.DefaultfalsestreambooleanoptionalAnswer as server-sent events:
turn,plan,status, thenresult(the same body a waited-for call returns).DefaultfalsebackgroundbooleanoptionalAnswer 202 as soon as the turn is claimed and keep working; poll
GET /v1/turns/{id}.Defaultfalse
Can fail with 400 · 401 · 402 · 404 · 409 · 429 · 500 — the shape is in Errors & limits.
curl -X POST "https://app.attria.io/api/v1/studio/document/turns" \
-H "Authorization: Bearer $ATTRIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"message": "A 6-slide investor update for Q3: revenue $412k (+38% QoQ), 3 enterprise logos signed, runway 22 months, hiring 2 engineers.",
"auto": false
}'const res = await fetch("https://app.attria.io/api/v1/studio/document/turns", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
message: "A 6-slide investor update for Q3: revenue $412k (+38% QoQ), 3 enterprise logos signed, runway 22 months, hiring 2 engineers.",
auto: false,
}),
});
const turn = await res.json();import os, uuid, requests
res = requests.post(
"https://app.attria.io/api/v1/studio/document/turns",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"message": "A 6-slide investor update for Q3: revenue $412k (+38% QoQ), 3 enterprise logos signed, runway 22 months, hiring 2 engineers.",
"auto": False,
},
timeout=900, # a turn that waits can take minutes
)
turn = res.json(){
"id": "1e0f…",
"object": "studio.turn",
"status": "needs_input",
"studio": "document",
"artifact": {
"id": "a3c2…",
"type": "document",
"name": "Untitled",
"url": "https://app.attria.io/studio/documents/a3c2…",
"public_url": null
},
"conversation_id": "c81b…",
"message": "One quick question before I write this.",
"questions": [
{
"text": "Who is this deck for?",
"options": [
"Investors",
"Customers",
"My team",
"Custom"
]
}
],
"created_at": "2026-09-27T18:02:11Z",
"completed_at": "2026-09-27T18:02:19Z"
}Talk to the App Builder — build or change a workspace app#
/v1/studio/app/turnsstudio_app_turnOne message to Apps Studio's builder, the same turn the app runs. Without artifact_id it builds a new app on the workspace's own records (Objects) — screens, the data they read and write, and any Actions — compiles it and commits it; an app that has never been live is published so it appears in the workspace's Apps. With artifact_id it changes that app; a change to a live app stays a draft until someone publishes it in Studio. Usually 45–120 s.
Say what the app is for, who uses it, what it shows and what people do in it. The builder may ask questions (needs_input); auto: true makes it decide and build.
Example: { "message": "A job tracker for our crew: list open jobs by date, mark a job done, and add notes and photos to a job.", "auto": true }
The answer (also what GET /v1/turns/{id} returns):
| field | |
|---|---|
id | The turn. Poll it with GET /v1/turns/{id}. |
status | completed · needs_input (answer questions/outline with another turn, same conversation_id or artifact_id) · building (still working — poll) · failed (see error). |
artifact | { id, type, name, url, public_url } — url opens it in Attria; public_url is set once it is public. |
conversation_id | The director conversation this turn belongs to. |
message | The director's own words. |
questions / outline | What it needs from you when status is needs_input. |
Send an Idempotency-Key header: a retry with the same key replays the turn instead of running (and charging) it twice. Once a turn has been claimed, a failure is answered as the turn with status: "failed" and an error; refusals before that (not found, no credits) are ordinary HTTP errors.
Slow: can take several minutes. Agents should send background: true and poll GET /v1/turns/{id} (tool studio_turn_get).
auto: true lets Attria decide instead of answering with status: "needs_input" (questions or an outline to confirm).
stream: true answers with Server-Sent Events; the last event is result (the same body a waiting call returns) or error.
background: true answers 202 Accepted at once with an id; poll GET /v1/turns/{id} (tool studio_turn_get) every 5–10 s until it finishes.
Send an Idempotency-Key header: a retry with the same key replays the first result instead of doing the work twice.
Headers
Idempotency-KeystringoptionalAny unique string (a UUID) per logical write. Retrying with the same key replays the original result.
Bodyapplication/json
messagestringrequiredWhat you would type to this Studio's director, in plain words. Answers to its questions go here too.
max length 18000artifact_idstring · uuidoptionalWork on an existing artifact of this Studio (from a previous answer's
artifact.idor a list endpoint). Omit to start a new one.conversation_idstring · uuidoptionalContinue a conversation: the
conversation_idof a previous answer. Needed to answer a website director's questions before the site exists; otherwiseartifact_idalready implies it.autobooleanoptional"Decide for me": the director settles anything unclear itself and makes the artifact now instead of asking. Default false — the director may answer
needs_inputwith questions or an outline, exactly as in the app.DefaultfalsestreambooleanoptionalAnswer as server-sent events:
turn,plan,status, thenresult(the same body a waited-for call returns).DefaultfalsebackgroundbooleanoptionalAnswer 202 as soon as the turn is claimed and keep working; poll
GET /v1/turns/{id}.Defaultfalse
Can fail with 400 · 401 · 402 · 404 · 409 · 429 · 500 — the shape is in Errors & limits.
curl -X POST "https://app.attria.io/api/v1/studio/app/turns" \
-H "Authorization: Bearer $ATTRIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"message": "A job tracker for our crew: list open jobs by date, mark a job done, and add notes to a job.",
"auto": true
}'const res = await fetch("https://app.attria.io/api/v1/studio/app/turns", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
message: "A job tracker for our crew: list open jobs by date, mark a job done, and add notes to a job.",
auto: true,
}),
});
const turn = await res.json();import os, uuid, requests
res = requests.post(
"https://app.attria.io/api/v1/studio/app/turns",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"message": "A job tracker for our crew: list open jobs by date, mark a job done, and add notes to a job.",
"auto": True,
},
timeout=900, # a turn that waits can take minutes
)
turn = res.json(){
"id": "d4b1…",
"object": "studio.turn",
"status": "completed",
"studio": "app",
"artifact": {
"id": "6e2f…",
"type": "app",
"name": "Job Tracker",
"url": "https://app.attria.io/studio/apps/6e2f…",
"public_url": "https://app.attria.io/apps/6e2f…"
},
"conversation_id": "0a9d…",
"message": "Built a job tracker with an open-jobs list, a done toggle and notes.",
"created_at": "2026-09-27T18:02:11Z",
"completed_at": "2026-09-27T18:03:20Z"
}Talk to the Images director — make images on a board#
/v1/studio/image/turnsstudio_image_turnOne message to Images Studio's director, the same turn the app runs. Without artifact_id it opens a new board; with one (a board id from GET /v1/images/boards) it works on that board. When the director decides to make something it renders with the Studio's own pipeline (brand-aware planning, model routing, storage, metering) and the answer carries the finished images with their URLs. Usually 20–60 s. A question or chat is simply answered.
The director may ask questions (needs_input); auto: true makes it decide. Images are made at 4:5 by default — say another size or how many (1–4) in the message. For a precise operation (edit, remove background, reframe, upscale, product photo…) use POST /v1/studio/image/apps/{app} instead.
Example: { "message": "Two scroll-stopping images for a summer iced-latte promo, bright and minimal", "auto": true }
The answer (also what GET /v1/turns/{id} returns):
| field | |
|---|---|
id | The turn. Poll it with GET /v1/turns/{id}. |
status | completed · needs_input (answer questions/outline with another turn, same conversation_id or artifact_id) · building (still working — poll) · failed (see error). |
artifact | { id, type, name, url, public_url } — url opens it in Attria; public_url is set once it is public. |
conversation_id | The director conversation this turn belongs to. |
message | The director's own words. |
questions / outline | What it needs from you when status is needs_input. |
Send an Idempotency-Key header: a retry with the same key replays the turn instead of running (and charging) it twice. Once a turn has been claimed, a failure is answered as the turn with status: "failed" and an error; refusals before that (not found, no credits) are ordinary HTTP errors.
Slow: can take several minutes. Agents should send background: true and poll GET /v1/turns/{id} (tool studio_turn_get).
auto: true lets Attria decide instead of answering with status: "needs_input" (questions or an outline to confirm).
stream: true answers with Server-Sent Events; the last event is result (the same body a waiting call returns) or error.
background: true answers 202 Accepted at once with an id; poll GET /v1/turns/{id} (tool studio_turn_get) every 5–10 s until it finishes.
Send an Idempotency-Key header: a retry with the same key replays the first result instead of doing the work twice.
Headers
Idempotency-KeystringoptionalAny unique string (a UUID) per logical write. Retrying with the same key replays the original result.
Bodyapplication/json
messagestringrequiredWhat you would type to this Studio's director, in plain words. Answers to its questions go here too.
max length 3800artifact_idstring · uuidoptionalWork on an existing artifact of this Studio (from a previous answer's
artifact.idor a list endpoint). Omit to start a new one.conversation_idstring · uuidoptionalContinue a conversation: the
conversation_idof a previous answer. Needed to answer a website director's questions before the site exists; otherwiseartifact_idalready implies it.autobooleanoptional"Decide for me": the director settles anything unclear itself and makes the artifact now instead of asking. Default false — the director may answer
needs_inputwith questions or an outline, exactly as in the app.DefaultfalsestreambooleanoptionalAnswer as server-sent events:
turn,plan,status, thenresult(the same body a waited-for call returns).DefaultfalsebackgroundbooleanoptionalAnswer 202 as soon as the turn is claimed and keep working; poll
GET /v1/turns/{id}.Defaultfalse
Can fail with 400 · 401 · 402 · 404 · 409 · 429 · 500 — the shape is in Errors & limits.
curl -X POST "https://app.attria.io/api/v1/studio/image/turns" \
-H "Authorization: Bearer $ATTRIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"message": "Two scroll-stopping images for a summer iced-latte promo, bright and minimal",
"auto": true
}'const res = await fetch("https://app.attria.io/api/v1/studio/image/turns", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
message: "Two scroll-stopping images for a summer iced-latte promo, bright and minimal",
auto: true,
}),
});
const turn = await res.json();import os, uuid, requests
res = requests.post(
"https://app.attria.io/api/v1/studio/image/turns",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"message": "Two scroll-stopping images for a summer iced-latte promo, bright and minimal",
"auto": True,
},
timeout=900, # a turn that waits can take minutes
)
turn = res.json(){
"id": "8c3e…",
"object": "studio.turn",
"status": "completed",
"studio": "image",
"artifact": {
"id": "b1f0…",
"type": "image",
"name": "Untitled board",
"url": "https://app.attria.io/studio/images/b1f0…",
"public_url": null
},
"conversation_id": "57de…",
"message": "Making two bright, minimal iced-latte promos.",
"images": [
{
"id": "e9a1…",
"url": "https://…/creative-assets/…png",
"media_type": "image",
"mime_type": "image/png",
"width": 1080,
"height": 1350,
"duration_seconds": null,
"prompt": "…",
"board_id": "b1f0…",
"created_at": "2026-09-27T18:02:40Z"
}
],
"created_at": "2026-09-27T18:02:11Z",
"completed_at": "2026-09-27T18:02:15Z"
}Talk to the Video director — edit a video project#
/v1/studio/video/turnsstudio_video_turnOne message to Video Studio's director, the same turn the app runs: it edits a video project — trims, reorders, captions, titles, overlays, music placement — and the edits are applied and saved. Without artifact_id a new, empty vertical project is opened (the director can plan shots but there is no footage to edit yet). Usually 10–40 s.
Things the app would ask a person to confirm and pay for — filming an AI shot, a motion graphic, captions, finding clips — come back in video.pending_requests and are not run by this call; make them with POST /v1/studio/video/apps/{app}.
Example: { "artifact_id": "…", "message": "Tighten the intro to 3 seconds and add a title card that says 'Summer Sale'" }
The answer (also what GET /v1/turns/{id} returns):
| field | |
|---|---|
id | The turn. Poll it with GET /v1/turns/{id}. |
status | completed · needs_input (answer questions/outline with another turn, same conversation_id or artifact_id) · building (still working — poll) · failed (see error). |
artifact | { id, type, name, url, public_url } — url opens it in Attria; public_url is set once it is public. |
conversation_id | The director conversation this turn belongs to. |
message | The director's own words. |
questions / outline | What it needs from you when status is needs_input. |
Send an Idempotency-Key header: a retry with the same key replays the turn instead of running (and charging) it twice. Once a turn has been claimed, a failure is answered as the turn with status: "failed" and an error; refusals before that (not found, no credits) are ordinary HTTP errors.
Slow: can take several minutes. Agents should send background: true and poll GET /v1/turns/{id} (tool studio_turn_get).
auto: true lets Attria decide instead of answering with status: "needs_input" (questions or an outline to confirm).
stream: true answers with Server-Sent Events; the last event is result (the same body a waiting call returns) or error.
background: true answers 202 Accepted at once with an id; poll GET /v1/turns/{id} (tool studio_turn_get) every 5–10 s until it finishes.
Send an Idempotency-Key header: a retry with the same key replays the first result instead of doing the work twice.
Headers
Idempotency-KeystringoptionalAny unique string (a UUID) per logical write. Retrying with the same key replays the original result.
Bodyapplication/json
messagestringrequiredWhat you would type to this Studio's director, in plain words. Answers to its questions go here too.
max length 3800artifact_idstring · uuidoptionalWork on an existing artifact of this Studio (from a previous answer's
artifact.idor a list endpoint). Omit to start a new one.conversation_idstring · uuidoptionalContinue a conversation: the
conversation_idof a previous answer. Needed to answer a website director's questions before the site exists; otherwiseartifact_idalready implies it.autobooleanoptional"Decide for me": the director settles anything unclear itself and makes the artifact now instead of asking. Default false — the director may answer
needs_inputwith questions or an outline, exactly as in the app.DefaultfalsestreambooleanoptionalAnswer as server-sent events:
turn,plan,status, thenresult(the same body a waited-for call returns).DefaultfalsebackgroundbooleanoptionalAnswer 202 as soon as the turn is claimed and keep working; poll
GET /v1/turns/{id}.Defaultfalse
Can fail with 400 · 401 · 402 · 404 · 409 · 429 · 500 — the shape is in Errors & limits.
curl -X POST "https://app.attria.io/api/v1/studio/video/turns" \
-H "Authorization: Bearer $ATTRIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"artifact_id": "5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a",
"message": "Tighten the intro to 3 seconds and add a title card that says '\''Summer Sale'\''"
}'const res = await fetch("https://app.attria.io/api/v1/studio/video/turns", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
artifact_id: "5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a",
message: "Tighten the intro to 3 seconds and add a title card that says 'Summer Sale'",
}),
});
const turn = await res.json();import os, uuid, requests
res = requests.post(
"https://app.attria.io/api/v1/studio/video/turns",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"artifact_id": "5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a",
"message": "Tighten the intro to 3 seconds and add a title card that says 'Summer Sale'",
},
timeout=900, # a turn that waits can take minutes
)
turn = res.json(){
"id": "f0e1…",
"object": "studio.turn",
"status": "completed",
"studio": "video",
"artifact": {
"id": "2d4c…",
"type": "video",
"name": "Summer promo",
"url": "https://app.attria.io/studio/video/2d4c…",
"public_url": null
},
"conversation_id": "11ab…",
"message": "Trimmed the intro to 3 seconds and added a 'Summer Sale' title card.",
"video": {
"operations_applied": 2,
"operations_skipped": [],
"pending_requests": []
},
"created_at": "2026-09-27T18:02:11Z",
"completed_at": "2026-09-27T18:02:30Z"
}Get a Studio turn#
/v1/turns/{id}studio_turn_getThe current state of a Studio turn — one started with background: true, or any earlier one. Same body as the turn's own answer. Poll it (every few seconds) while status is building; it ends as completed, needs_input or failed. For a new website, building lasts through the site's build (2–5 min) and job shows the stage.
Example: GET /v1/turns/5b7e2c8e-…
Path parameters
idstringrequired
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/turns/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/turns/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const turn = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/turns/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
turn = res.json(){
"id": "5b7e…",
"object": "studio.turn",
"status": "completed",
"studio": "website",
"artifact": {
"id": "0c1d…",
"type": "website",
"name": "Harbor Dental",
"url": "https://app.attria.io/studio/websites/0c1d…",
"public_url": null
},
"conversation_id": "9f2a…",
"message": "The website is built.",
"job": {
"id": "77aa…",
"status": "succeeded",
"stage": "complete",
"error": null
},
"created_at": "2026-09-27T18:02:11Z",
"completed_at": "2026-09-27T18:02:14Z"
}List the Studio's Content Apps#
/v1/studio/appsstudio_apps_listEvery Content App in the Image and Video Studios — the same catalog as the Studio's Add menu — with what each one needs and whether it can be run over the API.
Use it to discover apps before calling POST /v1/studio/{studio}/apps/{app}. For each app you get its studio, id, label, description, examples, group and kind; runnable (and not_runnable_reason when it is false); what a run returns (assets, job, motion); its typical timing; and inputs, a JSON Schema of the request body that app accepts.
Free and instant.
Query parameters
studiostringoptionalOnly this studio's apps.
"image""video"
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/studio/apps" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/studio/apps", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/studio/apps",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
data = res.json(){
"apps": [
{
"studio": "image",
"id": "remove-background",
"label": "Remove background",
"short_label": "Remove background",
"description": "Create a clean transparent version of the selected image.",
"examples": [
"Transparent PNG",
"Clean edges"
],
"group": "finish",
"kind": "utility",
"path": "/v1/studio/image/apps/remove-background",
"runnable": true,
"not_runnable_reason": null,
"returns": "assets",
"timing": "20–60 s",
"inputs": {
"type": "object",
"properties": {
"board_id": {
"type": "string",
"format": "uuid"
},
"source_asset_id": {
"type": "string",
"format": "uuid"
}
},
"required": [
"board_id",
"source_asset_id"
]
}
}
]
}Run a Studio Content App#
/v1/studio/{studio}/apps/{app}studio_app_runRuns one Content App — the same render, model routing, storage and credit metering as the app's form in the Studio. List the apps and each one's exact inputs with GET /v1/studio/apps; send only the fields that app takes (others are rejected).
Image apps (/v1/studio/image/apps/{app}: generate, product-photo, ad-creative, edit, compose, variations, remove-background, reframe, upscale) answer when the images are saved — usually 20–90 seconds — with assets (id, url, width, height, …). Without board_id results go to the Library; apps that start from an image need board_id, and every source or reference asset must already be on that board. count defaults to 1.
Video apps (/v1/studio/video/apps/{app}: generate, product-ad, ugc-ad, recast, remake, avatar) film inside a video project (project_id). They answer in seconds with status: "building" (HTTP 202) and a job; the shot takes about 1–3 minutes. To check on it, repeat the same request with the same Idempotency-Key: the repeat reads the job and moves it forward, never starting or charging a second one. When status is complete, asset is the video. The shot is also added to the end of the project's video. generate without project_id instead makes one 8-second Library shot from prompt and answers when it is done (1–2 minutes).
Motion (/v1/studio/video/apps/motion) generates a designed animation as an editable Remotion composition, inside the request (1–4 minutes). status may be needs_input with motion.questions when the brief is ambiguous; send a more specific prompt.
Apps with runnable: false in the list (e.g. reference, camera-path, story, clips, music, sound-effect) answer 422 with the reason. Credits are reserved before a run and only what was made is charged; not enough credits is 402. Send an Idempotency-Key so a retry is charged once.
Example — POST /v1/studio/image/apps/product-photo:
{ "board_id": "6f1c…", "product_asset_id": "9a2e…", "product": "A matte black insulated travel mug", "direction": "Dawn café, soft window light, condensation detail", "aspect": "4:5" }Example — POST /v1/studio/video/apps/generate:
{ "project_id": "c41d…", "prompt": "A close handheld shot of a runner tying bright red shoes before sunrise", "model": "standard", "seconds": 6 }Slow: can take several minutes; keep the connection open (the server allows up to 800 s).
Send an Idempotency-Key header: a retry with the same key replays the first result instead of doing the work twice.
Path parameters
studiostringrequired"image""video"appstringrequired
Headers
Idempotency-KeystringoptionalAny unique string (a UUID) per logical write. Retrying with the same key replays the original result.
Bodyapplication/json
board_idstring · uuidoptionalImage board to work on. Omit it and results are saved to the Library. Required whenever the app uses a source or reference image: those images must already be on this board.
project_idstring · uuidoptionalVideo project (from the Video Studio) the shot belongs to. The finished shot is added to the end of that video the next time it is opened.
promptstringoptionalWhat to make or change, in plain language.
max length 6000directionstringoptionalOptional extra direction.
max length 2000aspectstringoptionalOutput aspect ratio.
"4:5""1:1""16:9""9:16""4:3""3:4""21:9"countintegeroptionalHow many results to make (1–4). Each result is charged.
min 1max 4stylestringoptionalVisual style.
"none""cinematic""anime""photorealistic""digital-art""oil-painting""watercolor""3d-render"formatstringoptionalCreative format (ad structure).
autolets Attria choose."auto""product""lifestyle""testimonial""comparison""offer""problem-solution""demo""founder""listicle""native""before-after""meme""zero-star-review""notes-app""apology-letter""text-message""social-comment""receipt""statistic""press"modelstringoptionalModel. Image apps: an image model id (
auto= Attria chooses). Video Generate: a shot tier."auto""gemini-3-pro-image""gemini-3.1-flash-image""seedream-5-0-pro""gpt-5.4-image-2""draft""standard""cinema""seedance""kling"reference_asset_idsarray of string · uuidoptionalUp to 4 image asset ids on
board_idto guide the result.source_asset_idstring · uuidoptionalThe asset to work from (an image for image apps; a video clip of at most 15 seconds for Recast/Remake).
product_asset_idstring · uuidoptionalA clean photo of the product.
subject_asset_idstring · uuidoptionalThe person or product to place.
scene_asset_idstring · uuidoptionalThe destination scene or second subject.
identity_asset_idstring · uuidoptionalA clear image of the new person or character.
portrait_asset_idstring · uuidoptionalA front-facing portrait with one clearly visible person.
creator_asset_idstring · uuidoptionalOptional creator reference or opening frame.
start_frame_asset_idstring · uuidoptionalImage asset the shot starts from.
end_frame_asset_idstring · uuidoptionalImage asset the shot ends on (needs
start_frame_asset_id).productstringoptionalThe product: its name and what it does.
max length 500offerstringoptionalThe product and offer the ad is built around.
max length 2000audiencestringoptionalWho it is for.
max length 500anglestringoptionalThe angle or offer to lead with.
max length 500hookstringoptionalThe creator's opening line.
max length 500creatorstringoptionalWho is speaking, e.g. "a credible solo agency owner filming at their desk".
max length 300ad_formatstringoptionalThe ad's format, e.g. "cinematic demo", "tabletop showcase", "comparison".
max length 200scriptstringoptionalExactly what the presenter says (at most 38 words).
max length 400secondsintegeroptionalLength in seconds.
min 1max 60soundbooleanoptionalGenerate native audio with the shot.
camerastringoptionalCamera move.
autolets the model choose."auto""static""push-in""pull-back""orbit""pan""tilt-up""tracking""handheld""drone""crash-zoom"directbooleanoptionalLet Attria rewrite the description as a proper shot before filming (default true).
Can fail with 400 · 401 · 402 · 404 · 409 · 429 · 500 — the shape is in Errors & limits.
curl -X POST "https://app.attria.io/api/v1/studio/image/apps/generate" \
-H "Authorization: Bearer $ATTRIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"prompt": "A matte black insulated travel mug on a sunlit café counter",
"aspect": "4:5",
"count": 1
}'const res = await fetch("https://app.attria.io/api/v1/studio/image/apps/generate", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
prompt: "A matte black insulated travel mug on a sunlit café counter",
aspect: "4:5",
count: 1,
}),
});
const data = await res.json();import os, uuid, requests
res = requests.post(
"https://app.attria.io/api/v1/studio/image/apps/generate",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"prompt": "A matte black insulated travel mug on a sunlit café counter",
"aspect": "4:5",
"count": 1,
},
timeout=900, # a turn that waits can take minutes
)
data = res.json(){
"app": "generate",
"studio": "image",
"status": "complete",
"board_id": null,
"assets": [
{
"id": "e9a1…",
"url": "https://…/creative-assets/…png",
"width": 1080,
"height": 1350,
"mime_type": "image/png"
}
]
}List websites#
/v1/websiteswebsites_listThe websites made in Website Studio, most recently changed first.
status is the site's own state: draft (not built yet, or building), built (ready, not published), live (published at public_url), paused. For the build's progress fetch GET /v1/websites/{id}.
GET /v1/websites?limit=10Query parameters
limitintegeroptionalHow many items to return, 1–100. Default 25.
Default25min 1max 100cursorstringoptionalThe
next_cursorfrom the previous page. Omit for the first page.
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/websites?limit=10" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/websites?limit=10", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const page = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/websites?limit=10",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
page = res.json(){
"object": "list",
"data": [
{
"id": "0c1d…",
"object": "website",
"name": "Harbor Dental",
"status": "built",
"url": "https://app.attria.io/studio/websites/0c1d…",
"public_url": null,
"thumbnail_url": null,
"created_at": "2026-09-27T18:02:14Z",
"updated_at": "2026-09-27T18:06:40Z"
}
],
"has_more": false,
"next_cursor": null
}Get a website and its build#
/v1/websites/{id}websites_getOne website with its build status — poll this after a website turn answered building.
build.status:building(queued or running;build.stagesays which step),ready(built),failed(build.errorsays why), ornot_started.status: the site itself —draft,built,liveorpaused.public_urlis set while it islive.urlopens it in Website Studio.
A new site usually builds in 2–5 minutes; an edit's rebuild in 1–3. Poll every 5–10 seconds.
GET /v1/websites/0c1d2e3f-…Path parameters
idstringrequired
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/websites/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/websites/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const website = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/websites/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
website = res.json(){
"id": "0c1d…",
"object": "website",
"name": "Harbor Dental",
"status": "built",
"url": "https://app.attria.io/studio/websites/0c1d…",
"public_url": null,
"build": {
"status": "ready",
"job": {
"id": "77aa…",
"operation": "complete",
"stage": "complete",
"status": "succeeded",
"error": null,
"updated_at": "2026-09-27T18:06:40Z",
"completed_at": "2026-09-27T18:06:40Z"
}
},
"business": {
"name": "Harbor Dental",
"what_you_sell": "Family dentistry in Portland",
"who_its_for": null
},
"created_at": "2026-09-27T18:02:14Z",
"updated_at": "2026-09-27T18:06:40Z"
}List documents#
/v1/documentsdocuments_listThe decks, proposals and documents made in Documents Studio, most recently changed first. Summaries only — fetch GET /v1/documents/{id} for the content.
kind is deck, proposal or document. public_url is set while the document is shared by link.
GET /v1/documents?limit=10Query parameters
limitintegeroptionalHow many items to return, 1–100. Default 25.
Default25min 1max 100cursorstringoptionalThe
next_cursorfrom the previous page. Omit for the first page.
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/documents?limit=10" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/documents?limit=10", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const page = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/documents?limit=10",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
page = res.json(){
"object": "list",
"data": [
{
"id": "a3c2…",
"object": "document",
"title": "Q3 Investor Update",
"kind": "deck",
"version": 2,
"url": "https://app.attria.io/studio/documents/a3c2…",
"public_url": null,
"created_at": "2026-09-27T18:02:11Z",
"updated_at": "2026-09-27T18:03:01Z"
}
],
"has_more": false,
"next_cursor": null
}Get a document with its full content#
/v1/documents/{id}documents_getOne document with its full structured content — what the Studio renders:
content.layout—slides(a deck: one section per slide) orpage(a scrolling document).content.theme— the look.content.sections[]— each with anid, anintent(cover,narrative,pricing,stats, …) andblocks[](headings, text, lists, images, stats, pricing tables, buttons…).
To change it, send a turn with artifact_id set to this id (POST /v1/studio/document/turns).
GET /v1/documents/a3c2…Path parameters
idstringrequired
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/documents/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/documents/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const document = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/documents/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
document = res.json(){
"id": "a3c2…",
"object": "document",
"title": "Q3 Investor Update",
"kind": "deck",
"version": 2,
"url": "https://app.attria.io/studio/documents/a3c2…",
"public_url": null,
"content": {
"schema": 1,
"layout": "slides",
"theme": "brand",
"sections": [
{
"id": "s1",
"intent": "cover",
"blocks": [
{
"type": "heading",
"text": "Q3 Investor Update"
}
]
}
]
},
"created_at": "2026-09-27T18:02:11Z",
"updated_at": "2026-09-27T18:03:01Z"
}List apps#
/v1/appsapps_listThe workspace apps made in Apps Studio, most recently changed first.
live is true once a version has been published (it then appears in the workspace's Apps, at public_url, for signed-in members). version ahead of published_version means a change is waiting to be published.
GET /v1/appsQuery parameters
limitintegeroptionalHow many items to return, 1–100. Default 25.
Default25min 1max 100cursorstringoptionalThe
next_cursorfrom the previous page. Omit for the first page.
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/apps" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/apps", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const page = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/apps",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
page = res.json(){
"object": "list",
"data": [
{
"id": "6e2f…",
"object": "app",
"name": "Job Tracker",
"description": "Open jobs by date",
"version": 1,
"published_version": 1,
"live": true,
"url": "https://app.attria.io/studio/apps/6e2f…",
"public_url": "https://app.attria.io/apps/6e2f…",
"created_at": "2026-09-27T18:02:11Z",
"updated_at": "2026-09-27T18:03:20Z"
}
],
"has_more": false,
"next_cursor": null
}Get an app#
/v1/apps/{id}apps_getOne app: its summary, its manifest (the Objects it reads and writes, with access, and the Actions it calls), and the paths of its source files.
To change it, send a turn with artifact_id set to this id (POST /v1/studio/app/turns).
GET /v1/apps/6e2f…Path parameters
idstringrequired
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/apps/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/apps/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const app = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/apps/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
app = res.json(){
"id": "6e2f…",
"object": "app",
"name": "Job Tracker",
"description": "Open jobs by date",
"version": 1,
"published_version": 1,
"live": true,
"url": "https://app.attria.io/studio/apps/6e2f…",
"public_url": "https://app.attria.io/apps/6e2f…",
"manifest": {
"schema": 1,
"objects": [
{
"key": "jobs",
"access": [
"read",
"update"
]
}
],
"actions": []
},
"files": [
"src/App.tsx"
],
"created_at": "2026-09-27T18:02:11Z",
"updated_at": "2026-09-27T18:03:20Z"
}List image boards#
/v1/images/boardsimages_boards_listThe boards in Images Studio, most recently changed first. A board is where a Studio image conversation happens and its images are kept; pass a board's id as artifact_id to POST /v1/studio/image/turns to keep working on it, or as board_id to GET /v1/images.
GET /v1/images/boardsQuery parameters
limitintegeroptionalHow many items to return, 1–100. Default 25.
Default25min 1max 100cursorstringoptionalThe
next_cursorfrom the previous page. Omit for the first page.
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/images/boards" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/images/boards", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const page = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/images/boards",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
page = res.json(){
"object": "list",
"data": [
{
"id": "b1f0…",
"object": "image_board",
"name": "Summer promo",
"media_kind": "image",
"pinned": false,
"url": "https://app.attria.io/studio/images/b1f0…",
"created_at": "2026-09-27T18:02:11Z",
"updated_at": "2026-09-27T18:02:40Z"
}
],
"has_more": false,
"next_cursor": null
}List images#
/v1/imagesimages_listImages in the workspace's Studio — made or uploaded — newest first, each with a URL you can download. Filter to one board with board_id (see GET /v1/images/boards).
GET /v1/images?board_id=b1f0…&limit=20Query parameters
board_idstringoptionalOnly images on this board.
limitintegeroptionalHow many items to return, 1–100. Default 25.
Default25min 1max 100cursorstringoptionalThe
next_cursorfrom the previous page. Omit for the first page.
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/images" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/images", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const page = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/images",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
page = res.json(){
"object": "list",
"data": [
{
"id": "e9a1…",
"url": "https://…/creative-assets/…png",
"media_type": "image",
"mime_type": "image/png",
"width": 1080,
"height": 1350,
"duration_seconds": null,
"prompt": "…",
"board_id": "b1f0…",
"created_at": "2026-09-27T18:02:40Z"
}
],
"has_more": false,
"next_cursor": null
}List video projects#
/v1/videosvideos_listThe video projects in Video Studio, most recently changed first: name, aspect, duration and poster. Pass a project's id as artifact_id to POST /v1/studio/video/turns to edit it with the director.
GET /v1/videosQuery parameters
limitintegeroptionalHow many items to return, 1–100. Default 25.
Default25min 1max 100cursorstringoptionalThe
next_cursorfrom the previous page. Omit for the first page.
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/videos" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/videos", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const page = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/videos",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
page = res.json(){
"object": "list",
"data": [
{
"id": "2d4c…",
"object": "video",
"name": "Summer promo",
"aspect": "9:16",
"duration_seconds": 21.5,
"thumbnail_url": null,
"url": "https://app.attria.io/studio/video/2d4c…",
"created_at": "2026-09-27T18:02:11Z",
"updated_at": "2026-09-27T18:02:30Z"
}
],
"has_more": false,
"next_cursor": null
}