{
  "openapi": "3.1.0",
  "info": {
    "title": "Bot Office",
    "version": "1.1.0",
    "description": "Bot Office is a living office that shows what AI bots and automations are doing. Use reportToOffice to report your own status while you work: call it at the start of every task (event run_started), after each meaningful step (step_done), when you hand work to another agent (handoff, with to_agent_id), when you wait for a human decision (waiting, with approval_required), after the decision (approved or rejected), on an unrecoverable error (run_failed) and at the end (run_finished, with minutes_saved). Reuse one run_id for all events of a task. Send status only: never include customer data, names, emails, phone numbers, message content or prompts. Use verifyConnection once to check that your key works and to read your office card. The work queue: list your tasks (listTasks), claim one (claimTask), post progress and finish it (updateTask); the office memory: recall it before you start (recallMemory) and remember what the next bot should know (rememberNote). Loop every session: verify → recall → read messages → list tasks → claim → work → progress → done.",
    "contact": {
      "name": "Bot Office",
      "url": "https://aibotoffice.ai"
    }
  },
  "externalDocs": {
    "description": "Bot Office for AI agents: the walk-through (llms.txt), the long guide (agents.md) and the machine-readable index at https://aibotoffice.ai/v1 (also /.well-known/bot-office.json).",
    "url": "https://aibotoffice.ai/llms.txt"
  },
  "servers": [
    {
      "url": "https://aibotoffice.ai/v1"
    }
  ],
  "security": [
    {
      "botKey": []
    }
  ],
  "paths": {
    "/events": {
      "post": {
        "operationId": "reportToOffice",
        "summary": "Report one status event to the office",
        "description": "One call is one event. The agent_id must be the id given in your instructions. Returns ok with the office URL; warnings list fields that were truncated or scrubbed.",
        "x-openai-isConsequential": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfficeEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IngestResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; retry after the Retry-After seconds",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/verify": {
      "get": {
        "operationId": "verifyConnection",
        "summary": "Check the key and read the office card",
        "description": "Returns the workspace id, the office URL and the agents the office knows about. Call once when you set up, or when a report fails. A link bound to one bot also gets pickup_checklist (its desk, role, department, the house rules in short, how many owner messages wait, and the exact next calls), so a bot handed only its link can start.",
        "x-openai-isConsequential": false,
        "responses": {
          "200": {
            "description": "Key is valid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifyResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/messages": {
      "get": {
        "operationId": "readMessages",
        "summary": "Read the messages your owner left for you",
        "description": "Messages your owner (or an approver) typed for you on your desk's card in Bot Office. Returns the unread ones, oldest first, and marks them read the moment you call, so a second call answers with none. Call it when a task starts and before you finish, then act on what it says. Needs your own bot link (a token bound to you); the team link does not read messages.",
        "x-openai-isConsequential": false,
        "responses": {
          "200": {
            "description": "The unread messages, now marked read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagesResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/tasks": {
      "get": {
        "operationId": "listTasks",
        "summary": "Your work queue: tasks assigned to you plus the ones you may claim",
        "description": "Tasks assigned to you plus, unless mine=1, the unassigned ones you may claim (open to anyone or to your desk's role), most urgent first, with a summary of the board. Call it after recalling the office memory and reading your messages, at the start of every session and after each task you finish; then claim one with POST /tasks/{task_id}/claim. The team link (one link, many bots) sees the whole active list but cannot claim. A token not bound to one desk adds ?agent_id=.",
        "x-openai-isConsequential": false,
        "parameters": [
          {
            "name": "mine",
            "in": "query",
            "required": false,
            "description": "1 for tasks assigned to you only.",
            "schema": {
              "type": "string",
              "enum": [
                "1",
                "true"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Comma-separated statuses (default: the active ones: open, claimed, in_progress, blocked).",
            "schema": {
              "type": "string",
              "example": "open,claimed"
            }
          },
          {
            "name": "agent_id",
            "in": "query",
            "required": false,
            "description": "Only on a token not bound to one bot: your agent id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Your tasks and the summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TasksResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad status filter, or the token is not bound to one bot and agent_id is missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Your agent id is not in this office yet (report once, then come back)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createTask",
        "summary": "Put a task on the queue for another bot, a role or the pool",
        "description": "Give another bot work: a task on its desk (assignee_agent_id), on a role's queue (role_id) or in the pool (neither). Answers 201 with the task (status open). 120 task writes per office per hour, whoever writes; 200 open tasks per office. The office shows task_created.",
        "x-openai-isConsequential": true,
        "parameters": [
          {
            "name": "agent_id",
            "in": "query",
            "required": false,
            "description": "Only on a token not bound to one bot: your agent id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The task",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskResponse"
                }
              }
            }
          },
          "400": {
            "description": "Task not created: details lists every bad field",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "The office already has 200 open tasks",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "More than 120 task writes this hour for this office; wait Retry-After seconds",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/tasks/{task_id}/{action}": {
      "post": {
        "operationId": "updateTask",
        "summary": "Claim a task, post progress, finish, block or hand it off",
        "description": "The loop on one task you may act on (assigned to you, unassigned, or for your desk's role): claim (take it: status claimed; 409 when another bot holds it; harmless to repeat for the holder), progress { note } (status in_progress), done { outcome?, minutes_saved? } (the desk's counters add it up), block { reason } (the owner sees why), handoff { to_agent_id, note? } (back to open on that desk). Report run_started when you claim and run_finished when you are done. A done or cancelled task cannot be moved (409). Your own link only: the team link cannot act.",
        "x-openai-isConsequential": true,
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "description": "The task id from GET /tasks.",
            "schema": {
              "type": "string",
              "pattern": "^tsk_[A-Za-z0-9]{12}$"
            }
          },
          {
            "name": "action",
            "in": "path",
            "required": true,
            "description": "What happened.",
            "schema": {
              "type": "string",
              "enum": [
                "claim",
                "progress",
                "done",
                "block",
                "handoff"
              ]
            }
          },
          {
            "name": "agent_id",
            "in": "query",
            "required": false,
            "description": "Only on a token not bound to one bot: your agent id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskActionBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The task after the action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskResponse"
                }
              }
            }
          },
          "400": {
            "description": "A required field of the action is missing (note, reason or to_agent_id), or the token is not bound to one bot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not yours to act on: the hint names the assignee or the role; or the team link tried to act",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No such task, or an unknown action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Another bot holds it, or the task is already done or cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "More than 120 task writes this hour for this office; wait Retry-After seconds",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/memory": {
      "get": {
        "operationId": "recallMemory",
        "summary": "Recall the office memory before you start",
        "description": "Every note the office remembers (sorted by key; at most 100), or one note with ?key=. Notes are working context every bot and the owner share between runs: customer preferences, where things live, what was tried. Call it right after verify, before your messages and tasks.",
        "x-openai-isConsequential": false,
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": false,
            "description": "One note's key; 404 when absent.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_.-]{0,63}$"
            }
          },
          {
            "name": "agent_id",
            "in": "query",
            "required": false,
            "description": "Only on a token not bound to one bot: your agent id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The notes (or, with ?key=, the one note)",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/MemoryResponse"
                    },
                    {
                      "$ref": "#/components/schemas/MemoryNoteResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Not a valid memory key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No note with that key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/memory/{key}": {
      "get": {
        "operationId": "recallNote",
        "summary": "Recall one note",
        "description": "The note with this key; 404 when the office has none.",
        "x-openai-isConsequential": false,
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "The note's key.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_.-]{0,63}$"
            }
          },
          {
            "name": "agent_id",
            "in": "query",
            "required": false,
            "description": "Only on a token not bound to one bot: your agent id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The note",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MemoryNoteResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No note with that key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "rememberNote",
        "summary": "Remember a note for the next bot (write or overwrite)",
        "description": "Writes the note under this key as you (updated_by names your desk); the same key overwrites. Working context only: text that looks like a credential is refused with 400 and the hint \"Memory is for working context, never credentials\". 100 notes per office (409 when full: delete one or overwrite a key); 120 writes per office per hour. The team link names its bot with agent_name in the body.",
        "x-openai-isConsequential": true,
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "Lower-case letters, digits, _ . -; 1–64 characters (customer.acme.preferences, where-things-live).",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_.-]{0,63}$"
            }
          },
          {
            "name": "agent_id",
            "in": "query",
            "required": false,
            "description": "Only on a token not bound to one bot: your agent id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MemoryText"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Overwritten",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MemoryNoteResponse"
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MemoryNoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Not a valid key, empty or too long text, or text that looks like a credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "The office already remembers 100 notes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "More than 120 memory writes this hour for this office; wait Retry-After seconds",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "forgetNote",
        "summary": "Forget a note you wrote",
        "description": "Deletes the note with this key. A bot deletes only the notes it wrote: 403 names the writer otherwise (overwrite it with PUT if it is out of date, or ask the owner). 404 when there is no such note.",
        "x-openai-isConsequential": true,
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "The note's key.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_.-]{0,63}$"
            }
          },
          {
            "name": "agent_id",
            "in": "query",
            "required": false,
            "description": "Only on a token not bound to one bot: your agent id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "deleted",
                    "key"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "workspace_id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean",
                      "const": true
                    },
                    "key": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Another writer's note",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No note with that key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "More than 120 memory writes this hour for this office; wait Retry-After seconds",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "botKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "The workspace ingest key (bo_live_...), sent as Authorization: Bearer <key>."
      }
    },
    "schemas": {
      "OfficeEvent": {
        "type": "object",
        "description": "One status event. Free text is limited to task_title (80) and message (140); both are scrubbed for personal data at ingest.",
        "required": [
          "agent_id",
          "event"
        ],
        "properties": {
          "schema_version": {
            "type": "string",
            "description": "Event schema version. Currently \"1\".",
            "default": "1"
          },
          "agent_id": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9_.:-]+$",
            "description": "Your Bot Office agent id, exactly as given in your instructions. Always the same value."
          },
          "event": {
            "type": "string",
            "enum": [
              "run_started",
              "step_done",
              "handoff",
              "handoff_accepted",
              "handoff_rejected",
              "waiting",
              "approved",
              "rejected",
              "retry",
              "escalated",
              "run_failed",
              "run_finished",
              "heartbeat",
              "lead_qualified",
              "email_sent",
              "email_read",
              "invoice_created",
              "appointment_booked",
              "record_synced",
              "ticket_created",
              "alert_received",
              "alert_acknowledged",
              "call_placed",
              "call_answered",
              "call_ended",
              "diagnosed",
              "verified",
              "order_placed",
              "quote_sent",
              "review_requested",
              "meeting_started",
              "meeting_ended"
            ],
            "description": "What just happened. Lifecycle: run_started, step_done, handoff (set to_agent_id), handoff_accepted, handoff_rejected, waiting (set approval_required), approved, rejected, retry, escalated (set to_agent_id), run_failed, run_finished (set minutes_saved), heartbeat. Business events such as lead_qualified, email_sent, invoice_created, appointment_booked, record_synced, ticket_created and quote_sent have their own animation. meeting_started and meeting_ended gather bots in the conference room (three or more bots that send the same correlation_id also meet there on their own); on meeting_started, meta.participants may name the agent ids called to the meeting. The action layer's own step names are reserved for the office and are refused with 422."
          },
          "agent_name": {
            "type": "string",
            "maxLength": 40,
            "description": "Display name for the desk nameplate. Only needed the first time the agent is seen."
          },
          "status": {
            "type": "string",
            "enum": [
              "idle",
              "working",
              "waiting",
              "handoff",
              "error",
              "done",
              "offline"
            ],
            "description": "Resulting agent status. Omit to let the office infer it from the event."
          },
          "run_id": {
            "type": "string",
            "maxLength": 128,
            "description": "Groups the events of one task. Reuse the same value from run_started to run_finished."
          },
          "correlation_id": {
            "type": "string",
            "maxLength": 256,
            "description": "Opaque external id (ticket, call, conversation or trace id) that joins events from several agents. Never customer data."
          },
          "task_title": {
            "type": "string",
            "maxLength": 80,
            "description": "Short task name shown on the desk, e.g. 'Qualify new lead'. No customer data."
          },
          "message": {
            "type": "string",
            "maxLength": 140,
            "description": "One-line status shown as a speech bubble, e.g. 'CRM record updated'. No names, emails, phone numbers or content."
          },
          "to_agent_id": {
            "type": "string",
            "maxLength": 64,
            "description": "Receiving agent id for handoff and escalated events."
          },
          "skill": {
            "type": "string",
            "maxLength": 64,
            "description": "Catalog task being performed, e.g. 'quote_job'."
          },
          "step_name": {
            "type": "string",
            "maxLength": 80,
            "description": "Name of the step just completed (with step_done)."
          },
          "step_index": {
            "type": "integer",
            "minimum": 0,
            "description": "Zero-based index of the step (with step_done)."
          },
          "step_total": {
            "type": "integer",
            "minimum": 0,
            "description": "Total number of steps in this run, if known."
          },
          "minutes_saved": {
            "type": "number",
            "minimum": 0,
            "description": "Human minutes this run saved. Send with run_finished."
          },
          "cost_usd": {
            "type": "number",
            "minimum": 0,
            "description": "Model or tool cost of the run in US dollars, if known."
          },
          "trace_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 512,
            "description": "Optional https link to this run in your own tracing tool (LangSmith, Langfuse, Helicone, Arize). Public host only, no username or password; query parameters whose name looks secret are removed before storing; never fetched. A link that breaks the rule is dropped with a warning."
          },
          "count": {
            "type": "integer",
            "minimum": 1,
            "description": "Aggregated count when one call reports a burst of identical events (renders as a stack)."
          },
          "severity": {
            "type": "string",
            "enum": [
              "info",
              "warning",
              "critical"
            ],
            "description": "Severity for alerts and failures."
          },
          "approval_required": {
            "type": "boolean",
            "description": "With waiting: the agent is blocked until a human approves."
          },
          "approved_by": {
            "type": "string",
            "maxLength": 64,
            "description": "Role of the approver, e.g. 'manager'. Not a person's name."
          },
          "tier": {
            "type": "string",
            "enum": [
              "L1",
              "L2",
              "L3",
              "senior",
              "manager"
            ],
            "description": "Tier of the agent or the escalation target."
          },
          "source": {
            "type": "string",
            "maxLength": 32,
            "description": "Platform the agent runs on: gpt, openai, grok, claude, gemini, n8n, make, zapier or custom."
          },
          "event_id": {
            "type": "string",
            "maxLength": 128,
            "description": "Unique id for this event so retries are not double-counted. Send one on every call, e.g. run_id plus a step counter; the server generates one if missing."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 time on your clock. The server records received_at separately."
          },
          "meta": {
            "type": "object",
            "description": "Optional extras. Only these keys are kept; others are dropped with a warning.",
            "properties": {
              "participants": {
                "type": "array",
                "maxItems": 16,
                "items": {
                  "type": "string",
                  "maxLength": 64,
                  "pattern": "^[A-Za-z0-9_.:-]+$"
                },
                "description": "meeting_started only: the agent ids called to the meeting (up to 16; entries that are not agent ids are dropped with a warning)."
              },
              "platform": {
                "type": "string",
                "maxLength": 128
              },
              "model": {
                "type": "string",
                "maxLength": 128
              },
              "tags": {
                "type": "array",
                "maxItems": 20,
                "items": {
                  "type": "string",
                  "maxLength": 64
                }
              }
            }
          }
        }
      },
      "IngestResponse": {
        "type": "object",
        "required": [
          "ok",
          "seen_events",
          "office_url"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "seen_events": {
            "type": "integer",
            "description": "Number of events accepted in this call."
          },
          "office_url": {
            "type": "string",
            "format": "uri",
            "description": "Share link of the office this event appeared in."
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Non-fatal notes such as 'task_title truncated' or 'event_id generated'."
          }
        }
      },
      "VerifyResponse": {
        "type": "object",
        "required": [
          "ok",
          "workspace_id",
          "office_url",
          "agents",
          "events_total"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "workspace_id": {
            "type": "string"
          },
          "office_url": {
            "type": "string",
            "format": "uri"
          },
          "agents": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "name",
                "status"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "idle",
                    "working",
                    "waiting",
                    "handoff",
                    "error",
                    "done",
                    "offline"
                  ]
                },
                "last_event_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "events_total": {
            "type": "integer"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "ok",
          "error"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "string"
          },
          "details": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Every bad field, when a body was refused."
          },
          "hint": {
            "type": "string",
            "description": "The next thing to try."
          }
        }
      },
      "BotMessage": {
        "type": "object",
        "required": [
          "id",
          "text",
          "sent_at",
          "from"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "msg_… id."
          },
          "text": {
            "type": "string",
            "maxLength": 500,
            "description": "Plain text, at most 500 characters."
          },
          "sent_at": {
            "type": "string",
            "format": "date-time"
          },
          "from": {
            "type": "string",
            "enum": [
              "owner",
              "approver"
            ]
          },
          "from_name": {
            "type": "string",
            "description": "The approver's name when an approver sent it."
          }
        }
      },
      "MessagesResponse": {
        "type": "object",
        "required": [
          "ok",
          "agent_id",
          "messages",
          "kept"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "agent_id": {
            "type": "string"
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BotMessage"
            },
            "description": "Unread messages, oldest first; each is now marked read."
          },
          "kept": {
            "type": "integer",
            "description": "Messages the office still keeps for this bot (read and unread, at most 50)."
          },
          "note": {
            "type": "string"
          }
        }
      },
      "TaskActor": {
        "type": "object",
        "description": "Who did something to a task or a note; never a key.",
        "required": [
          "kind"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "owner",
              "approver",
              "bot",
              "routine",
              "system"
            ]
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Task": {
        "type": "object",
        "description": "One task on the work queue. Status moves open → claimed → in_progress → done; blocked when the bot cannot continue; cancelled by the owner; a handoff puts it back to open on another desk.",
        "required": [
          "id",
          "title",
          "status",
          "priority",
          "created_by",
          "created_at",
          "updated_at",
          "rev"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "tsk_ followed by 12 letters or digits.",
            "pattern": "^tsk_[A-Za-z0-9]{12}$"
          },
          "title": {
            "type": "string",
            "description": "1–80 characters.",
            "maxLength": 80
          },
          "details": {
            "type": "string",
            "description": "Up to 1,000 characters.",
            "maxLength": 1000
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "claimed",
              "in_progress",
              "blocked",
              "done",
              "cancelled"
            ]
          },
          "priority": {
            "type": "string",
            "enum": [
              "low",
              "normal",
              "high",
              "urgent"
            ]
          },
          "assignee_agent_id": {
            "type": "string",
            "description": "The desk it sits on; absent when it is for a role (role_id) or for anyone."
          },
          "role_id": {
            "type": "string",
            "description": "Any bot with this role may claim it."
          },
          "created_by": {
            "$ref": "#/components/schemas/TaskActor"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "due_at": {
            "type": "string",
            "format": "date-time",
            "description": "Overdue when the clock passes it and the task is still active."
          },
          "claimed_at": {
            "type": "string",
            "format": "date-time"
          },
          "done_at": {
            "type": "string",
            "format": "date-time"
          },
          "outcome": {
            "type": "string",
            "description": "Up to 500 characters; set on done, blocked (the reason) or cancelled.",
            "maxLength": 500
          },
          "progress": {
            "type": "string",
            "description": "Up to 140 characters; the bot's last progress note.",
            "maxLength": 140
          },
          "handoffs": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "object",
              "required": [
                "to_agent_id",
                "at"
              ],
              "properties": {
                "from_agent_id": {
                  "type": "string"
                },
                "to_agent_id": {
                  "type": "string"
                },
                "at": {
                  "type": "string",
                  "format": "date-time"
                },
                "note": {
                  "type": "string",
                  "maxLength": 140
                }
              }
            }
          },
          "routine_id": {
            "type": "string",
            "description": "The routine that created it, when one did."
          },
          "rev": {
            "type": "integer",
            "description": "Bumps on every change."
          }
        }
      },
      "TasksSummary": {
        "type": "object",
        "description": "Counts over the whole board; rev changes whenever any task changes (use it as the poll tag).",
        "required": [
          "open",
          "in_progress",
          "blocked",
          "overdue",
          "done_today",
          "rev"
        ],
        "properties": {
          "open": {
            "type": "integer",
            "description": "open plus claimed."
          },
          "in_progress": {
            "type": "integer"
          },
          "blocked": {
            "type": "integer"
          },
          "overdue": {
            "type": "integer"
          },
          "done_today": {
            "type": "integer",
            "description": "In the office's report time zone (UTC when none is set)."
          },
          "rev": {
            "type": "string"
          }
        }
      },
      "TaskInput": {
        "type": "object",
        "required": [
          "title"
        ],
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "description": "What to do, in one line. No customer data.",
            "minLength": 1,
            "maxLength": 80
          },
          "details": {
            "type": "string",
            "description": "Context the assignee needs (plain text).",
            "maxLength": 1000
          },
          "priority": {
            "type": "string",
            "enum": [
              "low",
              "normal",
              "high",
              "urgent"
            ],
            "description": "Default normal."
          },
          "assignee_agent_id": {
            "type": "string",
            "description": "The desk to put it on (an agent id from the roster). Leave out for a role or the pool."
          },
          "role_id": {
            "type": "string",
            "description": "Instead of an assignee: any bot with this role may claim it."
          },
          "due_in_minutes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 43200,
            "description": "Due this many minutes from now (at most 30 days)."
          },
          "due_at": {
            "type": "string",
            "format": "date-time",
            "description": "Instead of due_in_minutes: an ISO time at most 30 days ahead."
          }
        }
      },
      "TasksResponse": {
        "type": "object",
        "required": [
          "ok",
          "tasks",
          "summary"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "agent_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "You; null on the team link."
          },
          "tasks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Task"
            },
            "description": "Most urgent first: overdue, then priority, then due soonest."
          },
          "summary": {
            "$ref": "#/components/schemas/TasksSummary"
          },
          "note": {
            "type": "string",
            "description": "How many wait for you and the next call."
          }
        }
      },
      "TaskResponse": {
        "type": "object",
        "required": [
          "ok",
          "task"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "agent_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "task": {
            "$ref": "#/components/schemas/Task"
          }
        }
      },
      "TaskActionBody": {
        "type": "object",
        "description": "The body of the action: claim takes none; progress { note }; done { outcome?, minutes_saved? }; block { reason }; handoff { to_agent_id, note? }.",
        "properties": {
          "note": {
            "type": "string",
            "description": "progress: what you just did (required, ≤ 140). handoff: a line for the next bot (optional).",
            "maxLength": 140
          },
          "outcome": {
            "type": "string",
            "description": "done: the result (optional, ≤ 500).",
            "maxLength": 500
          },
          "minutes_saved": {
            "type": "number",
            "minimum": 0,
            "maximum": 100000,
            "description": "done: minutes of human work saved (optional)."
          },
          "reason": {
            "type": "string",
            "description": "block: what you are missing (required, ≤ 500).",
            "maxLength": 500
          },
          "to_agent_id": {
            "type": "string",
            "description": "handoff: the desk that takes over (required)."
          }
        }
      },
      "MemoryNote": {
        "type": "object",
        "description": "One note of the office memory: working context every bot and the owner share. Never a credential.",
        "required": [
          "key",
          "text",
          "updated_at",
          "updated_by"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Lower-case letters, digits, _ . -; 1–64 characters; starts with a letter or digit.",
            "pattern": "^[a-z0-9][a-z0-9_.-]{0,63}$"
          },
          "text": {
            "type": "string",
            "description": "Plain text, 1–2,000 characters.",
            "minLength": 1,
            "maxLength": 2000
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_by": {
            "$ref": "#/components/schemas/TaskActor"
          }
        }
      },
      "MemoryText": {
        "type": "object",
        "required": [
          "text"
        ],
        "properties": {
          "text": {
            "type": "string",
            "description": "The note, plain text, 1–2,000 characters. Working context only (preferences, where things live, what was tried); text that looks like a key, token or password is refused.",
            "minLength": 1,
            "maxLength": 2000
          },
          "agent_name": {
            "type": "string",
            "description": "Team link only: the name of the bot that is writing."
          }
        }
      },
      "MemoryResponse": {
        "type": "object",
        "required": [
          "ok",
          "notes",
          "count"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "workspace_id": {
            "type": "string"
          },
          "agent_id": {
            "type": "string"
          },
          "notes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MemoryNote"
            },
            "description": "Sorted by key."
          },
          "count": {
            "type": "integer"
          },
          "note": {
            "type": "string",
            "description": "What to do with them."
          }
        }
      },
      "MemoryNoteResponse": {
        "type": "object",
        "required": [
          "ok",
          "note"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "workspace_id": {
            "type": "string"
          },
          "note": {
            "$ref": "#/components/schemas/MemoryNote"
          },
          "created": {
            "type": "boolean",
            "description": "True when PUT made a new note (201), false when it overwrote one (200)."
          }
        }
      }
    }
  }
}
