{
  "openapi": "3.1.0",
  "info": {
    "title": "RSX AI",
    "version": "v1",
    "description": "Assistants for RSX systems: models with reasoning and read-only tools, knowledge of RSX's products, and operator-set tone and boundaries."
  },
  "servers": [
    {
      "url": "https://api.rsx.group"
    }
  ],
  "components": {
    "schemas": {
      "AiTierName": {
        "type": "string",
        "enum": [
          "fast",
          "balanced",
          "deep"
        ],
        "example": "balanced"
      },
      "AiThinkingLevel": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "off",
          "low",
          "medium",
          "high"
        ],
        "description": "The reasoning the tier runs with when a request does not choose. `null` leaves it to the model.",
        "example": "low"
      },
      "AiTier": {
        "type": "object",
        "properties": {
          "name": {
            "$ref": "#/components/schemas/AiTierName"
          },
          "model": {
            "type": "string",
            "example": "@cf/openai/gpt-oss-120b"
          },
          "thinking": {
            "$ref": "#/components/schemas/AiThinkingLevel"
          }
        },
        "required": [
          "name",
          "model",
          "thinking"
        ]
      },
      "AiThinkingMode": {
        "type": "string",
        "enum": [
          "none",
          "toggle",
          "always"
        ],
        "description": "`none`: the model does not reason. `toggle`: reasoning can be switched off. `always`: it reasons on every call, whatever is asked.",
        "example": "toggle"
      },
      "AiModel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "@cf/openai/gpt-oss-120b"
          },
          "label": {
            "type": "string",
            "example": "GPT-OSS 120B"
          },
          "description": {
            "type": "string",
            "example": "OpenAI open-weight model for reasoning and agentic tasks."
          },
          "tools": {
            "type": "boolean",
            "description": "Whether the model can be given tools. Measured by running it, not taken from the catalogue.",
            "example": true
          },
          "thinking": {
            "$ref": "#/components/schemas/AiThinkingMode"
          },
          "contextWindow": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Tokens, where Cloudflare states it. `null` means unknown, never unlimited.",
            "example": 131072
          }
        },
        "required": [
          "id",
          "label",
          "description",
          "tools",
          "thinking",
          "contextWindow"
        ]
      },
      "AiModelList": {
        "type": "object",
        "properties": {
          "tiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AiTier"
            }
          },
          "models": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AiModel"
            }
          }
        },
        "required": [
          "tiers",
          "models"
        ]
      },
      "Problem": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "https://docs.rsx.group/errors/not-found"
          },
          "title": {
            "type": "string",
            "example": "Not found"
          },
          "status": {
            "type": "integer",
            "example": 404
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string",
            "example": "/access/v1/installations/get"
          },
          "requestId": {
            "type": "string"
          }
        },
        "required": [
          "type",
          "title",
          "status"
        ]
      },
      "AiToolName": {
        "type": "string",
        "enum": [
          "web_fetch",
          "rsx_status",
          "rsx_page_read"
        ],
        "example": "rsx_status"
      },
      "AiToolReach": {
        "type": "string",
        "enum": [
          "rsx",
          "internet"
        ],
        "description": "Whether the tool reads RSX or the open internet.",
        "example": "rsx"
      },
      "AiTool": {
        "type": "object",
        "properties": {
          "name": {
            "$ref": "#/components/schemas/AiToolName"
          },
          "reach": {
            "$ref": "#/components/schemas/AiToolReach"
          },
          "description": {
            "type": "string",
            "example": "Current status of RSX services from status.rsx.group."
          }
        },
        "required": [
          "name",
          "reach",
          "description"
        ]
      },
      "AiToolCall": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "example": "knowledge_search"
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "failed",
              "refused"
            ],
            "description": "`refused`: the call never ran. `failed`: it ran and did not succeed. The model is told either way."
          },
          "reach": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AiToolReach"
              },
              {
                "type": [
                  "string",
                  "null"
                ]
              }
            ]
          },
          "ms": {
            "type": "integer",
            "example": 42
          }
        },
        "required": [
          "name",
          "status",
          "reach",
          "ms"
        ]
      },
      "AiUsage": {
        "type": "object",
        "properties": {
          "inputTokens": {
            "type": "integer",
            "example": 2785
          },
          "outputTokens": {
            "type": "integer",
            "example": 96
          },
          "neurons": {
            "type": "number",
            "description": "Workers AI's billing unit, as reported with the answer. Approximate; not an invoice.",
            "example": 100.4
          }
        },
        "required": [
          "inputTokens",
          "outputTokens"
        ]
      },
      "AiResponse": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "example": "Go to https://id.rsx.group/profile. The Profile picture panel is at the top."
          },
          "reasoning": {
            "type": "string"
          },
          "finishReason": {
            "type": "string",
            "enum": [
              "stop",
              "length",
              "step-limit"
            ],
            "description": "`stop`: the model answered. `length`: the answer hit `maxOutputTokens`. `step-limit`: the model answered without tools it was still using, because `maxSteps` ran out."
          },
          "model": {
            "type": "string",
            "description": "The model that answered, always the resolved id, so a caller on a tier can see the tier move.",
            "example": "@cf/openai/gpt-oss-120b"
          },
          "steps": {
            "type": "integer",
            "example": 1
          },
          "toolCalls": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AiToolCall"
            }
          },
          "usage": {
            "$ref": "#/components/schemas/AiUsage"
          }
        },
        "required": [
          "text",
          "finishReason",
          "model",
          "steps",
          "toolCalls",
          "usage"
        ]
      },
      "AiMessage": {
        "type": "object",
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "user",
              "assistant"
            ]
          },
          "content": {
            "type": "string",
            "minLength": 1,
            "maxLength": 16000
          }
        },
        "required": [
          "role",
          "content"
        ]
      },
      "AiPersona": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "What the assistant calls itself.",
            "example": "RSX Help"
          },
          "tone": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "example": "friendly and brief"
          },
          "language": {
            "type": "string",
            "minLength": 1,
            "maxLength": 32,
            "description": "Reply in this language whatever the person writes in. Omit to mirror them.",
            "example": "English"
          },
          "instructions": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8000,
            "description": "Free-form operator instructions. They cannot override the platform's rules for assistants."
          }
        }
      },
      "AiKnowledge": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[a-z0-9-]{1,48}$",
            "example": "opening-hours"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "example": "Support hours"
          },
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8000,
            "example": "Support replies within one working day."
          },
          "url": {
            "type": "string",
            "maxLength": 2048,
            "format": "uri"
          },
          "keywords": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            },
            "maxItems": 20
          }
        },
        "required": [
          "title",
          "body"
        ]
      },
      "AiBoundaries": {
        "type": "object",
        "properties": {
          "never": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "maxItems": 30,
            "description": "Topics the assistant must not discuss. A prompt, not a guarantee: keep anything secret out of the request entirely.",
            "example": [
              "unreleased products"
            ]
          },
          "refusal": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "What the assistant replies, word for word, when a message touches one of those topics.",
            "example": "That is not something I can discuss."
          },
          "stayOnTopic": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "minLength": 1,
                "maxLength": 100
              }
            ],
            "description": "Decline anything off one subject. `true` means RSX when `awareness` is `rsx`."
          }
        }
      },
      "AiContext": {
        "type": "object",
        "properties": {
          "surface": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "example": "RSX ID (id.rsx.group)"
          },
          "page": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "example": "https://id.rsx.group/security"
          },
          "viewer": {
            "type": "object",
            "additionalProperties": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 200
                },
                {
                  "type": "number"
                },
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                },
                {
                  "type": "null"
                }
              ]
            },
            "description": "Non-secret facts about the person asking, such as their handle or whether their email is verified. Never a token, key or password.",
            "example": {
              "handle": "sam",
              "developer": false
            }
          }
        }
      },
      "AiToolRequest": {
        "anyOf": [
          {
            "$ref": "#/components/schemas/AiToolName"
          },
          {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "enum": [
                  "web_fetch"
                ]
              },
              "allowedHosts": {
                "type": "array",
                "items": {
                  "type": "string",
                  "pattern": "^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$"
                },
                "minItems": 1,
                "maxItems": 20,
                "description": "The only sites (and their subdomains) the assistant may read, redirects included.",
                "example": [
                  "developers.cloudflare.com"
                ]
              }
            },
            "required": [
              "name",
              "allowedHosts"
            ]
          }
        ]
      },
      "AiLimits": {
        "type": "object",
        "properties": {
          "maxSteps": {
            "type": "integer",
            "minimum": 1,
            "maximum": 8,
            "description": "Model calls in the turn, including the one that answers. Tools need at least 2. Default 6.",
            "example": 4
          },
          "maxOutputTokens": {
            "type": "integer",
            "minimum": 256,
            "maximum": 8192,
            "description": "Per model call. Reasoning counts against it. Default 4096.",
            "example": 2048
          }
        }
      },
      "AiRespond": {
        "type": "object",
        "properties": {
          "model": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "default": "balanced",
            "description": "A tier (`fast`, `balanced`, `deep`) or a model id from `models/list`.",
            "example": "balanced"
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AiMessage"
            },
            "minItems": 1,
            "maxItems": 50,
            "description": "The conversation so far, oldest first. The service keeps none of it."
          },
          "awareness": {
            "type": "string",
            "enum": [
              "none",
              "rsx"
            ],
            "default": "none",
            "description": "`rsx` gives the assistant RSX's identity and knowledge of how its products work."
          },
          "audience": {
            "type": "string",
            "enum": [
              "public",
              "staff"
            ],
            "default": "public",
            "description": "Which RSX knowledge it may use. `staff` needs the `ai:knowledge:staff` scope."
          },
          "persona": {
            "$ref": "#/components/schemas/AiPersona"
          },
          "knowledge": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AiKnowledge"
            },
            "maxItems": 50
          },
          "boundaries": {
            "$ref": "#/components/schemas/AiBoundaries"
          },
          "context": {
            "$ref": "#/components/schemas/AiContext"
          },
          "tools": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AiToolRequest"
            },
            "maxItems": 3
          },
          "thinking": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AiThinkingLevel"
              },
              {
                "type": "string",
                "description": "How much the model reasons before it answers. `off` is best-effort on models that always reason; asking a model that does not reason for any other level is refused."
              }
            ]
          },
          "limits": {
            "$ref": "#/components/schemas/AiLimits"
          },
          "includeReasoning": {
            "type": "boolean",
            "default": false,
            "description": "Return the model's reasoning with the answer. It is written for the model, not for people."
          },
          "stream": {
            "type": "boolean",
            "default": false,
            "description": "Answer as `text/event-stream` instead of one JSON document, so text arrives as the model writes it. The events are described on the 200 response."
          }
        },
        "required": [
          "messages"
        ]
      },
      "AiBudget": {
        "type": "object",
        "properties": {
          "keyId": {
            "type": "string",
            "example": "0198f2b1-6c2a-7c9e-9d3a-1e2f3a4b5c6d"
          },
          "dailyNeurons": {
            "type": [
              "integer",
              "null"
            ],
            "description": "This key's own daily budget. `null` follows the default. `0` switches the key off.",
            "example": 200000
          },
          "maxConcurrent": {
            "type": [
              "integer",
              "null"
            ],
            "description": "This key's own cap on turns in flight. `null` follows the default.",
            "example": null
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "example": "Help box on rsx.group; raised for launch week."
          },
          "effectiveDailyNeurons": {
            "type": "integer",
            "example": 200000
          },
          "effectiveMaxConcurrent": {
            "type": "integer",
            "example": 8
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedBy": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "keyId",
          "dailyNeurons",
          "maxConcurrent",
          "note",
          "effectiveDailyNeurons",
          "effectiveMaxConcurrent",
          "updatedAt",
          "updatedBy"
        ]
      },
      "AiDayUsage": {
        "type": "object",
        "properties": {
          "day": {
            "type": "string",
            "description": "A UTC date.",
            "example": "2026-09-16"
          },
          "turns": {
            "type": "integer",
            "example": 412
          },
          "refused": {
            "type": "integer",
            "description": "Turns refused for the budget or the concurrency cap.",
            "example": 0
          },
          "neurons": {
            "type": "number",
            "example": 38211.5
          },
          "inputTokens": {
            "type": "integer"
          },
          "outputTokens": {
            "type": "integer"
          }
        },
        "required": [
          "day",
          "turns",
          "refused",
          "neurons",
          "inputTokens",
          "outputTokens"
        ]
      },
      "AiKeyUsage": {
        "type": "object",
        "properties": {
          "keyId": {
            "type": "string"
          },
          "budget": {
            "$ref": "#/components/schemas/AiBudget"
          },
          "inFlight": {
            "type": "integer",
            "example": 1
          },
          "today": {
            "$ref": "#/components/schemas/AiDayUsage"
          },
          "days": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AiDayUsage"
            },
            "description": "Newest first. Days without a turn are absent."
          },
          "lastTurnAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "keyId",
          "budget",
          "inFlight",
          "today",
          "days",
          "lastTurnAt"
        ]
      },
      "AiUsageList": {
        "type": "object",
        "properties": {
          "day": {
            "type": "string",
            "example": "2026-09-16"
          },
          "resetsAt": {
            "type": "string",
            "format": "date-time",
            "description": "When today's budgets reset: the next midnight UTC."
          },
          "defaults": {
            "type": "object",
            "properties": {
              "dailyNeurons": {
                "type": "integer",
                "example": 50000
              },
              "maxConcurrent": {
                "type": "integer",
                "example": 8
              }
            },
            "required": [
              "dailyNeurons",
              "maxConcurrent"
            ]
          },
          "keys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AiKeyUsage"
            },
            "description": "Every key with a turn in the period or a budget of its own, most spent today first."
          }
        },
        "required": [
          "day",
          "resetsAt",
          "defaults",
          "keys"
        ]
      },
      "AiAuditEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "018f3b1c-9a2e-7c31-8f4d-2a6b5c7e9d12"
          },
          "actor": {
            "type": "string",
            "example": "ops@rsx.group"
          },
          "action": {
            "type": "string",
            "enum": [
              "budget.updated"
            ]
          },
          "targetType": {
            "type": "string",
            "enum": [
              "key"
            ]
          },
          "targetId": {
            "type": "string"
          },
          "targetLabel": {
            "type": [
              "string",
              "null"
            ]
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-09-12T10:16:30.000Z"
          },
          "detail": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "actor",
          "action",
          "targetType",
          "targetId",
          "targetLabel",
          "at",
          "detail"
        ]
      },
      "AiUpdateBudget": {
        "type": "object",
        "properties": {
          "keyId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "keyLabel": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 120,
            "default": null,
            "description": "The key's name, recorded with the change so the trail still reads after the key is revoked."
          },
          "dailyNeurons": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 100000000
          },
          "maxConcurrent": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 100
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 280,
            "default": null
          }
        },
        "required": [
          "keyId",
          "dailyNeurons",
          "maxConcurrent"
        ]
      }
    },
    "parameters": {}
  },
  "paths": {
    "/ai/v1/models/list": {
      "get": {
        "operationId": "listAiModels",
        "summary": "List the tiers and the models behind them",
        "description": "Every enabled text model, with whether it can use tools and how its reasoning can be steered, and the three tiers. Prefer a tier: a tier is a promise about cost and capability that outlives any one model id.",
        "tags": [
          "models"
        ],
        "responses": {
          "200": {
            "description": "Tiers and models",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AiModelList"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No platform API key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks this endpoint's scope",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/ai/v1/tools/list": {
      "get": {
        "operationId": "listAiTools",
        "summary": "List the tools an assistant can be given",
        "description": "Every tool is read-only. Knowledge lookups are not listed because nobody asks for them: an assistant gets them whenever its knowledge is too large for the prompt.",
        "tags": [
          "tools"
        ],
        "responses": {
          "200": {
            "description": "Tools",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AiTool"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No platform API key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks this endpoint's scope",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/ai/v1/chat/respond": {
      "post": {
        "operationId": "respondAiChat",
        "summary": "Run an assistant for one turn",
        "description": "Send the conversation so far; the assistant reasons, uses any tools it was given, and answers. Nothing is stored, so send the whole conversation every turn.\n\nEach key has a daily neuron budget and a cap on turns in flight; a turn past either is refused with 429 and `Retry-After`.\n\nNot idempotent and not an `add`: the same request can be answered differently, and each call is billed.",
        "tags": [
          "chat"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AiRespond"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The assistant's answer: JSON, or with `stream: true` an event stream",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AiResponse"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-sent events, one JSON object per `data` line:\n\n- `text`: `{ text }`, the next piece of the answer.\n- `reasoning`: `{ text }`, only with `includeReasoning`.\n- `tool-call`: `{ name, reach }`, a tool the assistant is using. Arguments are not sent.\n- `tool-result`: `{ name, status, reach, ms }`.\n- `done`: the same object `data` holds in a JSON answer. The last event of a turn that succeeded.\n- `error`: a problem document (`type`, `title`, `status`, `detail`). The last event of a turn that failed after the model had started answering.\n\nA turn that fails before the model starts answering (an invalid request, a missing scope, a model that is not available, a refusal by the content policy) is an ordinary problem response with its own status, not a stream.\nComment lines (`: ping`) keep the connection open while the model reasons. Closing the connection ends the turn and stops the model."
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, a model that is not available, or a capability the model does not have",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No platform API key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks this endpoint's scope",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "The AI Gateway's content policy refused the conversation or the reply",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "The key's daily budget is spent, or it has too many turns in flight",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "502": {
            "description": "Workers AI failed, or the model finished without writing an answer",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "The usage ledger could not answer, so no turn was run",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/ai/v1/admin/usage/list": {
      "get": {
        "operationId": "listAiUsage",
        "summary": "Usage and budgets, per key",
        "description": "Every key that ran or was refused a turn in the period, or has a budget of its own: today's spend against its budget, turns in flight, and one row per day.",
        "tags": [
          "admin"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90,
              "default": 7,
              "example": 7
            },
            "required": false,
            "name": "days",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AiUsageList"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or wrong platform admin token",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Not called over the dashboard's service binding",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "The usage ledger could not answer",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/ai/v1/admin/budgets/update": {
      "post": {
        "operationId": "updateAiBudget",
        "summary": "Set a key's budget",
        "description": "Sets this key's daily neuron budget and its cap on turns in flight; `null` for either follows the default. Takes effect on the key's next turn. Recorded in the trail with the operator and the before and after.",
        "tags": [
          "admin"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AiUpdateBudget"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The key's budget as it now stands",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AiBudget"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "event": {
                          "$ref": "#/components/schemas/AiAuditEvent"
                        }
                      },
                      "required": [
                        "event"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or wrong platform admin token",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Not called over the dashboard's service binding",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "The usage ledger could not answer",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/ai/v1/admin/audit/list": {
      "get": {
        "operationId": "listAiAudit",
        "summary": "The operator trail",
        "description": "Every budget change, newest first. Merged into the dashboard's Activity feed.",
        "tags": [
          "admin"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "key"
              ]
            },
            "required": false,
            "name": "targetType",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "required": false,
            "name": "targetId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 320
            },
            "required": false,
            "name": "actor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "budget.updated"
              ]
            },
            "required": false,
            "name": "action",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "example": 0
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "example": 50
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Events",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AiAuditEvent"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "offset": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer"
                        },
                        "more": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "offset",
                        "limit",
                        "more"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or wrong platform admin token",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Not called over the dashboard's service binding",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "The usage ledger could not answer",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {}
}
