# Attria API — guide for agents > Base URL `https://app.attria.io/api` · auth `Authorization: Bearer attria_sk_…` · JSON in, JSON out · OpenAPI 3.1: https://app.attria.io/api/v1/openapi.json · MCP: https://app.attria.io/api/v1/mcp Attria is a marketing product: a creative Studio (websites, documents, apps, images, video), agents that do work on a schedule or on demand, and the workspace's objects (records). This API drives the same code paths as the app. Everything you create lands in the key owner's workspace and is billed to it in credits. ## Quick start ```bash export ATTRIA_API_KEY=attria_sk_… # Settings → API curl -sS https://app.attria.io/api/v1/me -H "Authorization: Bearer $ATTRIA_API_KEY" ``` ## Authentication - Create a key in **Settings → API**. It is shown once; store it as a secret. It acts as the member who made it, with full access to that workspace. - Send it on every request as `Authorization: Bearer attria_sk_…`. A missing, malformed or revoked key gets `401` with `error.type = "authentication"`. - Never put a key in a URL, a client-side bundle or a log line. ## Conventions - Path parameters appear in `{braces}`; GETs take query parameters; POST and PATCH take a JSON body (`Content-Type: application/json`). - Ids are UUIDs. Times are ISO-8601 in UTC. - Every response has an `X-Request-Id` header; error bodies repeat it as `request_id`. Quote it when something looks wrong. You may send your own `X-Request-Id`. ## Turns: how Studio (and agent runs) work A **turn** is one message to a Studio director — exactly what typing into the app does. Send `message`; to keep working on the same thing, send the `conversation_id` and/or `artifact_id` the previous turn returned. The body's controls: - `auto` (default `false`): the director may stop and answer with `status: "needs_input"` — clarifying questions or an outline to approve, the same as in the app. Reply with another turn in the same conversation. `auto: true` means "decide for me" and goes straight to building. **Unattended agents should send `auto: true`** unless a human is there to answer. - **Wait** (default): the request stays open until the turn finishes — up to several minutes (the server allows 800 s) — and returns the result. - `stream: true`: the same request answers with Server-Sent Events (see below) as the director plans and builds; the last event is `result` or `error`. - `background: true`: answers `202 Accepted` at once with the turn's id. Poll `GET /v1/turns/{id}` (MCP tool `studio_turn_get`) every 5–10 s until its status is no longer queued or running. Best for agents and for anything you would otherwise hold a connection open for. Turns are durable: a dropped connection does not cancel the work, and a retry with the same `Idempotency-Key` returns the same turn instead of starting a second one. ## Long-running work Endpoints marked **slow** can take minutes. Prefer `background: true` and polling; back off between polls (5 s, then 10 s), and stop polling once the status is final. Never start a second copy of a turn because the first seems slow — poll it. ## Idempotency Send `Idempotency-Key: ` on every POST, PATCH and DELETE, one key per logical action. Retrying with the same key (after a timeout or a `5xx`) replays the first result instead of doing the work — and spending the credits — twice. Use a new key for a new action. ## Errors Every error has one shape: ```json { "error": { "type": "invalid_request", "message": "Invalid request body.", "details": { "properties": { "message": { "errors": [ "Required" ] } } } }, "request_id": "9b2f0c1e-5d47-4a8e-b3a6-2c1d0e9f8a77" } ``` | Status | `error.type` | What to do | |---|---|---| | 400 | `invalid_request` | Fix the field named in `details`; do not retry unchanged. | | 401 | `authentication` | The key is missing, malformed or revoked. Stop and ask for a valid key. | | 402 | `insufficient_credits` | Out of credits. Stop; a person must top up in Settings → Billing. | | 403 | `permission` | The key may not do this. Do not retry. | | 404 | `not_found` | No such id in this workspace. Re-list to find the right one. | | 409 | `conflict` | State changed, or an Idempotency-Key was reused with a different body. Re-read, then retry with a new key. | | 429 | `rate_limited` | Wait (`Retry-After` seconds when present), then retry with the same key. | | 5xx | `server_error` | Retry with backoff and the SAME Idempotency-Key. | ## Streaming (SSE) With `stream: true` the response is `text/event-stream`. Each event is `event: ` plus one `data: ` line. Show or log intermediate events as progress; the final event is `result` (its data is the body a waiting call would have returned) or `error` (`{ "message", "request_id" }`). The stream closes after it. ## MCP server Every endpoint below is also an MCP tool at `https://app.attria.io/api/v1/mcp` (Streamable HTTP, stateless, protocol 2026-07-28 with 2025-03-26 – 2025-11-25 clients also served). Authenticate with the same key. Tool name = the endpoint's id; arguments = its path, query and body fields flattened into one object. Write tools take an optional `idempotency_key`. Over MCP, turn endpoints default to `background: true` — a tool call that holds for ten minutes times out in most clients — so call the turn tool, then poll the returned id with the turn-reading tool. Pass `background: false` to wait instead; clients that send a `progressToken` then receive the director's events as progress notifications. **Claude Code** ```bash claude mcp add --transport http attria https://app.attria.io/api/v1/mcp --header "Authorization: Bearer $ATTRIA_API_KEY" ``` **Cursor** (`~/.cursor/mcp.json` or `.cursor/mcp.json`) ```json { "mcpServers": { "attria": { "url": "https://app.attria.io/api/v1/mcp", "headers": { "Authorization": "Bearer attria_sk_…" } } } } ``` ## Endpoints ### 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 `POST /v1/studio/website/turns` · MCP tool `studio_website_turn` · **slow** One 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. Parameters: - `message` (body, string, required) — What you would type to this Studio's director, in plain words. Answers to its questions go here too. - `artifact_id` (body, string (uuid), optional) — Work on an existing artifact of this Studio (from a previous answer's `artifact.id` or a list endpoint). Omit to start a new one. - `conversation_id` (body, string (uuid), optional) — Continue a conversation: the `conversation_id` of a previous answer. Needed to answer a website director's questions before the site exists; otherwise `artifact_id` already implies it. - `auto` (body, boolean, optional, default false) — "Decide for me": the director settles anything unclear itself and makes the artifact now instead of asking. Default false — the director may answer `needs_input` with questions or an outline, exactly as in the app. - `stream` (body, boolean, optional, default false) — Answer as server-sent events: `turn`, `plan`, `status`, then `result` (the same body a waited-for call returns). - `background` (body, boolean, optional, default false) — Answer 202 as soon as the turn is claimed and keep working; poll `GET /v1/turns/{id}`. Request: ```bash curl -sS -X POST "https://app.attria.io/api/v1/studio/website/turns" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"message":"Build a landing page for Harbor Dental, a family dentist in Portland. Goal: book a first cleaning.","auto":true}' ``` Response `200`: ```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 `POST /v1/studio/document/turns` · MCP tool `studio_document_turn` · **slow** One 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. Parameters: - `message` (body, string, required) — What you would type to this Studio's director, in plain words. Answers to its questions go here too. - `artifact_id` (body, string (uuid), optional) — Work on an existing artifact of this Studio (from a previous answer's `artifact.id` or a list endpoint). Omit to start a new one. - `conversation_id` (body, string (uuid), optional) — Continue a conversation: the `conversation_id` of a previous answer. Needed to answer a website director's questions before the site exists; otherwise `artifact_id` already implies it. - `auto` (body, boolean, optional, default false) — "Decide for me": the director settles anything unclear itself and makes the artifact now instead of asking. Default false — the director may answer `needs_input` with questions or an outline, exactly as in the app. - `stream` (body, boolean, optional, default false) — Answer as server-sent events: `turn`, `plan`, `status`, then `result` (the same body a waited-for call returns). - `background` (body, boolean, optional, default false) — Answer 202 as soon as the turn is claimed and keep working; poll `GET /v1/turns/{id}`. Request: ```bash curl -sS -X POST "https://app.attria.io/api/v1/studio/document/turns" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -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}' ``` Response `200`: ```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 `POST /v1/studio/app/turns` · MCP tool `studio_app_turn` · **slow** One 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. Parameters: - `message` (body, string, required) — What you would type to this Studio's director, in plain words. Answers to its questions go here too. - `artifact_id` (body, string (uuid), optional) — Work on an existing artifact of this Studio (from a previous answer's `artifact.id` or a list endpoint). Omit to start a new one. - `conversation_id` (body, string (uuid), optional) — Continue a conversation: the `conversation_id` of a previous answer. Needed to answer a website director's questions before the site exists; otherwise `artifact_id` already implies it. - `auto` (body, boolean, optional, default false) — "Decide for me": the director settles anything unclear itself and makes the artifact now instead of asking. Default false — the director may answer `needs_input` with questions or an outline, exactly as in the app. - `stream` (body, boolean, optional, default false) — Answer as server-sent events: `turn`, `plan`, `status`, then `result` (the same body a waited-for call returns). - `background` (body, boolean, optional, default false) — Answer 202 as soon as the turn is claimed and keep working; poll `GET /v1/turns/{id}`. Request: ```bash curl -sS -X POST "https://app.attria.io/api/v1/studio/app/turns" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -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}' ``` Response `200`: ```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 `POST /v1/studio/image/turns` · MCP tool `studio_image_turn` · **slow** One 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. Parameters: - `message` (body, string, required) — What you would type to this Studio's director, in plain words. Answers to its questions go here too. - `artifact_id` (body, string (uuid), optional) — Work on an existing artifact of this Studio (from a previous answer's `artifact.id` or a list endpoint). Omit to start a new one. - `conversation_id` (body, string (uuid), optional) — Continue a conversation: the `conversation_id` of a previous answer. Needed to answer a website director's questions before the site exists; otherwise `artifact_id` already implies it. - `auto` (body, boolean, optional, default false) — "Decide for me": the director settles anything unclear itself and makes the artifact now instead of asking. Default false — the director may answer `needs_input` with questions or an outline, exactly as in the app. - `stream` (body, boolean, optional, default false) — Answer as server-sent events: `turn`, `plan`, `status`, then `result` (the same body a waited-for call returns). - `background` (body, boolean, optional, default false) — Answer 202 as soon as the turn is claimed and keep working; poll `GET /v1/turns/{id}`. Request: ```bash curl -sS -X POST "https://app.attria.io/api/v1/studio/image/turns" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"message":"Two scroll-stopping images for a summer iced-latte promo, bright and minimal","auto":true}' ``` Response `200`: ```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 `POST /v1/studio/video/turns` · MCP tool `studio_video_turn` · **slow** One 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. Parameters: - `message` (body, string, required) — What you would type to this Studio's director, in plain words. Answers to its questions go here too. - `artifact_id` (body, string (uuid), optional) — Work on an existing artifact of this Studio (from a previous answer's `artifact.id` or a list endpoint). Omit to start a new one. - `conversation_id` (body, string (uuid), optional) — Continue a conversation: the `conversation_id` of a previous answer. Needed to answer a website director's questions before the site exists; otherwise `artifact_id` already implies it. - `auto` (body, boolean, optional, default false) — "Decide for me": the director settles anything unclear itself and makes the artifact now instead of asking. Default false — the director may answer `needs_input` with questions or an outline, exactly as in the app. - `stream` (body, boolean, optional, default false) — Answer as server-sent events: `turn`, `plan`, `status`, then `result` (the same body a waited-for call returns). - `background` (body, boolean, optional, default false) — Answer 202 as soon as the turn is claimed and keep working; poll `GET /v1/turns/{id}`. Request: ```bash curl -sS -X POST "https://app.attria.io/api/v1/studio/video/turns" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"artifact_id":"2d4c…","message":"Tighten the intro to 3 seconds and add a title card that says '\''Summer Sale'\''"}' ``` Response `200`: ```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 `GET /v1/turns/{id}` · MCP tool `studio_turn_get` The 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-…` Parameters: - `id` (path, string (uuid), required) Request: ```bash curl -sS "https://app.attria.io/api/v1/turns/5b7e%E2%80%A6" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: ```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 `GET /v1/studio/apps` · MCP tool `studio_apps_list` Every 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. Parameters: - `studio` (query, "image" | "video", optional) — Only this studio's apps. Request: ```bash curl -sS "https://app.attria.io/api/v1/studio/apps?studio=image" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: ```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 `POST /v1/studio/{studio}/apps/{app}` · MCP tool `studio_app_run` · **slow** Runs 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`: ```json { "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`: ```json { "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). Parameters: - `studio` (path, "image" | "video", required) - `app` (path, string, required) - `board_id` (body, string (uuid), optional) — Image 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_id` (body, string (uuid), optional) — Video 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. - `prompt` (body, string, optional) — What to make or change, in plain language. - `direction` (body, string, optional) — Optional extra direction. - `aspect` (body, "4:5" | "1:1" | "16:9" | "9:16" | "4:3" | "3:4" | "21:9", optional) — Output aspect ratio. - `count` (body, integer, optional) — How many results to make (1–4). Each result is charged. - `style` (body, "none" | "cinematic" | "anime" | "photorealistic" | "digital-art" | "oil-painting" | "watercolor" | "3d-render", optional) — Visual style. - `format` (body, "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", optional) — Creative format (ad structure). `auto` lets Attria choose. - `model` (body, "auto" | "gemini-3-pro-image" | "gemini-3.1-flash-image" | "seedream-5-0-pro" | "gpt-5.4-image-2" | "draft" | "standard" | "cinema" | "seedance" | "kling", optional) — Model. Image apps: an image model id (`auto` = Attria chooses). Video Generate: a shot tier. - `reference_asset_ids` (body, string (uuid)[], optional) — Up to 4 image asset ids on `board_id` to guide the result. - `source_asset_id` (body, string (uuid), optional) — The asset to work from (an image for image apps; a video clip of at most 15 seconds for Recast/Remake). - `product_asset_id` (body, string (uuid), optional) — A clean photo of the product. - `subject_asset_id` (body, string (uuid), optional) — The person or product to place. - `scene_asset_id` (body, string (uuid), optional) — The destination scene or second subject. - `identity_asset_id` (body, string (uuid), optional) — A clear image of the new person or character. - `portrait_asset_id` (body, string (uuid), optional) — A front-facing portrait with one clearly visible person. - `creator_asset_id` (body, string (uuid), optional) — Optional creator reference or opening frame. - `start_frame_asset_id` (body, string (uuid), optional) — Image asset the shot starts from. - `end_frame_asset_id` (body, string (uuid), optional) — Image asset the shot ends on (needs `start_frame_asset_id`). - `product` (body, string, optional) — The product: its name and what it does. - `offer` (body, string, optional) — The product and offer the ad is built around. - `audience` (body, string, optional) — Who it is for. - `angle` (body, string, optional) — The angle or offer to lead with. - `hook` (body, string, optional) — The creator's opening line. - `creator` (body, string, optional) — Who is speaking, e.g. "a credible solo agency owner filming at their desk". - `ad_format` (body, string, optional) — The ad's format, e.g. "cinematic demo", "tabletop showcase", "comparison". - `script` (body, string, optional) — Exactly what the presenter says (at most 38 words). - `seconds` (body, integer, optional) — Length in seconds. - `sound` (body, boolean, optional) — Generate native audio with the shot. - `camera` (body, "auto" | "static" | "push-in" | "pull-back" | "orbit" | "pan" | "tilt-up" | "tracking" | "handheld" | "drone" | "crash-zoom", optional) — Camera move. `auto` lets the model choose. - `direct` (body, boolean, optional) — Let Attria rewrite the description as a proper shot before filming (default true). Request: ```bash curl -sS -X POST "https://app.attria.io/api/v1/studio/image/apps/generate" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"prompt":"A matte black insulated travel mug on a sunlit café counter","aspect":"4:5","count":1}' ``` Response `200`: ```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 `GET /v1/websites` · MCP tool `websites_list` The 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=10 ``` Parameters: - `limit` (query, integer, optional, default 25) — How many items to return, 1–100. Default 25. - `cursor` (query, string, optional) — The `next_cursor` from the previous page. Omit for the first page. Request: ```bash curl -sS "https://app.attria.io/api/v1/websites?limit=10" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: ```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 `GET /v1/websites/{id}` · MCP tool `websites_get` One website with its **build status** — poll this after a website turn answered `building`. - `build.status`: `building` (queued or running; `build.stage` says which step), `ready` (built), `failed` (`build.error` says why), or `not_started`. - `status`: the site itself — `draft`, `built`, `live` or `paused`. `public_url` is set while it is `live`. - `url` opens 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-… ``` Parameters: - `id` (path, string (uuid), required) Request: ```bash curl -sS "https://app.attria.io/api/v1/websites/0c1d%E2%80%A6" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: ```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 `GET /v1/documents` · MCP tool `documents_list` The 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=10 ``` Parameters: - `limit` (query, integer, optional, default 25) — How many items to return, 1–100. Default 25. - `cursor` (query, string, optional) — The `next_cursor` from the previous page. Omit for the first page. Request: ```bash curl -sS "https://app.attria.io/api/v1/documents?limit=10" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: ```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 `GET /v1/documents/{id}` · MCP tool `documents_get` One document with its **full structured content** — what the Studio renders: - `content.layout` — `slides` (a deck: one section per slide) or `page` (a scrolling document). - `content.theme` — the look. - `content.sections[]` — each with an `id`, an `intent` (`cover`, `narrative`, `pricing`, `stats`, …) and `blocks[]` (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… ``` Parameters: - `id` (path, string (uuid), required) Request: ```bash curl -sS "https://app.attria.io/api/v1/documents/a3c2%E2%80%A6" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: ```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 `GET /v1/apps` · MCP tool `apps_list` The 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/apps ``` Parameters: - `limit` (query, integer, optional, default 25) — How many items to return, 1–100. Default 25. - `cursor` (query, string, optional) — The `next_cursor` from the previous page. Omit for the first page. Request: ```bash curl -sS "https://app.attria.io/api/v1/apps" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: ```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 `GET /v1/apps/{id}` · MCP tool `apps_get` One 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… ``` Parameters: - `id` (path, string (uuid), required) Request: ```bash curl -sS "https://app.attria.io/api/v1/apps/6e2f%E2%80%A6" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: ```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 `GET /v1/images/boards` · MCP tool `images_boards_list` The 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/boards ``` Parameters: - `limit` (query, integer, optional, default 25) — How many items to return, 1–100. Default 25. - `cursor` (query, string, optional) — The `next_cursor` from the previous page. Omit for the first page. Request: ```bash curl -sS "https://app.attria.io/api/v1/images/boards" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: ```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 `GET /v1/images` · MCP tool `images_list` Images 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=20 ``` Parameters: - `board_id` (query, string (uuid), optional) — Only images on this board. - `limit` (query, integer, optional, default 25) — How many items to return, 1–100. Default 25. - `cursor` (query, string, optional) — The `next_cursor` from the previous page. Omit for the first page. Request: ```bash curl -sS "https://app.attria.io/api/v1/images?board_id=b1f0%E2%80%A6" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: ```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 `GET /v1/videos` · MCP tool `videos_list` The 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/videos ``` Parameters: - `limit` (query, integer, optional, default 25) — How many items to return, 1–100. Default 25. - `cursor` (query, string, optional) — The `next_cursor` from the previous page. Omit for the first page. Request: ```bash curl -sS "https://app.attria.io/api/v1/videos" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: ```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 } ``` ### Agents List, create, update and run the workspace's agents; follow their runs, steps and approvals. #### List Agents `GET /v1/agents` · MCP tool `agents_list` The workspace's Agents, oldest first. Archived Agents are left out unless you ask for them with `status=archived`. Each item is a summary (`id`, `name`, `status`, `active_version_id`, timestamps). Fetch `GET /v1/agents/{id}` for the plan, wake-ups and schedule. `status` is one of `draft` (never turned on), `active` (listening for its wake-ups), `paused`, `archived`. ``` GET /v1/agents?status=active&limit=10 ``` Parameters: - `status` (query, "draft" | "active" | "paused" | "archived", optional) — Only Agents in this state. - `limit` (query, integer, optional, default 25) — How many items to return, 1–100. Default 25. - `cursor` (query, string, optional) — The `next_cursor` from the previous page. Omit for the first page. Request: ```bash curl -sS "https://app.attria.io/api/v1/agents" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: A JSON object; see the description above. #### Create an Agent from a description `POST /v1/agents` · MCP tool `agents_create` · **slow** Describe an Agent in plain language and Attria's Agent Builder drafts its operating plan — the same builder as Agent Studio, with the same checks. The new Agent is a **draft**: turn it on with `POST /v1/agents/{id}/activate`. The answer is an `agent_build`: - `status: "built"` — the plan was drafted; `agent` is the full Agent and `message` is the builder's summary. - `status: "needs_input"` — the builder needs an answer first. `questions` lists them (each with suggested `options`). The draft Agent already exists: answer with `PATCH /v1/agents/{id}` and a `message`. Send `auto: true` to forbid questions. Uses credits (a model call). Takes 10–90 seconds. Send an `Idempotency-Key` header: a retry with the same key returns the first result instead of building a second Agent. ```json { "name": "Lead follow-up", "message": "When a new Lead object is created, email them a friendly intro within 5 minutes and notify me.", "auto": true } ``` - **Slow:** can take several minutes; keep the connection open (the server allows up to 800 s). - `auto: true` lets Attria decide instead of answering with `status: "needs_input"` (questions or an outline to confirm). Parameters: - `name` (body, string, optional) — A name for the Agent. The builder may refine it. - `message` (body, string, required) — What the Agent should do, or what to change — plain language, as you would type it in Agent Studio. - `auto` (body, boolean, optional, default false) — `true`: the builder must draft the plan now and may not ask questions. `false` (default): it may answer `needs_input` with questions. Request: ```bash curl -sS -X POST "https://app.attria.io/api/v1/agents" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"message":""}' ``` Response `201`: A JSON object; see the description above. #### Get an Agent `GET /v1/agents/{id}` · MCP tool `agents_get` One Agent in full: - `profile` — purpose, instructions, autonomy (`automatic`, `ask_first`, …), model and tool grants. - `plan.nodes` / `plan.edges` — the operating plan as drawn in Agent Studio (`kind` is `agent`, `trigger`, `email`, `ai_decide`, `branch`, …). - `wakeups` — every way it can be woken (`chat`, `schedule`, `webhook`, `object_created`, `object_updated`, `integration_trigger`, `app_call`), each with its live `deployment` when the Agent is active. - `schedule` — the scheduled wake-ups and their `next_run_at` (UTC). - `validation` — `ready: true` when Activate would succeed; otherwise `issues` says what to fix. - `pending_approvals` — how many actions are waiting for a person (see `GET /v1/approvals`). Secrets never appear: integration credentials are stored encrypted and config values under secret-looking keys are masked. Parameters: - `id` (path, string (uuid), required) Request: ```bash curl -sS "https://app.attria.io/api/v1/agents/00000000-0000-4000-8000-000000000000" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: A JSON object; see the description above. #### Change an Agent `PATCH /v1/agents/{id}` · MCP tool `agents_update` · **slow** Rename an Agent, or ask the Agent Builder to change its plan in plain language — one Agent Studio turn. The builder sees the current plan and this Agent's builder history, so a `message` can simply answer the questions from a `needs_input` result. - `name` alone renames it (no credits). - `message` runs a builder turn and answers with an `agent_build` (`built` or `needs_input`), like create. Changes edit the **draft**. An active Agent keeps running its activated version until you `activate` again. ```json { "message": "Also send me an SMS when the lead's budget is over $10k.", "auto": true } ``` - **Slow:** can take several minutes; keep the connection open (the server allows up to 800 s). - `auto: true` lets Attria decide instead of answering with `status: "needs_input"` (questions or an outline to confirm). Parameters: - `id` (path, string (uuid), required) - `name` (body, string, optional) — A new name. - `message` (body, string, optional) — What the Agent should do, or what to change — plain language, as you would type it in Agent Studio. - `auto` (body, boolean, optional, default false) — `true`: the builder must draft the plan now and may not ask questions. `false` (default): it may answer `needs_input` with questions. Request: ```bash curl -sS -X PATCH "https://app.attria.io/api/v1/agents/00000000-0000-4000-8000-000000000000" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{}' ``` Response `200`: A JSON object; see the description above. #### Turn an Agent on `POST /v1/agents/{id}/activate` · MCP tool `agents_activate` · **slow** Compiles the draft plan, provisions every wake-up (schedules, webhooks, connected-app triggers) and publishes one immutable version. From then on the Agent wakes by itself. Answers `{ status: "active", version, version_id, deployments }`. A `webhook` deployment carries its `endpoint`: POST JSON there to wake the Agent. Keep that URL private — anyone holding it can wake this Agent. If the plan is not ready the answer is `422` with `error.details.issues` — the same list `GET /v1/agents/{id}` shows under `validation`. Activating an active Agent publishes a new version from the current draft. - **Slow:** can take several minutes; keep the connection open (the server allows up to 800 s). Parameters: - `id` (path, string (uuid), required) Request: ```bash curl -sS -X POST "https://app.attria.io/api/v1/agents/00000000-0000-4000-8000-000000000000/activate" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" ``` Response `200`: A JSON object; see the description above. #### Pause an Agent `POST /v1/agents/{id}/pause` · MCP tool `agents_pause` Stops every wake-up (schedules, webhooks, connected-app triggers are torn down) and sets the Agent to `paused`. Runs already in flight finish. Turn it back on with `POST /v1/agents/{id}/activate`. Pausing a paused Agent is harmless. Parameters: - `id` (path, string (uuid), required) Request: ```bash curl -sS -X POST "https://app.attria.io/api/v1/agents/00000000-0000-4000-8000-000000000000/pause" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" ``` Response `200`: A JSON object; see the description above. #### Talk to an Agent (run it) `POST /v1/agents/{id}/runs` · MCP tool `agents_runs_create` · **slow** Send an Agent a message — or a JSON `payload` as an event — exactly as someone typing to it in Attria. The Agent decides whether to answer from what it knows or to **run** its plan (emails, research, object writes, …). A run may pause for an approval; see `GET /v1/approvals`. The answer is an `agent_invocation`: `answer` (what the Agent said), `status` (`completed`, `waiting`, `failed`, `cancelled`), `run_id` when it ran, the `steps` it took, and `thread_id`. Pass `thread_id` back to continue the same conversation. **Idempotency.** Send an `Idempotency-Key`: a retry with the same key and body is answered from the first run and never runs the Agent — its emails, webhooks, credits — twice. **Long runs.** - `stream: true` — server-sent events: `started` (`{ thread_id }`) at once, then one `result` (the invocation) or `error`. - `background: true` — answers `202` at once with `status: "running"`, `thread_id` and the `idempotency_key` in use (minted if you sent none). To get the result, repeat the **same request with the same key**: `202` while it runs, then the finished invocation. The run also appears in `GET /v1/agents/{id}/runs?thread_id=…`. Uses credits. Works on draft Agents too (a draft answers but only acts through its `chat` wake-up). ```json { "message": "Summarise yesterday's new leads and email me the top 3.", "background": true } ``` - **Slow:** can take several minutes. Agents should send `background: true` and poll `GET /v1/turns/{id}` (tool `studio_turn_get`). - `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. Parameters: - `id` (path, string (uuid), required) - `message` (body, string, optional) — What to say to the Agent. - `payload` (body, object, optional) — A JSON event for the Agent to act on, instead of or beside a message. - `thread_id` (body, string (uuid), optional) — Continue this conversation (from an earlier invocation). - `stream` (body, boolean, optional) — Answer as server-sent events. - `background` (body, boolean, optional) — Answer 202 at once; poll by repeating the request with the same Idempotency-Key. Request: ```bash curl -sS -X POST "https://app.attria.io/api/v1/agents/00000000-0000-4000-8000-000000000000/runs" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{}' ``` Response `200`: A JSON object; see the description above. #### List an Agent's runs `GET /v1/agents/{id}/runs` · MCP tool `agents_runs_list` The Agent's runs, newest first — every execution of its plan, whether woken by a person talking to it, a schedule, a webhook, an object change or a connected app. `status` is `running`, `waiting` (paused for time, an event or an approval — see `waiting_for`), `completed`, `failed` or `cancelled`. Filter with `status`, or with `thread_id` to find the run of one conversation. Fetch `GET /v1/runs/{id}` for its steps. ``` GET /v1/agents/7c1e…/runs?status=waiting ``` Parameters: - `id` (path, string (uuid), required) - `status` (query, "running" | "waiting" | "completed" | "failed" | "cancelled", optional) — Only runs in this state. - `thread_id` (query, string (uuid), optional) — Only runs started by this conversation. - `limit` (query, integer, optional, default 25) — How many items to return, 1–100. Default 25. - `cursor` (query, string, optional) — The `next_cursor` from the previous page. Omit for the first page. Request: ```bash curl -sS "https://app.attria.io/api/v1/agents/00000000-0000-4000-8000-000000000000/runs" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: A JSON object; see the description above. #### Get a run with its steps `GET /v1/runs/{id}` · MCP tool `runs_get` One Agent run in full: its `status`, the `event` that woke it, every `step` it took in order (each with the plan node's `kind`, its `input`, `result` and `error`), any sub-tasks it delegated (`tasks`, with the tools they called), its `approvals`, what it cost (`cost_usd`) and — for a conversation — the Agent's `answer`. Poll this while `status` is `running` or `waiting`. Parameters: - `id` (path, string (uuid), required) Request: ```bash curl -sS "https://app.attria.io/api/v1/runs/00000000-0000-4000-8000-000000000000" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: A JSON object; see the description above. #### List approvals `GET /v1/approvals` · MCP tool `approvals_list` Actions Agents want to take that need a person's yes — sending an email, spending money, changing or deleting records, depending on each Agent's autonomy. Pending ones first-come-first-listed. Each shows `summary` (a sentence), the exact `input` that will run, its `risk` (`write`, `send`, `spend`, `destructive`) and `can_always_allow`. Decide with `POST /v1/approvals/{id}`. ``` GET /v1/approvals?status=pending&agent_id=7c1e… ``` Parameters: - `status` (query, "pending" | "approved" | "rejected" | "all", optional, default "pending") — Which decisions to list. Default `pending`. - `agent_id` (query, string (uuid), optional) — Only this Agent's approvals. - `limit` (query, integer, optional, default 25) — How many items to return, 1–100. Default 25. - `cursor` (query, string, optional) — The `next_cursor` from the previous page. Omit for the first page. Request: ```bash curl -sS "https://app.attria.io/api/v1/approvals" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: A JSON object; see the description above. #### Approve or reject an Agent action `POST /v1/approvals/{id}` · MCP tool `approvals_decide` · **slow** Decide one pending approval, as the key's owner. - `approve` — the call runs now exactly as shown in the approval's `input`, and a run waiting on it resumes. The answer carries the resumed run's `outcome`. - `reject` — the call never runs. A run waiting on it is cancelled; a call filed from inside a task is closed and the task has already moved on. `always: true` (with `approve`) also lets this Agent take this kind of action without asking again. Only allowed where `can_always_allow` is true. A decided approval answers `409`. ```json { "decision": "approve" } ``` - **Slow:** can take several minutes; keep the connection open (the server allows up to 800 s). Parameters: - `id` (path, string (uuid), required) - `decision` (body, "approve" | "reject", required) — `approve` runs the action; `reject` refuses it. - `always` (body, boolean, optional, default false) — With `approve`: remember this action for this Agent. Request: ```bash curl -sS -X POST "https://app.attria.io/api/v1/approvals/00000000-0000-4000-8000-000000000000" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"decision":"approve"}' ``` Response `200`: A JSON object; see the description above. ### Objects The workspace's objects: definitions, records, queries and relationships. #### List Objects `GET /v1/objects` · MCP tool `objects_list` Every Object type in the workspace, each with its fields. Two kinds: - `custom` — record systems the workspace made (Deals, Candidates, Properties…). Their records are read and written under `/v1/objects/{key}/records`. - `system` — Attria's built-in Objects (keys `agents`, `websites`, `image_boards`, `video_boards`, `files`). They can be related to, and carry extra custom fields, but their records belong to their own feature. A field's `key` is what record `values` use; `type` says what a value may be; `options` lists the choices of a `select`, `multi_select` or `stage` field (write either the option's `id` or its `label`). Parameters: - `kind` (query, "custom" | "system", optional) — Only this kind. - `limit` (query, integer, optional, default 25) — How many items to return, 1–100. Default 25. - `cursor` (query, string, optional) — The `next_cursor` from the previous page. Omit for the first page. Request: ```bash curl -sS "https://app.attria.io/api/v1/objects" \ -H "Authorization: Bearer $ATTRIA_API_KEY" ``` Response `200`: A JSON object; see the description above. #### Create an Object `POST /v1/objects` · MCP tool `objects_create` Make a new record system — the same thing the Objects screen's **New object** makes, with a Table view. The Object's `key` is the plural name in snake_case (`Sales leads` → `sales_leads`); if that key is taken the answer is `409` naming the existing Object, so a retried create never makes a twin. Each field needs a `label` and a `type` (`text`, `long_text`, `number`, `currency`, `date` (YYYY-MM-DD), `datetime` (ISO 8601), `boolean`, `email`, `phone`, `url`, `select`, `multi_select`, `stage`, `image`, `file`). Choice types need `options`. `title_field` names the field that titles a record (default: the first). ```json { "singular_name": "Deal", "plural_name": "Deals", "fields": [ { "label": "Name", "type": "text", "required": true }, { "label": "Value", "type": "currency" }, { "label": "Stage", "type": "stage", "options": ["New", "Proposal", "Won", "Lost"] }, { "label": "Contact email", "type": "email" } ] } ``` Parameters: - `singular_name` (body, string, required) — One record: `Deal`. - `plural_name` (body, string, required) — Many: `Deals`. Also makes the key. - `description` (body, string, optional, default "") - `icon` (body, string, optional, default "Box") — A Lucide icon name, e.g. `Briefcase`. - `color` (body, string, optional, default "#64748b") - `title_field` (body, string, optional) — Key of the field that titles a record. Default: the first field. - `fields` (body, object[], required) Request: ```bash curl -sS -X POST "https://app.attria.io/api/v1/objects" \ -H "Authorization: Bearer $ATTRIA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"singular_name":"","plural_name":"","fields":[{"label":"