{
  "openapi": "3.1.0",
  "info": {
    "title": "Attria API",
    "version": "1.0.0",
    "summary": "Drive Attria — Studio, Agents and Objects — from code or an AI agent.",
    "description": "Everything a request makes lands in the key owner's workspace and is billed to it in credits.\n\n- **Auth:** `Authorization: Bearer attria_sk_…` (Settings → API).\n- **Errors:** always `{ \"error\": { \"type\", \"message\", \"details?\" }, \"request_id\" }`.\n- **Writes:** send `Idempotency-Key` so a retry replays instead of repeating.\n- **Agents:** read the guide at https://app.attria.io/api/v1/llms-full.txt, or connect the MCP server at https://app.attria.io/api/v1/mcp with the same key."
  },
  "servers": [
    {
      "url": "https://app.attria.io/api",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Studio",
      "description": "Make and edit websites, documents, apps, images and video by talking to the same Studio director the product uses. Each message is a turn."
    },
    {
      "name": "Agents",
      "description": "List, create, update and run the workspace's agents; follow their runs, steps and approvals."
    },
    {
      "name": "Objects",
      "description": "The workspace's objects: definitions, records, queries and relationships."
    },
    {
      "name": "Account",
      "description": "Who a key acts as, the workspace it reaches, and what the account can use."
    }
  ],
  "externalDocs": {
    "description": "Guide for AI agents",
    "url": "https://app.attria.io/api/v1/llms-full.txt"
  },
  "paths": {
    "/v1/me": {
      "get": {
        "operationId": "account_me",
        "tags": [
          "Account"
        ],
        "summary": "Who this key acts as",
        "description": "The workspace and member this API key acts as, and the key itself. Use it to check a key works.",
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "workspace": {
                    "id": "6f1c2a9e-3b7d-4e0a-9c51-2d8e7f4a1b30",
                    "name": "Acme Marketing",
                    "timezone": "Europe/Lisbon"
                  },
                  "member": {
                    "id": "b0e4d7c2-91a3-4f68-8d25-7c3e1a9f0b44",
                    "role": "owner"
                  },
                  "key": {
                    "id": "3a9d5e17-c2b8-4f0e-a6d1-94e2b7c8f013",
                    "name": "Claude Code",
                    "prefix": "attria_sk_Ab12Cd",
                    "created_at": "2026-09-27T14:02:11.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "account_me"
      }
    },
    "/v1/studio/website/turns": {
      "post": {
        "operationId": "studio_website_turn",
        "tags": [
          "Studio"
        ],
        "summary": "Talk to the Website Director — build or change a website",
        "description": "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.\n\nIf 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.\n\nWith `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`).\n\nExample: `{ \"message\": \"Build a landing page for Harbor Dental, a family dentist in Portland. Goal: book a first cleaning.\", \"auto\": true }`\n\n**The answer** (also what `GET /v1/turns/{id}` returns):\n\n| field | |\n|---|---|\n| `id` | The turn. Poll it with `GET /v1/turns/{id}`. |\n| `status` | `completed` · `needs_input` (answer `questions`/`outline` with another turn, same `conversation_id` or `artifact_id`) · `building` (still working — poll) · `failed` (see `error`). |\n| `artifact` | `{ id, type, name, url, public_url }` — `url` opens it in Attria; `public_url` is set once it is public. |\n| `conversation_id` | The director conversation this turn belongs to. |\n| `message` | The director's own words. |\n| `questions` / `outline` | What it needs from you when `status` is `needs_input`. |\n\nSend 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.\n\n**Slow:** can take several minutes. Agents should send `background: true` and poll `GET /v1/turns/{id}` (tool `studio_turn_get`).\n\n`auto: true` lets Attria decide instead of answering with `status: \"needs_input\"` (questions or an outline to confirm).\n\n`stream: true` answers with Server-Sent Events; the last event is `result` (the same body a waiting call returns) or `error`.\n\n`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.\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 3800,
                    "description": "What you would type to this Studio's director, in plain words. Answers to its questions go here too."
                  },
                  "artifact_id": {
                    "description": "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.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "conversation_id": {
                    "description": "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.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "auto": {
                    "default": false,
                    "description": "\"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.",
                    "type": "boolean"
                  },
                  "stream": {
                    "default": false,
                    "description": "Answer as server-sent events: `turn`, `plan`, `status`, then `result` (the same body a waited-for call returns).",
                    "type": "boolean"
                  },
                  "background": {
                    "default": false,
                    "description": "Answer 202 as soon as the turn is claimed and keep working; poll `GET /v1/turns/{id}`.",
                    "type": "boolean"
                  }
                },
                "required": [
                  "message"
                ]
              },
              "example": {
                "message": "Build a landing page for Harbor Dental, a family dentist in Portland. Goal: book a first cleaning.",
                "auto": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-Sent Events when the body has `stream: true`. The last event is `result` or `error`."
                }
              }
            }
          },
          "202": {
            "description": "Accepted — sent with `background: true`; poll for the result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-attria-slow": true,
        "x-mcp-tool": "studio_website_turn"
      }
    },
    "/v1/studio/document/turns": {
      "post": {
        "operationId": "studio_document_turn",
        "tags": [
          "Studio"
        ],
        "summary": "Talk to the Documents director — write or change a deck, proposal or document",
        "description": "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.\n\nBy 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.\n\nPut 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}`.\n\nExample: `{ \"message\": \"A 6-slide investor update for Q3: revenue $412k (+38% QoQ), 3 enterprise logos signed, runway 22 months, hiring 2 engineers.\", \"auto\": true }`\n\n**The answer** (also what `GET /v1/turns/{id}` returns):\n\n| field | |\n|---|---|\n| `id` | The turn. Poll it with `GET /v1/turns/{id}`. |\n| `status` | `completed` · `needs_input` (answer `questions`/`outline` with another turn, same `conversation_id` or `artifact_id`) · `building` (still working — poll) · `failed` (see `error`). |\n| `artifact` | `{ id, type, name, url, public_url }` — `url` opens it in Attria; `public_url` is set once it is public. |\n| `conversation_id` | The director conversation this turn belongs to. |\n| `message` | The director's own words. |\n| `questions` / `outline` | What it needs from you when `status` is `needs_input`. |\n\nSend 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.\n\n**Slow:** can take several minutes. Agents should send `background: true` and poll `GET /v1/turns/{id}` (tool `studio_turn_get`).\n\n`auto: true` lets Attria decide instead of answering with `status: \"needs_input\"` (questions or an outline to confirm).\n\n`stream: true` answers with Server-Sent Events; the last event is `result` (the same body a waiting call returns) or `error`.\n\n`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.\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 18000,
                    "description": "What you would type to this Studio's director, in plain words. Answers to its questions go here too."
                  },
                  "artifact_id": {
                    "description": "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.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "conversation_id": {
                    "description": "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.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "auto": {
                    "default": false,
                    "description": "\"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.",
                    "type": "boolean"
                  },
                  "stream": {
                    "default": false,
                    "description": "Answer as server-sent events: `turn`, `plan`, `status`, then `result` (the same body a waited-for call returns).",
                    "type": "boolean"
                  },
                  "background": {
                    "default": false,
                    "description": "Answer 202 as soon as the turn is claimed and keep working; poll `GET /v1/turns/{id}`.",
                    "type": "boolean"
                  }
                },
                "required": [
                  "message"
                ]
              },
              "example": {
                "message": "A 6-slide investor update for Q3: revenue $412k (+38% QoQ), 3 enterprise logos signed, runway 22 months, hiring 2 engineers.",
                "auto": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-Sent Events when the body has `stream: true`. The last event is `result` or `error`."
                }
              }
            }
          },
          "202": {
            "description": "Accepted — sent with `background: true`; poll for the result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-attria-slow": true,
        "x-mcp-tool": "studio_document_turn"
      }
    },
    "/v1/studio/app/turns": {
      "post": {
        "operationId": "studio_app_turn",
        "tags": [
          "Studio"
        ],
        "summary": "Talk to the App Builder — build or change a workspace app",
        "description": "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.**\n\nSay 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.\n\nExample: `{ \"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 }`\n\n**The answer** (also what `GET /v1/turns/{id}` returns):\n\n| field | |\n|---|---|\n| `id` | The turn. Poll it with `GET /v1/turns/{id}`. |\n| `status` | `completed` · `needs_input` (answer `questions`/`outline` with another turn, same `conversation_id` or `artifact_id`) · `building` (still working — poll) · `failed` (see `error`). |\n| `artifact` | `{ id, type, name, url, public_url }` — `url` opens it in Attria; `public_url` is set once it is public. |\n| `conversation_id` | The director conversation this turn belongs to. |\n| `message` | The director's own words. |\n| `questions` / `outline` | What it needs from you when `status` is `needs_input`. |\n\nSend 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.\n\n**Slow:** can take several minutes. Agents should send `background: true` and poll `GET /v1/turns/{id}` (tool `studio_turn_get`).\n\n`auto: true` lets Attria decide instead of answering with `status: \"needs_input\"` (questions or an outline to confirm).\n\n`stream: true` answers with Server-Sent Events; the last event is `result` (the same body a waiting call returns) or `error`.\n\n`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.\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 18000,
                    "description": "What you would type to this Studio's director, in plain words. Answers to its questions go here too."
                  },
                  "artifact_id": {
                    "description": "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.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "conversation_id": {
                    "description": "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.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "auto": {
                    "default": false,
                    "description": "\"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.",
                    "type": "boolean"
                  },
                  "stream": {
                    "default": false,
                    "description": "Answer as server-sent events: `turn`, `plan`, `status`, then `result` (the same body a waited-for call returns).",
                    "type": "boolean"
                  },
                  "background": {
                    "default": false,
                    "description": "Answer 202 as soon as the turn is claimed and keep working; poll `GET /v1/turns/{id}`.",
                    "type": "boolean"
                  }
                },
                "required": [
                  "message"
                ]
              },
              "example": {
                "message": "A job tracker for our crew: list open jobs by date, mark a job done, and add notes to a job.",
                "auto": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-Sent Events when the body has `stream: true`. The last event is `result` or `error`."
                }
              }
            }
          },
          "202": {
            "description": "Accepted — sent with `background: true`; poll for the result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-attria-slow": true,
        "x-mcp-tool": "studio_app_turn"
      }
    },
    "/v1/studio/image/turns": {
      "post": {
        "operationId": "studio_image_turn",
        "tags": [
          "Studio"
        ],
        "summary": "Talk to the Images director — make images on a board",
        "description": "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.\n\nThe 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.\n\nExample: `{ \"message\": \"Two scroll-stopping images for a summer iced-latte promo, bright and minimal\", \"auto\": true }`\n\n**The answer** (also what `GET /v1/turns/{id}` returns):\n\n| field | |\n|---|---|\n| `id` | The turn. Poll it with `GET /v1/turns/{id}`. |\n| `status` | `completed` · `needs_input` (answer `questions`/`outline` with another turn, same `conversation_id` or `artifact_id`) · `building` (still working — poll) · `failed` (see `error`). |\n| `artifact` | `{ id, type, name, url, public_url }` — `url` opens it in Attria; `public_url` is set once it is public. |\n| `conversation_id` | The director conversation this turn belongs to. |\n| `message` | The director's own words. |\n| `questions` / `outline` | What it needs from you when `status` is `needs_input`. |\n\nSend 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.\n\n**Slow:** can take several minutes. Agents should send `background: true` and poll `GET /v1/turns/{id}` (tool `studio_turn_get`).\n\n`auto: true` lets Attria decide instead of answering with `status: \"needs_input\"` (questions or an outline to confirm).\n\n`stream: true` answers with Server-Sent Events; the last event is `result` (the same body a waiting call returns) or `error`.\n\n`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.\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 3800,
                    "description": "What you would type to this Studio's director, in plain words. Answers to its questions go here too."
                  },
                  "artifact_id": {
                    "description": "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.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "conversation_id": {
                    "description": "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.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "auto": {
                    "default": false,
                    "description": "\"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.",
                    "type": "boolean"
                  },
                  "stream": {
                    "default": false,
                    "description": "Answer as server-sent events: `turn`, `plan`, `status`, then `result` (the same body a waited-for call returns).",
                    "type": "boolean"
                  },
                  "background": {
                    "default": false,
                    "description": "Answer 202 as soon as the turn is claimed and keep working; poll `GET /v1/turns/{id}`.",
                    "type": "boolean"
                  }
                },
                "required": [
                  "message"
                ]
              },
              "example": {
                "message": "Two scroll-stopping images for a summer iced-latte promo, bright and minimal",
                "auto": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-Sent Events when the body has `stream: true`. The last event is `result` or `error`."
                }
              }
            }
          },
          "202": {
            "description": "Accepted — sent with `background: true`; poll for the result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-attria-slow": true,
        "x-mcp-tool": "studio_image_turn"
      }
    },
    "/v1/studio/video/turns": {
      "post": {
        "operationId": "studio_video_turn",
        "tags": [
          "Studio"
        ],
        "summary": "Talk to the Video director — edit a video project",
        "description": "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.**\n\nThings 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}`.\n\nExample: `{ \"artifact_id\": \"…\", \"message\": \"Tighten the intro to 3 seconds and add a title card that says 'Summer Sale'\" }`\n\n**The answer** (also what `GET /v1/turns/{id}` returns):\n\n| field | |\n|---|---|\n| `id` | The turn. Poll it with `GET /v1/turns/{id}`. |\n| `status` | `completed` · `needs_input` (answer `questions`/`outline` with another turn, same `conversation_id` or `artifact_id`) · `building` (still working — poll) · `failed` (see `error`). |\n| `artifact` | `{ id, type, name, url, public_url }` — `url` opens it in Attria; `public_url` is set once it is public. |\n| `conversation_id` | The director conversation this turn belongs to. |\n| `message` | The director's own words. |\n| `questions` / `outline` | What it needs from you when `status` is `needs_input`. |\n\nSend 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.\n\n**Slow:** can take several minutes. Agents should send `background: true` and poll `GET /v1/turns/{id}` (tool `studio_turn_get`).\n\n`auto: true` lets Attria decide instead of answering with `status: \"needs_input\"` (questions or an outline to confirm).\n\n`stream: true` answers with Server-Sent Events; the last event is `result` (the same body a waiting call returns) or `error`.\n\n`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.\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 3800,
                    "description": "What you would type to this Studio's director, in plain words. Answers to its questions go here too."
                  },
                  "artifact_id": {
                    "description": "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.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "conversation_id": {
                    "description": "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.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "auto": {
                    "default": false,
                    "description": "\"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.",
                    "type": "boolean"
                  },
                  "stream": {
                    "default": false,
                    "description": "Answer as server-sent events: `turn`, `plan`, `status`, then `result` (the same body a waited-for call returns).",
                    "type": "boolean"
                  },
                  "background": {
                    "default": false,
                    "description": "Answer 202 as soon as the turn is claimed and keep working; poll `GET /v1/turns/{id}`.",
                    "type": "boolean"
                  }
                },
                "required": [
                  "message"
                ]
              },
              "example": {
                "artifact_id": "2d4c…",
                "message": "Tighten the intro to 3 seconds and add a title card that says 'Summer Sale'"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-Sent Events when the body has `stream: true`. The last event is `result` or `error`."
                }
              }
            }
          },
          "202": {
            "description": "Accepted — sent with `background: true`; poll for the result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-attria-slow": true,
        "x-mcp-tool": "studio_video_turn"
      }
    },
    "/v1/turns/{id}": {
      "get": {
        "operationId": "studio_turn_get",
        "tags": [
          "Studio"
        ],
        "summary": "Get a Studio turn",
        "description": "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.\n\nExample: `GET /v1/turns/5b7e2c8e-…`",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "studio_turn_get"
      }
    },
    "/v1/studio/apps": {
      "get": {
        "operationId": "studio_apps_list",
        "tags": [
          "Studio"
        ],
        "summary": "List the Studio's Content Apps",
        "description": "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.\n\nUse 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.\n\nFree and instant.",
        "parameters": [
          {
            "name": "studio",
            "in": "query",
            "required": false,
            "description": "Only this studio's apps.",
            "schema": {
              "description": "Only this studio's apps.",
              "type": "string",
              "enum": [
                "image",
                "video"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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"
                        ]
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "studio_apps_list"
      }
    },
    "/v1/studio/{studio}/apps/{app}": {
      "post": {
        "operationId": "studio_app_run",
        "tags": [
          "Studio"
        ],
        "summary": "Run a Studio Content App",
        "description": "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).\n\n**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.\n\n**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).\n\n**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`.\n\nApps 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.\n\nExample — `POST /v1/studio/image/apps/product-photo`:\n```json\n{ \"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\" }\n```\nExample — `POST /v1/studio/video/apps/generate`:\n```json\n{ \"project_id\": \"c41d…\", \"prompt\": \"A close handheld shot of a runner tying bright red shoes before sunrise\", \"model\": \"standard\", \"seconds\": 6 }\n```\n\n**Slow:** can take several minutes; keep the connection open (the server allows up to 800 s).\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "name": "studio",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "image",
                "video"
              ]
            }
          },
          {
            "name": "app",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "board_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "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": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "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": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 6000,
                    "description": "What to make or change, in plain language."
                  },
                  "direction": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000,
                    "description": "Optional extra direction."
                  },
                  "aspect": {
                    "type": "string",
                    "enum": [
                      "4:5",
                      "1:1",
                      "16:9",
                      "9:16",
                      "4:3",
                      "3:4",
                      "21:9"
                    ],
                    "description": "Output aspect ratio."
                  },
                  "count": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 4,
                    "description": "How many results to make (1–4). Each result is charged."
                  },
                  "style": {
                    "type": "string",
                    "enum": [
                      "none",
                      "cinematic",
                      "anime",
                      "photorealistic",
                      "digital-art",
                      "oil-painting",
                      "watercolor",
                      "3d-render"
                    ],
                    "description": "Visual style."
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "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"
                    ],
                    "description": "Creative format (ad structure). `auto` lets Attria choose."
                  },
                  "model": {
                    "type": "string",
                    "enum": [
                      "auto",
                      "gemini-3-pro-image",
                      "gemini-3.1-flash-image",
                      "seedream-5-0-pro",
                      "gpt-5.4-image-2",
                      "draft",
                      "standard",
                      "cinema",
                      "seedance",
                      "kling"
                    ],
                    "description": "Model. Image apps: an image model id (`auto` = Attria chooses). Video Generate: a shot tier."
                  },
                  "reference_asset_ids": {
                    "maxItems": 4,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid",
                      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                    },
                    "description": "Up to 4 image asset ids on `board_id` to guide the result."
                  },
                  "source_asset_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "The asset to work from (an image for image apps; a video clip of at most 15 seconds for Recast/Remake)."
                  },
                  "product_asset_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "A clean photo of the product."
                  },
                  "subject_asset_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "The person or product to place."
                  },
                  "scene_asset_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "The destination scene or second subject."
                  },
                  "identity_asset_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "A clear image of the new person or character."
                  },
                  "portrait_asset_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "A front-facing portrait with one clearly visible person."
                  },
                  "creator_asset_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "Optional creator reference or opening frame."
                  },
                  "start_frame_asset_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "Image asset the shot starts from."
                  },
                  "end_frame_asset_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "Image asset the shot ends on (needs `start_frame_asset_id`)."
                  },
                  "product": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500,
                    "description": "The product: its name and what it does."
                  },
                  "offer": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000,
                    "description": "The product and offer the ad is built around."
                  },
                  "audience": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500,
                    "description": "Who it is for."
                  },
                  "angle": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500,
                    "description": "The angle or offer to lead with."
                  },
                  "hook": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500,
                    "description": "The creator's opening line."
                  },
                  "creator": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 300,
                    "description": "Who is speaking, e.g. \"a credible solo agency owner filming at their desk\"."
                  },
                  "ad_format": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "The ad's format, e.g. \"cinematic demo\", \"tabletop showcase\", \"comparison\"."
                  },
                  "script": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 400,
                    "description": "Exactly what the presenter says (at most 38 words)."
                  },
                  "seconds": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 60,
                    "description": "Length in seconds."
                  },
                  "sound": {
                    "type": "boolean",
                    "description": "Generate native audio with the shot."
                  },
                  "camera": {
                    "type": "string",
                    "enum": [
                      "auto",
                      "static",
                      "push-in",
                      "pull-back",
                      "orbit",
                      "pan",
                      "tilt-up",
                      "tracking",
                      "handheld",
                      "drone",
                      "crash-zoom"
                    ],
                    "description": "Camera move. `auto` lets the model choose."
                  },
                  "direct": {
                    "type": "boolean",
                    "description": "Let Attria rewrite the description as a proper shot before filming (default true)."
                  }
                },
                "additionalProperties": {}
              },
              "example": {
                "prompt": "A matte black insulated travel mug on a sunlit café counter",
                "aspect": "4:5",
                "count": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-attria-slow": true,
        "x-mcp-tool": "studio_app_run"
      }
    },
    "/v1/websites": {
      "get": {
        "operationId": "websites_list",
        "tags": [
          "Studio"
        ],
        "summary": "List websites",
        "description": "The websites made in Website Studio, most recently changed first.\n\n`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}`.\n\n```\nGET /v1/websites?limit=10\n```",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return, 1–100. Default 25.",
            "schema": {
              "default": 25,
              "description": "How many items to return, 1–100. Default 25.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous page. Omit for the first page.",
            "schema": {
              "description": "The `next_cursor` from the previous page. Omit for the first page.",
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "websites_list"
      }
    },
    "/v1/websites/{id}": {
      "get": {
        "operationId": "websites_get",
        "tags": [
          "Studio"
        ],
        "summary": "Get a website and its build",
        "description": "One website with its **build status** — poll this after a website turn answered `building`.\n\n- `build.status`: `building` (queued or running; `build.stage` says which step), `ready` (built), `failed` (`build.error` says why), or `not_started`.\n- `status`: the site itself — `draft`, `built`, `live` or `paused`. `public_url` is set while it is `live`.\n- `url` opens it in Website Studio.\n\nA new site usually builds in 2–5 minutes; an edit's rebuild in 1–3. Poll every 5–10 seconds.\n\n```\nGET /v1/websites/0c1d2e3f-…\n```",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "websites_get"
      }
    },
    "/v1/documents": {
      "get": {
        "operationId": "documents_list",
        "tags": [
          "Studio"
        ],
        "summary": "List documents",
        "description": "The decks, proposals and documents made in Documents Studio, most recently changed first. Summaries only — fetch `GET /v1/documents/{id}` for the content.\n\n`kind` is `deck`, `proposal` or `document`. `public_url` is set while the document is shared by link.\n\n```\nGET /v1/documents?limit=10\n```",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return, 1–100. Default 25.",
            "schema": {
              "default": 25,
              "description": "How many items to return, 1–100. Default 25.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous page. Omit for the first page.",
            "schema": {
              "description": "The `next_cursor` from the previous page. Omit for the first page.",
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "documents_list"
      }
    },
    "/v1/documents/{id}": {
      "get": {
        "operationId": "documents_get",
        "tags": [
          "Studio"
        ],
        "summary": "Get a document with its full content",
        "description": "One document with its **full structured content** — what the Studio renders:\n\n- `content.layout` — `slides` (a deck: one section per slide) or `page` (a scrolling document).\n- `content.theme` — the look.\n- `content.sections[]` — each with an `id`, an `intent` (`cover`, `narrative`, `pricing`, `stats`, …) and `blocks[]` (headings, text, lists, images, stats, pricing tables, buttons…).\n\nTo change it, send a turn with `artifact_id` set to this id (`POST /v1/studio/document/turns`).\n\n```\nGET /v1/documents/a3c2…\n```",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "documents_get"
      }
    },
    "/v1/apps": {
      "get": {
        "operationId": "apps_list",
        "tags": [
          "Studio"
        ],
        "summary": "List apps",
        "description": "The workspace apps made in Apps Studio, most recently changed first.\n\n`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.\n\n```\nGET /v1/apps\n```",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return, 1–100. Default 25.",
            "schema": {
              "default": 25,
              "description": "How many items to return, 1–100. Default 25.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous page. Omit for the first page.",
            "schema": {
              "description": "The `next_cursor` from the previous page. Omit for the first page.",
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "apps_list"
      }
    },
    "/v1/apps/{id}": {
      "get": {
        "operationId": "apps_get",
        "tags": [
          "Studio"
        ],
        "summary": "Get an app",
        "description": "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`.\n\nTo change it, send a turn with `artifact_id` set to this id (`POST /v1/studio/app/turns`).\n\n```\nGET /v1/apps/6e2f…\n```",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "apps_get"
      }
    },
    "/v1/images/boards": {
      "get": {
        "operationId": "images_boards_list",
        "tags": [
          "Studio"
        ],
        "summary": "List image boards",
        "description": "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`.\n\n```\nGET /v1/images/boards\n```",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return, 1–100. Default 25.",
            "schema": {
              "default": 25,
              "description": "How many items to return, 1–100. Default 25.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous page. Omit for the first page.",
            "schema": {
              "description": "The `next_cursor` from the previous page. Omit for the first page.",
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "images_boards_list"
      }
    },
    "/v1/images": {
      "get": {
        "operationId": "images_list",
        "tags": [
          "Studio"
        ],
        "summary": "List images",
        "description": "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`).\n\n```\nGET /v1/images?board_id=b1f0…&limit=20\n```",
        "parameters": [
          {
            "name": "board_id",
            "in": "query",
            "required": false,
            "description": "Only images on this board.",
            "schema": {
              "description": "Only images on this board.",
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return, 1–100. Default 25.",
            "schema": {
              "default": 25,
              "description": "How many items to return, 1–100. Default 25.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous page. Omit for the first page.",
            "schema": {
              "description": "The `next_cursor` from the previous page. Omit for the first page.",
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "images_list"
      }
    },
    "/v1/videos": {
      "get": {
        "operationId": "videos_list",
        "tags": [
          "Studio"
        ],
        "summary": "List video projects",
        "description": "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.\n\n```\nGET /v1/videos\n```",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return, 1–100. Default 25.",
            "schema": {
              "default": 25,
              "description": "How many items to return, 1–100. Default 25.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous page. Omit for the first page.",
            "schema": {
              "description": "The `next_cursor` from the previous page. Omit for the first page.",
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "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
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "videos_list"
      }
    },
    "/v1/agents": {
      "get": {
        "operationId": "agents_list",
        "tags": [
          "Agents"
        ],
        "summary": "List Agents",
        "description": "The workspace's Agents, oldest first. Archived Agents are left out unless you ask for them with `status=archived`.\n\nEach item is a summary (`id`, `name`, `status`, `active_version_id`, timestamps). Fetch `GET /v1/agents/{id}` for the plan, wake-ups and schedule.\n\n`status` is one of `draft` (never turned on), `active` (listening for its wake-ups), `paused`, `archived`.\n\n```\nGET /v1/agents?status=active&limit=10\n```",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only Agents in this state.",
            "schema": {
              "description": "Only Agents in this state.",
              "type": "string",
              "enum": [
                "draft",
                "active",
                "paused",
                "archived"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return, 1–100. Default 25.",
            "schema": {
              "default": 25,
              "description": "How many items to return, 1–100. Default 25.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous page. Omit for the first page.",
            "schema": {
              "description": "The `next_cursor` from the previous page. Omit for the first page.",
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "agents_list"
      },
      "post": {
        "operationId": "agents_create",
        "tags": [
          "Agents"
        ],
        "summary": "Create an Agent from a description",
        "description": "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`.\n\nThe answer is an `agent_build`:\n\n- `status: \"built\"` — the plan was drafted; `agent` is the full Agent and `message` is the builder's summary.\n- `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.\n\nUses 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.\n\n```json\n{\n  \"name\": \"Lead follow-up\",\n  \"message\": \"When a new Lead object is created, email them a friendly intro within 5 minutes and notify me.\",\n  \"auto\": true\n}\n```\n\n**Slow:** can take several minutes; keep the connection open (the server allows up to 800 s).\n\n`auto: true` lets Attria decide instead of answering with `status: \"needs_input\"` (questions or an outline to confirm).\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "description": "A name for the Agent. The builder may refine it.",
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 20000,
                    "description": "What the Agent should do, or what to change — plain language, as you would type it in Agent Studio."
                  },
                  "auto": {
                    "default": false,
                    "description": "`true`: the builder must draft the plan now and may not ask questions. `false` (default): it may answer `needs_input` with questions.",
                    "type": "boolean"
                  }
                },
                "required": [
                  "message"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-attria-slow": true,
        "x-mcp-tool": "agents_create"
      }
    },
    "/v1/agents/{id}": {
      "get": {
        "operationId": "agents_get",
        "tags": [
          "Agents"
        ],
        "summary": "Get an Agent",
        "description": "One Agent in full:\n\n- `profile` — purpose, instructions, autonomy (`automatic`, `ask_first`, …), model and tool grants.\n- `plan.nodes` / `plan.edges` — the operating plan as drawn in Agent Studio (`kind` is `agent`, `trigger`, `email`, `ai_decide`, `branch`, …).\n- `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.\n- `schedule` — the scheduled wake-ups and their `next_run_at` (UTC).\n- `validation` — `ready: true` when Activate would succeed; otherwise `issues` says what to fix.\n- `pending_approvals` — how many actions are waiting for a person (see `GET /v1/approvals`).\n\nSecrets never appear: integration credentials are stored encrypted and config values under secret-looking keys are masked.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "agents_get"
      },
      "patch": {
        "operationId": "agents_update",
        "tags": [
          "Agents"
        ],
        "summary": "Change an Agent",
        "description": "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.\n\n- `name` alone renames it (no credits).\n- `message` runs a builder turn and answers with an `agent_build` (`built` or `needs_input`), like create.\n\nChanges edit the **draft**. An active Agent keeps running its activated version until you `activate` again.\n\n```json\n{ \"message\": \"Also send me an SMS when the lead's budget is over $10k.\", \"auto\": true }\n```\n\n**Slow:** can take several minutes; keep the connection open (the server allows up to 800 s).\n\n`auto: true` lets Attria decide instead of answering with `status: \"needs_input\"` (questions or an outline to confirm).\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "description": "A new name.",
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 20000,
                    "description": "What the Agent should do, or what to change — plain language, as you would type it in Agent Studio."
                  },
                  "auto": {
                    "default": false,
                    "description": "`true`: the builder must draft the plan now and may not ask questions. `false` (default): it may answer `needs_input` with questions.",
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-attria-slow": true,
        "x-mcp-tool": "agents_update"
      }
    },
    "/v1/agents/{id}/activate": {
      "post": {
        "operationId": "agents_activate",
        "tags": [
          "Agents"
        ],
        "summary": "Turn an Agent on",
        "description": "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.\n\nAnswers `{ 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.\n\nIf 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.\n\n**Slow:** can take several minutes; keep the connection open (the server allows up to 800 s).\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-attria-slow": true,
        "x-mcp-tool": "agents_activate"
      }
    },
    "/v1/agents/{id}/pause": {
      "post": {
        "operationId": "agents_pause",
        "tags": [
          "Agents"
        ],
        "summary": "Pause an Agent",
        "description": "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.\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "agents_pause"
      }
    },
    "/v1/agents/{id}/runs": {
      "post": {
        "operationId": "agents_runs_create",
        "tags": [
          "Agents"
        ],
        "summary": "Talk to an Agent (run it)",
        "description": "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`.\n\nThe 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.\n\n**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.\n\n**Long runs.**\n\n- `stream: true` — server-sent events: `started` (`{ thread_id }`) at once, then one `result` (the invocation) or `error`.\n- `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=…`.\n\nUses credits. Works on draft Agents too (a draft answers but only acts through its `chat` wake-up).\n\n```json\n{ \"message\": \"Summarise yesterday's new leads and email me the top 3.\", \"background\": true }\n```\n\n**Slow:** can take several minutes. Agents should send `background: true` and poll `GET /v1/turns/{id}` (tool `studio_turn_get`).\n\n`stream: true` answers with Server-Sent Events; the last event is `result` (the same body a waiting call returns) or `error`.\n\n`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.\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "description": "What to say to the Agent.",
                    "type": "string",
                    "maxLength": 20000
                  },
                  "payload": {
                    "description": "A JSON event for the Agent to act on, instead of or beside a message.",
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {}
                  },
                  "thread_id": {
                    "description": "Continue this conversation (from an earlier invocation).",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "stream": {
                    "description": "Answer as server-sent events.",
                    "type": "boolean"
                  },
                  "background": {
                    "description": "Answer 202 at once; poll by repeating the request with the same Idempotency-Key.",
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-Sent Events when the body has `stream: true`. The last event is `result` or `error`."
                }
              }
            }
          },
          "202": {
            "description": "Accepted — sent with `background: true`; poll for the result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-attria-slow": true,
        "x-mcp-tool": "agents_runs_create"
      },
      "get": {
        "operationId": "agents_runs_list",
        "tags": [
          "Agents"
        ],
        "summary": "List an Agent's runs",
        "description": "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.\n\n`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.\n\n```\nGET /v1/agents/7c1e…/runs?status=waiting\n```",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only runs in this state.",
            "schema": {
              "description": "Only runs in this state.",
              "type": "string",
              "enum": [
                "running",
                "waiting",
                "completed",
                "failed",
                "cancelled"
              ]
            }
          },
          {
            "name": "thread_id",
            "in": "query",
            "required": false,
            "description": "Only runs started by this conversation.",
            "schema": {
              "description": "Only runs started by this conversation.",
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return, 1–100. Default 25.",
            "schema": {
              "default": 25,
              "description": "How many items to return, 1–100. Default 25.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous page. Omit for the first page.",
            "schema": {
              "description": "The `next_cursor` from the previous page. Omit for the first page.",
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "agents_runs_list"
      }
    },
    "/v1/runs/{id}": {
      "get": {
        "operationId": "runs_get",
        "tags": [
          "Agents"
        ],
        "summary": "Get a run with its steps",
        "description": "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`.\n\nPoll this while `status` is `running` or `waiting`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "runs_get"
      }
    },
    "/v1/approvals": {
      "get": {
        "operationId": "approvals_list",
        "tags": [
          "Agents"
        ],
        "summary": "List approvals",
        "description": "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.\n\nEach 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}`.\n\n```\nGET /v1/approvals?status=pending&agent_id=7c1e…\n```",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Which decisions to list. Default `pending`.",
            "schema": {
              "default": "pending",
              "description": "Which decisions to list. Default `pending`.",
              "type": "string",
              "enum": [
                "pending",
                "approved",
                "rejected",
                "all"
              ]
            }
          },
          {
            "name": "agent_id",
            "in": "query",
            "required": false,
            "description": "Only this Agent's approvals.",
            "schema": {
              "description": "Only this Agent's approvals.",
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return, 1–100. Default 25.",
            "schema": {
              "default": 25,
              "description": "How many items to return, 1–100. Default 25.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous page. Omit for the first page.",
            "schema": {
              "description": "The `next_cursor` from the previous page. Omit for the first page.",
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "approvals_list"
      }
    },
    "/v1/approvals/{id}": {
      "post": {
        "operationId": "approvals_decide",
        "tags": [
          "Agents"
        ],
        "summary": "Approve or reject an Agent action",
        "description": "Decide one pending approval, as the key's owner.\n\n- `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`.\n- `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.\n\n`always: true` (with `approve`) also lets this Agent take this kind of action without asking again. Only allowed where `can_always_allow` is true.\n\nA decided approval answers `409`.\n\n```json\n{ \"decision\": \"approve\" }\n```\n\n**Slow:** can take several minutes; keep the connection open (the server allows up to 800 s).\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "decision": {
                    "type": "string",
                    "enum": [
                      "approve",
                      "reject"
                    ],
                    "description": "`approve` runs the action; `reject` refuses it."
                  },
                  "always": {
                    "default": false,
                    "description": "With `approve`: remember this action for this Agent.",
                    "type": "boolean"
                  }
                },
                "required": [
                  "decision"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-attria-slow": true,
        "x-mcp-tool": "approvals_decide"
      }
    },
    "/v1/objects": {
      "get": {
        "operationId": "objects_list",
        "tags": [
          "Objects"
        ],
        "summary": "List Objects",
        "description": "Every Object type in the workspace, each with its fields. Two kinds:\n\n- `custom` — record systems the workspace made (Deals, Candidates, Properties…). Their records are read and written under `/v1/objects/{key}/records`.\n- `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.\n\nA 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": [
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Only this kind.",
            "schema": {
              "description": "Only this kind.",
              "type": "string",
              "enum": [
                "custom",
                "system"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return, 1–100. Default 25.",
            "schema": {
              "default": 25,
              "description": "How many items to return, 1–100. Default 25.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous page. Omit for the first page.",
            "schema": {
              "description": "The `next_cursor` from the previous page. Omit for the first page.",
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "objects_list"
      },
      "post": {
        "operationId": "objects_create",
        "tags": [
          "Objects"
        ],
        "summary": "Create an Object",
        "description": "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.\n\nEach 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).\n\n```json\n{\n  \"singular_name\": \"Deal\",\n  \"plural_name\": \"Deals\",\n  \"fields\": [\n    { \"label\": \"Name\", \"type\": \"text\", \"required\": true },\n    { \"label\": \"Value\", \"type\": \"currency\" },\n    { \"label\": \"Stage\", \"type\": \"stage\", \"options\": [\"New\", \"Proposal\", \"Won\", \"Lost\"] },\n    { \"label\": \"Contact email\", \"type\": \"email\" }\n  ]\n}\n```\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "singular_name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80,
                    "description": "One record: `Deal`."
                  },
                  "plural_name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80,
                    "description": "Many: `Deals`. Also makes the key."
                  },
                  "description": {
                    "default": "",
                    "type": "string",
                    "maxLength": 2000
                  },
                  "icon": {
                    "default": "Box",
                    "description": "A Lucide icon name, e.g. `Briefcase`.",
                    "type": "string",
                    "maxLength": 64
                  },
                  "color": {
                    "default": "#64748b",
                    "type": "string",
                    "maxLength": 32
                  },
                  "title_field": {
                    "description": "Key of the field that titles a record. Default: the first field.",
                    "type": "string",
                    "pattern": "^[a-z][a-z0-9_]{0,47}$"
                  },
                  "fields": {
                    "minItems": 1,
                    "maxItems": 100,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "label": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 120,
                          "description": "What people see: `Deal value`."
                        },
                        "key": {
                          "description": "Stable key used in record values. Default: the label in snake_case (`deal_value`).",
                          "type": "string",
                          "pattern": "^[a-z][a-z0-9_]{0,47}$"
                        },
                        "type": {
                          "default": "text",
                          "description": "The field's type.",
                          "type": "string",
                          "enum": [
                            "text",
                            "long_text",
                            "number",
                            "currency",
                            "date",
                            "datetime",
                            "boolean",
                            "email",
                            "phone",
                            "url",
                            "select",
                            "multi_select",
                            "stage",
                            "image",
                            "file"
                          ]
                        },
                        "options": {
                          "description": "For `select`, `multi_select` and `stage`: the choices, as labels or `{ id, label, color }`.",
                          "maxItems": 200,
                          "type": "array",
                          "items": {
                            "anyOf": [
                              {
                                "type": "string",
                                "minLength": 1,
                                "maxLength": 200
                              },
                              {
                                "type": "object",
                                "properties": {
                                  "id": {
                                    "type": "string",
                                    "minLength": 1,
                                    "maxLength": 64
                                  },
                                  "label": {
                                    "type": "string",
                                    "minLength": 1,
                                    "maxLength": 200
                                  },
                                  "color": {
                                    "type": "string",
                                    "maxLength": 32
                                  }
                                },
                                "required": [
                                  "label"
                                ]
                              }
                            ]
                          }
                        },
                        "required": {
                          "default": false,
                          "description": "Records must have a value.",
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "label"
                      ]
                    }
                  }
                },
                "required": [
                  "singular_name",
                  "plural_name",
                  "fields"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "objects_create"
      }
    },
    "/v1/objects/{object}": {
      "get": {
        "operationId": "objects_get",
        "tags": [
          "Objects"
        ],
        "summary": "Get an Object",
        "description": "One Object type and its fields, by key (`deals`) or id. Read this before writing records: it says which `values` keys exist, their types, which are `required`, and the `options` a choice field accepts.",
        "parameters": [
          {
            "name": "object",
            "in": "path",
            "required": true,
            "description": "The Object's key (`deals`) or id.",
            "schema": {
              "type": "string",
              "description": "The Object's key (`deals`) or id."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "objects_get"
      },
      "patch": {
        "operationId": "objects_update",
        "tags": [
          "Objects"
        ],
        "summary": "Change an Object",
        "description": "Rename an Object, or add, change and remove its fields. Only what you send changes.\n\n- `fields` — each item whose `key` already exists **updates** that field (`label`, `type`, `options`, `required`); a new `key` (or a label with no key) **adds** a field.\n- `remove_fields` — keys of custom fields to delete. Their values stay on existing records but are no longer shown or validated.\n\nBuilt-in fields (`origin: system`) cannot be changed. The Object's `key` never changes, so code that uses it keeps working.\n\n```json\n{\n  \"fields\": [\n    { \"key\": \"stage\", \"label\": \"Stage\", \"type\": \"stage\", \"options\": [\"New\", \"Proposal\", \"Negotiation\", \"Won\", \"Lost\"] },\n    { \"label\": \"Close date\", \"type\": \"date\" }\n  ],\n  \"remove_fields\": [\"legacy_notes\"]\n}\n```\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "name": "object",
            "in": "path",
            "required": true,
            "description": "The Object's key (`deals`) or id.",
            "schema": {
              "type": "string",
              "description": "The Object's key (`deals`) or id."
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "singular_name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  },
                  "plural_name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "icon": {
                    "type": "string",
                    "maxLength": 64
                  },
                  "color": {
                    "type": "string",
                    "maxLength": 32
                  },
                  "title_field": {
                    "type": "string",
                    "pattern": "^[a-z][a-z0-9_]{0,47}$"
                  },
                  "fields": {
                    "description": "Fields to change (by existing `key`) or add.",
                    "maxItems": 100,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "key": {
                          "description": "An existing field's key to change it; a new key (or none, with a label) to add one.",
                          "type": "string",
                          "pattern": "^[a-z][a-z0-9_]{0,47}$"
                        },
                        "label": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 120
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "text",
                            "long_text",
                            "number",
                            "currency",
                            "date",
                            "datetime",
                            "boolean",
                            "email",
                            "phone",
                            "url",
                            "select",
                            "multi_select",
                            "stage",
                            "image",
                            "file"
                          ]
                        },
                        "options": {
                          "description": "Replaces the field's choices.",
                          "maxItems": 200,
                          "type": "array",
                          "items": {
                            "anyOf": [
                              {
                                "type": "string",
                                "minLength": 1,
                                "maxLength": 200
                              },
                              {
                                "type": "object",
                                "properties": {
                                  "id": {
                                    "type": "string",
                                    "minLength": 1,
                                    "maxLength": 64
                                  },
                                  "label": {
                                    "type": "string",
                                    "minLength": 1,
                                    "maxLength": 200
                                  },
                                  "color": {
                                    "type": "string",
                                    "maxLength": 32
                                  }
                                },
                                "required": [
                                  "label"
                                ]
                              }
                            ]
                          }
                        },
                        "required": {
                          "type": "boolean"
                        }
                      }
                    }
                  },
                  "remove_fields": {
                    "maxItems": 100,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^[a-z][a-z0-9_]{0,47}$"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "objects_update"
      }
    },
    "/v1/objects/{object}/records": {
      "get": {
        "operationId": "objects_records_list",
        "tags": [
          "Objects"
        ],
        "summary": "List and query records",
        "description": "An Object's records, newest change first. Narrow with:\n\n- `filter` — a JSON object of field `key` → value; every pair must match exactly. Values are normalised as on write (a choice matches by option `id` or `label`; `\"true\"` matches a boolean). For a `multi_select` a list matches records that contain **all** of it. `null` matches an empty field.\n- `search` — text found anywhere in the record's values (case-insensitive).\n- `sort` — `updated_at`, `created_at` or any field key; prefix `-` for descending. Default `-updated_at`.\n\nPage with `limit` and the `next_cursor` of the previous page.\n\n```\nGET /v1/objects/deals/records?filter={\"stage\":\"Won\"}&sort=-value&limit=20\n```",
        "parameters": [
          {
            "name": "object",
            "in": "path",
            "required": true,
            "description": "The Object's key (`deals`) or id.",
            "schema": {
              "type": "string",
              "description": "The Object's key (`deals`) or id."
            }
          },
          {
            "name": "filter",
            "in": "query",
            "required": false,
            "description": "JSON object: `{\"stage\":\"Won\",\"owner_email\":\"ana@acme.com\"}` (URL-encode it).",
            "schema": {
              "description": "JSON object: `{\"stage\":\"Won\",\"owner_email\":\"ana@acme.com\"}` (URL-encode it).",
              "type": "string",
              "maxLength": 4000
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Text to find in any value.",
            "schema": {
              "description": "Text to find in any value.",
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "`updated_at`, `created_at` or a field key; `-` prefix for descending.",
            "schema": {
              "default": "-updated_at",
              "description": "`updated_at`, `created_at` or a field key; `-` prefix for descending.",
              "type": "string",
              "pattern": "^-?[a-z][a-z0-9_]{0,47}$"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return, 1–100. Default 25.",
            "schema": {
              "default": 25,
              "description": "How many items to return, 1–100. Default 25.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous page. Omit for the first page.",
            "schema": {
              "description": "The `next_cursor` from the previous page. Omit for the first page.",
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "objects_records_list"
      },
      "post": {
        "operationId": "objects_records_create",
        "tags": [
          "Objects"
        ],
        "summary": "Create a record",
        "description": "Add one record. `values` is keyed by field `key` and checked against the Object's fields exactly as the app checks them: unknown keys, missing `required` fields and values of the wrong type are refused with `422` and a message naming the field. Choice fields accept an option's `id` or `label` and store the `id`.\n\nSend an `Idempotency-Key`: a retry with the same key returns the record the first request made instead of a duplicate.\n\n```json\n{ \"values\": { \"name\": \"Acme renewal\", \"value\": 12000, \"stage\": \"Proposal\", \"contact_email\": \"ana@acme.com\" } }\n```\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "name": "object",
            "in": "path",
            "required": true,
            "description": "The Object's key (`deals`) or id.",
            "schema": {
              "type": "string",
              "description": "The Object's key (`deals`) or id."
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "values": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {},
                    "description": "Field key → value."
                  }
                },
                "required": [
                  "values"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "objects_records_create"
      }
    },
    "/v1/objects/{object}/records/{id}": {
      "get": {
        "operationId": "objects_records_get",
        "tags": [
          "Objects"
        ],
        "summary": "Get a record",
        "description": "One record of an Object, with all its `values`.",
        "parameters": [
          {
            "name": "object",
            "in": "path",
            "required": true,
            "description": "The Object's key (`deals`) or id.",
            "schema": {
              "type": "string",
              "description": "The Object's key (`deals`) or id."
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "objects_records_get"
      },
      "patch": {
        "operationId": "objects_records_update",
        "tags": [
          "Objects"
        ],
        "summary": "Update a record",
        "description": "Change some of a record's values. Only the keys you send change; every other value is kept. The change is merged in the database under a row lock, so it never overwrites an edit an Agent or a person made a moment earlier. Send `null` to clear a field. Values are validated like a create (`422` names the field).\n\n```json\n{ \"values\": { \"stage\": \"Won\", \"value\": 14500 } }\n```\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "name": "object",
            "in": "path",
            "required": true,
            "description": "The Object's key (`deals`) or id.",
            "schema": {
              "type": "string",
              "description": "The Object's key (`deals`) or id."
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "values": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {},
                    "description": "Field key → new value."
                  }
                },
                "required": [
                  "values"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "objects_records_update"
      },
      "delete": {
        "operationId": "objects_records_delete",
        "tags": [
          "Objects"
        ],
        "summary": "Delete a record",
        "description": "Delete one record, and every relationship that points to or from it. This cannot be undone.\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "name": "object",
            "in": "path",
            "required": true,
            "description": "The Object's key (`deals`) or id.",
            "schema": {
              "type": "string",
              "description": "The Object's key (`deals`) or id."
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "objects_records_delete"
      }
    },
    "/v1/objects/{object}/records/{id}/relationships": {
      "get": {
        "operationId": "objects_relationships_list",
        "tags": [
          "Objects"
        ],
        "summary": "List a record's relationships",
        "description": "Everything this record is linked to, in both directions. Each relationship has a `relation_key` (`account`, `owner`, `mentioned_in`…), a `direction` (`outgoing` when this record is the source) and the `other` end: its Object `key` and its `id`.\n\nWorks for any Object, built-in ones too: `/v1/objects/agents/records/{agent_id}/relationships` lists what an Agent is linked to.",
        "parameters": [
          {
            "name": "object",
            "in": "path",
            "required": true,
            "description": "The Object's key (`deals`) or id.",
            "schema": {
              "type": "string",
              "description": "The Object's key (`deals`) or id."
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "name": "relation_key",
            "in": "query",
            "required": false,
            "description": "Only this kind of link.",
            "schema": {
              "description": "Only this kind of link.",
              "type": "string",
              "pattern": "^[a-z][a-z0-9_]{0,47}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "objects_relationships_list"
      },
      "post": {
        "operationId": "objects_relationships_create",
        "tags": [
          "Objects"
        ],
        "summary": "Link a record to another",
        "description": "Link this record (the source) to another record of any Object (the `target`) under a `relation_key` — `company`, `owner`, `related_to`… — in snake_case. Both ends must be in this workspace. The target can be a built-in Object: `{ \"object\": \"agents\", \"id\": \"<agent id>\" }`.\n\nThe same source, target and `relation_key` is one link: sending it again returns the existing one.\n\n```json\n{ \"relation_key\": \"company\", \"target\": { \"object\": \"companies\", \"id\": \"0b6f…\" }, \"label\": \"Buyer\" }\n```\n\nSend an `Idempotency-Key` header: a retry with the same key replays the first result instead of doing the work twice.",
        "parameters": [
          {
            "name": "object",
            "in": "path",
            "required": true,
            "description": "The Object's key (`deals`) or id.",
            "schema": {
              "type": "string",
              "description": "The Object's key (`deals`) or id."
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "relation_key": {
                    "type": "string",
                    "pattern": "^[a-z][a-z0-9_]{0,47}$",
                    "description": "What the link means, snake_case: `company`."
                  },
                  "target": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "type": "string",
                        "description": "The Object's key (`deals`) or id."
                      },
                      "id": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                        "description": "The target record's id."
                      }
                    },
                    "required": [
                      "object",
                      "id"
                    ]
                  },
                  "label": {
                    "default": "",
                    "type": "string",
                    "maxLength": 200
                  },
                  "metadata": {
                    "default": {},
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {}
                  }
                },
                "required": [
                  "relation_key",
                  "target"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Authentication"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-mcp-tool": "objects_relationships_create"
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "attria_sk_…",
        "description": "An API key from Settings → API: `attria_sk_` followed by 40 letters and digits. It acts as the member who made it, with full access to their workspace."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Any unique string (a UUID) per logical write. Retrying with the same key replays the original result.",
        "schema": {
          "type": "string",
          "maxLength": 255
        }
      }
    },
    "headers": {
      "RequestId": {
        "description": "This request's id; also `request_id` in error bodies.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "request_id"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "type",
              "message"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "authentication",
                  "permission",
                  "not_found",
                  "conflict",
                  "insufficient_credits",
                  "rate_limited",
                  "server_error"
                ]
              },
              "message": {
                "type": "string",
                "description": "Written for the caller; safe to show a person."
              },
              "details": {
                "description": "Field-level problems for `invalid_request` (zod's tree of errors)."
              }
            }
          },
          "request_id": {
            "type": "string",
            "description": "Also in the `X-Request-Id` header. Quote it when reporting a problem."
          }
        },
        "example": {
          "error": {
            "type": "not_found",
            "message": "Not found."
          },
          "request_id": "9b2f0c1e-5d47-4a8e-b3a6-2c1d0e9f8a77"
        }
      }
    },
    "responses": {
      "InvalidRequest": {
        "description": "`invalid_request` — the parameters or body do not match the schema; `details` says which field.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Authentication": {
        "description": "`authentication` — the key is missing, malformed or revoked.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InsufficientCredits": {
        "description": "`insufficient_credits` — the workspace is out of credits. Top up in Settings → Billing.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Permission": {
        "description": "`permission` — the key may not do this.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "`not_found` — no such resource in this key's workspace.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "`conflict` — the resource changed underneath the request, or the Idempotency-Key was reused with a different body.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "`rate_limited` — slow down; honour `Retry-After` when present.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServerError": {
        "description": "`server_error` — our fault. Retry with the same Idempotency-Key.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
