{
  "openapi": "3.1.0",
  "info": {
    "title": "Agent Charting Developer API",
    "version": "2026-10-17",
    "description": "Chart images, external-agent drawings, and explicitly requested managed chart analysis. Available on configured local, self-hosted, and private installations. The public prelaunch site does not provision API accounts or expose these routes. Historical data is provider-dependent; no API hostname, hosted OAuth service, brokerage execution, or billing service is implied."
  },
  "servers": [
    {
      "url": "http://127.0.0.1:8789",
      "description": "Default local API. Replace with the authenticated API origin supplied by your installation."
    }
  ],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    {
      "name": "Charts",
      "description": "Render chart images and commands without a managed model call."
    },
    {
      "name": "Analysis",
      "description": "Explicitly run the managed chart agent and continue its session."
    },
    {
      "name": "Artifacts",
      "description": "Owned PNG images. Bearer authorization is required on downloads."
    },
    { "name": "Capabilities" },
    { "name": "Operations", "description": "Low-cost liveness/readiness probes and protected operator diagnostics. No model or market calls." }
  ],
  "paths": {
    "/livez": {
      "get": {
        "tags": ["Operations"], "operationId": "getLiveness", "summary": "Check HTTP process liveness", "security": [], "x-required-scopes": [],
        "description": "Minimal unauthenticated process probe. Does not claim the database, renderer, feed or model is healthy. /health is a compatibility alias. HEAD is also supported.",
        "responses": { "200": { "description": "HTTP process is alive", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthProbe" } } } } }
      }
    },
    "/readyz": {
      "get": {
        "tags": ["Operations"], "operationId": "getReadiness", "summary": "Check whether this API can accept work", "security": [], "x-required-scopes": [],
        "description": "Checks local database read/write availability, worker lease and renderer configuration. Returns 503 while unready or draining. Does not launch Chromium or call market/model providers. HEAD is also supported.",
        "responses": {
          "200": { "description": "Ready", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthProbe" } } } },
          "503": { "description": "Not ready; Retry-After is supplied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthProbe" } } } }
        }
      }
    },
    "/v1/health": {
      "get": {
        "tags": ["Operations"], "operationId": "getOperatorHealth", "summary": "Read protected operator diagnostics", "x-required-scopes": ["audit:read"],
        "description": "Sanitized database, worker, renderer, queue and stream status. Requires audit:read; consumes zero submission units. No paths, tokens, provider credentials or model calls. HEAD is also supported.",
        "responses": {
          "200": { "description": "Ready with diagnostic checks", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthDetail" } } } },
          "503": { "description": "Unready with diagnostic checks", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthDetail" } } } },
          "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/capabilities": {
      "get": {
        "operationId": "getCapabilities",
        "tags": ["Capabilities"],
        "summary": "Read supported image and job capabilities",
        "description": "Requires chart:read. Returns the configured server's API limits and managed-analysis availability.",
        "x-required-scopes": ["chart:read"],
        "responses": {
          "200": {
            "description": "Capability metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": { "const": "api.capabilities" },
                    "version": { "type": "string" },
                    "charts": { "type": "object" },
                    "analysis": { "type": "object" },
                    "artifacts": { "type": "object" },
                    "jobs": { "type": "object" },
                    "dataProviders": {
                      "type": "object",
                      "description": "Installation-specific market availability, not a license or a guarantee of every symbol/date.",
                      "properties": {
                        "public": { "type": "array", "items": { "type": "string" } },
                        "connectedFeed": { "type": "string", "example": "date_anchored_provider_dependent" },
                        "customerCandles": { "type": "boolean" }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/charts/renders": {
      "post": {
        "operationId": "createChartRender",
        "tags": ["Charts"],
        "summary": "Render a chart and optional external-agent commands",
        "description": "Requires market:read and chart:read; commands additionally require chart:write. Does not invoke the managed model or mutate an open terminal. Synchronous by default while worker capacity is available. Prefer: respond-async returns a queued job. Prefer: wait=N waits at most 0–10 seconds and then returns 202 if unfinished; that wait never cancels a job.",
        "x-required-scopes": ["market:read", "chart:read"],
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" },
          { "$ref": "#/components/parameters/Prefer" },
          { "$ref": "#/components/parameters/RequestId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RenderRequest" },
              "example": {
                "market": {
                  "symbol": "BINANCE:BTCUSDT",
                  "interval": "60",
                  "range": { "bars": 320 }
                },
                "commands": [],
                "view": { "width": 1440, "height": 900, "theme": "dark" },
                "delivery": { "mode": "artifact" }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Completed render. Accept: image/png requests direct bytes; inspect the content type because queued requests remain JSON.",
            "headers": {
              "Location": { "$ref": "#/components/headers/Location" },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              },
              "X-Render-Id": { "schema": { "type": "string" } },
              "X-Artifact-Id": { "schema": { "type": "string" } }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Job" }
              },
              "image/png": {
                "schema": { "type": "string", "format": "binary" }
              }
            }
          },
          "202": { "$ref": "#/components/responses/Accepted" },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "415": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/FailedRender" },
          "429": { "$ref": "#/components/responses/Error" },
          "502": { "$ref": "#/components/responses/FailedRender" },
          "503": { "$ref": "#/components/responses/FailedRender" }
        }
      }
    },
    "/v1/charts/renders/{renderId}": {
      "parameters": [
        {
          "name": "renderId",
          "in": "path",
          "required": true,
          "schema": { "type": "string", "pattern": "^render_[a-f0-9-]+$" }
        }
      ],
      "get": {
        "operationId": "getChartRender",
        "tags": ["Charts"],
        "summary": "Read an owned render job",
        "description": "Requires chart:read. HTTP 200 means the job exists; inspect its status for completion or failure.",
        "x-required-scopes": ["chart:read"],
        "responses": {
          "200": { "$ref": "#/components/responses/Job" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/analysis/runs": {
      "post": {
        "operationId": "createAnalysisRun",
        "tags": ["Analysis"],
        "summary": "Ask the managed chart agent to analyze and draw",
        "description": "Requires market:read chart:read chart:write agent:run memory:write setup:write. The agent receives an image of the exact requested candle window using the existing brain and tools. Stores input and result PNGs. Queued by default. Client disconnect does not cancel the job. Historical candle cutoffs do not remove a model's knowledge of later events and do not establish a lookahead-free backtest.",
        "x-required-scopes": [
          "market:read",
          "chart:read",
          "chart:write",
          "agent:run",
          "memory:write",
          "setup:write"
        ],
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" },
          { "$ref": "#/components/parameters/Prefer" },
          { "$ref": "#/components/parameters/RequestId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/AnalysisRequest" },
              "example": {
                "market": {
                  "symbol": "BINANCE:BTCUSDT",
                  "interval": "60",
                  "range": { "bars": 320 }
                },
                "instruction": {
                  "mode": "prompted",
                  "prompt": "Review the visible structure, draw useful decision levels and explain the preferred scenario and invalidation."
                }
              }
            }
          }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/Job" },
          "202": { "$ref": "#/components/responses/Accepted" },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "415": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/analysis/runs/{runId}": {
      "parameters": [{ "$ref": "#/components/parameters/RunId" }],
      "get": {
        "operationId": "getAnalysisRun",
        "tags": ["Analysis"],
        "summary": "Read an owned managed-analysis job",
        "description": "Requires chart:read. Inspect status and error; an existing failed job still returns HTTP 200.",
        "x-required-scopes": ["chart:read"],
        "responses": {
          "200": { "$ref": "#/components/responses/Job" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/analysis/runs/{runId}/messages": {
      "parameters": [{ "$ref": "#/components/parameters/RunId" }],
      "post": {
        "operationId": "continueAnalysisRun",
        "tags": ["Analysis"],
        "summary": "Continue a completed chart-agent conversation",
        "description": "Requires the same scopes as managed creation. Creates a new immutable run in the same owned session, preserving the parent's exact frozen chart snapshot and commands unless market is explicitly overridden. Changing symbol or interval discards inherited drawings. A completed parent is required.",
        "x-required-scopes": [
          "market:read",
          "chart:read",
          "chart:write",
          "agent:run",
          "memory:write",
          "setup:write"
        ],
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" },
          { "$ref": "#/components/parameters/Prefer" },
          { "$ref": "#/components/parameters/RequestId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/MessageRequest" },
              "example": {
                "prompt": "Explain that invalidation and revise the chart if it is too tight."
              }
            }
          }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/Job" },
          "202": { "$ref": "#/components/responses/Accepted" },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/analysis/runs/{runId}/events": {
      "parameters": [{ "$ref": "#/components/parameters/RunId" }],
      "get": {
        "operationId": "streamAnalysisEvents",
        "tags": ["Analysis"],
        "summary": "Stream durable job progress",
        "description": "Requires chart:read. Event IDs are ascending sequence numbers; Last-Event-ID resumes after a delivered sequence. The stream closes when the job becomes terminal. Disconnecting does not cancel the job.",
        "x-required-scopes": ["chart:read"],
        "parameters": [
          {
            "name": "Last-Event-ID",
            "in": "header",
            "schema": { "type": "string", "pattern": "^[0-9]{1,10}$" }
          }
        ],
        "responses": {
          "200": {
            "description": "Server-Sent Events: queued, running, progress and terminal status events.",
            "content": {
              "text/event-stream": {
                "schema": { "type": "string" },
                "example": "id: 1\nevent: queued\ndata: {\"sequence\":1,\"type\":\"queued\",\"status\":\"queued\"}\n\n"
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/analysis/runs/{runId}/cancel": {
      "parameters": [{ "$ref": "#/components/parameters/RunId" }],
      "post": {
        "operationId": "cancelAnalysisRun",
        "tags": ["Analysis"],
        "summary": "Explicitly cancel an owned job",
        "description": "Requires chart:read and agent:run. Does not replay a terminal job or guarantee a refund for work already performed by a model provider.",
        "x-required-scopes": ["chart:read", "agent:run"],
        "responses": {
          "200": { "$ref": "#/components/responses/Job" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/artifacts/{imageId}": {
      "parameters": [
        {
          "name": "imageId",
          "in": "path",
          "required": true,
          "schema": { "type": "string", "pattern": "^img_[a-f0-9-]+$" }
        }
      ],
      "get": {
        "operationId": "getChartImage",
        "tags": ["Artifacts"],
        "summary": "Download an owned PNG",
        "description": "Requires chart:read and the same workspace. The URL is not public or signed. Default retention is seven days; expired and other-workspace resources return 404.",
        "x-required-scopes": ["chart:read"],
        "responses": {
          "200": {
            "description": "PNG bytes",
            "headers": {
              "ETag": {
                "schema": { "type": "string" },
                "description": "Quoted SHA-256 of the image bytes"
              }
            },
            "content": {
              "image/png": {
                "schema": { "type": "string", "format": "binary" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      },
      "head": {
        "operationId": "headChartImage",
        "tags": ["Artifacts"],
        "summary": "Read owned PNG metadata headers",
        "x-required-scopes": ["chart:read"],
        "responses": {
          "200": {
            "description": "Image content type, length and ETag without a body"
          },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Existing workspace-scoped API key, or a valid audience-bound access token from a configured authorization server. Public hosted OAuth provisioning is not implied."
      }
    },
    "parameters": {
      "RunId": {
        "name": "runId",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "pattern": "^run_[a-f0-9-]+$" }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "description": "Safe retry key scoped to the workspace and operation. Same normalized request returns the original job; changed request returns 409. Expired keys remain tombstones.",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9_.:-]+$"
        }
      },
      "Prefer": {
        "name": "Prefer",
        "in": "header",
        "description": "respond-async returns without waiting. wait=N waits up to 0–10 seconds for completion, then returns 202 if unfinished. Neither preference cancels the job.",
        "schema": { "type": "string" },
        "examples": {
          "async": { "value": "respond-async" },
          "wait": { "value": "wait=5" }
        }
      },
      "RequestId": {
        "name": "X-Request-Id",
        "in": "header",
        "description": "Optional request identifier; returned in response headers and job/error metadata.",
        "schema": { "type": "string" }
      }
    },
    "headers": {
      "Location": {
        "description": "Relative job polling URL on this API origin",
        "schema": { "type": "string" }
      },
      "IdempotencyReplayed": {
        "description": "Whether an existing job was returned",
        "schema": { "type": "string", "enum": ["true", "false"] }
      }
    },
    "responses": {
      "Job": {
        "description": "Owned job. Inspect status; completed image and analysis fields appear only when available.",
        "headers": {
          "Retry-After": {
            "schema": { "type": "string" },
            "description": "Suggested polling delay in seconds for unfinished jobs"
          }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Job" }
          }
        }
      },
      "Accepted": {
        "description": "Durable job accepted. Follow Location or links.self. Client disconnect does not cancel processing.",
        "headers": {
          "Location": { "$ref": "#/components/headers/Location" },
          "Retry-After": { "schema": { "type": "string", "example": "2" } },
          "Idempotency-Replayed": {
            "$ref": "#/components/headers/IdempotencyReplayed"
          }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Job" }
          }
        }
      },
      "Error": {
        "description": "Request rejected. Developer endpoints return a structured error with a code, message, and request ID. Older terminal workspace endpoints have a separate compatibility contract.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "FailedRender": {
        "description": "Synchronous render failed with a stored failed-job object, or the request was rejected before creation.",
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                { "$ref": "#/components/schemas/Job" },
                { "$ref": "#/components/schemas/Error" }
              ]
            }
          }
        }
      }
    },
    "schemas": {
      "HealthProbe": {
        "type": "object", "required": ["ok", "status", "service"],
        "properties": { "ok": { "type": "boolean" }, "status": { "type": "string" }, "service": { "const": "agent-charting-api" } }, "additionalProperties": false
      },
      "HealthDetail": {
        "type": "object", "required": ["ready", "status", "checks", "jobs", "requestId"],
        "properties": { "service": { "const": "agent-charting-api" }, "ready": { "type": "boolean" }, "status": { "type": "string" }, "checks": { "type": "object", "description": "Sanitized database, worker and renderer checks. Renderer verification is configuration_only." }, "jobs": { "type": "object", "description": "Queue and local worker counts, not customer data." }, "streams": { "type": "object", "description": "Per-process SSE capacity and active subscriptions." }, "requestId": { "type": "string" } }
      },
      "Candle": {
        "type": "object",
        "required": ["time", "open", "high", "low", "close"],
        "properties": {
          "time": {
            "type": "integer",
            "minimum": 1,
            "description": "Ascending unique Unix timestamp in seconds"
          },
          "open": { "type": "number", "exclusiveMinimum": 0 },
          "high": { "type": "number", "exclusiveMinimum": 0 },
          "low": { "type": "number", "exclusiveMinimum": 0 },
          "close": { "type": "number", "exclusiveMinimum": 0 },
          "volume": { "type": "number", "minimum": 0, "default": 0 }
        }
      },
      "Range": {
        "oneOf": [
          {
            "type": "object",
            "required": ["bars"],
            "additionalProperties": false,
            "properties": {
              "bars": {
                "type": "integer",
                "minimum": 2,
                "maximum": 2000,
                "default": 320
              }
            }
          },
          {
            "type": "object",
            "required": ["from", "to"],
            "additionalProperties": false,
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time",
                "description": "Inclusive RFC3339 start, with timezone"
              },
              "to": {
                "type": "string",
                "format": "date-time",
                "description": "Exclusive RFC3339 end, with timezone"
              }
            }
          }
        ]
      },
      "Market": {
        "type": "object",
        "required": ["symbol"],
        "properties": {
          "symbol": {
            "type": "string",
            "example": "BINANCE:BTCUSDT",
            "pattern": "^[A-Za-z0-9_.-]{1,32}:[A-Za-z0-9_.!/-]{1,64}$",
            "description": "Exchange-qualified symbol"
          },
          "interval": {
            "type": "string",
            "enum": ["1", "5", "15", "60", "240", "D"],
            "default": "60"
          },
          "range": { "$ref": "#/components/schemas/Range" },
          "source": {
            "type": "string",
            "enum": ["provided", "binance", "workspace"],
            "description": "Inferred from supplied bars/symbol if omitted. Binance supports BTCUSDT/ETHUSDT/SOLUSDT spot history. The configured workspace feed accepts date-anchored requests with provider-dependent instrument/history availability and provider-reported coverage, not consolidated-feed or licensing guarantees."
          },
          "dataSource": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "Caller-provided source attribution for supplied candles"
          },
          "bars": {
            "type": "array",
            "minItems": 2,
            "maxItems": 2000,
            "items": { "$ref": "#/components/schemas/Candle" }
          }
        }
      },
      "View": {
        "type": "object",
        "properties": {
          "width": {
            "type": "integer",
            "minimum": 640,
            "maximum": 2560,
            "default": 1440
          },
          "height": {
            "type": "integer",
            "minimum": 360,
            "maximum": 1600,
            "default": 900
          },
          "format": { "const": "png", "default": "png" },
          "theme": {
            "type": "string",
            "enum": ["dark", "light"],
            "default": "dark"
          },
          "includeVolume": { "type": "boolean", "default": true }
        }
      },
      "Point": {
        "type": "object",
        "required": ["time", "price"],
        "properties": {
          "time": { "type": "number" },
          "price": { "type": "number" }
        }
      },
      "Command": {
        "type": "object",
        "required": ["type"],
        "description": "Existing validated application chart-command vocabulary. Fields depend on type. Navigation and broker commands are not accepted for image jobs.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "draw_horizontal",
              "draw_trend",
              "draw_fib",
              "draw_rectangle",
              "draw_zone",
              "draw_text",
              "draw_marker",
              "render_chart_scene",
              "add_indicator",
              "clear_drawings",
              "fit_chart",
              "inspect_chart"
            ]
          },
          "commandId": { "type": "string" },
          "replacesCommandId": { "type": "string" },
          "price": { "type": "number" },
          "role": { "type": "string" },
          "label": { "type": "string" },
          "text": { "type": "string" },
          "time": { "type": "number" },
          "a": { "$ref": "#/components/schemas/Point" },
          "b": { "$ref": "#/components/schemas/Point" },
          "scene": { "type": "array", "items": { "type": "object" } },
          "kind": {
            "type": "string",
            "description": "Indicator kind, such as SMA, EMA, RSI or ATR"
          },
          "period": { "type": "integer" }
        },
        "additionalProperties": true
      },
      "Delivery": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": ["artifact", "inline_base64"],
            "default": "artifact"
          }
        }
      },
      "Instruction": {
        "type": "object",
        "description": "prompt is required except when mode is professional_auto.",
        "properties": {
          "mode": {
            "type": "string",
            "enum": ["prompted", "professional_auto", "respond_only"],
            "default": "prompted"
          },
          "prompt": { "type": "string", "maxLength": 20000 }
        }
      },
      "Model": {
        "type": "object",
        "required": ["provider"],
        "properties": {
          "provider": { "type": "string", "enum": ["openai", "grok"] },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "pattern": "^[A-Za-z0-9_.:/-]{1,120}$",
            "description": "A model supported by the installation's configured provider integration"
          }
        }
      },
      "RenderRequest": {
        "type": "object",
        "required": ["market"],
        "properties": {
          "market": { "$ref": "#/components/schemas/Market" },
          "view": { "$ref": "#/components/schemas/View" },
          "commands": {
            "type": "array",
            "maxItems": 100,
            "items": { "$ref": "#/components/schemas/Command" }
          },
          "overlays": {
            "type": "array",
            "maxItems": 100,
            "description": "Alias for commands. Do not supply both.",
            "items": { "$ref": "#/components/schemas/Command" }
          },
          "delivery": { "$ref": "#/components/schemas/Delivery" }
        }
      },
      "AnalysisRequest": {
        "allOf": [
          { "$ref": "#/components/schemas/RenderRequest" },
          {
            "type": "object",
            "properties": {
              "instruction": { "$ref": "#/components/schemas/Instruction" },
              "prompt": {
                "type": "string",
                "maxLength": 20000,
                "description": "Convenience alias for instruction.prompt"
              },
              "model": { "$ref": "#/components/schemas/Model" },
              "sessionId": {
                "type": "string",
                "pattern": "^api_sess_[a-f0-9-]{36}$",
                "description": "A session returned by an earlier owned analysis run"
              }
            }
          }
        ]
      },
      "MessageRequest": {
        "type": "object",
        "description": "Provide prompt or instruction. Other fields are optional overrides. Without a market override, the exact parent candle snapshot is retained.",
        "properties": {
          "prompt": { "type": "string", "minLength": 1, "maxLength": 20000 },
          "instruction": { "$ref": "#/components/schemas/Instruction" },
          "market": { "$ref": "#/components/schemas/Market" },
          "view": { "$ref": "#/components/schemas/View" },
          "commands": {
            "type": "array",
            "maxItems": 100,
            "items": { "$ref": "#/components/schemas/Command" }
          },
          "model": { "$ref": "#/components/schemas/Model" },
          "delivery": { "$ref": "#/components/schemas/Delivery" }
        }
      },
      "Image": {
        "type": "object",
        "required": [
          "id",
          "url",
          "mimeType",
          "width",
          "height",
          "sha256",
          "expiresAt",
          "authentication"
        ],
        "properties": {
          "id": { "type": "string" },
          "object": { "const": "chart.image" },
          "url": {
            "type": "string",
            "description": "Relative authenticated API artifact URL"
          },
          "mimeType": { "const": "image/png" },
          "width": { "type": "integer" },
          "height": { "type": "integer" },
          "sha256": { "type": "string" },
          "byteLength": { "type": "integer" },
          "expiresAt": { "type": "string", "format": "date-time" },
          "authentication": { "const": "bearer" },
          "base64": {
            "type": "string",
            "format": "byte",
            "description": "Only for inline_base64 delivery"
          }
        }
      },
      "JobError": {
        "type": "object",
        "required": ["code", "message"],
        "properties": {
          "code": { "type": "string" },
          "message": { "type": "string" },
          "httpStatus": { "type": "integer" },
          "param": { "type": "string" }
        }
      },
      "Job": {
        "type": "object",
        "required": [
          "id",
          "object",
          "status",
          "requestId",
          "createdAt",
          "expiresAt",
          "links"
        ],
        "properties": {
          "id": { "type": "string" },
          "object": {
            "type": "string",
            "enum": ["chart.render", "analysis.run"]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "completed",
              "failed",
              "cancelled",
              "interrupted"
            ]
          },
          "sessionId": { "type": "string" },
          "requestId": { "type": "string" },
          "createdAt": { "type": "string", "format": "date-time" },
          "startedAt": { "type": "string", "format": "date-time" },
          "completedAt": { "type": "string", "format": "date-time" },
          "expiresAt": { "type": "string", "format": "date-time" },
          "links": {
            "type": "object",
            "required": ["self"],
            "properties": {
              "self": { "type": "string" },
              "events": { "type": "string" },
              "messages": { "type": "string" }
            }
          },
          "market": {
            "type": "object",
            "description": "Requested/actual ranges and actual provider attribution. A requested listing can resolve to a different feed instrument; inspect data.resolvedSymbol and feed rather than assuming consolidated exchange coverage.",
            "properties": {
              "symbol": { "type": "string", "description": "Requested exchange-qualified symbol." },
              "interval": { "type": "string" },
              "source": { "type": "string" },
              "requestedRange": { "$ref": "#/components/schemas/Range" },
              "actualRange": {
                "type": "object",
                "properties": {
                  "from": { "type": "string", "format": "date-time" },
                  "lastCandle": { "type": "string", "format": "date-time", "description": "Opening timestamp of the final included candle." },
                  "to": { "type": "string", "format": "date-time", "description": "Actual closing boundary of the final included candle, which can precede the requested cutoff." },
                  "bars": { "type": "integer", "minimum": 2, "maximum": 2000 },
                  "completedCandlesOnly": { "type": "boolean", "description": "Whether adapter metadata or explicit candle-close times establish that all returned candles are closed by the cutoff. Built-in market resolvers establish this before rendering." }
                }
              },
              "asOf": { "type": "string", "format": "date-time", "description": "Requested cutoff, not necessarily the final market-session close." },
              "receivedAt": { "type": "string", "format": "date-time" },
              "data": {
                "type": "object",
                "description": "Adapter-specific metadata. Connected-feed historical coverage checks every expected completed candle against the provider-reported session calendar, rejecting missing first/interior/final candles; it is not independently exchange-certified.",
                "properties": {
                  "provider": { "type": "string" },
                  "requestedSymbol": { "type": "string", "example": "NASDAQ:AAPL" },
                  "resolvedSymbol": { "type": "string", "example": "BATS:AAPL" },
                  "feed": { "type": "string" },
                  "listedExchange": { "type": ["string", "null"] },
                  "adjustment": { "type": "string", "example": "splits" },
                  "session": { "type": "string", "example": "regular" },
                  "calendar": { "type": "object", "description": "Provider exchange timezone, session, holidays and corrections when available." },
                  "historicalAvailability": { "type": "string", "example": "provider_dependent" },
                  "completeness": { "type": "string", "example": "provider_reported_not_independently_certified" },
                  "calendarGridVerified": { "type": "boolean", "description": "True after every expected completed candle matches the provider-reported calendar grid for an explicit historical window." },
                  "coverage": {
                    "type": "object",
                    "description": "Connected-feed calendar-grid evidence for the requested historical window, not independent verification against an exchange dataset.",
                    "properties": {
                      "from": { "type": "number", "description": "Earliest fetched opening timestamp in Unix seconds; it may precede the final displayed range." },
                      "to": { "type": "number", "description": "Anchored request cutoff in Unix seconds." },
                      "complete": { "type": "boolean" },
                      "method": { "type": "string", "example": "anchored_history" },
                      "verification": { "type": "string", "example": "provider_query_and_reported_calendar_grid" },
                      "expectedBars": { "type": "integer", "minimum": 0, "maximum": 2000, "description": "Completed candles expected under the provider-reported session calendar; missing candles fail the historical request." }
                    }
                  }
                }
              }
            }
          },
          "image": { "$ref": "#/components/schemas/Image" },
          "inputImage": { "$ref": "#/components/schemas/Image" },
          "commands": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Command" }
          },
          "analysis": {
            "type": "object",
            "properties": {
              "text": { "type": "string" },
              "details": {},
              "citations": { "type": "array", "items": {} }
            }
          },
          "setup": { "type": ["object", "null"] },
          "verification": {
            "type": "object",
            "description": "Renderer/readback evidence; does not imply model visual approval"
          },
          "provenance": { "type": "object" },
          "usage": {
            "type": "object",
            "description": "Observed job work, not a customer invoice or commercial credit allowance. Failed or cancelled jobs can retain known partial usage; absent provider usage remains unknown.",
            "properties": {
              "managedAgentCalls": { "type": "integer", "minimum": 0, "description": "Managed brain invocations; one invocation can make multiple provider requests and tool passes." },
              "chartRenders": { "type": "integer", "minimum": 0, "description": "Successfully completed chart renders." },
              "chartRenderAttempts": { "type": "integer", "minimum": 0, "description": "Attempted renders, including attempts that failed." },
              "modelUsageAvailable": { "type": "boolean", "description": "Whether the brain returned a model-usage record. False does not prove no provider work occurred." },
              "model": { "description": "Model usage returned by the brain when available; does not imply a billed dollar amount." }
            }
          },
          "error": { "$ref": "#/components/schemas/JobError" }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error", "requestId"],
        "properties": {
          "error": {
            "allOf": [
              { "$ref": "#/components/schemas/JobError" },
              {
                "type": "object",
                "properties": {
                  "type": { "const": "api_error" },
                  "requestId": { "type": "string" },
                  "retryable": { "type": "boolean", "description": "Present on authentication/quota responses when supplied; not guaranteed for every error." }
                }
              }
            ]
          },
          "requestId": { "type": "string" }
        }
      }
    }
  }
}
