{
  "openapi": "3.1.0",
  "info": {
    "title": "Spiron Agents API",
    "version": "1.0.0",
    "summary": "Create Spiron agents and talk to them from any service.",
    "description": "Every message sent through this API lands in the same agent and the same inbox as a message on any channel: an agent is one continuous mind. Authenticate with a Spiron API key (`Authorization: Bearer spiron_sk_...`). Errors are RFC 9457 problem details (`application/problem+json`). Every POST accepts an `Idempotency-Key`.",
    "contact": {
      "name": "Spiron",
      "url": "https://spiron.ai"
    }
  },
  "servers": [
    {
      "url": "https://api.spiron.ai"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Agents",
      "description": "Create, read and change agents."
    },
    {
      "name": "Messages",
      "description": "Talk to an agent. A message enters the agent's one inbox, like a message on any channel, and opens a task."
    },
    {
      "name": "Interactions",
      "description": "Every kind of parked interaction: the approvals an agent parks (harness D4), and the questions, forms, requests, proposals and reviews it asks (D16)."
    },
    {
      "name": "Console tasks",
      "description": "Work a declared ops key hands an agent to do on its own, outside its conversation: queued, run in its own session, and ended by the agent."
    },
    {
      "name": "Takeovers",
      "description": "Requests from an agent for a person to take over its computer, browser or device."
    },
    {
      "name": "Budget",
      "description": "The owner's monthly spend cap, and the Goals paused when a budget is reached."
    },
    {
      "name": "Webhooks",
      "description": "HTTPS endpoints that receive replies as Standard Webhooks."
    },
    {
      "name": "API keys",
      "description": "Keys that authenticate a service. Managed by a signed-in workspace owner or admin."
    },
    {
      "name": "Sandbox",
      "description": "Calls from code running in an agent's own sandbox. Not for other services: the credential exists only inside a sandbox exec."
    },
    {
      "name": "Device authorization",
      "description": "RFC 8628 device authorization: an agent or CLI gets a scoped API key after a workspace owner or admin approves a code on spiron.ai/device."
    },
    {
      "name": "Agent transfers",
      "description": "Give an agent to another person's workspace with its memory, skills, channels and history. Offered and accepted by signed-in people, never by a key."
    },
    {
      "name": "Automations",
      "description": "The owner's controls for an agent's automations: list, pause, resume, delete."
    },
    {
      "name": "Cron jobs",
      "description": "The owner's controls for an agent's cron jobs: list, enable, disable, delete."
    },
    {
      "name": "Channels",
      "description": "Where an agent is reachable: create a connection, connect it to its provider, and set who may message it. Writes need an owner or admin of the agent's workspace (a key acts with its creator's standing)."
    },
    {
      "name": "Contact directory",
      "description": "The workspace's own contact directory: the people, organisations and services its agents know, every channel identity of each, and the relationships between them."
    }
  ],
  "paths": {
    "/v1/agents": {
      "get": {
        "operationId": "listAgents",
        "tags": [
          "Agents"
        ],
        "summary": "List agents",
        "description": "Needs `spiron:agents.read`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of agents.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      },
      "post": {
        "operationId": "createAgent",
        "tags": [
          "Agents"
        ],
        "summary": "Create an agent",
        "description": "Needs `spiron:agents.write` and a key made by a workspace owner or admin.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The agent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyReused"
          }
        }
      }
    },
    "/v1/agents/{agent_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        }
      ],
      "get": {
        "operationId": "getAgent",
        "tags": [
          "Agents"
        ],
        "summary": "Get an agent",
        "description": "Needs `spiron:agents.read`.",
        "responses": {
          "200": {
            "description": "The agent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateAgent",
        "tags": [
          "Agents"
        ],
        "summary": "Change an agent",
        "description": "Needs `spiron:agents.write`. Only the fields sent change.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The agent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      },
      "delete": {
        "operationId": "deleteAgent",
        "tags": [
          "Agents"
        ],
        "summary": "Delete an agent",
        "description": "Needs `spiron:agents.write`. The agent is archived: it stops answering on every channel and its history stays.",
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "object",
                    "deleted"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "object": {
                      "const": "agent"
                    },
                    "deleted": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/messages": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        }
      ],
      "post": {
        "operationId": "sendMessage",
        "tags": [
          "Messages"
        ],
        "summary": "Send a message to an agent",
        "description": "Needs `spiron:agents.write`. The message enters the agent's one inbox on the caller's `api` connection. How the answer comes back: `Prefer: wait=N` (the default, 30 s, at most 120 s) answers 200 with the completed task, or 202 with the task so far; `Prefer: respond-async` answers 202 at once; `Accept: text/event-stream` streams the task's events (see `GET /v1/tasks/{task_id}/events`). The `Idempotency-Key` is also the message's id: a retry is admitted once and answered with the same task.\n\n**End users as subjects.** Without `end_user` the caller writes as itself. With `end_user` the message is the end user's: the sender is the end user (one per connection and id), its trust is `outsider` (outsiders are answered by the agent's front desk, never by the agent's own mind), the end user is resolved to a directory contact carrying `identities`, and the conversation's audience is the end user plus the caller. `labels` are what you labelled the case (`title:HKMT`, `locale:zh-HK`); they are stored on the conversation and are what a workspace's reply grants and knowledge sources are scoped by.\n\n**Staff items.** `role: staff_reply` with `author` records the business's own answer on the case: it is delivered to the end user's conversation, it never wakes the agent, and its task is complete at once. `role: staff_note` with `author` is the business's own note about the case: its audience is staff-only (the end user never reads it) and it wakes the agent for that case.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "Prefer",
            "in": "header",
            "required": false,
            "description": "`wait=N` or `respond-async` (RFC 7240).",
            "schema": {
              "type": "string",
              "examples": [
                "wait=60",
                "respond-async"
              ]
            }
          },
          {
            "name": "Accept",
            "in": "header",
            "required": false,
            "description": "`text/event-stream` to stream.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The turn ended within the wait: the completed task with its reply.",
            "headers": {
              "Preference-Applied": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                },
                "description": "When `Accept: text/event-stream`."
              }
            }
          },
          "202": {
            "description": "Accepted: the task so far. Read it with `GET /v1/tasks/{task_id}`.",
            "headers": {
              "Preference-Applied": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyReused"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/tasks": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        }
      ],
      "post": {
        "operationId": "submitConsoleTask",
        "tags": [
          "Console tasks"
        ],
        "summary": "Submit a console task",
        "description": "Needs `spiron:agents.write`, and a key the company declared as an ops key (`SPIRON_OPS_API_KEY_IDS`) or a signed-in workspace owner or admin; any other key is 403, whatever its scopes. A console task is run on its own, in its own session, outside the agent's conversation: it is queued (one running task per agent, highest `priority` first, then oldest), told the spec as an instruction, and ends when the agent calls `tasks.complete`. `sites` and `vault_items` only narrow what the task may reach: a credential is typed, or a value kept, only on a listed site and for a listed item. The `Idempotency-Key` is also the task's id key for ever: a retry, of any age, answers `200` with the same task. Answers `404` while console tasks are off.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConsoleTaskCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The task this `Idempotency-Key` already named.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsoleTask"
                }
              }
            }
          },
          "202": {
            "description": "Queued: the task so far.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsoleTask"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      },
      "get": {
        "operationId": "listConsoleTasks",
        "tags": [
          "Console tasks"
        ],
        "summary": "List an agent's console tasks",
        "description": "Needs `spiron:agents.read`. Newest first.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "submitted",
                "working",
                "input_required",
                "completed",
                "failed",
                "canceled"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The tasks.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsoleTaskList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/tasks/health": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        }
      ],
      "get": {
        "operationId": "getConsoleTaskHealth",
        "tags": [
          "Console tasks"
        ],
        "summary": "Read the health of an agent's console tasks",
        "description": "Needs `spiron:agents.read`, and a key the company declared as an ops key (`SPIRON_OPS_API_KEY_IDS`) or a signed-in workspace owner or admin; any other key is 403, whatever its scopes. Shows the queue depth by priority and the oldest queued age, the running tasks with their progress age (the last journaled step, never a lease timer), the tasks parked on a person with their age, the recent failures and settled tasks, the conditions that would alert now, and the agent's mind mailbox backlog beside the task queue, so a stalled mind next to a flowing queue is visible. Answers `404` while console tasks are off.",
        "responses": {
          "200": {
            "description": "The health of the agent's console tasks.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsoleTaskHealth"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/tasks/{task_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TaskId"
        }
      ],
      "get": {
        "operationId": "getTask",
        "tags": [
          "Messages"
        ],
        "summary": "Get a task",
        "description": "Needs `spiron:agents.read`. Only the caller that sent the message can read its task. A console task (see `POST /v1/agents/{agent_id}/tasks`) is read here too, by any key of its workspace; it answers as a `ConsoleTask`.",
        "responses": {
          "200": {
            "description": "The task.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/tasks/{task_id}/events": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TaskId"
        }
      ],
      "get": {
        "operationId": "streamTask",
        "tags": [
          "Messages"
        ],
        "summary": "Stream a task's events",
        "description": "Server-Sent Events for one task, ending after it is final (`completed`, `canceled` or `failed`). Durable events carry an `id`; send `Last-Event-ID` to resume after it. One effect, one event: `task.updated` (`{task_id, status}`, and the full task last), `message.completed` (`{task_id, output}`: the reply to deliver, whose `output.id` is its idempotency key), `interaction.requested` (`{task_id, output}`: a card the agent drew), and `interaction.resolved` (`{task_id, output}`: the answer that settled one). While the agent works, live events without an `id` show its work: `text.delta`, `tool.started`, `tool.completed` and `progress`. A reconnect may repeat live events; drop repeats by `event_id`.",
        "parameters": [
          {
            "name": "Last-Event-ID",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The event stream.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                },
                "description": "Server-Sent Events, ending after the task is final (`completed`, `canceled` or `failed`). Durable events have an `id`; send `Last-Event-ID` to resume after it. One effect, one event: `task.updated` (`{task_id, status}`, and the full task on the last one), `message.completed` (`{task_id, output}`: the reply to deliver, whose `output.id` is its idempotency key), `interaction.requested` (`{task_id, output}`: a card the agent drew — `output` is the interaction item below), and `interaction.resolved` (`{task_id, output}`: the answer that settled one). While the agent works, live events without an `id` show its work: `text.delta` (`{task_id, round, offset, text}`: the reply as it is written; place `text` at `offset` within the round's text; provisional until `message.completed`), `tool.started` (`{task_id, event_id, call_id, tool, input, at}`), `tool.completed` (`{task_id, event_id, call_id, tool, duration_ms, output, at}`), and `progress` (`{task_id, event_id, kind, data, at}`: a step or thinking line). A reconnect may repeat live events; drop repeats by `event_id`. A comment line keeps the connection open."
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/tasks/{task_id}:cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TaskId"
        }
      ],
      "post": {
        "operationId": "cancelTask",
        "tags": [
          "Messages"
        ],
        "summary": "Cancel a task",
        "description": "Needs `spiron:agents.write`. The same stop a person sends on a channel: the agent pauses the work the message started, and its running turn stops at its next step. Repeating it changes nothing. A completed task is 409. A console task is cancelled the same way, with the standing a console task needs (a declared ops key, or a signed-in owner or admin): a queued or parked task is `canceled` at once, a running one at its next step, and a task that already settled is answered as it is.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The task, `canceled`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/tasks/{task_id}:park": {
      "parameters": [{"$ref": "#/components/parameters/TaskId"}],
      "post": {
        "operationId": "parkTask",
        "tags": ["Messages"],
        "summary": "Park a task on typed waits",
        "description": "Needs spiron:agents.write on the caller's admitted task. Commits the typed awaits and task.parked delivery together. A reason-only park is 400; a changed park id or session pin is 409. Work alone settles the waits and resumes this same task and session.",
        "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TaskPark"}}}},
        "responses": {
          "200": {"description": "The task, parked or replayed.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Task"}}}},
          "400": {"description": "Missing or invalid typed await."},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/Forbidden"},
          "404": {"$ref": "#/components/responses/NotFound"},
          "409": {"$ref": "#/components/responses/Conflict"},
          "503": {"$ref": "#/components/responses/Unavailable"}
        }
      }
    },
    "/v1/connect/work/{org_id}": {
      "post": {
        "operationId": "resumeTaskFromWork",
        "tags": ["Messages"],
        "summary": "Receive a committed Work task settlement",
        "description": "Standard Webhooks signed CloudEvents 1.0. data.event.data carries the exact task_id, session_id, park_id, awaits, mode and resolutions. Event ids and all three engine ids fence durable same-task resumption. Replayed, obsolete or deadline-expired parks are acknowledged without execution.",
        "security": [],
        "parameters": [{"name": "org_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}}],
        "requestBody": {"required": true, "content": {"application/cloudevents+json": {"schema": {"type": "object"}}}},
        "responses": {
          "200": {"description": "Resume journaled, or an obsolete or duplicate park acknowledged."},
          "400": {"description": "Malformed event."},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"description": "Connector is not configured."},
          "409": {"$ref": "#/components/responses/Conflict"},
          "503": {"$ref": "#/components/responses/Unavailable"}
        }
      }
    },
    "/v1/tasks/{task_id}:resume": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TaskId"
        }
      ],
      "post": {
        "operationId": "resumeConsoleTask",
        "tags": [
          "Console tasks"
        ],
        "summary": "Resume a console task",
        "description": "The custom method `{id}:resume`. Needs `spiron:agents.write` and the same standing as submitting. Resumes a task that is `input_required` (parked on a person, after a takeover, an approval or an ID check): it goes back on the queue and continues in the same session, with the optional `note` in front of it. A task that is not waiting is 409. A takeover handed back, or an interaction answered, resumes it without this call.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "note": {
                    "type": "string",
                    "maxLength": 1000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The task, back on the queue.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsoleTask"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/conversations": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        }
      ],
      "get": {
        "operationId": "listConversations",
        "tags": [
          "Messages"
        ],
        "summary": "List your conversations with an agent",
        "description": "Needs `spiron:agents.read`. Only the caller's own conversations, newest first. For a signed-in person this includes their dashboard chat threads from before the API (ids `web-…`).",
        "responses": {
          "200": {
            "description": "The caller's conversations.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversationList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/conversations/{conversation_id}/messages": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        },
        {
          "name": "conversation_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "listConversationMessages",
        "tags": [
          "Messages"
        ],
        "summary": "Read a conversation",
        "description": "Needs `spiron:agents.read`. Each message the caller sent, then the replies the agent addressed to it. A page holds the newest `limit` messages the caller sent (default 50) with their replies, oldest first; `next_cursor` names the page before it. The response carries a weak `ETag`: send it back as `If-None-Match` to get `304` when nothing changed. `Server-Timing` reports the server's time.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many of the caller's messages one page holds.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "before",
            "in": "query",
            "description": "A `next_cursor` from a previous page: read the messages older than it.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "304": {
            "description": "The page is unchanged since the `If-None-Match` tag."
          },
          "400": {
            "description": "`limit` or `before` is not a value this API accepts."
          },
          "200": {
            "description": "The transcript.",
            "headers": {
              "ETag": {
                "description": "A weak validator for this page.",
                "schema": {
                  "type": "string"
                }
              },
              "Server-Timing": {
                "description": "The server's time: `db` and `total`, in milliseconds.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/conversations/{conversation_id}/events": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        },
        {
          "name": "conversation_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "streamConversationEvents",
        "tags": [
          "Messages"
        ],
        "summary": "Stream a conversation",
        "description": "Needs `spiron:agents.read`. Server-Sent Events for one conversation, whoever starts a turn: the caller's own messages, a turn from another tab or device, a proactive reply, a wake. Events: `message.created` (the caller's message or a reply, by transcript row `id`), `interaction.requested`, `interaction.resolved`, `task.updated`, `turn.started`, `turn.ended`, `text.delta` (`turn`, `round`, `offset`, `text`: the reply as it is written), `tool.started`, `tool.completed` and `progress`. Every frame carries `id`, one opaque cursor; reconnect with it as `Last-Event-ID` and the stream replays exactly what follows, text included (kept for 15 minutes). A cursor this API did not issue is `400`.",
        "parameters": [
          {
            "name": "Last-Event-ID",
            "in": "header",
            "description": "The `id` of the last frame received.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "An event stream.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "`Last-Event-ID` is not a cursor this API issued."
          },
          "404": {
            "description": "No such agent."
          }
        }
      }
    },
    "/v1/agents/{agent_id}/conversations/{conversation_id}/recap": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        },
        {
          "name": "conversation_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getConversationRecap",
        "tags": [
          "Messages"
        ],
        "summary": "Where a conversation stands",
        "description": "Needs `spiron:agents.read`. What the agent is doing, what waits on you, what finished while you were away, the next steps and the recent turns of one of your conversations. `recap` is null until the agent has taken a message.",
        "responses": {
          "200": {
            "description": "The recap.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "conversation",
                    "agent_id",
                    "recap"
                  ],
                  "properties": {
                    "object": {
                      "const": "conversation_recap"
                    },
                    "conversation": {
                      "type": "string"
                    },
                    "agent_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "recap": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "`narrative`, `watermark`, `recently {turns}`, `waiting_on_user`, `running_now`, `while_away {completed_work, artifacts, events}`, `next_steps`, `generated_at`."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/interactions": {
      "get": {
        "operationId": "listInteractions",
        "tags": [
          "Interactions"
        ],
        "summary": "List interactions",
        "description": "Needs `spiron:agents.read`. The workspace's interactions, newest first: `status=pending` is the queue still waiting on a person. Filter by `kind` too, and page with `limit`/`cursor`. A pending card whose deadline has passed reads as `expired` and leaves the pending queue.",
        "parameters": [
          {
            "name": "agent",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only this status. `pending` leaves out cards whose `expires_at` has passed (they read as `expired`), and `expired` names them.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "answered",
                "granted",
                "consumed",
                "denied",
                "accepted",
                "declined",
                "expired",
                "cancelled",
                "escalated"
              ]
            }
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "approval",
                "question",
                "form",
                "request",
                "proposal",
                "review"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 200,
              "default": 50,
              "description": "Rows per page, at most 200."
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "`next_cursor` of the page before."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of interactions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InteractionList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    },
    "/v1/interactions/{interaction_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/InteractionId"
        }
      ],
      "get": {
        "operationId": "getInteraction",
        "tags": [
          "Interactions"
        ],
        "summary": "Read one interaction",
        "description": "Needs `spiron:agents.read`. Any kind and status; the caller's workspace only.",
        "responses": {
          "200": {
            "description": "The interaction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Interaction"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "parameters": []
      }
    },
    "/v1/interactions/{interaction_id}:answer": {
      "parameters": [
        {
          "$ref": "#/components/parameters/InteractionId"
        }
      ],
      "post": {
        "operationId": "answerInteraction",
        "tags": [
          "Interactions"
        ],
        "summary": "Answer",
        "description": "The custom method `{id}:answer`. Needs a key with `spiron:approvals.decide` or a signed-in owner or admin, and goes through the same authority the buttons on any channel use, so a card tapped on Telegram and a card answered here write the same row and wake the same work. An approval takes `decision`; a question, form, request, proposal or review takes `answer`. `answered_by` names the person who decided it (harness E1); without one the key's own identity is recorded.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The settled interaction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Interaction"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnswerInteractionRequest"
              }
            }
          }
        }
      }
    },
    "/v1/interactions/{interaction_id}:cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/InteractionId"
        }
      ],
      "post": {
        "operationId": "withdrawInteraction",
        "tags": [
          "Interactions"
        ],
        "summary": "Withdraw",
        "description": "The custom method `{id}:cancel`: withdraw a pending interaction without a decision, to clear a stray card. Needs a key with `spiron:approvals.withdraw` (or a signed-in owner or admin); `spiron:approvals.decide` does not open it, and withdrawing can never approve, deny or answer. The card becomes `cancelled` with `cancel_reason` `operator_withdrawn` (the state the agent's own `channel_ask_cancel` writes), its message on the channel is edited to say an operator withdrew it, the agent wakes to a cancellation (not a denial), and who, when and why are recorded. Repeating it is the same `200`. An interaction already answered, decided or expired is `409`, and so is a `short_id` that is not the card's current generation.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The settled interaction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Interaction"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WithdrawInteractionRequest"
              }
            }
          }
        }
      }
    },
    "/v1/contact-directory/export": {
      "get": {
        "operationId": "exportContactDirectory",
        "tags": [
          "Contact directory"
        ],
        "summary": "Export the contact directory",
        "description": "One page of the caller's whole workspace contact directory, archived and rejected records included: every contact, then every identity, then every relationship, each in id order, at most `limit` rows per page. Needs a key holding `spiron:directory.read`, which a key gets only when it is named at creation (never by default), or a signed-in workspace owner or admin. The workspace is always the caller's: no parameter or cursor names another, and a cursor from another workspace is `400`. Concatenate `contacts`, `identities` and `relationships` of every page until `next_cursor` is `null`; every page of one export carries the same `source_org_id`, `generation` and `inventory`. Each page reads one consistent snapshot, and if the directory changes between pages the next page is `409` `generation_changed`: start again without `cursor` rather than mix two versions. Responses carry `Cache-Control: no-store`.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100,
              "description": "Rows per page across all three arrays, 1 to 500."
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "`next_cursor` of the page before, unchanged. Omit it to start an export."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the export.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactDirectoryExportPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "The directory changed after the export started (`generation_changed`). Start again without `cursor`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhooks",
        "responses": {
          "200": {
            "description": "Live subscriptions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "createWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Subscribe an endpoint",
        "description": "Needs `spiron:agents.write`. The webhook belongs to the calling key: every reply an agent addresses to one of that key's conversations is POSTed to the endpoint as a Standard Webhook (`webhook-id`, `webhook-timestamp`, `webhook-signature`, signed with the returned `secret`), and retried until it answers 2xx. Body: `{type, timestamp, data: {agent_id, connection_id, conversation, output}}`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The subscription, with its secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookCreated"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyReused"
          }
        }
      }
    },
    "/v1/webhooks/{webhook_id}": {
      "parameters": [
        {
          "name": "webhook_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "operationId": "deleteWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Remove a webhook",
        "responses": {
          "200": {
            "description": "Removed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "const": "webhook"
                    },
                    "deleted": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/api-keys": {
      "get": {
        "operationId": "listApiKeys",
        "tags": [
          "API keys"
        ],
        "summary": "List API keys",
        "description": "Signed-in owner or admin only; a key cannot list keys.",
        "responses": {
          "200": {
            "description": "Every key of the workspace, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "createApiKey",
        "tags": [
          "API keys"
        ],
        "summary": "Create an API key",
        "description": "Signed-in owner or admin only. The secret `key` is returned once.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiKeyCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The key, with its secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreated"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyReused"
          }
        }
      }
    },
    "/v1/api-keys/{key_id}": {
      "parameters": [
        {
          "name": "key_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "operationId": "revokeApiKey",
        "tags": [
          "API keys"
        ],
        "summary": "Revoke an API key",
        "responses": {
          "200": {
            "description": "Revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "object",
                    "revoked"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "object": {
                      "const": "api_key"
                    },
                    "revoked": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/sandbox/capability-calls": {
      "post": {
        "operationId": "callSandboxCapability",
        "tags": [
          "Sandbox"
        ],
        "summary": "Call one of the agent's capabilities from its sandbox",
        "description": "A sandbox's nested `tools.*` call. The capability must be in the session's or program run's grants and its arguments must match the capability schema.",
        "security": [
          {
            "sandboxToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "capabilityId"
                ],
                "properties": {
                  "capabilityId": {
                    "type": "string"
                  },
                  "capabilityVersion": {
                    "type": "string"
                  },
                  "arguments": {
                    "type": "object"
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Malformed call, or arguments that do not match the capability schema.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "description": "Machine-readable reason, for example `unauthorized` or `not_granted`."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No valid sandbox transport token.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "description": "Machine-readable reason, for example `unauthorized` or `not_granted`."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The capability is not granted to this session or run.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "description": "Machine-readable reason, for example `unauthorized` or `not_granted`."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The program run does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "description": "Machine-readable reason, for example `unauthorized` or `not_granted`."
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The program run is not in a state that can call capabilities.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "description": "Machine-readable reason, for example `unauthorized` or `not_granted`."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/device/code": {
      "post": {
        "operationId": "startDeviceAuthorization",
        "tags": [
          "Device authorization"
        ],
        "summary": "Start a device authorization",
        "description": "No authentication. Only `spiron:agents.read` and `spiron:agents.write` can be asked for; any other scope is `invalid_scope`. The person opens `verification_uri_complete`, signs in and approves.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceCodeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The codes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceCode"
                }
              }
            }
          },
          "400": {
            "description": "An RFC 8628 error: `authorization_pending`, `slow_down` (with a longer `interval`), `access_denied`, `expired_token`, `invalid_grant`, `invalid_request` or `unsupported_grant_type`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceError"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/device/token": {
      "post": {
        "operationId": "pollDeviceAuthorization",
        "tags": [
          "Device authorization"
        ],
        "summary": "Exchange a device code or rotate device access",
        "description": "No authentication header. A device_code grant polls at interval seconds until approval. A refresh_token grant rotates both tokens without widening scope or changing the Settings key id. Access expires in one hour; refresh authorization ends thirty days after approval. Reusing a consumed refresh token revokes the connection. Accepts JSON or a form body.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceTokenRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/DeviceTokenRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceToken"
                }
              }
            }
          },
          "400": {
            "description": "An RFC 8628 error: `authorization_pending`, `slow_down` (with a longer `interval`), `access_denied`, `expired_token`, `invalid_grant`, `invalid_request` or `unsupported_grant_type`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceError"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/device/grants/{user_code}": {
      "parameters": [
        {
          "name": "user_code",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The code as shown, XXXX-XXXX."
        }
      ],
      "get": {
        "operationId": "getDeviceGrant",
        "tags": [
          "Device authorization"
        ],
        "summary": "Read a device grant",
        "description": "Signed-in session only; a key cannot read grants. For the approval page.",
        "responses": {
          "200": {
            "description": "The grant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceGrant"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/device/grants/{user_code}/approve": {
      "parameters": [
        {
          "name": "user_code",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The code as shown, XXXX-XXXX."
        }
      ],
      "post": {
        "operationId": "approveDeviceGrant",
        "tags": [
          "Device authorization"
        ],
        "summary": "Approve a device grant",
        "description": "Signed-in workspace owner or admin only. Repeating an approval is a no-op; an expired, denied or used code is `409`.",
        "responses": {
          "200": {
            "description": "The grant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceGrant"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/device/grants/{user_code}/deny": {
      "parameters": [
        {
          "name": "user_code",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The code as shown, XXXX-XXXX."
        }
      ],
      "post": {
        "operationId": "denyDeviceGrant",
        "tags": [
          "Device authorization"
        ],
        "summary": "Deny a device grant",
        "description": "Signed-in workspace owner or admin only. Repeating a denial is a no-op.",
        "responses": {
          "200": {
            "description": "The grant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceGrant"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/takeovers": {
      "get": {
        "operationId": "listTakeovers",
        "tags": [
          "Takeovers"
        ],
        "summary": "List takeover requests",
        "description": "Needs `spiron:agents.read`. The workspace's computer takeover requests still waiting on a person (`requested`, deadline not passed), soonest deadline first. Opening one is the web page `/app/computer?agent=<agent_id>&surface=<surface>&takeover=<id>`.",
        "responses": {
          "200": {
            "description": "The waiting takeover requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TakeoverList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/budget-pauses": {
      "get": {
        "operationId": "listBudgetPauses",
        "tags": [
          "Budget"
        ],
        "summary": "List Goals paused at a budget",
        "description": "Needs `spiron:agents.read`. The workspace's Goals parked as \"paused: budget reached — raise or resume\", newest first: the owner's spend cap or an owner-set task budget was reached. Nothing is posted to a channel; this list is what the Needs-you view reads.",
        "responses": {
          "200": {
            "description": "The paused Goals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BudgetPauseList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/budget-pauses/{goal_id}/resume": {
      "post": {
        "operationId": "resumeBudgetPause",
        "tags": [
          "Budget"
        ],
        "summary": "Resume a Goal paused at a budget",
        "description": "Needs `spiron:approvals.decide` (a workspace owner or admin for a signed-in person). Continues the Goal. A Goal parked by the spend cap resumes only while usage is under the cap (`409` otherwise: raise the cap first); a Goal parked by its task budget resumes only with that budget raised: `raise_task_budget_micros` above zero (`400` otherwise).",
        "parameters": [
          {
            "name": "goal_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "raise_task_budget_micros": {
                    "type": "integer",
                    "minimum": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The Goal is active again.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "object": {
                      "const": "budget_pause"
                    },
                    "status": {
                      "const": "resumed"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/spend-cap": {
      "get": {
        "operationId": "getSpendCap",
        "tags": [
          "Budget"
        ],
        "summary": "Read the monthly spend cap",
        "description": "Needs `spiron:agents.read`. The authenticated workspace's monthly spend cap and usage in customer-retail micros, resolved plan tier and billing window, and seven complete UTC days of aggregate billing receipts. A total daily peak is null when overlapping months contain legacy usage that cannot be attributed to days. No upstream-price-zero claim is made.",
        "responses": {
          "200": {
            "description": "The cap and usage.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpendCap"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "put": {
        "operationId": "setSpendCap",
        "tags": [
          "Budget"
        ],
        "summary": "Set or clear the monthly spend cap",
        "description": "Needs `spiron:approvals.decide` (a workspace owner or admin for a signed-in person). `null` clears the owner's cap, so the plan's limit applies. When usage reaches the owner's cap, model calls stop and open Goals are paused. Raising the cap above usage resumes the Goals the cap paused (`resumed_goals`).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "spend_cap_micros"
                ],
                "properties": {
                  "spend_cap_micros": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The new cap, usage and the Goals resumed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpendCap"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/goal-budgets": {
      "get": {
        "operationId": "listGoalBudgets",
        "tags": [
          "Budget"
        ],
        "summary": "List Goal task budgets",
        "description": "Needs `spiron:agents.read`. The workspace's open Goals that carry an owner-set task budget, with what each has spent. Read-only.",
        "responses": {
          "200": {
            "description": "The budgeted Goals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GoalBudgetList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/agents/{agent_id}:transfer": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        }
      ],
      "post": {
        "operationId": "transferAgent",
        "tags": [
          "Agent transfers"
        ],
        "summary": "Offer an agent to a person",
        "description": "The custom method `{id}:transfer`. A signed-in owner or admin of the agent's workspace only; an API key is refused. Opens one offer (at most one per agent) that the person accepts within 7 days at `accept_url`. The response carries the one-time `token` once and is not replayed through `Idempotency-Key`; to get a new token, cancel the offer and make another.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransferAgentRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The open offer, with its token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentTransfer"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    },
    "/v1/agent-transfers:accept": {
      "post": {
        "operationId": "acceptAgentTransfer",
        "tags": [
          "Agent transfers"
        ],
        "summary": "Accept an agent",
        "description": "The person the offer names, signed in (not a key), takes the agent into a workspace they own or administer. One transaction moves the agent with its memory, skills, channels, schedules and history; standing approvals and the old workspace's roles on the agent are revoked, so the new owner approves its actions from now on. Refused (409) while the agent holds a computer, or an erasure of it is open, or the receiving workspace already has an agent with the same slug.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AcceptAgentTransferRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The accepted transfer with its receipt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentTransfer"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    },
    "/v1/agent-transfers/{transfer_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TransferId"
        }
      ],
      "get": {
        "operationId": "getAgentTransfer",
        "tags": [
          "Agent transfers"
        ],
        "summary": "Read a transfer",
        "description": "An owner or admin of the giving or the receiving workspace.",
        "responses": {
          "200": {
            "description": "The transfer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentTransfer"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/agent-transfers/{transfer_id}:cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TransferId"
        }
      ],
      "post": {
        "operationId": "cancelAgentTransfer",
        "tags": [
          "Agent transfers"
        ],
        "summary": "Cancel an offer",
        "description": "An owner or admin of the giving workspace withdraws a pending offer; its token stops working.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The offer, `cancelled`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentTransfer"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/automations": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        }
      ],
      "get": {
        "operationId": "listAutomations",
        "tags": [
          "Automations"
        ],
        "summary": "List an agent's automations",
        "description": "Needs `spiron:agents.read` and an owner or admin of the agent's workspace (a key acts with its creator's standing). Newest edit first, at most 100.",
        "responses": {
          "200": {
            "description": "The automations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Automation"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/automations/{automation_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        },
        {
          "$ref": "#/components/parameters/AutomationId"
        }
      ],
      "delete": {
        "operationId": "deleteAutomation",
        "tags": [
          "Automations"
        ],
        "summary": "Delete an automation",
        "description": "Needs `spiron:agents.write` and an owner or admin of the agent's workspace. Acts as the owner, so it deletes an automation the owner paused too.",
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "const": "automation"
                    },
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "deleted": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/automations/{automation_id}:pause": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        },
        {
          "$ref": "#/components/parameters/AutomationId"
        }
      ],
      "post": {
        "operationId": "pauseAutomation",
        "tags": [
          "Automations"
        ],
        "summary": "Pause an automation",
        "description": "Needs `spiron:agents.write` and an owner or admin of the agent's workspace. The pause is the owner's (`pause_reason` `owner`): the agent cannot lift it.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The automation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Automation"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/automations/{automation_id}:resume": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        },
        {
          "$ref": "#/components/parameters/AutomationId"
        }
      ],
      "post": {
        "operationId": "resumeAutomation",
        "tags": [
          "Automations"
        ],
        "summary": "Resume an automation",
        "description": "Needs `spiron:agents.write` and an owner or admin of the agent's workspace. Lifts any pause, including the owner's one an agent transfer sets, and clears the failure count.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The automation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Automation"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/cron-jobs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        }
      ],
      "get": {
        "operationId": "listCronJobs",
        "tags": [
          "Cron jobs"
        ],
        "summary": "List an agent's cron jobs",
        "description": "Needs `spiron:agents.read` and an owner or admin of the agent's workspace (a key acts with its creator's standing). At most 200.",
        "responses": {
          "200": {
            "description": "The cron jobs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CronJob"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/cron-jobs/{cron_job_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        },
        {
          "$ref": "#/components/parameters/CronJobId"
        }
      ],
      "delete": {
        "operationId": "deleteCronJob",
        "tags": [
          "Cron jobs"
        ],
        "summary": "Delete a cron job",
        "description": "Needs `spiron:agents.write` and an owner or admin of the agent's workspace.",
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "const": "cron_job"
                    },
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "deleted": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/cron-jobs/{cron_job_id}:enable": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        },
        {
          "$ref": "#/components/parameters/CronJobId"
        }
      ],
      "post": {
        "operationId": "enableCronJob",
        "tags": [
          "Cron jobs"
        ],
        "summary": "Enable a cron job",
        "description": "Needs `spiron:agents.write` and an owner or admin of the agent's workspace. Turns on a job an agent transfer disabled. The next run is read from now, so missed runs are not fired. A one-off whose time has passed, or a finished job, is `409`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The cron job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CronJob"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/cron-jobs/{cron_job_id}:disable": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        },
        {
          "$ref": "#/components/parameters/CronJobId"
        }
      ],
      "post": {
        "operationId": "disableCronJob",
        "tags": [
          "Cron jobs"
        ],
        "summary": "Disable a cron job",
        "description": "Needs `spiron:agents.write` and an owner or admin of the agent's workspace.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The cron job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CronJob"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/channels": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        }
      ],
      "get": {
        "operationId": "listChannels",
        "tags": [
          "Channels"
        ],
        "summary": "List an agent's channels",
        "description": "Needs `spiron:agents.read`.",
        "responses": {
          "200": {
            "description": "The channels.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ChannelConnection"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "operationId": "createChannel",
        "tags": [
          "Channels"
        ],
        "summary": "Create a channel connection",
        "description": "Needs `spiron:agents.write` and an owner or admin of the agent's workspace (a key acts with its creator's standing). The connection starts pending; connect it next.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChannelConnectionCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The channel.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelConnection"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyReused"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/channels/{channel_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        },
        {
          "$ref": "#/components/parameters/ChannelId"
        }
      ],
      "get": {
        "operationId": "getChannel",
        "tags": [
          "Channels"
        ],
        "summary": "Get a channel connection",
        "description": "Needs `spiron:agents.read`.",
        "responses": {
          "200": {
            "description": "The channel.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelConnection"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateChannel",
        "tags": [
          "Channels"
        ],
        "summary": "Change a channel connection",
        "description": "Needs `spiron:agents.write` and an owner or admin of the agent's workspace (a key acts with its creator's standing). Only the fields sent change; `policy` replaces who may message it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChannelConnectionUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The channel.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelConnection"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/channels/{channel_id}:prepare": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        },
        {
          "$ref": "#/components/parameters/ChannelId"
        }
      ],
      "post": {
        "operationId": "prepareChannel",
        "tags": [
          "Channels"
        ],
        "summary": "Read a channel's callback URL and minted secrets",
        "description": "Needs `spiron:agents.write` and an owner or admin of the agent's workspace (a key acts with its creator's standing). Not replayed through `Idempotency-Key`: the answer carries secrets, which are never stored with a response.",
        "responses": {
          "200": {
            "description": "The setup.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelSetup"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/agents/{agent_id}/channels/{channel_id}:connect": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        },
        {
          "$ref": "#/components/parameters/ChannelId"
        }
      ],
      "post": {
        "operationId": "connectChannel",
        "tags": [
          "Channels"
        ],
        "summary": "Connect a channel to its provider",
        "description": "Needs `spiron:agents.write` and an owner or admin of the agent's workspace (a key acts with its creator's standing). Stores the options and credentials, validates them with the provider, and activates the connection. For email with no mailbox key yet, starts the device grant: the answer carries `authorization`; call connect again once the owner approves.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChannelConnect"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The connect step's result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelConnectResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyReused"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "spiron_sk_<64 hex>",
        "description": "A Spiron API key, or a signed-in Spiron session token. Sylphx Access keys are refused."
      },
      "sandboxToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sct_<token> | pct_<token>",
        "description": "The short-lived transport token Spiron mints for one sandbox exec (a session sandbox, or a program run), scoped to that session's or run's granted capabilities. API keys and service keys are refused."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Makes a retry safe: the first response is kept for 24 hours and replayed (with `Idempotent-Replayed: true`) for the same request. The same key with a different request is 422.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        }
      },
      "AgentId": {
        "name": "agent_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "TaskId": {
        "name": "task_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "InteractionId": {
        "name": "interaction_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 20
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "`next_cursor` from the previous page.",
        "schema": {
          "type": "string"
        }
      },
      "TransferId": {
        "name": "transfer_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "The transfer offer."
      },
      "AutomationId": {
        "name": "automation_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "The automation's id."
      },
      "CronJobId": {
        "name": "cron_job_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "The cron job's id."
      },
      "ChannelId": {
        "name": "channel_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "The channel connection's id."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "No valid API key or session.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key lacks a scope (`missing_scope`), or the caller lacks the standing (`forbidden`).",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such resource in this workspace. A resource of another workspace is also 404.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Invalid": {
        "description": "The request is malformed (`invalid_request`).",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Conflict": {
        "description": "The request conflicts with current state (`conflict`), or its Idempotency-Key is still in flight (`idempotency_key_in_use`).",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "IdempotencyReused": {
        "description": "The Idempotency-Key was used for a different request (`idempotency_key_reused`).",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Over the caller's request rate (`rate_limited`); honour `Retry-After`.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Unavailable": {
        "description": "A dependency failed; retry (`unavailable`).",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "schemas": {
      "ConversationList": {
        "type": "object",
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "object": {
                  "const": "conversation"
                },
                "agent_id": {
                  "type": "string"
                },
                "messages": {
                  "type": "integer"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "last_message_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "last_message_preview": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "maxLength": 120,
                  "description": "The latest user-visible message as one short plain line (at most 120 characters): the agent's last reply, or `You: ...` when the person spoke last. Only the newest conversation carries it; the others are null."
                }
              }
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "null"
          }
        }
      },
      "MessageList": {
        "type": "object",
        "properties": {
          "object": {
            "const": "list"
          },
          "conversation": {
            "type": "string"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "object": {
                  "const": "message"
                },
                "role": {
                  "type": "string",
                  "enum": [
                    "user",
                    "agent"
                  ]
                },
                "task_id": {
                  "type": "string"
                },
                "content": {
                  "type": "array"
                },
                "output": {
                  "$ref": "#/components/schemas/OutputItem"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "tools": {
                  "type": "array",
                  "description": "On a message the caller sent: the tool calls the agent's turn answering it made, oldest first (the durable trail of the live `tool.started` and `tool.completed` events). Absent when the turn called no tool. A call with no result yet has no `completed_at` and no `output`.",
                  "items": {
                    "type": "object",
                    "properties": {
                      "call_id": {
                        "type": "string"
                      },
                      "tool": {
                        "type": "string"
                      },
                      "started_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "completed_at": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "date-time"
                      },
                      "input": {},
                      "output": {}
                    }
                  }
                }
              }
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `before` to read the page of older messages; null on the oldest page."
          }
        }
      },
      "Interaction": {
        "type": "object",
        "required": [
          "id",
          "short_id",
          "object",
          "agent_id",
          "kind",
          "status",
          "prompt",
          "created_at"
        ],
        "description": "One thing an agent parked for a person, whatever the kind.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "interaction"
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "conversation": {
            "type": "string",
            "description": "The conversation the card was addressed to."
          },
          "channel": {
            "type": "string",
            "description": "The channel it was drawn on."
          },
          "kind": {
            "type": "string",
            "enum": [
              "approval",
              "question",
              "form",
              "request",
              "proposal",
              "review"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "answered",
              "granted",
              "consumed",
              "denied",
              "accepted",
              "declined",
              "expired",
              "cancelled",
              "escalated"
            ],
            "description": "`pending` while it still waits on a person. One whose `expires_at` has passed reads as `expired`, and answering it is a `409`."
          },
          "prompt": {
            "type": "string"
          },
          "options": {
            "type": [
              "array",
              "null"
            ],
            "description": "The choices, numbered from 0 — the numbers an answer names.",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer",
                  "description": "The option's number, from 0."
                },
                "label": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                },
                "prompt": {
                  "type": "string"
                }
              }
            }
          },
          "answer_spec": {
            "type": [
              "object",
              "null"
            ],
            "description": "What answer the card takes: `choice {multiple, min, max, optionCount}`, `text {multiline}`, `form {schema}` or `proposal {optionCount}`."
          },
          "decline_label": {
            "type": [
              "string",
              "null"
            ]
          },
          "tool": {
            "type": [
              "string",
              "null"
            ],
            "description": "For an approval: the tool it gates."
          },
          "params_preview": {
            "type": [
              "string",
              "null"
            ]
          },
          "cancel_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "answered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "answered_by": {
            "type": [
              "object",
              "null"
            ],
            "description": "Who answered, as recorded (harness E1): `{id, name}`. Null while it is still pending.",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "answer": {
            "type": [
              "object",
              "null"
            ],
            "description": "How it settled: an ask carries `{action, answer_type, option_index, option_indexes, option_label, option_labels, freeform_text, via, content}`, an approval `{decision, via}`."
          },
          "short_id": {
            "type": "string",
            "description": "The current approval-card generation. Supply exactly this value when deciding an approval."
          },
          "pending_call": {
            "type": [
              "object",
              "null"
            ],
            "description": "Safe stored-call identity and execution state; never executable arguments."
          },
          "outcome": {
            "description": "The recorded execution result, distinct from an approval decision."
          },
          "invalid_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the retained action needs a fresh decision or cannot execute."
          }
        }
      },
      "InteractionList": {
        "type": "object",
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Interaction"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The last id of this page: pass it as `cursor` for the next."
          }
        }
      },
      "WebhookEvent": {
        "type": "string",
        "enum": [
          "task.parked",
          "message.completed",
          "interaction.requested",
          "interaction.resolved",
          "conversation.control_changed"
        ]
      },
      "AskAnswer": {
        "type": "object",
        "required": [
          "type"
        ],
        "description": "The answer to a card: `{\"type\": \"choice\", \"index\"}`, `{\"type\": \"choices\", \"indexes\"}`, `{\"type\": \"text\", \"text\"}`, `{\"type\": \"form\", \"values\"}` or `{\"type\": \"decline\"}`.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "choice",
              "choices",
              "text",
              "form",
              "decline"
            ]
          },
          "index": {
            "type": "integer",
            "description": "For `choice`: the option's number, from 0."
          },
          "indexes": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "For `choices`: the numbers of the picked options."
          },
          "text": {
            "type": "string",
            "description": "For `text`: the words."
          },
          "values": {
            "type": "object",
            "description": "For `form`: the filled-in blanks, by field key."
          }
        },
        "additionalProperties": false
      },
      "AnsweredBy": {
        "type": "object",
        "required": [
          "id"
        ],
        "description": "The person who answered, named by the caller (harness E1).",
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 200,
            "description": "The person's id in your own system."
          },
          "name": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "WithdrawInteractionRequest": {
        "type": "object",
        "required": [
          "short_id",
          "reason"
        ],
        "description": "Which card to withdraw and why. The reason is recorded with the caller's identity and the time.",
        "properties": {
          "short_id": {
            "type": "string",
            "minLength": 1,
            "description": "The current generation from the interaction you read; a stale one is a 409 and nothing changes."
          },
          "reason": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "Why the card is withdrawn, in plain words."
          }
        },
        "additionalProperties": false
      },
      "AnswerInteractionRequest": {
        "type": "object",
        "description": "Exactly one of `decision` (an approval) or `answer` (a question, form, request, proposal or review). An approval decision may name its current short_id; without it only a pending card is answered.",
        "properties": {
          "decision": {
            "type": "string",
            "enum": [
              "approve",
              "reject"
            ],
            "description": "For an approval."
          },
          "answer": {
            "$ref": "#/components/schemas/AskAnswer"
          },
          "answered_by": {
            "$ref": "#/components/schemas/AnsweredBy"
          },
          "short_id": {
            "type": "string",
            "minLength": 1,
            "description": "With decision: the current generation from the interaction you read; a stale one is a 409 and a refreshed card needs a fresh decision. Optional while the card is pending; a decision on a card that is not pending without it is a 400."
          }
        },
        "additionalProperties": false
      },
      "Webhook": {
        "type": "object",
        "required": [
          "id",
          "object",
          "url",
          "events",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "webhook"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookCreate": {
        "type": "object",
        "required": [
          "url"
        ],
        "additionalProperties": false,
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://"
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          }
        }
      },
      "WebhookCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Webhook"
          },
          {
            "type": "object",
            "required": [
              "secret"
            ],
            "properties": {
              "secret": {
                "type": "string",
                "description": "The whsec_ signing secret. Shown once."
              }
            }
          }
        ]
      },
      "WebhookList": {
        "type": "object",
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "null"
          }
        }
      },
      "MessageCreate": {
        "type": "object",
        "required": [
          "content"
        ],
        "additionalProperties": false,
        "properties": {
          "conversation": {
            "type": "string",
            "pattern": "^[A-Za-z0-9._-]{1,128}$",
            "description": "Your conversation id. Replies are addressed to it, and its audience is its senders. A new `conv_…` id when absent."
          },
          "content": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "type",
                    "text"
                  ],
                  "properties": {
                    "type": {
                      "const": "text"
                    },
                    "text": {
                      "type": "string"
                    }
                  }
                }
              }
            ],
            "description": "The message: a string, or text parts."
          },
          "attachments": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "format": "uri",
                  "pattern": "^https://"
                },
                "mime_type": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                }
              }
            }
          },
          "metadata": {
            "type": "object",
            "description": "Passed to the agent with the message (under 16 KB)."
          },
          "time_zone": {
            "type": "string",
            "description": "The IANA time zone of the person who wrote, such as `Europe/London`; a browser has it from `Intl.DateTimeFormat().resolvedOptions().timeZone`. The agent states the time of this and later messages in it, and a newer value replaces the stored one, so it follows the person when they travel."
          },
          "end_user": {
            "$ref": "#/components/schemas/EndUser",
            "description": "The end user this message is from. The message is recorded as them, not as the key: sender `enduser:{connection}:{id}`, `outsider` trust, a directory contact with these `identities`, and `labels` on the case."
          },
          "role": {
            "type": "string",
            "enum": [
              "staff_reply",
              "staff_note"
            ],
            "description": "`staff_reply`: the business's answer to the case, recorded and never waking it. `staff_note`: the business's own note about the case, staff-only, wakes the agent for that case. Needs `author` and `end_user`."
          },
          "author": {
            "$ref": "#/components/schemas/Author",
            "description": "Who on the business's staff wrote it (with `role`)."
          }
        }
      },
      "OutputItem": {
        "type": "object",
        "required": [
          "id",
          "type"
        ],
        "description": "One output of the task's turn: the reply the agent addressed to the conversation, a media message, or an interaction card and the answer that settled it.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "media",
              "interaction",
              "card"
            ]
          },
          "interaction_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "For an interaction: the row it names, whichever channel asked. Read and answer it with `/v1/interactions/{id}`."
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "pending",
              "resolved",
              null
            ],
            "description": "For an interaction: `pending` is a card still waiting on a person; `resolved` is one that has been answered."
          },
          "kind": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "approval",
              "question",
              "form",
              "request",
              "proposal",
              "review",
              null
            ],
            "description": "For a pending interaction: what it is."
          },
          "resolution": {
            "type": [
              "string",
              "null"
            ],
            "description": "For a resolved interaction: how it settled, in the row's own words (`granted`, `denied`, `accepted`, `declined`, `answered`, `cancelled`, …) — the same value `status` carries in `/v1/interactions/{id}`."
          },
          "detail": {
            "type": [
              "string",
              "null"
            ],
            "description": "For a resolved interaction: the detail the answer carried, when there is one."
          },
          "text": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "mime_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "caption": {
            "type": [
              "string",
              "null"
            ]
          },
          "prompt": {
            "type": "string"
          },
          "actions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "link": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "One-time web link that completes the same interaction."
          }
        }
      },
      "TaskAwait": {
        "type": "object", "additionalProperties": false, "required": ["kind", "ref"],
        "properties": {
          "kind": {"type": "string", "enum": ["promise", "signal", "time"]},
          "ref": {"type": "string", "minLength": 1, "description": "An actual promise id, <name>:<correlation-key>, or RFC 3339 until timestamp."}
        }
      },
      "TaskPark": {
        "type": "object", "additionalProperties": false, "required": ["park_id", "awaits"],
        "properties": {
          "session_id": {"type": "string", "format": "uuid"},
          "park_id": {"type": "string", "format": "uuid"},
          "awaits": {"type": "array", "minItems": 1, "maxItems": 16, "items": {"$ref": "#/components/schemas/TaskAwait"}},
          "mode": {"type": "string", "enum": ["all", "any"], "default": "all"},
          "reason": {"type": ["string", "null"], "description": "Display-only; never parsed."}
        }
      },
      "Task": {
        "type": "object",
        "required": [
          "id",
          "object",
          "agent_id",
          "conversation",
          "status",
          "input",
          "output",
          "reply",
          "created_at"
        ],
        "description": "What one message opened. Statuses follow A2A: `submitted` (in the inbox), `working`, `input_required` (a card waits on a person; list them at `GET /v1/interactions?status=pending`), `completed` (the turn ended with addressed output), `canceled`, `failed` (the turn ended for good with no answer; `failure.reason` says why: `context_exhausted` means the conversation no longer fits the model's context even after the older turns were summarised, so start a new conversation). A task is never left `working` or `input_required` once its turn can no longer run.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "task"
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "conversation": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "submitted",
              "working",
              "input_required",
              "completed",
              "canceled",
              "failed"
            ]
          },
          "session_id": {"type": ["string", "null"], "format": "uuid", "description": "The existing engine session, pinned across waits and resumes."},
          "park_id": {"type": ["string", "null"], "format": "uuid"},
          "awaits": {"type": ["array", "null"], "items": {"$ref": "#/components/schemas/TaskAwait"}},
          "mode": {"type": ["string", "null"], "enum": ["all", "any", null]},
          "failure": {
            "type": "object",
            "description": "Present when `status` is `failed`.",
            "required": [
              "reason",
              "message"
            ],
            "properties": {
              "reason": {
                "type": "string",
                "enum": [
                  "context_exhausted",
                  "no_reply",
                  "wait_expired"
                ],
                "description": "`context_exhausted`: the conversation no longer fits the model's context window even after compaction. `no_reply`: the agent's turn ended without addressed output after the reply grace. `wait_expired`: Work closed the typed wait without a resumable settlement."
              },
              "message": {
                "type": "string",
                "description": "A plain line saying what happened and what to do."
              }
            }
          },
          "input": {
            "type": "object",
            "properties": {
              "content": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "const": "text"
                    },
                    "text": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "output": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutputItem"
            }
          },
          "reply": {
            "type": [
              "string",
              "null"
            ],
            "description": "Every text reply, joined."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details.",
        "required": [
          "type",
          "title",
          "status",
          "code"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "unauthorized",
              "missing_scope",
              "forbidden",
              "not_found",
              "invalid_request",
              "conflict",
              "generation_changed",
              "idempotency_key_reused",
              "idempotency_key_in_use",
              "rate_limited",
              "unavailable"
            ]
          }
        }
      },
      "Tools": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tool names the agent may use; `[\"*\"]` is every tool.",
            "default": [
              "*"
            ]
          },
          "disabled": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tools excluded when `enabled` is `[\"*\"]`.",
            "default": []
          }
        },
        "additionalProperties": false
      },
      "Channel": {
        "type": "object",
        "required": [
          "id",
          "type",
          "name",
          "enabled"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "examples": [
              "telegram",
              "slack",
              "email",
              "custom"
            ]
          },
          "name": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          }
        }
      },
      "Agent": {
        "type": "object",
        "required": [
          "id",
          "object",
          "name",
          "slug",
          "instructions",
          "model",
          "tools",
          "memory",
          "timezone",
          "support",
          "github",
          "status",
          "channels",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "agent"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "instructions": {
            "type": [
              "string",
              "null"
            ],
            "description": "The system prompt."
          },
          "model": {
            "type": [
              "string",
              "null"
            ],
            "description": "Model id; null uses the workspace default."
          },
          "tools": {
            "$ref": "#/components/schemas/Tools"
          },
          "memory": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              }
            },
            "required": [
              "enabled"
            ]
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ],
            "description": "IANA timezone of the agent's audience."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive",
              "archived"
            ]
          },
          "channels": {
            "type": "array",
            "readOnly": true,
            "items": {
              "$ref": "#/components/schemas/Channel"
            },
            "description": "Where the agent is reachable. Managed under `/v1/agents/{agent_id}/channels`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "support": {
            "type": "object",
            "required": [
              "products"
            ],
            "properties": {
              "products": {
                "type": "array",
                "maxItems": 50,
                "items": {
                  "type": "string",
                  "pattern": "^[a-z][a-z0-9_]+$"
                },
                "description": "Product slugs a support agent files problems against; `[]` clears."
              }
            }
          },
          "github": {
            "type": "object",
            "required": [
              "repositories"
            ],
            "properties": {
              "repositories": {
                "type": "array",
                "maxItems": 20,
                "items": {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$"
                },
                "description": "Repositories (owner/name) the agent's commands may get a short-lived GitHub token for, minted by the Sylphx Broker when a command names one in the bash `github` profile's `repository`; `[]` clears."
              }
            }
          }
        }
      },
      "AgentCreate": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "slug": {
            "type": "string",
            "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
            "description": "Derived from the name when absent."
          },
          "instructions": {
            "type": "string",
            "maxLength": 100000
          },
          "model": {
            "type": "string"
          },
          "tools": {
            "$ref": "#/components/schemas/Tools"
          },
          "memory": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              }
            },
            "required": [
              "enabled"
            ]
          },
          "timezone": {
            "type": "string"
          },
          "support": {
            "type": "object",
            "required": [
              "products"
            ],
            "properties": {
              "products": {
                "type": "array",
                "maxItems": 50,
                "items": {
                  "type": "string",
                  "pattern": "^[a-z][a-z0-9_]+$"
                },
                "description": "Product slugs a support agent files problems against; `[]` clears."
              }
            }
          },
          "github": {
            "type": "object",
            "required": [
              "repositories"
            ],
            "properties": {
              "repositories": {
                "type": "array",
                "maxItems": 20,
                "items": {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$"
                },
                "description": "Repositories (owner/name) the agent's commands may get a short-lived GitHub token for, minted by the Sylphx Broker when a command names one in the bash `github` profile's `repository`; `[]` clears."
              }
            }
          }
        }
      },
      "AgentUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "slug": {
            "type": "string",
            "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
          },
          "instructions": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100000,
            "description": "null or \"\" clears."
          },
          "model": {
            "type": [
              "string",
              "null"
            ],
            "description": "null returns to the workspace default."
          },
          "tools": {
            "$ref": "#/components/schemas/Tools"
          },
          "memory": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              }
            },
            "required": [
              "enabled"
            ]
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive"
            ]
          },
          "support": {
            "type": "object",
            "required": [
              "products"
            ],
            "properties": {
              "products": {
                "type": "array",
                "maxItems": 50,
                "items": {
                  "type": "string",
                  "pattern": "^[a-z][a-z0-9_]+$"
                },
                "description": "Product slugs a support agent files problems against; `[]` clears."
              }
            }
          },
          "github": {
            "type": "object",
            "required": [
              "repositories"
            ],
            "properties": {
              "repositories": {
                "type": "array",
                "maxItems": 20,
                "items": {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$"
                },
                "description": "Repositories (owner/name) the agent's commands may get a short-lived GitHub token for, minted by the Sylphx Broker when a command names one in the bash `github` profile's `repository`; `[]` clears."
              }
            }
          }
        }
      },
      "AgentList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Agent"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Scope": {
        "type": "string",
        "enum": [
          "spiron:agents.read",
          "spiron:agents.write",
          "spiron:approvals.decide",
          "spiron:approvals.withdraw",
          "spiron:acceptance.operate",
          "spiron:directory.read"
        ]
      },
      "ApiKey": {
        "type": "object",
        "required": [
          "id",
          "object",
          "name",
          "hint",
          "scopes",
          "created_at",
          "last_used_at",
          "revoked_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "api_key"
          },
          "name": {
            "type": "string"
          },
          "hint": {
            "type": "string",
            "examples": [
              "spiron_sk_…a1b2"
            ]
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ApiKeyCreate": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "scopes": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/Scope"
            },
            "description": "Defaults to all four."
          }
        }
      },
      "ApiKeyCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ApiKey"
          },
          {
            "type": "object",
            "required": [
              "key"
            ],
            "properties": {
              "key": {
                "type": "string",
                "description": "The secret. Shown once."
              }
            }
          }
        ]
      },
      "ApiKeyList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKey"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "null"
          }
        }
      },
      "ContactDirectoryExportPage": {
        "type": "object",
        "description": "One page of a contact directory export. Rows are the directory's own records, field for field.",
        "required": [
          "format",
          "source_org_id",
          "source_namespace",
          "source_ref",
          "exported_at",
          "generation",
          "inventory",
          "contacts",
          "identities",
          "relationships",
          "next_cursor"
        ],
        "properties": {
          "format": {
            "const": "customer-directory/v1"
          },
          "source_org_id": {
            "type": "string",
            "format": "uuid",
            "description": "The caller's workspace."
          },
          "source_namespace": {
            "const": "https://api.spiron.ai/v1/contact-directory"
          },
          "source_ref": {
            "const": "https://api.spiron.ai/v1/contact-directory/export"
          },
          "exported_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this page was read. Keep the first page's."
          },
          "generation": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "The directory's version this export reads. Every page of one export has the same one."
          },
          "inventory": {
            "type": "object",
            "description": "How many rows the whole export holds.",
            "required": [
              "contacts",
              "identities",
              "relationships"
            ],
            "properties": {
              "contacts": {
                "type": "integer",
                "minimum": 0
              },
              "identities": {
                "type": "integer",
                "minimum": 0
              },
              "relationships": {
                "type": "integer",
                "minimum": 0
              }
            }
          },
          "contacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DirectoryContact"
            }
          },
          "identities": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DirectoryContactIdentity"
            }
          },
          "relationships": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DirectoryContactRelationship"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page; `null` on the last page."
          }
        }
      },
      "DirectoryContact": {
        "type": "object",
        "required": [
          "id",
          "org_id",
          "display_name",
          "kind",
          "origin",
          "status",
          "metadata",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "display_name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "person",
              "organization",
              "service"
            ]
          },
          "origin": {
            "type": "string",
            "enum": [
              "agent",
              "operator",
              "system"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DirectoryContactIdentity": {
        "type": "object",
        "required": [
          "id",
          "org_id",
          "contact_id",
          "channel_type",
          "external_id",
          "verification_status",
          "label",
          "metadata",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "contact_id": {
            "type": "string",
            "format": "uuid"
          },
          "channel_type": {
            "type": "string"
          },
          "external_id": {
            "type": "string"
          },
          "verification_status": {
            "type": "string",
            "enum": [
              "observed",
              "claimed",
              "verified",
              "rejected"
            ]
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DirectoryContactRelationship": {
        "type": "object",
        "required": [
          "id",
          "org_id",
          "source_contact_id",
          "target_contact_id",
          "relationship_type",
          "label",
          "status",
          "confidence",
          "metadata",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "source_contact_id": {
            "type": "string",
            "format": "uuid"
          },
          "target_contact_id": {
            "type": "string",
            "format": "uuid"
          },
          "relationship_type": {
            "type": "string"
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          },
          "confidence": {
            "type": "string",
            "enum": [
              "observed",
              "asserted",
              "verified",
              "rejected"
            ]
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EndUser": {
        "type": "object",
        "required": [
          "id"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 200,
            "description": "The end user's id in your system, scoped to this connection: the same id on two keys is two people."
          },
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "locale": {
            "type": "string",
            "maxLength": 35
          },
          "identities": {
            "type": "array",
            "maxItems": 16,
            "items": {
              "$ref": "#/components/schemas/EndUserIdentity"
            },
            "description": "Who the end user is elsewhere, on your side of the boundary. Each is resolved to the case's directory contact."
          },
          "labels": {
            "type": "array",
            "maxItems": 64,
            "items": {
              "type": "string",
              "maxLength": 100
            },
            "description": "What you labelled the case. Stored on the conversation; a message that sends none leaves what the case already has."
          }
        }
      },
      "EndUserIdentity": {
        "type": "object",
        "required": [
          "kind",
          "value"
        ],
        "additionalProperties": false,
        "properties": {
          "kind": {
            "type": "string",
            "maxLength": 50,
            "description": "The channel the identity lives in, lowercase and specific to your system (`email`, `phone`, `external` for your own user id, `account`, `auth_user`)."
          },
          "value": {
            "type": "string",
            "maxLength": 500
          },
          "verified": {
            "type": "boolean",
            "default": false,
            "description": "Whether you verified this identity yourself. A verified identity is recorded as verified in the contact directory, which stops a later send from re-pointing it at another contact."
          }
        }
      },
      "Author": {
        "type": "object",
        "required": [
          "id"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 200,
            "description": "The staff person's id in your system."
          },
          "name": {
            "type": "string",
            "maxLength": 255
          }
        }
      },
      "DeviceCodeRequest": {
        "type": "object",
        "required": [
          "client_name"
        ],
        "properties": {
          "client_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "scopes": {
            "type": "array",
            "items": {
              "enum": [
                "spiron:agents.read",
                "spiron:agents.write"
              ]
            },
            "description": "Default: both."
          }
        }
      },
      "DeviceCode": {
        "type": "object",
        "required": [
          "device_code",
          "user_code",
          "verification_uri",
          "verification_uri_complete",
          "expires_in",
          "interval"
        ],
        "properties": {
          "device_code": {
            "type": "string"
          },
          "user_code": {
            "type": "string",
            "pattern": "^[BCDFGHJKLMNPQRSTVWXZ]{4}-[BCDFGHJKLMNPQRSTVWXZ]{4}$"
          },
          "verification_uri": {
            "type": "string",
            "format": "uri"
          },
          "verification_uri_complete": {
            "type": "string",
            "format": "uri"
          },
          "expires_in": {
            "type": "integer"
          },
          "interval": {
            "type": "integer"
          }
        }
      },
      "DeviceTokenRequest": {
        "oneOf": [
          {
            "type": "object",
            "required": ["grant_type", "device_code"],
            "properties": {
              "grant_type": {"const": "urn:ietf:params:oauth:grant-type:device_code"},
              "device_code": {"type": "string"}
            }
          },
          {
            "type": "object",
            "required": ["grant_type", "refresh_token"],
            "properties": {
              "grant_type": {"const": "refresh_token"},
              "refresh_token": {"type": "string"}
            }
          }
        ]
      },
      "DeviceToken": {
        "type": "object",
        "required": [
          "access_token",
          "refresh_token",
          "expires_in",
          "token_type",
          "scope"
        ],
        "properties": {
          "access_token": {
            "type": "string",
            "description": "Short-lived spiron_sk_ device bearer; the same connection identity remains revocable in Settings."
          },
          "refresh_token": {
            "type": "string",
            "description": "Single-use rotating refresh token. Keep its replacement atomically."
          },
          "expires_in": {
            "type": "integer",
            "description": "Access-token lifetime in seconds."
          },
          "token_type": {
            "const": "Bearer"
          },
          "scope": {
            "type": "string",
            "description": "Space-separated scopes."
          }
        }
      },
      "DeviceError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "interval": {
            "type": "integer"
          }
        }
      },
      "DeviceGrant": {
        "type": "object",
        "required": [
          "object",
          "user_code",
          "client_name",
          "scopes",
          "status",
          "expires_at"
        ],
        "properties": {
          "object": {
            "const": "device_grant"
          },
          "user_code": {
            "type": "string"
          },
          "client_name": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "enum": [
              "pending",
              "approved",
              "denied",
              "expired",
              "consumed"
            ]
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "workspace_name": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ConsoleTaskCreate": {
        "type": "object",
        "required": [
          "title"
        ],
        "additionalProperties": false,
        "description": "What a console task says. Unknown fields are refused. At most 32 KiB.",
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "label": {
            "type": "string",
            "maxLength": 80,
            "description": "Who or what asked, for the audit (`desk:ops`)."
          },
          "steps": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "string",
              "maxLength": 1000
            }
          },
          "login": {
            "type": "string",
            "pattern": "^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$",
            "description": "The company whose consoles the task signs in to (`sylphx`, `cubeage`). Required when `sites`, `exclusive_sites` or `vault_items` is set. The task's browser profiles belong to this login alone, and an exclusive site is locked per login."
          },
          "sites": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string"
            },
            "description": "Hosts (`secure.example.com`, or `*.example.com`) a credential may be typed into, or a value captured from. Empty reaches no site."
          },
          "exclusive_sites": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string"
            },
            "description": "Exact hosts (`dashboard.stripe.com`) whose account only one task of the agent under the same `login` may touch at a time."
          },
          "vault_items": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "object",
              "required": [
                "vault",
                "item"
              ],
              "additionalProperties": false,
              "properties": {
                "vault": {
                  "type": "string"
                },
                "item": {
                  "type": "string",
                  "description": "An item id or exact title; for a capture, the title of the new item."
                }
              }
            },
            "description": "The company 1Password items the task may use. Empty reaches none."
          },
          "stop_rules": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "max_minutes": {
                "type": "integer",
                "minimum": 1,
                "maximum": 240,
                "default": 60
              },
              "max_cost_usd": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 1000
              },
              "never": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "stop_if": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "reply": {
            "type": "object",
            "description": "A JSON Schema the result must satisfy (no `$ref`). Without it, any object."
          },
          "priority": {
            "type": "string",
            "enum": [
              "p0",
              "revenue",
              "normal"
            ],
            "default": "normal"
          },
          "latest_by": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ConsoleTask": {
        "type": "object",
        "required": [
          "id",
          "object",
          "kind",
          "agent_id",
          "status",
          "priority",
          "created_at"
        ],
        "description": "A console task. Statuses follow A2A: `submitted` (queued), `working`, `input_required` (parked on a person: `waiting_on` says what; `:resume` or the person's answer continues it), `completed`, `failed`, `canceled`.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "task"
          },
          "kind": {
            "const": "console_task"
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "submitted",
              "working",
              "input_required",
              "completed",
              "failed",
              "canceled"
            ]
          },
          "priority": {
            "type": "string",
            "enum": [
              "p0",
              "revenue",
              "normal"
            ]
          },
          "latest_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "queued_for_s": {
            "type": [
              "integer",
              "null"
            ]
          },
          "progress_age_s": {
            "type": [
              "integer",
              "null"
            ]
          },
          "waiting_on": {
            "type": [
              "object",
              "null"
            ],
            "description": "`{kind: takeover|interaction|resume, id, ask}` while `input_required`."
          },
          "result": {
            "type": [
              "object",
              "null"
            ],
            "description": "The validated result `tasks.complete` wrote."
          },
          "error_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "cost": {
            "type": "object",
            "properties": {
              "micros": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          }
        }
      },
      "ConsoleTaskList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConsoleTask"
            }
          }
        }
      },
      "ConsoleTaskHealth": {
        "type": "object",
        "required": [
          "object",
          "agent_id",
          "checked_at",
          "tasks",
          "mind_mailbox"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "task_health"
            ]
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "checked_at": {
            "type": "string",
            "format": "date-time"
          },
          "tasks": {
            "type": "object",
            "description": "`queue` (`depth`, `by_priority` of p0, revenue and normal, `oldest_queued_age_s`, `oldest_first`), `running` (each with `progress_age_s` and `running_for_s`), `waiting` (each with `waiting_for_s`, `ask`, `takeover_id`), `recent_failures`, `recent` (the last settled tasks with `duration_s` and `cost_micros`), `open_alerts` (`run_id` and `condition`) and `last_success_at`."
          },
          "mind_mailbox": {
            "type": "object",
            "description": "The agent's mind mailbox: `stream_length`, `consumer_group` (false when no group exists, which is not a drained mailbox), `pending` and `oldest_pending_age_s`. `{error}` when Redis could not be read."
          }
        }
      },
      "TakeoverList": {
        "type": "object",
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "object": {
                  "const": "takeover"
                },
                "agent_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "surface": {
                  "type": "string",
                  "enum": [
                    "computer",
                    "browser",
                    "device"
                  ]
                },
                "reason": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "status": {
                  "const": "requested"
                },
                "requested_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "deadline": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "BudgetPauseList": {
        "type": "object",
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "object": {
                  "const": "budget_pause"
                },
                "agent_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "objective": {
                  "type": "string"
                },
                "reason": {
                  "type": "string",
                  "enum": [
                    "spend_cap",
                    "task_budget"
                  ]
                },
                "status": {
                  "type": "string"
                },
                "paused_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "SpendCap": {
        "type": "object",
        "properties": {
          "object": {
            "const": "spend_cap"
          },
          "spend_cap_micros": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The cap the owner set on the workspace billing record, in AI usage at list rate (the unit of the plan allowance); null = none set."
          },
          "used_micros": {
            "type": "integer",
            "description": "The workspace's AI usage at list rate in its billing period, across all its agents."
          },
          "reached": {
            "type": "boolean"
          },
          "resumed_goals": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only on PUT."
          },
          "plan_cap_micros": {
            "type": "integer",
            "description": "The workspace's AI limit for the period, resolved the same way a turn resolves it (the plan's hard quota, else its included allowance; a hard quota of 0 means 0)."
          },
          "effective_cap_micros": {
            "type": "integer",
            "description": "The cap in force: the smaller of the plan's limit and the owner's cap."
          },
          "plan_tier_id": {
            "type": "string",
            "description": "Only on GET. The resolved plan tier used by workspace billing admission."
          },
          "period_start": {
            "type": "string",
            "format": "date-time",
            "description": "Only on GET. Inclusive billing-period start."
          },
          "period_end": {
            "type": "string",
            "format": "date-time",
            "description": "Only on GET. Exclusive billing-period end."
          },
          "period_source": {
            "type": "string",
            "enum": [
              "subscription",
              "calendar_month"
            ],
            "description": "Only on GET. Billing-period authority."
          },
          "usage_7d": {
            "$ref": "#/components/schemas/WorkspaceUsageReadback"
          }
        }
      },
      "WorkspaceUsageReadback": {
        "type": "object",
        "description": "Only on GET spend-cap. Seven complete UTC days of receipts, with explicit legacy coverage; sell micros are the app's gateway charge, not the upstream provider cost.",
        "required": [
          "from",
          "to",
          "days",
          "peak_recorded_daily_retail_micros",
          "peak_daily_retail_micros",
          "legacy_overlapping_months_retail_micros",
          "legacy_overlapping_months_model_steps"
        ],
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "format": "date-time"
          },
          "days": {
            "type": "array",
            "minItems": 7,
            "maxItems": 7,
            "items": {
              "$ref": "#/components/schemas/WorkspaceUsageDay"
            }
          },
          "peak_recorded_daily_retail_micros": {
            "type": "integer"
          },
          "peak_daily_retail_micros": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Null if legacy usage in any overlapping month prevents a complete daily peak."
          },
          "legacy_overlapping_months_retail_micros": {
            "type": "integer"
          },
          "legacy_overlapping_months_model_steps": {
            "type": "integer"
          }
        }
      },
      "WorkspaceUsageDay": {
        "type": "object",
        "required": [
          "day",
          "retail_micros",
          "sylphx_ai_sell_micros",
          "model_steps",
          "browser_seconds"
        ],
        "properties": {
          "day": {
            "type": "string",
            "format": "date"
          },
          "retail_micros": {
            "type": "integer"
          },
          "sylphx_ai_sell_micros": {
            "type": "integer"
          },
          "model_steps": {
            "type": "integer"
          },
          "browser_seconds": {
            "type": "integer"
          }
        }
      },
      "GoalBudgetList": {
        "type": "object",
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "object": {
                  "const": "goal_budget"
                },
                "agent_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "objective": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                },
                "budget_micros": {
                  "type": "integer"
                },
                "spent_micros": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "TransferAgentRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "recipient_email"
        ],
        "properties": {
          "recipient_email": {
            "type": "string",
            "format": "email",
            "description": "The person the agent is offered to. They accept signed in with this email."
          }
        }
      },
      "AcceptAgentTransferRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "token"
        ],
        "properties": {
          "token": {
            "type": "string",
            "description": "The one-time token from the offer (`spiron_tr_…`)."
          },
          "workspace_id": {
            "type": "string",
            "format": "uuid",
            "description": "The receiving workspace. Defaults to the signed-in person's workspace; they must own or administer it."
          }
        }
      },
      "AgentTransfer": {
        "type": "object",
        "required": [
          "object",
          "id",
          "agent_id",
          "status",
          "recipient_email",
          "from_workspace_id",
          "expires_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "const": "agent_transfer"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "enum": [
              "pending",
              "accepted",
              "cancelled",
              "expired"
            ]
          },
          "recipient_email": {
            "type": "string"
          },
          "from_workspace_id": {
            "type": "string",
            "format": "uuid"
          },
          "to_workspace_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "decided_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "receipt": {
            "type": [
              "object",
              "null"
            ],
            "description": "For an accepted transfer: the rows moved per table, the grants and per-agent roles revoked, the automations paused, cron jobs disabled and push configs expired, the directory contacts carried, and the channel managers after the move."
          },
          "token": {
            "type": "string",
            "description": "Only in the response that creates the offer. Shown once; only its hash is kept."
          },
          "accept_url": {
            "type": "string",
            "format": "uri",
            "description": "Only in the response that creates the offer: the page where the person signs in and accepts."
          }
        }
      },
      "Automation": {
        "type": "object",
        "required": [
          "object",
          "id",
          "agent_id",
          "name",
          "trigger_kind",
          "status",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "object": {
            "const": "automation"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "trigger_kind": {
            "enum": [
              "time",
              "webhook",
              "event",
              "condition"
            ]
          },
          "status": {
            "enum": [
              "registering",
              "active",
              "paused",
              "failing",
              "ended"
            ]
          },
          "pause_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "`owner` is a pause only an owner or admin can lift; an agent transfer pauses every automation this way."
          },
          "paused_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "reply_channel": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_fired_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "next_due_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CronJob": {
        "type": "object",
        "required": [
          "object",
          "id",
          "agent_id",
          "name",
          "schedule",
          "enabled",
          "status",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "object": {
            "const": "cron_job"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "schedule": {
            "type": "string"
          },
          "schedule_kind": {
            "enum": [
              "cron",
              "at",
              "in"
            ]
          },
          "timezone": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "status": {
            "type": "string"
          },
          "reply_channel": {
            "type": [
              "string",
              "null"
            ]
          },
          "run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_run": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "next_run": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ChannelConnection": {
        "type": "object",
        "required": [
          "id",
          "object",
          "agent_id",
          "type",
          "key",
          "name",
          "default",
          "enabled",
          "status",
          "policy",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "channel"
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "examples": [
              "email",
              "telegram",
              "slack",
              "custom"
            ]
          },
          "key": {
            "type": "string",
            "description": "The connection's key on the agent; `default` for the first one of a type."
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "default": {
            "type": "boolean"
          },
          "enabled": {
            "type": "boolean"
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "pending",
              "active",
              "degraded",
              "failing",
              null
            ],
            "description": "Provider health; `active` once connected."
          },
          "verification_error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the connection is not active."
          },
          "policy": {
            "type": "object",
            "description": "Who may message the connection, for example `{\"dmPolicy\": \"open\"}`. `{}` is the channel's default."
          },
          "delivery": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "webhook",
              "none",
              null
            ],
            "description": "A custom connection's delivery mode; null for other types."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ChannelConnectionCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "examples": [
              "email",
              "telegram",
              "slack",
              "custom"
            ]
          },
          "key": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9_-]*$",
            "description": "Defaults to `default`; a second connection of the same type needs its own key."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "default": {
            "type": "boolean"
          }
        }
      },
      "ChannelConnectionUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255
          },
          "enabled": {
            "type": "boolean"
          },
          "default": {
            "const": true,
            "description": "Makes this the agent's default connection of its type."
          },
          "policy": {
            "type": "object",
            "description": "Replaces who may message the connection, for example `{\"dmPolicy\": \"open\"}` for an inbox that answers anyone."
          },
          "delivery": {
            "type": "string",
            "enum": [
              "webhook",
              "none"
            ],
            "description": "Custom connections only."
          }
        }
      },
      "ChannelConnect": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "options": {
            "type": "object",
            "description": "Setup options of the channel type, for example `{\"mailbox\": \"orgs/…/mailboxes/support\"}` for email."
          },
          "credentials": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Provider credentials by name, for example `{\"BOT_TOKEN\": \"…\"}`. Stored encrypted; never returned."
          }
        }
      },
      "ChannelConnectResult": {
        "type": "object",
        "required": [
          "object",
          "channel_id",
          "validated"
        ],
        "properties": {
          "object": {
            "const": "channel_connect"
          },
          "channel_id": {
            "type": "string",
            "format": "uuid"
          },
          "validated": {
            "type": "boolean",
            "description": "The provider accepted the credentials and the connection is active."
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "authorization": {
            "type": "object",
            "description": "Email only, while the mailbox key waits for the owner's approval: the owner opens `verification_uri_complete` (the code is already in it), checks the organization, mailbox and scopes the page shows, and approves; then call connect again. `verification_uri` with `user_code` typed by hand also works.",
            "properties": {
              "verification_uri": {
                "type": "string"
              },
              "verification_uri_complete": {
                "type": "string",
                "description": "`verification_uri` with `user_code` in its query (RFC 8628 3.3.1)."
              },
              "user_code": {
                "type": "string"
              },
              "expires_at": {},
              "resource": {
                "type": "string"
              },
              "scope": {
                "type": "string"
              }
            }
          },
          "error": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        }
      },
      "ChannelSetup": {
        "type": "object",
        "required": [
          "object",
          "channel_id",
          "generated"
        ],
        "properties": {
          "object": {
            "const": "channel_setup"
          },
          "channel_id": {
            "type": "string",
            "format": "uuid"
          },
          "callback_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the provider sends events."
          },
          "generated": {
            "type": "array",
            "description": "Secrets Spiron mints for the provider console, the same on every call.",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "setup": {
            "type": "object",
            "description": "The channel type's setup guide."
          }
        }
      }
    }
  }
}
