{
  "components": {
    "headers": {
      "RateLimit-Limit": {
        "description": "Requests allowed per minute for your account.",
        "example": 60,
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left in the current minute window.",
        "example": 57,
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the current minute window resets (or, when the daily token quota is what blocks you, until the quota resets).",
        "example": 41,
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "example": 23,
        "schema": {
          "type": "integer"
        }
      },
      "WWW-Authenticate": {
        "description": "Always `Bearer`: the request did not authenticate.",
        "example": "Bearer",
        "schema": {
          "type": "string"
        }
      },
      "X-Request-ID": {
        "description": "The request id: your own `X-Request-ID` when it matches `^[a-zA-Z0-9_-]{1,64}$`, otherwise a server-minted 32-character hexadecimal id. Quote it when contacting support.",
        "example": "8f1c2d3e4b5a69781c0d2e3f4a5b6c7d",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "ChatCompletion": {
        "description": "A chat completion in the OpenAI `chat.completion` shape, plus the `citations` extension on the message.",
        "properties": {
          "choices": {
            "description": "Exactly one choice.",
            "items": {
              "$ref": "#/components/schemas/Choice"
            },
            "type": "array"
          },
          "created": {
            "description": "Unix time in seconds (UTC) when the completion was created.",
            "type": "integer"
          },
          "id": {
            "description": "`chatcmpl-` followed by 24 hexadecimal characters.",
            "type": "string"
          },
          "model": {
            "description": "The canonical slug of the bot that answered, whatever name the request sent.",
            "type": "string"
          },
          "object": {
            "const": "chat.completion",
            "default": "chat.completion",
            "type": "string"
          },
          "usage": {
            "$ref": "#/components/schemas/Usage"
          }
        },
        "required": [
          "id",
          "object",
          "created",
          "model",
          "choices",
          "usage"
        ],
        "title": "ChatCompletion",
        "type": "object"
      },
      "ChatCompletionChunk": {
        "description": "One frame of a streamed chat completion, in the OpenAI `chat.completion.chunk` shape. `id`, `created`, and `model` are identical on every chunk of a stream. The optional usage chunk (requested with `stream_options.include_usage`) has `choices: []` and `usage`.",
        "properties": {
          "choices": {
            "description": "One choice, or an empty array on the usage chunk.",
            "items": {
              "$ref": "#/components/schemas/ChunkChoice"
            },
            "type": "array"
          },
          "created": {
            "description": "Unix time in seconds (UTC), the same on every chunk.",
            "type": "integer"
          },
          "id": {
            "description": "The completion id, the same on every chunk.",
            "type": "string"
          },
          "model": {
            "description": "The bot's canonical slug.",
            "type": "string"
          },
          "object": {
            "const": "chat.completion.chunk",
            "default": "chat.completion.chunk",
            "type": "string"
          },
          "usage": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Usage"
              },
              {
                "type": "null"
              }
            ],
            "description": "Present on the usage chunk only."
          }
        },
        "required": [
          "id",
          "created",
          "model",
          "choices"
        ],
        "title": "ChatCompletionChunk",
        "type": "object"
      },
      "ChatCompletionMessage": {
        "description": "The assistant message of a chat completion. Every key is always present (`null` where it does not apply), as in the OpenAI object.",
        "properties": {
          "citations": {
            "description": "eCourtDate extension. The passages behind the `[Source N]` markers in `content`: an array (possibly empty) on a text answer from a bot with a knowledge base and citations enabled; `null` on a tool-call turn and for bots that do not cite.",
            "items": {
              "$ref": "#/components/schemas/Citation"
            },
            "type": [
              "array",
              "null"
            ],
            "x-ecd-extension": true
          },
          "content": {
            "description": "The answer text, with `[Source N]` markers where it cites the knowledge base. `null` when the model only returned tool calls.",
            "type": [
              "string",
              "null"
            ]
          },
          "refusal": {
            "description": "Always `null`.",
            "type": "null"
          },
          "role": {
            "const": "assistant",
            "default": "assistant",
            "type": "string"
          },
          "tool_calls": {
            "description": "Tool calls to run, or `null` when the model called none.",
            "items": {
              "$ref": "#/components/schemas/ToolCall"
            },
            "type": [
              "array",
              "null"
            ]
          }
        },
        "required": [
          "role",
          "content",
          "refusal",
          "tool_calls",
          "citations"
        ],
        "title": "ChatCompletionMessage",
        "type": "object"
      },
      "ChatCompletionRequest": {
        "additionalProperties": true,
        "description": "A chat completion request in the OpenAI shape. Unknown fields are ignored.",
        "properties": {
          "max_completion_tokens": {
            "description": "Upper bound on generated tokens. Values above 8192 are clamped to 8192. Omit for the bot's default.",
            "minimum": 1.0,
            "type": [
              "integer",
              "null"
            ]
          },
          "max_tokens": {
            "description": "Legacy name for `max_completion_tokens`; used when that field is omitted.",
            "minimum": 1.0,
            "type": [
              "integer",
              "null"
            ]
          },
          "messages": {
            "description": "The conversation so far, 1 to 200 messages. The bot's knowledge base is searched with the last message when it is a `user` message.",
            "items": {
              "$ref": "#/components/schemas/ChatMessageParam"
            },
            "maxItems": 200,
            "minItems": 1,
            "type": "array"
          },
          "model": {
            "description": "A bot slug or alias from `GET /v1/models`. Whitespace is trimmed; omitted, `null`, blank, or `default` selects your account's default bot. An unknown name is 404 `model_not_found`. The response echoes the bot's canonical slug.",
            "type": [
              "string",
              "null"
            ]
          },
          "n": {
            "default": 1,
            "description": "Number of choices. Only `1` is supported.",
            "type": [
              "integer",
              "null"
            ]
          },
          "response_format": {
            "description": "Output format. `text` (the default) is free text; `json_object` makes the answer a single JSON object; `json_schema` makes it conform to `json_schema.schema`. The JSON modes cannot be combined with `tools` (400, `param: response_format`).",
            "properties": {
              "json_schema": {
                "description": "Required when `type` is `json_schema`.",
                "properties": {
                  "description": {
                    "type": "string"
                  },
                  "name": {
                    "description": "A name for the schema.",
                    "minLength": 1,
                    "type": "string"
                  },
                  "schema": {
                    "description": "The JSON Schema the answer must satisfy.",
                    "type": "object"
                  },
                  "strict": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "name",
                  "schema"
                ],
                "type": "object"
              },
              "type": {
                "enum": [
                  "text",
                  "json_object",
                  "json_schema"
                ],
                "type": "string"
              }
            },
            "required": [
              "type"
            ],
            "type": [
              "object",
              "null"
            ]
          },
          "stop": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "One or more sequences at which generation stops. An empty string is a 400."
          },
          "stream": {
            "default": false,
            "description": "Stream the answer as server-sent events (`text/event-stream`) instead of one JSON object.",
            "type": "boolean"
          },
          "stream_options": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/StreamOptions"
              },
              {
                "type": "null"
              }
            ]
          },
          "temperature": {
            "description": "Sampling temperature, 0 to 2; values above 1 are treated as 1 by the underlying language model. Omit for the model's default. Some models ignore sampling parameters.",
            "maximum": 2.0,
            "minimum": 0.0,
            "type": [
              "number",
              "null"
            ]
          },
          "tool_choice": {
            "description": "How the model uses `tools`: `auto` (the default when tools are sent), `none`, `required`, or one named function. Validated after the message rules and the tool definitions, even without `tools`: any other form is a 400 with `param: tool_choice`, and so is naming a function that `tools` does not declare. A well-formed value is ignored when no `tools` are sent.",
            "oneOf": [
              {
                "enum": [
                  "auto",
                  "none",
                  "required"
                ],
                "type": "string"
              },
              {
                "properties": {
                  "function": {
                    "properties": {
                      "name": {
                        "description": "The name of one of the functions in `tools`.",
                        "type": "string"
                      }
                    },
                    "required": [
                      "name"
                    ],
                    "type": "object"
                  },
                  "type": {
                    "enum": [
                      "function"
                    ],
                    "type": "string"
                  }
                },
                "required": [
                  "type",
                  "function"
                ],
                "type": "object"
              },
              {
                "type": "null"
              }
            ]
          },
          "tools": {
            "description": "Functions the model may call. Every name must be on the bot's tool allowlist (400 `tool_not_allowed` with `param: tools[i]` otherwise). A malformed entry (`type` other than `function`, a missing or invalid `function.name`, a `function.description` that is not a string, or `function.parameters` that is not an object) is a 400 with `param: tools[i]`. An empty array is the same as omitting the field.",
            "items": {
              "description": "A function the model may call. Only `type: function` is supported.",
              "properties": {
                "function": {
                  "properties": {
                    "description": {
                      "description": "What the function does; the model uses it to decide when to call it.",
                      "type": "string"
                    },
                    "name": {
                      "description": "1 to 64 letters, digits, underscores, or dashes. Must be on the bot's tool allowlist, or the request is 400 `tool_not_allowed`.",
                      "pattern": "^[a-zA-Z0-9_-]{1,64}$",
                      "type": "string"
                    },
                    "parameters": {
                      "description": "JSON Schema for the function's arguments. Defaults to an object schema with no properties.",
                      "type": "object"
                    }
                  },
                  "required": [
                    "name"
                  ],
                  "type": "object"
                },
                "type": {
                  "enum": [
                    "function"
                  ],
                  "type": "string"
                }
              },
              "required": [
                "type",
                "function"
              ],
              "type": "object"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "top_p": {
            "description": "Nucleus sampling, 0 to 1; ignored when `temperature` is also set. Omit for the model's default. Some models ignore sampling parameters.",
            "maximum": 1.0,
            "minimum": 0.0,
            "type": [
              "number",
              "null"
            ]
          },
          "user": {
            "description": "An identifier for your end user. Accepted and ignored.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "messages"
        ],
        "title": "ChatCompletionRequest",
        "type": "object"
      },
      "ChatMessageParam": {
        "additionalProperties": true,
        "description": "One message of the conversation sent to the model.",
        "properties": {
          "content": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "items": {
                  "$ref": "#/components/schemas/ContentPartText"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "The message text: a string, or an array of `text` parts joined with newlines. At most 100,000 characters. Required and non-blank (400, `param: messages[i].content`) on `system`, `developer`, `user`, and `assistant` turns, except an `assistant` turn that carries `tool_calls`, where it may be `null`. A `tool` turn may send `null` or an empty string; both are treated as an empty result."
          },
          "name": {
            "description": "Accepted for compatibility and ignored.",
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "description": "`system` and `developer` text is appended to the bot's own instructions (it can add task guidance but never replaces the bot's grounding rules). The first non-system message must be `user`; the last must be `user` or `tool`.",
            "enum": [
              "system",
              "developer",
              "user",
              "assistant",
              "tool"
            ],
            "type": "string"
          },
          "tool_call_id": {
            "description": "On a `tool` turn (required there): the id of the tool call this message answers, exactly as the response returned it.",
            "type": [
              "string",
              "null"
            ]
          },
          "tool_calls": {
            "description": "On an `assistant` turn: the tool calls it made, echoed back from the earlier response so the model can continue after the `tool` results.",
            "items": {
              "description": "A tool call the assistant made earlier in the conversation, exactly as it was returned in `choices[0].message.tool_calls`.",
              "properties": {
                "function": {
                  "properties": {
                    "arguments": {
                      "description": "The call's arguments as a JSON-encoded string.",
                      "type": "string"
                    },
                    "name": {
                      "minLength": 1,
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "arguments"
                  ],
                  "type": "object"
                },
                "id": {
                  "description": "The tool call id exactly as the response returned it (`call_` plus an opaque token); a later `tool` message answers it by `tool_call_id`.",
                  "minLength": 1,
                  "type": "string"
                },
                "type": {
                  "enum": [
                    "function"
                  ],
                  "type": "string"
                }
              },
              "required": [
                "id",
                "type",
                "function"
              ],
              "type": "object"
            },
            "type": [
              "array",
              "null"
            ]
          }
        },
        "required": [
          "role"
        ],
        "title": "ChatMessageParam",
        "type": "object"
      },
      "Choice": {
        "description": "The single choice of a chat completion (`n` is always 1).",
        "properties": {
          "finish_reason": {
            "default": "stop",
            "description": "`stop`: the model finished; `length`: `max_completion_tokens` was reached; `content_filter`: the model declined; `tool_calls`: run `message.tool_calls` and continue the conversation with `tool` messages.",
            "enum": [
              "stop",
              "length",
              "content_filter",
              "tool_calls"
            ],
            "type": "string"
          },
          "index": {
            "default": 0,
            "description": "Always `0`.",
            "type": "integer"
          },
          "logprobs": {
            "description": "Always `null`.",
            "type": "null"
          },
          "message": {
            "$ref": "#/components/schemas/ChatCompletionMessage"
          }
        },
        "required": [
          "index",
          "message",
          "logprobs",
          "finish_reason"
        ],
        "title": "Choice",
        "type": "object"
      },
      "ChunkChoice": {
        "description": "The single choice of a streamed chunk.",
        "properties": {
          "delta": {
            "$ref": "#/components/schemas/ChunkDelta"
          },
          "finish_reason": {
            "description": "`null` on every chunk except the finish chunk.",
            "enum": [
              "stop",
              "length",
              "content_filter",
              "tool_calls",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "index": {
            "default": 0,
            "description": "Always `0`.",
            "type": "integer"
          },
          "logprobs": {
            "description": "Always `null`.",
            "type": "null"
          }
        },
        "required": [
          "delta"
        ],
        "title": "ChunkChoice",
        "type": "object"
      },
      "ChunkDelta": {
        "description": "The incremental content of one streamed chunk. Keys are present only when they carry a value: the first chunk has `role` and an empty `content`, content chunks have `content`, tool chunks have `tool_calls`, and the finish chunk is `{}` or carries `citations`.",
        "properties": {
          "citations": {
            "description": "eCourtDate extension. On the finish chunk of a text answer from a bot with a knowledge base and citations enabled: the passages behind the `[Source N]` markers (possibly an empty array). Absent on tool-call turns and for bots that do not cite.",
            "items": {
              "$ref": "#/components/schemas/Citation"
            },
            "type": [
              "array",
              "null"
            ],
            "x-ecd-extension": true
          },
          "content": {
            "description": "A text fragment. Concatenate fragments in order.",
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "description": "`assistant`, on the first chunk only.",
            "enum": [
              "assistant",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "tool_calls": {
            "items": {
              "$ref": "#/components/schemas/ChunkToolCall"
            },
            "type": [
              "array",
              "null"
            ]
          }
        },
        "title": "ChunkDelta",
        "type": "object"
      },
      "ChunkToolCall": {
        "description": "One streamed tool-call delta.",
        "properties": {
          "function": {
            "$ref": "#/components/schemas/ChunkToolCallFunction"
          },
          "id": {
            "description": "The tool call id (`call_` plus an opaque token), sent on the first delta of a call only.",
            "type": [
              "string",
              "null"
            ]
          },
          "index": {
            "description": "Zero-based position of the call among this answer's tool calls.",
            "type": "integer"
          },
          "type": {
            "description": "Sent on the first delta of a call only.",
            "enum": [
              "function",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "index",
          "function"
        ],
        "title": "ChunkToolCall",
        "type": "object"
      },
      "ChunkToolCallFunction": {
        "description": "Function fields of a streamed tool-call delta.",
        "properties": {
          "arguments": {
            "description": "An arguments fragment; concatenate per `index`, then parse as JSON.",
            "type": "string"
          },
          "name": {
            "description": "Sent on the first delta of a call only.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "arguments"
        ],
        "title": "ChunkToolCallFunction",
        "type": "object"
      },
      "Citation": {
        "description": "One knowledge-base passage the answer cites. eCourtDate extension to the OpenAI message object: resolves a `[Source N]` marker in the assistant message `content` (`N` is `source_index`).",
        "properties": {
          "chunk_index": {
            "description": "Zero-based position of the cited passage within the document.",
            "minimum": 0.0,
            "type": "integer"
          },
          "document_id": {
            "description": "Id of the cited document (`GET /v1/documents/{documentId}`).",
            "type": "string"
          },
          "score": {
            "description": "Relevance score of the passage for the question, higher is more relevant.",
            "type": "number"
          },
          "source_filename": {
            "description": "The document's `filename`: the upload name, or the page URL for crawled pages.",
            "type": "string"
          },
          "source_index": {
            "description": "The `N` in the `[Source N]` marker this citation resolves, starting at 1.",
            "minimum": 1.0,
            "type": "integer"
          },
          "text_preview": {
            "description": "The first 200 characters of the cited passage.",
            "maxLength": 200,
            "type": "string"
          }
        },
        "required": [
          "source_index",
          "document_id",
          "source_filename",
          "chunk_index",
          "text_preview",
          "score"
        ],
        "title": "Citation",
        "type": "object",
        "x-ecd-extension": true
      },
      "ContentPartText": {
        "additionalProperties": true,
        "description": "One part of an array-form message content. Only text parts are supported.",
        "properties": {
          "text": {
            "description": "The text of this part. Required: a `text` part without it is a 400 with `param: messages[i].content[j].text`.",
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "const": "text",
            "description": "Only `text` is supported; any other part type is a 400 with `param: messages[i].content[j].type`.",
            "type": "string"
          }
        },
        "required": [
          "type"
        ],
        "title": "ContentPartText",
        "type": "object"
      },
      "Conversation": {
        "description": "A server-side conversation: its binding to a bot, your metadata, and timestamps.",
        "properties": {
          "created": {
            "description": "Unix time in seconds (UTC) when the conversation was created.",
            "type": "integer"
          },
          "id": {
            "description": "The conversation id (a UUID).",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Your key/value pairs as stored; `{}` when none were sent.",
            "type": "object"
          },
          "model": {
            "description": "The canonical slug of the bot the conversation is bound to, or `null` when it follows your account's default bot (resolved on each message).",
            "type": [
              "string",
              "null"
            ]
          },
          "object": {
            "const": "conversation",
            "default": "conversation",
            "type": "string"
          },
          "updated": {
            "description": "Unix time in seconds (UTC) of the last stored turn; equal to `created` until the first message.",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "object",
          "model",
          "metadata",
          "created",
          "updated"
        ],
        "title": "Conversation",
        "type": "object"
      },
      "ConversationCreate": {
        "description": "Create a conversation. Both fields are optional. Unknown fields are ignored.",
        "properties": {
          "metadata": {
            "additionalProperties": {
              "maxLength": 512,
              "type": "string"
            },
            "description": "Up to 16 key/value pairs for your own identifiers (a session id, a case number, the channel). Keys are 1 to 64 characters; values are strings of at most 512 characters. Stored with the conversation and returned by create and retrieve.",
            "maxProperties": 16,
            "propertyNames": {
              "maxLength": 64,
              "minLength": 1
            },
            "type": [
              "object",
              "null"
            ]
          },
          "model": {
            "description": "A bot slug or alias, resolved exactly as on chat completions, or the literal `default`, which binds the conversation to the canonical slug of the account's default bot at creation time (when that default is the built-in assistant, because no bot is enabled, null is stored instead and the conversation follows the default). Omit, or send null or a blank string, to store null and follow whatever the account's default bot is at the time of each message; that is a 400 with `param: model` when several bots are enabled and no default is configured. Whitespace is trimmed. Responses carry the bound bot's canonical slug, or null for a conversation that follows the default.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "ConversationCreate",
        "type": "object"
      },
      "ConversationDetail": {
        "description": "A conversation with its full stored transcript.",
        "properties": {
          "created": {
            "description": "Unix time in seconds (UTC) when the conversation was created.",
            "type": "integer"
          },
          "id": {
            "description": "The conversation id (a UUID).",
            "type": "string"
          },
          "messages": {
            "description": "Every stored message in order, alternating user and assistant.",
            "items": {
              "$ref": "#/components/schemas/ConversationMessage"
            },
            "type": "array"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Your key/value pairs as stored; `{}` when none were sent.",
            "type": "object"
          },
          "model": {
            "description": "The canonical slug of the bot the conversation is bound to, or `null` when it follows your account's default bot (resolved on each message).",
            "type": [
              "string",
              "null"
            ]
          },
          "object": {
            "const": "conversation",
            "default": "conversation",
            "type": "string"
          },
          "updated": {
            "description": "Unix time in seconds (UTC) of the last stored turn; equal to `created` until the first message.",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "object",
          "model",
          "metadata",
          "created",
          "updated",
          "messages"
        ],
        "title": "ConversationDetail",
        "type": "object"
      },
      "ConversationMessage": {
        "description": "A stored message of a conversation.",
        "properties": {
          "citations": {
            "description": "eCourtDate extension. On an assistant message: the passages behind the `[Source N]` markers, an array (possibly empty) when the bot has a knowledge base and citations enabled, `null` otherwise. Always `null` on user messages.",
            "items": {
              "$ref": "#/components/schemas/Citation"
            },
            "type": [
              "array",
              "null"
            ],
            "x-ecd-extension": true
          },
          "content": {
            "description": "The message text. Assistant text carries `[Source N]` markers where it cites the knowledge base.",
            "type": "string"
          },
          "role": {
            "enum": [
              "user",
              "assistant"
            ],
            "type": "string"
          }
        },
        "required": [
          "role",
          "content",
          "citations"
        ],
        "title": "ConversationMessage",
        "type": "object"
      },
      "ConversationMessageChunk": {
        "description": "One frame of a streamed conversation reply: a delta frame while the answer is generated, then one done frame carrying the complete message.",
        "oneOf": [
          {
            "properties": {
              "conversation_id": {
                "type": "string"
              },
              "delta": {
                "properties": {
                  "content": {
                    "description": "A text fragment. Concatenate fragments in order.",
                    "type": "string"
                  }
                },
                "required": [
                  "content"
                ],
                "type": "object"
              }
            },
            "required": [
              "conversation_id",
              "delta"
            ],
            "type": "object"
          },
          {
            "properties": {
              "conversation_id": {
                "type": "string"
              },
              "done": {
                "enum": [
                  true
                ],
                "type": "boolean"
              },
              "message": {
                "$ref": "#/components/schemas/ConversationMessage"
              }
            },
            "required": [
              "conversation_id",
              "message",
              "done"
            ],
            "type": "object"
          }
        ],
        "title": "ConversationMessageChunk"
      },
      "ConversationMessageRequest": {
        "description": "A user message to add to a conversation. Unknown fields are ignored.",
        "properties": {
          "content": {
            "description": "The user message, 1 to 100,000 characters. Not trimmed, but it must not be blank: whitespace-only content is a 400 (`param: content`), as on chat completions.",
            "maxLength": 100000,
            "minLength": 1,
            "type": "string"
          },
          "stream": {
            "default": false,
            "description": "Stream the assistant reply as server-sent events.",
            "type": "boolean"
          }
        },
        "required": [
          "content"
        ],
        "title": "ConversationMessageRequest",
        "type": "object"
      },
      "ConversationMessageResponse": {
        "description": "The assistant's reply to a conversation message. The user message and this reply are both stored on the conversation.",
        "properties": {
          "conversation_id": {
            "description": "The conversation the turn was stored on.",
            "type": "string"
          },
          "message": {
            "$ref": "#/components/schemas/ConversationMessage",
            "description": "The assistant reply."
          }
        },
        "required": [
          "conversation_id",
          "message"
        ],
        "title": "ConversationMessageResponse",
        "type": "object"
      },
      "CrawlCompletedEvent": {
        "description": "Sent once when a crawl job reaches a terminal status. Mirrors the job object from `GET /v1/ingest/crawl/{crawlJobId}` plus `event` and `namespace`.",
        "properties": {
          "crawl_job_id": {
            "description": "The crawl job id (a UUID).",
            "type": "string"
          },
          "errors": {
            "description": "One entry per failed page (each has a `failed` document carrying the same error, except a page that answered with an HTTP error status, which creates no document), or one `crawl` entry when the crawl itself failed or timed out (a timeout also fails every document of the crawl still processing).",
            "items": {
              "$ref": "#/components/schemas/CrawlError"
            },
            "type": "array"
          },
          "event": {
            "const": "crawl.completed",
            "description": "The event name, also sent as `X-ECD-Event`.",
            "type": "string"
          },
          "namespace": {
            "description": "The knowledge-base namespace that received the pages.",
            "type": "string"
          },
          "pages_crawled": {
            "description": "Pages fetched successfully, including pages with no text; pages that answered with an HTTP error status are not counted.",
            "type": "integer"
          },
          "pages_indexed": {
            "description": "Pages that became ready documents.",
            "type": "integer"
          },
          "status": {
            "description": "completed: finished with no page errors; completed_with_errors: some pages indexed and some failed; failed: nothing indexed, or the crawl itself failed (a crawl that timed out after indexing some pages is `failed` with `pages_indexed` above zero).",
            "enum": [
              "completed",
              "completed_with_errors",
              "failed"
            ],
            "type": "string"
          }
        },
        "required": [
          "event",
          "crawl_job_id",
          "namespace",
          "status",
          "pages_crawled",
          "pages_indexed",
          "errors"
        ],
        "title": "CrawlCompletedEvent",
        "type": "object"
      },
      "CrawlError": {
        "description": "One page that failed, or the whole crawl.",
        "properties": {
          "error": {
            "description": "Sanitized failure reason. `HTTP <status>` for a page the site answered with an error status (it is not indexed); `Crawl timed out.` under the `crawl` sentinel when the 10-minute budget ran out.",
            "type": "string"
          },
          "url": {
            "description": "The page URL, or the sentinel 'crawl' for a whole-crawl failure.",
            "type": "string"
          }
        },
        "required": [
          "url",
          "error"
        ],
        "title": "CrawlError",
        "type": "object"
      },
      "CrawlJobStatus": {
        "description": "The state of a crawl job and its page counts.",
        "properties": {
          "crawl_job_id": {
            "description": "The crawl job id (a UUID).",
            "type": "string"
          },
          "errors": {
            "description": "One entry per failed page (each has a `failed` document carrying the same error, except a page that answered with an HTTP error status, which creates no document), or one `crawl` entry when the crawl itself failed or timed out (a timeout also fails every document of the crawl still processing).",
            "items": {
              "$ref": "#/components/schemas/CrawlError"
            },
            "type": "array"
          },
          "pages_crawled": {
            "description": "Pages fetched successfully so far, including pages with no text; pages that answered with an HTTP error status are not counted.",
            "type": "integer"
          },
          "pages_indexed": {
            "description": "Pages that became ready documents so far.",
            "type": "integer"
          },
          "status": {
            "description": "processing: queued or running; completed: finished with no page errors; completed_with_errors: some pages indexed and some failed; failed: nothing indexed, or the crawl itself failed (exception, timeout, cancellation). `failed` describes the crawl as a whole: a crawl that timed out after indexing some pages is `failed` with `pages_indexed` above zero, and those documents stay ready. The counts and `errors` are updated after every page, so they are live while `processing`.",
            "enum": [
              "processing",
              "completed",
              "completed_with_errors",
              "failed"
            ],
            "type": "string"
          }
        },
        "required": [
          "crawl_job_id",
          "status",
          "pages_crawled",
          "pages_indexed",
          "errors"
        ],
        "title": "CrawlJobStatus",
        "type": "object"
      },
      "CrawlRequest": {
        "description": "A website crawl to run. Unknown fields are ignored.",
        "properties": {
          "allowed_domains": {
            "default": [],
            "description": "Hosts the crawler may fetch from: an exact, case-sensitive match on the URL's host, port included, except that a scheme's default port is not significant (`example.com:443` is `example.com` for https, `example.com:80` for http). Empty derives the allowlist from the seed URLs' hosts.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "max_depth": {
            "default": 3,
            "description": "How many link hops to follow from the seed URLs. Seeds are depth 0, so `1` fetches the seeds and the pages they link to, `2` one hop further, and so on. `max_pages` caps the total either way.",
            "maximum": 10.0,
            "minimum": 1.0,
            "type": "integer"
          },
          "max_pages": {
            "default": 50,
            "description": "Stop after this many pages have been fetched successfully (pages that answer with an HTTP error status do not count). The whole crawl, indexing included, must fit the 10-minute budget.",
            "maximum": 500.0,
            "minimum": 1.0,
            "type": "integer"
          },
          "namespace": {
            "description": "Knowledge-base namespace that receives the crawled documents.",
            "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$",
            "type": "string"
          },
          "rate_limit_rps": {
            "default": 2.0,
            "description": "Maximum fetches per second against the crawled site.",
            "maximum": 10.0,
            "minimum": 0.1,
            "type": "number"
          },
          "respect_robots_txt": {
            "default": true,
            "description": "Honor the site's robots.txt (disallowed paths are skipped).",
            "type": "boolean"
          },
          "seed_urls": {
            "description": "1 to 100 public http(s) URLs to start from.",
            "items": {
              "type": "string"
            },
            "maxItems": 100,
            "minItems": 1,
            "type": "array"
          }
        },
        "required": [
          "namespace",
          "seed_urls"
        ],
        "title": "CrawlRequest",
        "type": "object"
      },
      "CrawlResponse": {
        "description": "An accepted crawl: the job to poll.",
        "properties": {
          "crawl_job_id": {
            "description": "UUID of the crawl job; poll `GET /v1/ingest/crawl/{crawlJobId}`.",
            "type": "string"
          },
          "status": {
            "const": "processing",
            "default": "processing",
            "description": "Always `processing` at submission.",
            "type": "string"
          }
        },
        "required": [
          "crawl_job_id",
          "status"
        ],
        "title": "CrawlResponse",
        "type": "object"
      },
      "Document": {
        "description": "A document in your knowledge base: an uploaded file or a crawled page.",
        "properties": {
          "chunk_count": {
            "default": 0,
            "description": "Indexed chunks; > 0 only when ready.",
            "type": "integer"
          },
          "created_at": {
            "description": "When the document was created: RFC 3339 UTC with a `Z` suffix, always in the form `2026-08-21T14:03:11.214000Z`. Millisecond precision, rendered with six fractional digits (the last three are always `000`).",
            "examples": [
              "2026-08-21T20:15:03.123000Z"
            ],
            "format": "date-time",
            "type": "string"
          },
          "error": {
            "description": "Sanitized failure reason when status is failed (same text as the job's errors[] entry); null otherwise.",
            "type": [
              "string",
              "null"
            ]
          },
          "filename": {
            "description": "Sanitized upload filename, or the final page URL for crawled pages.",
            "type": "string"
          },
          "id": {
            "description": "The document id (a UUID).",
            "type": "string"
          },
          "namespace": {
            "description": "The knowledge-base namespace the document was ingested into.",
            "type": "string"
          },
          "object": {
            "const": "document",
            "default": "document",
            "type": "string"
          },
          "source_type": {
            "description": "`upload` for a file sent to `POST /v1/ingest/files`; `crawl` for a crawled page.",
            "enum": [
              "upload",
              "crawl"
            ],
            "type": "string"
          },
          "source_url": {
            "description": "Final page URL for crawled pages; `null` for uploads.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "description": "processing: waiting for or undergoing extraction and indexing (also between automatic retries); ready: indexed and citable; failed: permanently failed, see error.",
            "enum": [
              "processing",
              "ready",
              "failed"
            ],
            "type": "string"
          },
          "updated_at": {
            "description": "When the document last changed status, in the same form as `created_at`.",
            "examples": [
              "2026-08-21T20:15:03.123000Z"
            ],
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "namespace",
          "filename",
          "source_type",
          "source_url",
          "status",
          "chunk_count",
          "error",
          "created_at",
          "updated_at"
        ],
        "title": "Document",
        "type": "object"
      },
      "DocumentDeleted": {
        "description": "Confirmation that a document and its indexed passages were removed.",
        "properties": {
          "chunks_deleted": {
            "default": 0,
            "description": "Indexed passages removed with the document.",
            "type": "integer"
          },
          "deleted": {
            "default": true,
            "description": "Always `true`.",
            "type": "boolean"
          },
          "id": {
            "description": "The deleted document's id.",
            "type": "string"
          },
          "object": {
            "const": "document.deleted",
            "default": "document.deleted",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "deleted",
          "chunks_deleted"
        ],
        "title": "DocumentDeleted",
        "type": "object"
      },
      "DocumentList": {
        "description": "A page of documents, newest first, in the OpenAI list envelope with cursor pagination.",
        "properties": {
          "data": {
            "description": "Up to `limit` documents.",
            "items": {
              "$ref": "#/components/schemas/Document"
            },
            "type": "array"
          },
          "first_id": {
            "description": "id of the first item in data; null when empty.",
            "type": [
              "string",
              "null"
            ]
          },
          "has_more": {
            "description": "true when more documents follow the last item in data.",
            "type": "boolean"
          },
          "last_id": {
            "description": "id of the last item in data (pass as `after` for the next page); null when empty.",
            "type": [
              "string",
              "null"
            ]
          },
          "object": {
            "const": "list",
            "default": "list",
            "type": "string"
          }
        },
        "required": [
          "object",
          "data",
          "first_id",
          "last_id",
          "has_more"
        ],
        "title": "DocumentList",
        "type": "object"
      },
      "Embedding": {
        "description": "One embedding, in input order.",
        "properties": {
          "embedding": {
            "anyOf": [
              {
                "items": {
                  "type": "number"
                },
                "type": "array"
              },
              {
                "type": "string"
              }
            ],
            "description": "1024 numbers (`encoding_format: float`) or a base64 string of 1024 little-endian 32-bit floats (`encoding_format: base64`)."
          },
          "index": {
            "description": "Position of the input this embedding belongs to.",
            "type": "integer"
          },
          "object": {
            "const": "embedding",
            "default": "embedding",
            "type": "string"
          }
        },
        "required": [
          "object",
          "index",
          "embedding"
        ],
        "title": "Embedding",
        "type": "object"
      },
      "EmbeddingList": {
        "description": "Embeddings in the OpenAI list envelope.",
        "properties": {
          "data": {
            "description": "One embedding per input, in input order.",
            "items": {
              "$ref": "#/components/schemas/Embedding"
            },
            "type": "array"
          },
          "model": {
            "description": "The `model` label from the request, or `default`.",
            "type": "string"
          },
          "object": {
            "const": "list",
            "default": "list",
            "type": "string"
          },
          "usage": {
            "$ref": "#/components/schemas/EmbeddingUsage"
          }
        },
        "required": [
          "object",
          "data",
          "model",
          "usage"
        ],
        "title": "EmbeddingList",
        "type": "object"
      },
      "EmbeddingRequest": {
        "additionalProperties": true,
        "description": "An embeddings request in the OpenAI shape. Unknown fields are ignored.",
        "properties": {
          "dimensions": {
            "description": "Embeddings are always 1024-dimensional. Any other value is a 400.",
            "type": [
              "integer",
              "null"
            ]
          },
          "encoding_format": {
            "default": "float",
            "description": "`float`: each embedding is an array of numbers; `base64`: each embedding is a base64 string of little-endian 32-bit floats (what the OpenAI SDKs request by default).",
            "enum": [
              "float",
              "base64"
            ],
            "type": "string"
          },
          "input": {
            "description": "The text to embed: one string, or an array of 1 to 96 strings. Each string is 1 to 100,000 characters. An offending item is reported with `param: input[i]`. Token arrays are not accepted.",
            "oneOf": [
              {
                "maxLength": 100000,
                "minLength": 1,
                "type": "string"
              },
              {
                "items": {
                  "maxLength": 100000,
                  "minLength": 1,
                  "type": "string"
                },
                "maxItems": 96,
                "minItems": 1,
                "type": "array"
              }
            ]
          },
          "input_type": {
            "default": "search_document",
            "description": "eCourtDate extension. `search_document` for text you will store and search over; `search_query` for a question you will match against stored embeddings.",
            "enum": [
              "search_document",
              "search_query"
            ],
            "type": "string",
            "x-ecd-extension": true
          },
          "model": {
            "description": "A label echoed back unchanged in the response (`default` when omitted). The API serves one text embedding model, so the value does not select anything.",
            "type": [
              "string",
              "null"
            ]
          },
          "user": {
            "description": "An identifier for your end user. Accepted and ignored.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "input"
        ],
        "title": "EmbeddingRequest",
        "type": "object"
      },
      "EmbeddingUsage": {
        "description": "Token usage of an embeddings request; counts toward your daily token quota.",
        "properties": {
          "prompt_tokens": {
            "default": 0,
            "description": "Input tokens across every input.",
            "type": "integer"
          },
          "total_tokens": {
            "default": 0,
            "description": "Equal to `prompt_tokens`.",
            "type": "integer"
          }
        },
        "required": [
          "prompt_tokens",
          "total_tokens"
        ],
        "title": "EmbeddingUsage",
        "type": "object"
      },
      "Error": {
        "description": "The error envelope returned by every error response on every route, in the OpenAI shape. Program against the HTTP status and `code`; `message` wording may change.",
        "properties": {
          "error": {
            "properties": {
              "code": {
                "description": "Stable machine-readable code, or `null` for validation errors and unexpected server errors. `invalid_api_key` (401); `insufficient_scope` (403); `tool_not_allowed` (400); `model_not_found` and `not_found` (404); `request_too_large` (413); `rate_limit_exceeded` and `insufficient_quota` (429); `upstream_unavailable` (503). New codes may be added; fall back on the HTTP status for an unknown code.",
                "enum": [
                  "invalid_api_key",
                  "insufficient_scope",
                  "tool_not_allowed",
                  "model_not_found",
                  "not_found",
                  "request_too_large",
                  "rate_limit_exceeded",
                  "insufficient_quota",
                  "upstream_unavailable",
                  null
                ],
                "type": [
                  "string",
                  "null"
                ]
              },
              "message": {
                "description": "Human-readable explanation. Do not branch on it.",
                "type": "string"
              },
              "param": {
                "description": "The request field at fault in bracket form (`messages[2].tool_call_id`, `tools[0]`, `input[3]`, `after`), or `null` when no single field is responsible.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "type": {
                "description": "`authentication_error` (401); `invalid_request_error` (400, 403, 404, 405, 413); `rate_limit_error` (429); `server_error` (500, 503).",
                "enum": [
                  "invalid_request_error",
                  "authentication_error",
                  "rate_limit_error",
                  "server_error"
                ],
                "type": "string"
              }
            },
            "required": [
              "message",
              "type",
              "param",
              "code"
            ],
            "type": "object"
          }
        },
        "required": [
          "error"
        ],
        "title": "Error",
        "type": "object"
      },
      "HealthResponse": {
        "description": "Liveness status.",
        "properties": {
          "status": {
            "const": "ok",
            "default": "ok",
            "description": "Always `ok` when the API is serving.",
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "title": "HealthResponse",
        "type": "object"
      },
      "IngestCompletedEvent": {
        "description": "Sent once when an ingest job reaches a terminal status. Mirrors the job object from `GET /v1/ingest/jobs/{jobId}` plus `event` and `namespace`.",
        "properties": {
          "completed_documents": {
            "description": "Documents that reached status ready.",
            "type": "integer"
          },
          "errors": {
            "description": "One entry per failed document. A document deleted while the job was running is recorded as `Document was deleted before it was processed.` and has no document left to carry the error.",
            "items": {
              "$ref": "#/components/schemas/JobError"
            },
            "type": "array"
          },
          "event": {
            "const": "ingest.completed",
            "description": "The event name, also sent as `X-ECD-Event`.",
            "type": "string"
          },
          "failed_documents": {
            "description": "Documents that ended in status failed, including any document deleted while the job was running.",
            "type": "integer"
          },
          "job_id": {
            "description": "The ingest job id (a UUID).",
            "type": "string"
          },
          "namespace": {
            "description": "The knowledge-base namespace the files were ingested into.",
            "type": "string"
          },
          "status": {
            "description": "completed: every document is ready; completed_with_errors: some documents are ready and some failed; failed: no document is ready.",
            "enum": [
              "completed",
              "completed_with_errors",
              "failed"
            ],
            "type": "string"
          },
          "total_documents": {
            "description": "Documents in the job (one per uploaded file).",
            "type": "integer"
          }
        },
        "required": [
          "event",
          "job_id",
          "namespace",
          "status",
          "total_documents",
          "completed_documents",
          "failed_documents",
          "errors"
        ],
        "title": "IngestCompletedEvent",
        "type": "object"
      },
      "IngestFilesRequest": {
        "description": "The `multipart/form-data` body of `POST /v1/ingest/files`: one `file` part per file and an optional `namespace` field.",
        "properties": {
          "file": {
            "description": "1 to 20 files, one `file` part each, 25 MiB (26,214,400 bytes) per file. Accepted types, decided from the extension and confirmed by content: .csv, .docx, .eml, .htm, .html, .md, .pdf, .txt, .xlsx. Filenames are at most 255 bytes with no path separators, control characters, or other non-printable characters (for example a non-breaking space or a zero-width joiner); ordinary spaces and printable non-ASCII characters are kept as sent.",
            "items": {
              "contentMediaType": "application/octet-stream",
              "type": "string"
            },
            "type": "array"
          },
          "namespace": {
            "default": "general",
            "description": "Knowledge-base namespace that receives the documents. Lowercase letters, digits, `_` and `-`, 1 to 64 characters (`^[a-z0-9][a-z0-9_-]{0,63}$`). Defaults to `general` when omitted; an empty value is a 400.",
            "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$",
            "type": "string"
          }
        },
        "required": [
          "file"
        ],
        "title": "IngestFilesRequest",
        "type": "object"
      },
      "IngestFilesResponse": {
        "description": "An accepted upload: the job to poll and the documents it will index.",
        "properties": {
          "document_ids": {
            "description": "UUID of the document created for each file, in the order sent.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "job_id": {
            "description": "UUID of the ingest job; poll `GET /v1/ingest/jobs/{jobId}`.",
            "type": "string"
          },
          "status": {
            "const": "processing",
            "default": "processing",
            "description": "Always `processing` at submission.",
            "type": "string"
          }
        },
        "required": [
          "job_id",
          "document_ids",
          "status"
        ],
        "title": "IngestFilesResponse",
        "type": "object"
      },
      "JobError": {
        "description": "One document that failed to index.",
        "properties": {
          "document_id": {
            "description": "The failed document's id.",
            "type": "string"
          },
          "error": {
            "description": "Sanitized failure reason (same text as the document's `error`).",
            "type": "string"
          }
        },
        "required": [
          "document_id",
          "error"
        ],
        "title": "JobError",
        "type": "object"
      },
      "JobStatus": {
        "description": "The state of an ingest job and its per-document outcome.",
        "properties": {
          "completed_documents": {
            "description": "Documents that reached status ready so far.",
            "type": "integer"
          },
          "errors": {
            "description": "One entry per failed document. A document deleted while the job was running is recorded here as `Document was deleted before it was processed.` (there is no document left to carry the error).",
            "items": {
              "$ref": "#/components/schemas/JobError"
            },
            "type": "array"
          },
          "failed_documents": {
            "description": "Documents that ended in status failed so far, including any document deleted while the job was running.",
            "type": "integer"
          },
          "job_id": {
            "description": "The ingest job id (a UUID).",
            "type": "string"
          },
          "status": {
            "description": "processing: queued or running (also between automatic retries); completed: every document is ready; completed_with_errors: some documents are ready and some failed; failed: no document is ready. The counts and `errors` are updated after every document, so they are live while `processing` and final once the status is terminal.",
            "enum": [
              "processing",
              "completed",
              "completed_with_errors",
              "failed"
            ],
            "type": "string"
          },
          "total_documents": {
            "description": "Documents in the job (one per uploaded file).",
            "type": "integer"
          }
        },
        "required": [
          "job_id",
          "status",
          "total_documents",
          "completed_documents",
          "failed_documents",
          "errors"
        ],
        "title": "JobStatus",
        "type": "object"
      },
      "Model": {
        "description": "A model your key can use: a bot slug, an alias, or `default`.",
        "properties": {
          "created": {
            "description": "Unix time in seconds (UTC) when the bot was created. For an alias, its target bot's; for `default`, the default bot's.",
            "type": "integer"
          },
          "id": {
            "description": "The name to send as `model`: a bot slug, an alias, or `default`.",
            "type": "string"
          },
          "object": {
            "const": "model",
            "default": "model",
            "type": "string"
          },
          "owned_by": {
            "description": "Your account id.",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "created",
          "owned_by"
        ],
        "title": "Model",
        "type": "object"
      },
      "ModelList": {
        "description": "The models available to your key, in the OpenAI list envelope.",
        "properties": {
          "data": {
            "description": "Every enabled bot, then every alias whose target is enabled, then `default` whenever the account default resolves: a configured default bot, a single enabled bot, or the built-in assistant when no bot is enabled. `default` is absent only when several bots are enabled and none is the default.",
            "items": {
              "$ref": "#/components/schemas/Model"
            },
            "type": "array"
          },
          "object": {
            "const": "list",
            "default": "list",
            "type": "string"
          }
        },
        "required": [
          "object",
          "data"
        ],
        "title": "ModelList",
        "type": "object"
      },
      "StreamOptions": {
        "additionalProperties": true,
        "description": "Options for streamed responses. Validated on every request (a non-boolean `include_usage` is a 400 even with `stream: false`) but acted on only when `stream` is `true`.",
        "properties": {
          "include_usage": {
            "default": false,
            "description": "Send one extra chunk after the finish chunk with `choices: []` and the stream's `usage`.",
            "type": "boolean"
          }
        },
        "title": "StreamOptions",
        "type": "object"
      },
      "ToolCall": {
        "description": "A function call the model wants your application to run.",
        "properties": {
          "function": {
            "$ref": "#/components/schemas/ToolCallFunction"
          },
          "id": {
            "description": "`call_` followed by an opaque token. Pass it back unchanged: as `id` on the echoed assistant turn and as `tool_call_id` on the `tool` message that answers it.",
            "type": "string"
          },
          "type": {
            "const": "function",
            "default": "function",
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "function"
        ],
        "title": "ToolCall",
        "type": "object"
      },
      "ToolCallFunction": {
        "description": "The function the model asked to call.",
        "properties": {
          "arguments": {
            "description": "The arguments as a JSON-encoded string. Parse it before use.",
            "type": "string"
          },
          "name": {
            "description": "The function name, as declared in `tools`.",
            "type": "string"
          }
        },
        "required": [
          "name",
          "arguments"
        ],
        "title": "ToolCallFunction",
        "type": "object"
      },
      "Usage": {
        "description": "Token usage of a chat completion; counts toward your daily token quota.",
        "properties": {
          "completion_tokens": {
            "default": 0,
            "description": "Generated tokens.",
            "type": "integer"
          },
          "prompt_tokens": {
            "default": 0,
            "description": "Input tokens, including the bot's instructions and retrieved passages.",
            "type": "integer"
          },
          "total_tokens": {
            "default": 0,
            "description": "`prompt_tokens` plus `completion_tokens`.",
            "type": "integer"
          }
        },
        "required": [
          "prompt_tokens",
          "completion_tokens",
          "total_tokens"
        ],
        "title": "Usage",
        "type": "object"
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "bearerFormat": "ecd_sk_<key>",
        "description": "Your account's API key, issued and activated by eCourtDate, sent as `Authorization: Bearer ecd_sk_...`. Keys carry the scopes `chat` and/or `ingest`; each operation states the scope it needs as `x-scope`.",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "contact": {
      "name": "eCourtDate",
      "url": "https://www.ecourtdate.com"
    },
    "description": "Grounded, cited answers for court and agency websites, call centers, and\nportals. Each bot on your account answers from its own **knowledge base** of\nuploaded documents and crawled web pages, with the personality and rules\neCourtDate configures for it, and cites the passages it used.\n\nThe API is **OpenAI-compatible**: point the official OpenAI SDKs at\n`https://api.chatbots.ecourtdate.com/v1` with your API key and use chat\ncompletions, models, and embeddings unchanged. Server-side **conversations**,\n**document management**, file **ingestion**, website **crawling**, and\ncompletion **webhooks** extend that surface in the same style.\n\nAccess requires a paid subscription. API keys are issued and activated for\nyour account by eCourtDate.\n\n## Authentication\n\nEvery `/v1` request carries your key in the `Authorization` header:\n\n```\nAuthorization: Bearer ecd_sk_...\n```\n\nA missing, malformed, unknown, expired, or revoked key is `401` with\n`code: invalid_api_key` and a `WWW-Authenticate: Bearer` header. Each key\ncarries scopes that gate what it may call; a key without the scope an\noperation needs is `403` with `code: insufficient_scope`. Every operation\nstates its scope as `x-scope`.\n\n| Scope | Grants |\n|---|---|\n| `chat` | Chat completions, models, embeddings, conversations |\n| `ingest` | File uploads, website crawls, ingest and crawl jobs, document management |\n\n`GET /health` is the only operation that needs no key.\n\n## Quickstart\n\nAsk your default bot a question:\n\n```bash\ncurl \"https://api.chatbots.ecourtdate.com/v1/chat/completions\" \\\n  -H \"Authorization: Bearer $API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"messages\": [\n      { \"role\": \"user\", \"content\": \"When is my traffic hearing, and what happens if I miss it?\" }\n    ]\n  }'\n```\n\nThe same call with the OpenAI Python SDK:\n\n```python\nfrom openai import OpenAI\nclient = OpenAI(api_key=API_KEY, base_url=\"https://api.chatbots.ecourtdate.com/v1\")\nprint(client.chat.completions.create(model=\"court-assistant\", messages=[{\"role\": \"user\", \"content\": \"What are the Traffic Division hours?\"}]).choices[0].message.content)\n```\n\n`model` is a bot slug or alias from `GET /v1/models`; omit it to use your\naccount's default bot. The answer cites the knowledge base with `[Source N]`\nmarkers that the `citations` array on the message resolves to documents.\n\n## Conventions\n\n- **JSON only.** Request bodies are JSON with `Content-Type: application/json`\n  (the one exception is `POST /v1/ingest/files`, which is\n  `multipart/form-data`). Responses are JSON, except streamed answers\n  (`text/event-stream`) and `DELETE /v1/conversations/{conversationId}`\n  (`204`, no body).\n- **One error envelope.** Every error, on every route, is\n  `{\"error\": {\"message\", \"type\", \"param\", \"code\"}}` (the `Error` schema).\n  Program against the HTTP status and `code`; `param` names the request\n  field at fault in bracket form (`messages[2].tool_call_id`, `tools[0]`,\n  `input[3]`) or is `null`.\n- **Request ids.** Every response carries `X-Request-ID`. Send your own\n  (`^[a-zA-Z0-9_-]{1,64}$`) and it is echoed back; otherwise a 32-character\n  hexadecimal id is minted. Quote it when contacting support.\n- **Rate limit headers.** Every response to an authenticated `/v1` request\n  carries `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset`\n  (requests allowed per minute, requests left in the current window, seconds\n  until it resets): success responses and `400`, `404`, `429`, `500`, and\n  `503` alike. They are absent when the request is rejected before\n  authentication completes: `401`, `403`, `413`, a body that is not valid\n  JSON (`400`), and a malformed `Content-Length` header (`400`). They are\n  also absent while the rate limiter itself is unavailable: the request is\n  then served without them, or, when the API is configured to fail closed,\n  rejected with `429` `Rate limiter unavailable.` carrying `Retry-After` only.\n- **CORS preflight** responses (`OPTIONS` with `Origin` and\n  `Access-Control-Request-Method`) are outside this contract: they carry no\n  `X-Request-ID` and no error envelope. Browser-direct use is not supported.\n- **Malformed framing.** A request whose `Content-Length` header is not a\n  whole number is `400` with the message `Invalid Content-Length header.`,\n  `param: null`, and no `code`, on every route and before authentication.\n- **Retry-After.** Every `429` and `503` carries `Retry-After` in seconds.\n- **Strict typing.** Request fields are not coerced from strings:\n  `\"stream\": \"yes\"` and `\"temperature\": \"0.5\"` are `400`s. Integers are\n  accepted where a number is expected.\n- **Unknown fields** in request bodies are ignored. Unknown response fields\n  may appear at any time; ignore what you do not recognize.\n- **Identifiers** are opaque, case-sensitive strings: conversation,\n  document, and job ids are UUIDs; chat completion ids are `chatcmpl-` plus\n  24 hexadecimal characters; model ids are bot slugs or aliases.\n- **Timestamps** are Unix seconds (UTC) on the OpenAI-style objects\n  (`created`, `updated`) and RFC 3339 UTC on documents (`created_at`,\n  `updated_at`), always in one form: six fractional digits and a `Z` suffix\n  (`2026-08-20T09:15:00.000000Z`).\n\n## Streaming\n\nSet `\"stream\": true` on `POST /v1/chat/completions` or\n`POST /v1/conversations/{conversationId}/messages` and the answer arrives as\nserver-sent events. Each frame is `data: ` followed by one compact JSON\ndocument and a blank line; the stream ends with `data: [DONE]`. Chat\ncompletions stream OpenAI `chat.completion.chunk` objects (the finish chunk\ncarries `citations`; an optional usage chunk follows when\n`stream_options.include_usage` is set), so the OpenAI SDKs consume them\nunchanged. A failure after the stream has started is reported in-band as a\nframe carrying the error envelope, followed by `data: [DONE]`.\n\n## Rate limits\n\nTwo per-account limits apply to every `/v1` request, shared by all keys on\nthe account: a fixed-window **requests per minute** limit and a **daily\ntoken quota** (input plus output tokens of chat completions, conversation\nmessages, and embeddings, reset at midnight UTC). Exceeding either is `429`\nwith `type: rate_limit_error`, `code: rate_limit_exceeded` or\n`insufficient_quota`, and `Retry-After`. Rejected requests count toward the\nminute window. Contact eCourtDate to change your limits.\n\n## Versioning\n\nThe API is versioned in the path (`/v1`). Within `/v1`, existing fields,\ntheir types and meanings, status codes, the error envelope, and documented\npaths are stable. Additive changes (new optional request fields, new\nresponse fields, new operations, new enum values) ship without notice and\nare not breaking: clients must ignore unknown fields. Anything retired is\nannounced at least 12 months ahead and signaled with `Deprecation` and\n`Sunset` response headers in the meantime.\n",
    "title": "eCourtDate Chatbot API",
    "version": "2.0.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/health": {
      "get": {
        "description": "Liveness check for monitoring: returns `{\"status\": \"ok\"}` whenever the API is serving requests. It needs no API key, touches no dependency, and is not rate limited. It is the only route outside `/v1`.",
        "operationId": "health",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "ok": {
                    "value": {
                      "status": "ok"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            },
            "description": "The API is up.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "content_length": {
                    "summary": "The malformed Content-Length case shared by every route",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "Invalid Content-Length header.",
                        "param": null,
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Only the malformed `Content-Length` case that applies to every route (see Conventions in the introduction); this operation validates nothing of its own.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "security": [],
        "summary": "Health check",
        "tags": [
          "Status"
        ]
      }
    },
    "/v1/chat/completions": {
      "post": {
        "description": "Answer a conversation with the bot named by `model`, grounded in the bot's knowledge base.\n\nThe request and response follow the OpenAI chat completions shape, so the official OpenAI SDKs work unchanged. `model` is a bot slug or alias from `GET /v1/models`; omit it to use your account's default bot. When the last message is a `user` message, the bot's knowledge base is searched with it and the most relevant passages are given to the model; a text answer then cites them with `[Source N]` markers resolved by the `citations` extension field on the message. `system` and `developer` messages are appended to the bot's own instructions.\n\nTools: send `tools` (function definitions on the bot's allowlist) and the model may answer with `tool_calls` and `finish_reason: tool_calls`; run them, append the assistant turn and one `tool` message per call, and call again. `response_format` selects plain text, a JSON object, or JSON matching a schema; JSON modes cannot be combined with tools.\n\nSet `stream: true` for server-sent events: the response is `text/event-stream` with `chat.completion.chunk` frames and ends with `data: [DONE]`. Add `stream_options.include_usage` for a final usage chunk.\n\nErrors: 400 for an invalid body (the first failing field in `param`), a blank `system`, `developer`, `user`, or `assistant` message, a `tool` message whose `tool_call_id` does not answer a call of the assistant message before it (`param: messages[i].tool_call_id`), `n` other than 1, an empty `stop` sequence, a malformed tool definition, `tool_choice`, or `response_format`, a tool outside the bot's allowlist (`tool_not_allowed`), or an omitted `model` when the account has several bots and no default; 404 `model_not_found` for an unknown, disabled, or foreign `model`; 429 when the per-minute request limit, the daily token quota, or the underlying language model is exhausted; 503 `upstream_unavailable` when the model or the embedding service fails. Usage counts toward your daily token quota.",
        "operationId": "createChatCompletion",
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "hearing_question": {
                  "summary": "A grounded question",
                  "value": {
                    "messages": [
                      {
                        "content": "What are the Traffic Division hours?",
                        "role": "user"
                      }
                    ],
                    "model": "court-assistant"
                  }
                },
                "json_schema": {
                  "summary": "Structured output",
                  "value": {
                    "messages": [
                      {
                        "content": "What is the filing fee for a small claims case under $2,500?",
                        "role": "user"
                      }
                    ],
                    "model": "court-assistant",
                    "response_format": {
                      "json_schema": {
                        "name": "filing_fee",
                        "schema": {
                          "properties": {
                            "fee_usd": {
                              "type": "number"
                            },
                            "source": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "fee_usd"
                          ],
                          "type": "object"
                        }
                      },
                      "type": "json_schema"
                    }
                  }
                },
                "streaming": {
                  "summary": "Streamed, with a usage chunk",
                  "value": {
                    "messages": [
                      {
                        "content": "What are the Traffic Division hours?",
                        "role": "user"
                      }
                    ],
                    "model": "court-assistant",
                    "stream": true,
                    "stream_options": {
                      "include_usage": true
                    }
                  }
                },
                "tool_result": {
                  "summary": "Continuing after a tool call",
                  "value": {
                    "messages": [
                      {
                        "content": "When is the next hearing for case CR-2026-0412?",
                        "role": "user"
                      },
                      {
                        "content": null,
                        "role": "assistant",
                        "tool_calls": [
                          {
                            "function": {
                              "arguments": "{\"case_number\": \"CR-2026-0412\"}",
                              "name": "lookup_hearing"
                            },
                            "id": "call_01Hx7Qm2xT9vL4nR8sW1pB6c",
                            "type": "function"
                          }
                        ]
                      },
                      {
                        "content": "{\"hearing_date\": \"2026-09-14\", \"time\": \"9:00 a.m.\", \"courtroom\": \"3B\"}",
                        "role": "tool",
                        "tool_call_id": "call_01Hx7Qm2xT9vL4nR8sW1pB6c"
                      }
                    ],
                    "model": "clerk-tools",
                    "tools": [
                      {
                        "function": {
                          "description": "Look up the next hearing date for a case number.",
                          "name": "lookup_hearing",
                          "parameters": {
                            "properties": {
                              "case_number": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "case_number"
                            ],
                            "type": "object"
                          }
                        },
                        "type": "function"
                      }
                    ]
                  }
                },
                "tools": {
                  "summary": "Offering a tool",
                  "value": {
                    "messages": [
                      {
                        "content": "When is the next hearing for case CR-2026-0412?",
                        "role": "user"
                      }
                    ],
                    "model": "clerk-tools",
                    "tool_choice": "auto",
                    "tools": [
                      {
                        "function": {
                          "description": "Look up the next hearing date for a case number.",
                          "name": "lookup_hearing",
                          "parameters": {
                            "properties": {
                              "case_number": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "case_number"
                            ],
                            "type": "object"
                          }
                        },
                        "type": "function"
                      }
                    ]
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/ChatCompletionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "text_answer": {
                    "summary": "A cited text answer",
                    "value": {
                      "choices": [
                        {
                          "finish_reason": "stop",
                          "index": 0,
                          "logprobs": null,
                          "message": {
                            "citations": [
                              {
                                "chunk_index": 2,
                                "document_id": "17136f01-972d-4956-868e-8159833072e4",
                                "score": 0.93,
                                "source_filename": "traffic-division-faq.pdf",
                                "source_index": 1,
                                "text_preview": "Traffic Division hours: Monday through Friday, 8:00 a.m. to 4:30 p.m. Payments are accepted at the clerk's window until 4:00 p.m."
                              }
                            ],
                            "content": "The Traffic Division is open Monday through Friday, 8:00 a.m. to 4:30 p.m. Payments at the clerk's window are accepted until 4:00 p.m. [Source 1]",
                            "refusal": null,
                            "role": "assistant",
                            "tool_calls": null
                          }
                        }
                      ],
                      "created": 1787320991,
                      "id": "chatcmpl-8a317b1a95a74321b73b6567",
                      "model": "court-assistant",
                      "object": "chat.completion",
                      "usage": {
                        "completion_tokens": 143,
                        "prompt_tokens": 812,
                        "total_tokens": 955
                      }
                    }
                  },
                  "tool_call": {
                    "summary": "A tool call to run",
                    "value": {
                      "choices": [
                        {
                          "finish_reason": "tool_calls",
                          "index": 0,
                          "logprobs": null,
                          "message": {
                            "citations": null,
                            "content": null,
                            "refusal": null,
                            "role": "assistant",
                            "tool_calls": [
                              {
                                "function": {
                                  "arguments": "{\"case_number\": \"CR-2026-0412\"}",
                                  "name": "lookup_hearing"
                                },
                                "id": "call_01Hx7Qm2xT9vL4nR8sW1pB6c",
                                "type": "function"
                              }
                            ]
                          }
                        }
                      ],
                      "created": 1787320991,
                      "id": "chatcmpl-8a317b1a95a74321b73b6567",
                      "model": "clerk-tools",
                      "object": "chat.completion",
                      "usage": {
                        "completion_tokens": 143,
                        "prompt_tokens": 812,
                        "total_tokens": 955
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ChatCompletion"
                }
              },
              "text/event-stream": {
                "examples": {
                  "cited_answer": {
                    "summary": "A full stream with citations and usage",
                    "value": "data: {\"id\":\"chatcmpl-8a317b1a95a74321b73b6567\",\"object\":\"chat.completion.chunk\",\"created\":1787320991,\"model\":\"court-assistant\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\"},\"logprobs\":null,\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-8a317b1a95a74321b73b6567\",\"object\":\"chat.completion.chunk\",\"created\":1787320991,\"model\":\"court-assistant\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"The Traffic Division is open Monday through Friday, 8:00 a.m. to 4:30 p.m. [Sou\"},\"logprobs\":null,\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-8a317b1a95a74321b73b6567\",\"object\":\"chat.completion.chunk\",\"created\":1787320991,\"model\":\"court-assistant\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"rce 1]\"},\"logprobs\":null,\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-8a317b1a95a74321b73b6567\",\"object\":\"chat.completion.chunk\",\"created\":1787320991,\"model\":\"court-assistant\",\"choices\":[{\"index\":0,\"delta\":{\"citations\":[{\"source_index\":1,\"document_id\":\"17136f01-972d-4956-868e-8159833072e4\",\"source_filename\":\"traffic-division-faq.pdf\",\"chunk_index\":2,\"text_preview\":\"Traffic Division hours: Monday through Friday, 8:00 a.m. to 4:30 p.m. Payments are accepted at the clerk's window until 4:00 p.m.\",\"score\":0.93}]},\"logprobs\":null,\"finish_reason\":\"stop\"}]}\n\ndata: {\"id\":\"chatcmpl-8a317b1a95a74321b73b6567\",\"object\":\"chat.completion.chunk\",\"created\":1787320991,\"model\":\"court-assistant\",\"choices\":[],\"usage\":{\"prompt_tokens\":812,\"completion_tokens\":143,\"total_tokens\":955}}\n\ndata: [DONE]\n\n"
                  },
                  "in_band_error": {
                    "summary": "A failure after the stream started",
                    "value": "data: {\"id\":\"chatcmpl-8a317b1a95a74321b73b6567\",\"object\":\"chat.completion.chunk\",\"created\":1787320991,\"model\":\"court-assistant\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\"},\"logprobs\":null,\"finish_reason\":null}]}\n\ndata: {\"error\":{\"message\":\"The model is currently rate limited. Please retry shortly.\",\"type\":\"rate_limit_error\",\"param\":null,\"code\":\"rate_limit_exceeded\"}}\n\ndata: [DONE]\n\n"
                  },
                  "tool_call": {
                    "summary": "A streamed tool call",
                    "value": "data: {\"id\":\"chatcmpl-8a317b1a95a74321b73b6567\",\"object\":\"chat.completion.chunk\",\"created\":1787320991,\"model\":\"clerk-tools\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\"},\"logprobs\":null,\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-8a317b1a95a74321b73b6567\",\"object\":\"chat.completion.chunk\",\"created\":1787320991,\"model\":\"clerk-tools\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"call_01Hx7Qm2xT9vL4nR8sW1pB6c\",\"type\":\"function\",\"function\":{\"name\":\"lookup_hearing\",\"arguments\":\"\"}}]},\"logprobs\":null,\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-8a317b1a95a74321b73b6567\",\"object\":\"chat.completion.chunk\",\"created\":1787320991,\"model\":\"clerk-tools\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"function\":{\"arguments\":\"{\\\"case_number\\\": \"}}]},\"logprobs\":null,\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-8a317b1a95a74321b73b6567\",\"object\":\"chat.completion.chunk\",\"created\":1787320991,\"model\":\"clerk-tools\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"function\":{\"arguments\":\"\\\"CR-2026-0412\\\"}\"}}]},\"logprobs\":null,\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-8a317b1a95a74321b73b6567\",\"object\":\"chat.completion.chunk\",\"created\":1787320991,\"model\":\"clerk-tools\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"tool_calls\"}]}\n\ndata: [DONE]\n\n"
                  }
                },
                "schema": {
                  "contentMediaType": "text/event-stream",
                  "contentSchema": {
                    "$ref": "#/components/schemas/ChatCompletionChunk"
                  },
                  "description": "Server-sent events. Every frame is `data: `, one compact JSON `chat.completion.chunk` object (the `ChatCompletionChunk` schema), and a blank line; the stream ends with `data: [DONE]`. In order: one role chunk (`delta: {\"role\": \"assistant\", \"content\": \"\"}`), zero or more content or tool-call deltas, one finish chunk (the only non-null `finish_reason`; its `delta` is `{}` or carries `citations`), an optional usage chunk (`choices: []` plus `usage`, only with `stream_options.include_usage`), then `[DONE]`. A failure after the stream has started is one in-band frame carrying the `Error` envelope, followed by `[DONE]`, with no finish or usage chunk; nothing is metered. Its `code` is `rate_limit_exceeded` (`rate_limit_error`), `upstream_unavailable` (`server_error`), or `null` with `type` `server_error` (retry) or `invalid_request_error` (`The model provider rejected the request.`: do not retry, change the request).",
                  "type": "string"
                }
              }
            },
            "description": "The completion (`application/json`), or the event stream when `stream` is `true` (`text/event-stream`).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "empty_content": {
                    "summary": "An empty message",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "Message content must not be empty.",
                        "param": "messages[0].content",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "json_mode_with_tools": {
                    "summary": "JSON mode combined with tools",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "response_format (JSON mode) cannot be combined with tools.",
                        "param": "response_format",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "no_default_model": {
                    "summary": "`model` omitted with no account default",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "Multiple models are available for this account; pass 'model' explicitly, or ask eCourtDate to set a default model for your account.",
                        "param": "model",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "tool_call_id": {
                    "summary": "A `tool` message answering no preceding tool call",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "tool_call_id does not match a preceding tool call.",
                        "param": "messages[2].tool_call_id",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "tool_not_allowed": {
                    "summary": "A tool outside the bot's allowlist",
                    "value": {
                      "error": {
                        "code": "tool_not_allowed",
                        "message": "Tool 'get_weather' is not allowed for this model.",
                        "param": "tools[1]",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "validation": {
                    "summary": "A field failed validation",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "Input should be 'system', 'developer', 'user', 'assistant' or 'tool'",
                        "param": "messages[0].role",
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid request. `param` names the first failing field (`null` when the body is not a JSON object); `code` is `null` unless stated. The `RateLimit-*` headers are absent only when the body is not valid JSON (rejected before authentication).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'chat' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `chat` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "model_not_found": {
                    "value": {
                      "error": {
                        "code": "model_not_found",
                        "message": "Model 'traffic-bot' was not found. Available models: court-assistant, jury-helpdesk. Omit the 'model' field to use this account's default.",
                        "param": "model",
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No enabled bot or alias with that name on your account (`model_not_found`).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "request_too_large",
                    "message": "Request body exceeds the 5242880 byte limit.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request body exceeds 5 MiB (5,242,880 bytes). The check runs before authentication, on a routed request: for a body sent without `Content-Length` an unknown path or wrong method is reported first (404, 405).",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "model_rate_limited": {
                    "summary": "The model is rate limited",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "The model is currently rate limited. Please retry shortly.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted (requests per minute or the daily token quota), or the underlying language model is rate limited. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "upstream_unavailable",
                    "message": "The model provider is temporarily unavailable. Please retry shortly.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The underlying language model or the text embedding service failed or timed out. Nothing was stored or charged; wait `Retry-After` seconds and retry.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Create a chat completion",
        "tags": [
          "Chat"
        ],
        "x-scope": "chat"
      }
    },
    "/v1/conversations": {
      "post": {
        "description": "Create an empty server-side conversation. Unlike chat completions, where you resend the whole history on every call, a conversation stores its messages on the server: post one user message at a time and the reply is generated against the full stored history and the bot's knowledge base.\n\n`model` binds the conversation to a bot (the canonical slug is stored and returned) and is validated now, so a bad name fails here rather than on the first message; omit it to follow your account's default bot, resolved on every message. `metadata` holds up to 16 string key/value pairs of your own and is returned by create and retrieve. The body may be omitted entirely (no body, an empty body, or `null` is the same as `{}`).\n\nErrors: 400 for invalid `metadata` (shape, key count, key or value length, `param: metadata` or `metadata.<key>`) or an omitted `model` when the account has several bots and no default; 404 `model_not_found` for an unknown or disabled `model`.",
        "operationId": "createConversation",
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "bound": {
                  "summary": "Bound to a bot, with metadata",
                  "value": {
                    "metadata": {
                      "case_number": "TR-2026-08841",
                      "session_id": "web-7f3e2a"
                    },
                    "model": "court-assistant"
                  }
                },
                "default_bot": {
                  "summary": "Following the account default",
                  "value": {}
                }
              },
              "schema": {
                "$ref": "#/components/schemas/ConversationCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "examples": {
                  "conversation": {
                    "value": {
                      "created": 1787320991,
                      "id": "c856e096-99bb-45c1-a0df-044dbbe0aeb7",
                      "metadata": {
                        "case_number": "TR-2026-08841",
                        "session_id": "web-7f3e2a"
                      },
                      "model": "court-assistant",
                      "object": "conversation",
                      "updated": 1787320991
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Conversation"
                }
              }
            },
            "description": "The new conversation.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "metadata_count": {
                    "summary": "More than 16 metadata keys",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "Dictionary should have at most 16 items after validation, not 17",
                        "param": "metadata",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "metadata_value": {
                    "summary": "A metadata value over 512 characters",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "String should have at most 512 characters",
                        "param": "metadata.notes",
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid request. `param` names the first failing field (`null` when the body is not a JSON object); `code` is `null` unless stated. The `RateLimit-*` headers are absent only when the body is not valid JSON (rejected before authentication).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'chat' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `chat` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "model_not_found": {
                    "value": {
                      "error": {
                        "code": "model_not_found",
                        "message": "Model 'traffic-bot' was not found. Available models: court-assistant, jury-helpdesk. Omit the 'model' field to use this account's default.",
                        "param": "model",
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No enabled bot or alias with that name on your account (`model_not_found`).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "request_too_large",
                    "message": "Request body exceeds the 5242880 byte limit.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request body exceeds 5 MiB (5,242,880 bytes). The check runs before authentication, on a routed request: for a body sent without `Content-Length` an unknown path or wrong method is reported first (404, 405).",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted: requests per minute or the daily token quota. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Create a conversation",
        "tags": [
          "Conversations"
        ],
        "x-scope": "chat"
      }
    },
    "/v1/conversations/{conversationId}": {
      "delete": {
        "description": "Delete a conversation and its transcript. The response is 204 with no body. Deletion is permanent; a second delete of the same id is 404.\n\nErrors: 404 `not_found` when no conversation with that id exists on your account.",
        "operationId": "deleteConversation",
        "parameters": [
          {
            "description": "The conversation id.",
            "example": "c856e096-99bb-45c1-a0df-044dbbe0aeb7",
            "in": "path",
            "name": "conversationId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted. No body.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'chat' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `chat` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Conversation not found.",
                        "param": null,
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No conversation with that id on your account (`not_found`).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted: requests per minute or the daily token quota. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Delete a conversation",
        "tags": [
          "Conversations"
        ],
        "x-scope": "chat"
      },
      "get": {
        "description": "Retrieve a conversation with its complete stored transcript, in order, alternating `user` and `assistant` messages. Assistant messages carry the `citations` they were answered with. The transcript is unbounded: every stored turn is returned, even though the model sees only up to the 40 most recent messages when answering. A conversation bound to a bot that was later disabled is still retrievable.\n\nErrors: 404 `not_found` when no conversation with that id exists on your account (another account's id behaves the same).",
        "operationId": "retrieveConversation",
        "parameters": [
          {
            "description": "The conversation id.",
            "example": "c856e096-99bb-45c1-a0df-044dbbe0aeb7",
            "in": "path",
            "name": "conversationId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "conversation": {
                    "value": {
                      "created": 1787320991,
                      "id": "c856e096-99bb-45c1-a0df-044dbbe0aeb7",
                      "messages": [
                        {
                          "citations": null,
                          "content": "What time does jury duty start, and where do I report?",
                          "role": "user"
                        },
                        {
                          "citations": [
                            {
                              "chunk_index": 0,
                              "document_id": "2f6e1c0a-8d3b-4c57-9a41-5b2e8d7f0c13",
                              "score": 0.91,
                              "source_filename": "juror-handbook.pdf",
                              "source_index": 1,
                              "text_preview": "Report to the jury assembly room on the second floor by 8:30 a.m. Bring your summons and a photo id."
                            }
                          ],
                          "content": "Jury duty at the county courthouse begins at 8:30 a.m. Report to the jury assembly room on the second floor with your summons and a photo id. [Source 1]",
                          "role": "assistant"
                        }
                      ],
                      "metadata": {
                        "case_number": "TR-2026-08841",
                        "session_id": "web-7f3e2a"
                      },
                      "model": "court-assistant",
                      "object": "conversation",
                      "updated": 1787321022
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ConversationDetail"
                }
              }
            },
            "description": "The conversation with its transcript.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'chat' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `chat` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Conversation not found.",
                        "param": null,
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No conversation with that id on your account (`not_found`).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted: requests per minute or the daily token quota. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Retrieve a conversation",
        "tags": [
          "Conversations"
        ],
        "x-scope": "chat"
      }
    },
    "/v1/conversations/{conversationId}/messages": {
      "post": {
        "description": "Add a user message to a conversation and get the assistant reply. The bot (the conversation's bound bot, or your account's current default) answers against the stored history plus the new message, grounded in its knowledge base; the model sees up to the 40 most recent messages (the window always opens on a `user` turn, so it can hold 39). Your message and the reply are stored together, as one turn, after the reply is complete.\n\nSet `stream: true` for server-sent events: `text/event-stream` frames of `{\"conversation_id\", \"delta\": {\"content\"}}` followed by one `{\"conversation_id\", \"message\", \"done\": true}` frame and `data: [DONE]`. The turn is stored before the done frame is sent; a stream that ends in an error frame stores nothing.\n\nErrors: 400 for an empty or blank `content` or one over 100,000 characters, a non-boolean `stream`, or, for a conversation that follows the account default, an account that now has several bots and no default (`param: model`); 404 `not_found` for an unknown conversation and 404 `model_not_found` when the conversation's bot has been disabled since it was created; 429 when the per-minute request limit, the daily token quota, or the underlying language model is exhausted; 503 `upstream_unavailable` when the model or the embedding service fails. Usage counts toward your daily token quota.",
        "operationId": "addConversationMessage",
        "parameters": [
          {
            "description": "The conversation id.",
            "example": "c856e096-99bb-45c1-a0df-044dbbe0aeb7",
            "in": "path",
            "name": "conversationId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "question": {
                  "summary": "A question",
                  "value": {
                    "content": "What time does jury duty start, and where do I report?"
                  }
                },
                "streaming": {
                  "summary": "Streamed",
                  "value": {
                    "content": "What time does jury duty start, and where do I report?",
                    "stream": true
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/ConversationMessageRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "reply": {
                    "value": {
                      "conversation_id": "c856e096-99bb-45c1-a0df-044dbbe0aeb7",
                      "message": {
                        "citations": [
                          {
                            "chunk_index": 0,
                            "document_id": "2f6e1c0a-8d3b-4c57-9a41-5b2e8d7f0c13",
                            "score": 0.91,
                            "source_filename": "juror-handbook.pdf",
                            "source_index": 1,
                            "text_preview": "Report to the jury assembly room on the second floor by 8:30 a.m. Bring your summons and a photo id."
                          }
                        ],
                        "content": "Jury duty at the county courthouse begins at 8:30 a.m. Report to the jury assembly room on the second floor with your summons and a photo id. [Source 1]",
                        "role": "assistant"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ConversationMessageResponse"
                }
              },
              "text/event-stream": {
                "examples": {
                  "cited_reply": {
                    "summary": "A full stream with citations",
                    "value": "data: {\"conversation_id\":\"c856e096-99bb-45c1-a0df-044dbbe0aeb7\",\"delta\":{\"content\":\"Jury duty at the county courthouse begins at 8:\"}}\n\ndata: {\"conversation_id\":\"c856e096-99bb-45c1-a0df-044dbbe0aeb7\",\"delta\":{\"content\":\"30 a.m. Report to the jury assembly room on the second floor with your summons and a photo id. [Source 1]\"}}\n\ndata: {\"conversation_id\":\"c856e096-99bb-45c1-a0df-044dbbe0aeb7\",\"message\":{\"role\":\"assistant\",\"content\":\"Jury duty at the county courthouse begins at 8:30 a.m. Report to the jury assembly room on the second floor with your summons and a photo id. [Source 1]\",\"citations\":[{\"source_index\":1,\"document_id\":\"2f6e1c0a-8d3b-4c57-9a41-5b2e8d7f0c13\",\"source_filename\":\"juror-handbook.pdf\",\"chunk_index\":0,\"text_preview\":\"Report to the jury assembly room on the second floor by 8:30 a.m. Bring your summons and a photo id.\",\"score\":0.91}]},\"done\":true}\n\ndata: [DONE]\n\n"
                  },
                  "in_band_error": {
                    "summary": "A failure after the stream started",
                    "value": "data: {\"conversation_id\":\"c856e096-99bb-45c1-a0df-044dbbe0aeb7\",\"delta\":{\"content\":\"Jury duty at the county courthouse\"}}\n\ndata: {\"error\":{\"message\":\"The model provider is temporarily unavailable. Please retry shortly.\",\"type\":\"server_error\",\"param\":null,\"code\":\"upstream_unavailable\"}}\n\ndata: [DONE]\n\n"
                  }
                },
                "schema": {
                  "contentMediaType": "text/event-stream",
                  "contentSchema": {
                    "$ref": "#/components/schemas/ConversationMessageChunk"
                  },
                  "description": "Server-sent events. Every frame is `data: `, one compact JSON object (the `ConversationMessageChunk` schema), and a blank line; the stream ends with `data: [DONE]`. In order: zero or more delta frames (`{\"conversation_id\", \"delta\": {\"content\"}}`), then one done frame (`{\"conversation_id\", \"message\", \"done\": true}`) whose `message` is the same object the non-streaming call returns, then `[DONE]`. There is no role frame and no usage frame. A failure after the stream has started is one in-band frame carrying the `Error` envelope, followed by `[DONE]`; it can be the very first frame (same `code`/`type` vocabulary as chat completions, including the `invalid_request_error` frame that must not be retried). The turn is stored before the done frame is sent, so the transcript is complete once you read `done: true`; a stream that ends in an error frame stores nothing.",
                  "type": "string"
                }
              }
            },
            "description": "The assistant reply (`application/json`), or the event stream when `stream` is `true` (`text/event-stream`).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "blank_content": {
                    "summary": "A whitespace-only message",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "Message content must not be empty.",
                        "param": "content",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "empty_content": {
                    "summary": "An empty message",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "String should have at least 1 character",
                        "param": "content",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "stream_not_boolean": {
                    "summary": "`stream` is not a boolean",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "Input should be a valid boolean",
                        "param": "stream",
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid request. `param` names the first failing field (`null` when the body is not a JSON object); `code` is `null` unless stated. The `RateLimit-*` headers are absent only when the body is not valid JSON (rejected before authentication).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'chat' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `chat` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "model_not_found": {
                    "summary": "The conversation's bot was disabled",
                    "value": {
                      "error": {
                        "code": "model_not_found",
                        "message": "Model 'traffic-bot' was not found. Available models: court-assistant, jury-helpdesk. Omit the 'model' field to use this account's default.",
                        "param": "model",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "not_found": {
                    "summary": "Unknown conversation",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Conversation not found.",
                        "param": null,
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No conversation with that id on your account (`not_found`), or the conversation's bot has been disabled since it was created (`model_not_found`).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "request_too_large",
                    "message": "Request body exceeds the 5242880 byte limit.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request body exceeds 5 MiB (5,242,880 bytes). The check runs before authentication, on a routed request: for a body sent without `Content-Length` an unknown path or wrong method is reported first (404, 405).",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "model_rate_limited": {
                    "summary": "The model is rate limited",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "The model is currently rate limited. Please retry shortly.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted (requests per minute or the daily token quota), or the underlying language model is rate limited. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "upstream_unavailable",
                    "message": "The model provider is temporarily unavailable. Please retry shortly.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The underlying language model or the text embedding service failed or timed out. Nothing was stored or charged; wait `Retry-After` seconds and retry.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Add a message",
        "tags": [
          "Conversations"
        ],
        "x-scope": "chat"
      }
    },
    "/v1/documents": {
      "get": {
        "description": "List the documents in your knowledge base, newest first, with cursor pagination in the OpenAI list style. Pass the previous page's `last_id` as `after` until `has_more` is `false`. Filter by `namespace`, `status`, and `source_type`; filters combine.\n\nErrors: 400 for `limit` outside 1 to 1000, an empty `after` or `namespace`, an `after` id that is not one of your documents, or an unknown `status` or `source_type` value.",
        "operationId": "listDocuments",
        "parameters": [
          {
            "description": "Page size, 1 to 1000.",
            "example": 25,
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "maximum": 1000,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Cursor: the `last_id` of the previous page; the page starts after that document. An id that is not a document on your account is a 400.",
            "example": "17136f01-972d-4956-868e-8159833072e4",
            "in": "query",
            "name": "after",
            "required": false,
            "schema": {
              "minLength": 1,
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "description": "Only documents in this namespace (exact, case-sensitive match).",
            "example": "general",
            "in": "query",
            "name": "namespace",
            "required": false,
            "schema": {
              "minLength": 1,
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "description": "Only documents in this status.",
            "example": "ready",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "processing",
                "ready",
                "failed",
                null
              ],
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "description": "Only uploaded files (`upload`) or crawled pages (`crawl`).",
            "example": "upload",
            "in": "query",
            "name": "source_type",
            "required": false,
            "schema": {
              "enum": [
                "upload",
                "crawl",
                null
              ],
              "type": [
                "string",
                "null"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "empty": {
                    "summary": "No documents match",
                    "value": {
                      "data": [],
                      "first_id": null,
                      "has_more": false,
                      "last_id": null,
                      "object": "list"
                    }
                  },
                  "page": {
                    "value": {
                      "data": [
                        {
                          "chunk_count": 0,
                          "created_at": "2026-08-21T14:03:11.214000Z",
                          "error": "No readable text was found in this document. Scanned or image-only files must be run through OCR before upload.",
                          "filename": "fee-schedule-scan.pdf",
                          "id": "4b1f0c8e-2a77-4c0f-9d3e-6f2a1b9c7d10",
                          "namespace": "general",
                          "object": "document",
                          "source_type": "upload",
                          "source_url": null,
                          "status": "failed",
                          "updated_at": "2026-08-21T14:03:40.102000Z"
                        },
                        {
                          "chunk_count": 18,
                          "created_at": "2026-08-21T14:03:11.214000Z",
                          "error": null,
                          "filename": "traffic-division-faq.pdf",
                          "id": "17136f01-972d-4956-868e-8159833072e4",
                          "namespace": "general",
                          "object": "document",
                          "source_type": "upload",
                          "source_url": null,
                          "status": "ready",
                          "updated_at": "2026-08-21T14:03:42.908000Z"
                        },
                        {
                          "chunk_count": 42,
                          "created_at": "2026-08-20T09:15:00.000000Z",
                          "error": null,
                          "filename": "https://courts.example.gov/jury/handbook",
                          "id": "2f6e1c0a-8d3b-4c57-9a41-5b2e8d7f0c13",
                          "namespace": "general",
                          "object": "document",
                          "source_type": "crawl",
                          "source_url": "https://courts.example.gov/jury/handbook",
                          "status": "ready",
                          "updated_at": "2026-08-20T09:17:26.511000Z"
                        }
                      ],
                      "first_id": "4b1f0c8e-2a77-4c0f-9d3e-6f2a1b9c7d10",
                      "has_more": true,
                      "last_id": "2f6e1c0a-8d3b-4c57-9a41-5b2e8d7f0c13",
                      "object": "list"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DocumentList"
                }
              }
            },
            "description": "A page of documents.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "cursor": {
                    "summary": "Unknown `after` cursor",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "Unknown document id in `after`: '4b1f0c8e-2a77-4c0f-9d3e-6f2a1b9c7d10'.",
                        "param": "after",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "limit": {
                    "summary": "`limit` out of range",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "Input should be less than or equal to 1000",
                        "param": "limit",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "namespace": {
                    "summary": "An empty `namespace` filter",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "String should have at least 1 character",
                        "param": "namespace",
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid request. `param` names the first failing field (`null` when the body is not a JSON object); `code` is `null` unless stated.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'ingest' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `ingest` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted: requests per minute or the daily token quota. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "List documents",
        "tags": [
          "Documents"
        ],
        "x-scope": "ingest"
      }
    },
    "/v1/documents/{documentId}": {
      "delete": {
        "description": "Delete a document and every passage indexed from it, so it stops appearing in answers and citations immediately. Deletion is permanent; a second delete of the same id is 404.\n\nErrors: 404 `not_found` when no document with that id exists on your account.",
        "operationId": "deleteDocument",
        "parameters": [
          {
            "description": "The document id.",
            "example": "17136f01-972d-4956-868e-8159833072e4",
            "in": "path",
            "name": "documentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "deleted": {
                    "value": {
                      "chunks_deleted": 18,
                      "deleted": true,
                      "id": "17136f01-972d-4956-868e-8159833072e4",
                      "object": "document.deleted"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DocumentDeleted"
                }
              }
            },
            "description": "Deleted.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'ingest' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `ingest` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Document not found.",
                        "param": null,
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No document with that id on your account (`not_found`).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted: requests per minute or the daily token quota. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Delete a document",
        "tags": [
          "Documents"
        ],
        "x-scope": "ingest"
      },
      "get": {
        "description": "Retrieve one document: its namespace, origin, indexing status, chunk count, and, when indexing failed, the sanitized `error`.\n\nErrors: 404 `not_found` when no document with that id exists on your account.",
        "operationId": "retrieveDocument",
        "parameters": [
          {
            "description": "The document id.",
            "example": "17136f01-972d-4956-868e-8159833072e4",
            "in": "path",
            "name": "documentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "crawled": {
                    "summary": "A crawled page",
                    "value": {
                      "chunk_count": 42,
                      "created_at": "2026-08-20T09:15:00.000000Z",
                      "error": null,
                      "filename": "https://courts.example.gov/jury/handbook",
                      "id": "2f6e1c0a-8d3b-4c57-9a41-5b2e8d7f0c13",
                      "namespace": "general",
                      "object": "document",
                      "source_type": "crawl",
                      "source_url": "https://courts.example.gov/jury/handbook",
                      "status": "ready",
                      "updated_at": "2026-08-20T09:17:26.511000Z"
                    }
                  },
                  "failed": {
                    "summary": "A failed upload",
                    "value": {
                      "chunk_count": 0,
                      "created_at": "2026-08-21T14:03:11.214000Z",
                      "error": "No readable text was found in this document. Scanned or image-only files must be run through OCR before upload.",
                      "filename": "fee-schedule-scan.pdf",
                      "id": "4b1f0c8e-2a77-4c0f-9d3e-6f2a1b9c7d10",
                      "namespace": "general",
                      "object": "document",
                      "source_type": "upload",
                      "source_url": null,
                      "status": "failed",
                      "updated_at": "2026-08-21T14:03:40.102000Z"
                    }
                  },
                  "ready": {
                    "summary": "An indexed upload",
                    "value": {
                      "chunk_count": 18,
                      "created_at": "2026-08-21T14:03:11.214000Z",
                      "error": null,
                      "filename": "traffic-division-faq.pdf",
                      "id": "17136f01-972d-4956-868e-8159833072e4",
                      "namespace": "general",
                      "object": "document",
                      "source_type": "upload",
                      "source_url": null,
                      "status": "ready",
                      "updated_at": "2026-08-21T14:03:42.908000Z"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            },
            "description": "The document.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'ingest' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `ingest` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Document not found.",
                        "param": null,
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No document with that id on your account (`not_found`).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted: requests per minute or the daily token quota. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Retrieve a document",
        "tags": [
          "Documents"
        ],
        "x-scope": "ingest"
      }
    },
    "/v1/embeddings": {
      "post": {
        "description": "Embed one string or up to 96 strings with the API's text embedding model, in the OpenAI embeddings shape.\n\nEvery embedding has 1024 dimensions. `encoding_format` chooses arrays of numbers (`float`) or base64-encoded little-endian 32-bit floats (`base64`, the OpenAI SDK default). The `input_type` extension tells the model whether the text is a document to store (`search_document`, the default) or a query to match against stored documents (`search_query`). `model` is an echo-only label; the response repeats it (or `default`).\n\nErrors: 400 for an empty input, an item over 100,000 characters (`param: input[i]`), more than 96 items, a non-string item, or `dimensions` other than 1024; 429 when the per-minute request limit, the daily token quota, or the embedding service is exhausted; 503 `upstream_unavailable` when the embedding service fails. Input tokens count toward your daily token quota.",
        "operationId": "createEmbeddings",
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "documents": {
                  "summary": "Two passages to store",
                  "value": {
                    "input": [
                      "Traffic Division hours: Monday through Friday, 8:00 a.m. to 4:30 p.m.",
                      "Jury duty begins at 8:30 a.m. in the jury assembly room."
                    ],
                    "input_type": "search_document"
                  }
                },
                "query": {
                  "summary": "A query, base64-encoded result",
                  "value": {
                    "encoding_format": "base64",
                    "input": "When does jury duty start?",
                    "input_type": "search_query"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/EmbeddingRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "default_float": {
                    "summary": "`encoding_format: float` (vectors abbreviated)",
                    "value": {
                      "data": [
                        {
                          "embedding": [
                            0.0123,
                            -0.0456,
                            0.0789,
                            0.0011,
                            -0.0204,
                            0.0372,
                            0.0098,
                            -0.0156
                          ],
                          "index": 0,
                          "object": "embedding"
                        },
                        {
                          "embedding": [
                            -0.0156,
                            0.0098,
                            0.0372,
                            -0.0204,
                            0.0011,
                            0.0789,
                            -0.0456,
                            0.0123
                          ],
                          "index": 1,
                          "object": "embedding"
                        }
                      ],
                      "model": "default",
                      "object": "list",
                      "usage": {
                        "prompt_tokens": 31,
                        "total_tokens": 31
                      }
                    }
                  },
                  "encoded_base64": {
                    "summary": "`encoding_format: base64` (string abbreviated)",
                    "value": {
                      "data": [
                        {
                          "embedding": "AACAPwAAAAAAAIA/AAAAAA==",
                          "index": 0,
                          "object": "embedding"
                        }
                      ],
                      "model": "default",
                      "object": "list",
                      "usage": {
                        "prompt_tokens": 7,
                        "total_tokens": 7
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/EmbeddingList"
                }
              }
            },
            "description": "One embedding per input.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "dimensions": {
                    "summary": "Unsupported dimensions",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "This model produces 1024-dimensional embeddings; the 'dimensions' parameter is not supported.",
                        "param": "dimensions",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "empty_item": {
                    "summary": "An empty input item",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "input strings must not be empty.",
                        "param": "input[1]",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "too_many": {
                    "summary": "More than 96 inputs",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "input may hold at most 96 strings.",
                        "param": "input[96]",
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid request. `param` names the first failing field (`null` when the body is not a JSON object); `code` is `null` unless stated. The `RateLimit-*` headers are absent only when the body is not valid JSON (rejected before authentication).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'chat' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `chat` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "request_too_large",
                    "message": "Request body exceeds the 5242880 byte limit.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request body exceeds 5 MiB (5,242,880 bytes). The check runs before authentication, on a routed request: for a body sent without `Content-Length` an unknown path or wrong method is reported first (404, 405).",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "model_rate_limited": {
                    "summary": "The model is rate limited",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "The model is currently rate limited. Please retry shortly.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted (requests per minute or the daily token quota), or the text embedding service is rate limited. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "upstream_unavailable",
                    "message": "The model provider is temporarily unavailable. Please retry shortly.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The text embedding service failed or timed out. Nothing was stored or charged; wait `Retry-After` seconds and retry.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Create embeddings",
        "tags": [
          "Embeddings"
        ],
        "x-scope": "chat"
      }
    },
    "/v1/ingest/crawl": {
      "post": {
        "description": "Crawl a public website into the knowledge base. Starting from `seed_urls`, the crawler follows links on the allowed hosts up to `max_depth` and `max_pages`, honoring `robots.txt` by default, and adds each page with readable text as a `crawl` document under `namespace`. Pages the site answers with an HTTP error status (4xx or 5xx) are not indexed; they are listed in the job's `errors[]` as `HTTP <status>`. The response is immediate: poll `GET /v1/ingest/crawl/{crawlJobId}` (or receive the `crawl.completed` webhook) for the outcome.\n\nA crawl has a 10-minute budget, fetching and indexing included; exceeding it ends the job `failed` with a single `crawl` entry `Crawl timed out.` (pages indexed before then stay ready). Size `max_pages` and `rate_limit_rps` so the crawl can finish within it.\n\nOnly public `http` and `https` hosts can be crawled: private, loopback, link-local, and metadata addresses are rejected at submission and again at fetch time.\n\nErrors: 400 for an invalid `namespace`, no seed URLs or more than 100, a seed that is not a public http(s) URL (`param: seed_urls[i]`), or `max_pages`, `max_depth`, or `rate_limit_rps` outside their ranges.",
        "operationId": "startCrawl",
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "minimal": {
                  "summary": "Defaults (hosts derived from the seeds)",
                  "value": {
                    "namespace": "general",
                    "seed_urls": [
                      "https://courts.example.gov/self-help"
                    ]
                  }
                },
                "site": {
                  "summary": "A court website",
                  "value": {
                    "allowed_domains": [
                      "courts.example.gov"
                    ],
                    "max_depth": 3,
                    "max_pages": 200,
                    "namespace": "general",
                    "rate_limit_rps": 2.0,
                    "respect_robots_txt": true,
                    "seed_urls": [
                      "https://courts.example.gov/"
                    ]
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/CrawlRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "accepted": {
                    "value": {
                      "crawl_job_id": "2d6f8c0e-5b1a-4f3c-9e7d-0a1b2c3d4e5f",
                      "status": "processing"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/CrawlResponse"
                }
              }
            },
            "description": "The crawl job was created and queued.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "max_pages": {
                    "summary": "`max_pages` out of range",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "Input should be less than or equal to 500",
                        "param": "max_pages",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "namespace": {
                    "summary": "Invalid namespace",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "String should match pattern '^[a-z0-9][a-z0-9_-]{0,63}$'",
                        "param": "namespace",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "seed_url": {
                    "summary": "A seed that cannot be crawled",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "Seed URL 'http://10.0.0.5/admin' is not allowed: Host '10.0.0.5' resolves to a disallowed address (10.0.0.5).",
                        "param": "seed_urls[0]",
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid request. `param` names the first failing field (`null` when the body is not a JSON object); `code` is `null` unless stated. The `RateLimit-*` headers are absent only when the body is not valid JSON (rejected before authentication).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'ingest' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `ingest` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "request_too_large",
                    "message": "Request body exceeds the 5242880 byte limit.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request body exceeds 5 MiB (5,242,880 bytes). The check runs before authentication, on a routed request: for a body sent without `Content-Length` an unknown path or wrong method is reported first (404, 405).",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted: requests per minute or the daily token quota. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Start a crawl",
        "tags": [
          "Crawling"
        ],
        "x-scope": "ingest"
      }
    },
    "/v1/ingest/crawl/{crawlJobId}": {
      "get": {
        "description": "Retrieve the status of a crawl job started by `POST /v1/ingest/crawl`: live page counts while it is `processing` (updated after every page fetched and indexed), then a terminal `completed`, `completed_with_errors`, or `failed` with one `errors[]` entry per failed page (or a single `crawl` entry when the crawl itself failed or timed out). Poll with backoff, or receive the `crawl.completed` webhook.\n\nErrors: 404 `not_found` when no crawl job with that id exists on your account.",
        "operationId": "retrieveCrawlJob",
        "parameters": [
          {
            "description": "The crawl job id returned by `POST /v1/ingest/crawl`.",
            "example": "2d6f8c0e-5b1a-4f3c-9e7d-0a1b2c3d4e5f",
            "in": "path",
            "name": "crawlJobId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "completed_with_errors": {
                    "summary": "Finished, one failed page",
                    "value": {
                      "crawl_job_id": "2d6f8c0e-5b1a-4f3c-9e7d-0a1b2c3d4e5f",
                      "errors": [
                        {
                          "error": "The page could not be retrieved or contained no readable text.",
                          "url": "https://courts.example.gov/forms/fee-waiver"
                        }
                      ],
                      "pages_crawled": 148,
                      "pages_indexed": 146,
                      "status": "completed_with_errors"
                    }
                  },
                  "processing": {
                    "summary": "Still running",
                    "value": {
                      "crawl_job_id": "2d6f8c0e-5b1a-4f3c-9e7d-0a1b2c3d4e5f",
                      "errors": [],
                      "pages_crawled": 37,
                      "pages_indexed": 35,
                      "status": "processing"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/CrawlJobStatus"
                }
              }
            },
            "description": "The crawl job.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'ingest' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `ingest` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Crawl job not found.",
                        "param": null,
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No crawl job with that id on your account (`not_found`).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted: requests per minute or the daily token quota. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Retrieve a crawl job",
        "tags": [
          "Crawling"
        ],
        "x-scope": "ingest"
      }
    },
    "/v1/ingest/files": {
      "post": {
        "description": "Upload 1 to 20 files as `multipart/form-data` and start one ingest job that extracts their text, splits it into passages, embeds them, and adds them to the knowledge base under `namespace`. The response is immediate: poll `GET /v1/ingest/jobs/{jobId}` (or receive the `ingest.completed` webhook) for the outcome, and `GET /v1/documents/{documentId}` for each document's status.\n\nValidation is all-or-nothing: every part is checked (count, emptiness, size, filename, extension, content) before anything is stored, so a 400 never leaves part of a batch behind.\n\nErrors: 400 for an invalid `namespace`, more than 20 files, or a file that is empty, over 25 MiB, of a type not in the allowlist, whose content does not match its extension, that is not valid UTF-8 (text types), or whose filename is too long or invalid (`param: file[i]`); 413 `request_too_large` when the whole body exceeds 20 files of 25 MiB plus 1 MiB of multipart overhead.",
        "operationId": "ingestFiles",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "examples": {
                "two_files": {
                  "summary": "Two files into the `general` namespace",
                  "value": {
                    "file": [
                      "<traffic-division-faq.pdf bytes>",
                      "<fee-schedule-2026.docx bytes>"
                    ],
                    "namespace": "general"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/IngestFilesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "accepted": {
                    "value": {
                      "document_ids": [
                        "17136f01-972d-4956-868e-8159833072e4",
                        "4b1f0c8e-2a77-4c0f-9d3e-6f2a1b9c7d10"
                      ],
                      "job_id": "bfcde661-9eec-4142-a790-ca9a62c8e0f8",
                      "status": "processing"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/IngestFilesResponse"
                }
              }
            },
            "description": "The ingest job was created and queued.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "file_size": {
                    "summary": "A file over 25 MiB",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "File exceeds the 26214400 byte limit.",
                        "param": "file[0]",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "file_type": {
                    "summary": "A file type outside the allowlist",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "File type '.exe' is not allowed. Allowed: .csv, .docx, .eml, .htm, .html, .md, .pdf, .txt, .xlsx.",
                        "param": "file[1]",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "namespace": {
                    "summary": "Invalid namespace",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "namespace must match ^[a-z0-9][a-z0-9_-]{0,63}$ (lowercase letters, digits, '_' and '-', 1 to 64 characters).",
                        "param": "namespace",
                        "type": "invalid_request_error"
                      }
                    }
                  },
                  "too_many_files": {
                    "summary": "More than 20 files",
                    "value": {
                      "error": {
                        "code": null,
                        "message": "Too many files: 21 (max 20).",
                        "param": "file",
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid request. `param` names the first failing field (`null` when the body is not a JSON object); `code` is `null` unless stated.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'ingest' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `ingest` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "413": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "request_too_large",
                    "message": "Request body exceeds the 525336576 byte limit.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The body exceeds 20 files of 25 MiB plus 1 MiB of multipart overhead (525,336,576 bytes). The check runs before authentication, on a routed request.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted: requests per minute or the daily token quota. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Upload files",
        "tags": [
          "Ingestion"
        ],
        "x-scope": "ingest"
      }
    },
    "/v1/ingest/jobs/{jobId}": {
      "get": {
        "description": "Retrieve the status of an ingest job started by `POST /v1/ingest/files`: live document counts while it is `processing` (updated after every document; transient failures are retried automatically), then a terminal `completed`, `completed_with_errors`, or `failed` with one `errors[]` entry per failed document. Poll with backoff, or receive the `ingest.completed` webhook.\n\nErrors: 404 `not_found` when no ingest job with that id exists on your account.",
        "operationId": "retrieveIngestJob",
        "parameters": [
          {
            "description": "The ingest job id returned by `POST /v1/ingest/files`.",
            "example": "bfcde661-9eec-4142-a790-ca9a62c8e0f8",
            "in": "path",
            "name": "jobId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "completed_with_errors": {
                    "summary": "Finished, one failure",
                    "value": {
                      "completed_documents": 2,
                      "errors": [
                        {
                          "document_id": "4b1f0c8e-2a77-4c0f-9d3e-6f2a1b9c7d10",
                          "error": "No readable text was found in this document. Scanned or image-only files must be run through OCR before upload."
                        }
                      ],
                      "failed_documents": 1,
                      "job_id": "bfcde661-9eec-4142-a790-ca9a62c8e0f8",
                      "status": "completed_with_errors",
                      "total_documents": 3
                    }
                  },
                  "processing": {
                    "summary": "Still running",
                    "value": {
                      "completed_documents": 1,
                      "errors": [],
                      "failed_documents": 0,
                      "job_id": "bfcde661-9eec-4142-a790-ca9a62c8e0f8",
                      "status": "processing",
                      "total_documents": 3
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/JobStatus"
                }
              }
            },
            "description": "The job.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'ingest' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `ingest` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Job not found.",
                        "param": null,
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No ingest job with that id on your account (`not_found`).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted: requests per minute or the daily token quota. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Retrieve an ingest job",
        "tags": [
          "Ingestion"
        ],
        "x-scope": "ingest"
      }
    },
    "/v1/models": {
      "get": {
        "description": "List the names your key can send as `model`: every enabled bot on your account, then every alias whose target is enabled, then `default` whenever the account default resolves (a configured default bot, a single enabled bot, or the built-in assistant when no bot is enabled; `default` is absent only when several bots are enabled and none is the default). The list is complete (no pagination) and is read through the same cache as `GET /v1/models/{modelId}` and chat, so a name listed here resolves on the next chat completion.",
        "operationId": "listModels",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "models": {
                    "value": {
                      "data": [
                        {
                          "created": 1784822400,
                          "id": "court-assistant",
                          "object": "model",
                          "owned_by": "maple-county-courts"
                        },
                        {
                          "created": 1785427200,
                          "id": "jury-helpdesk",
                          "object": "model",
                          "owned_by": "maple-county-courts"
                        },
                        {
                          "created": 1784822400,
                          "id": "default",
                          "object": "model",
                          "owned_by": "maple-county-courts"
                        }
                      ],
                      "object": "list"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ModelList"
                }
              }
            },
            "description": "The models your key can use.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'chat' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `chat` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted: requests per minute or the daily token quota. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "List models",
        "tags": [
          "Models"
        ],
        "x-scope": "chat"
      }
    },
    "/v1/models/{modelId}": {
      "get": {
        "description": "Retrieve one entry of `GET /v1/models` by its id: a bot slug, an alias, or `default`. The lookup is case-sensitive. Anything the list does not show (a disabled bot, an alias of a disabled bot, another account's bot, or an unknown name) is 404 `model_not_found`, with the available names in the message.",
        "operationId": "retrieveModel",
        "parameters": [
          {
            "description": "A bot slug, an alias, or `default`.",
            "example": "court-assistant",
            "in": "path",
            "name": "modelId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "model": {
                    "value": {
                      "created": 1784822400,
                      "id": "court-assistant",
                      "object": "model",
                      "owned_by": "maple-county-courts"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Model"
                }
              }
            },
            "description": "The model.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Incorrect API key provided.",
                    "param": null,
                    "type": "authentication_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The request did not authenticate: the API key is missing, malformed, unknown, expired, or revoked.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "This API key does not have the 'chat' scope.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The key lacks the `chat` scope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "model_not_found": {
                    "value": {
                      "error": {
                        "code": "model_not_found",
                        "message": "Model 'traffic-bot' was not found. Available models: court-assistant, jury-helpdesk. Omit the 'model' field to use this account's default.",
                        "param": "model",
                        "type": "invalid_request_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No enabled bot or alias with that name on your account (`model_not_found`).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_quota": {
                    "summary": "Daily token quota",
                    "value": {
                      "error": {
                        "code": "insufficient_quota",
                        "message": "Daily token quota exceeded: 200000 tokens per day.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  },
                  "requests_per_minute": {
                    "summary": "Per-minute request limit",
                    "value": {
                      "error": {
                        "code": "rate_limit_exceeded",
                        "message": "Rate limit exceeded: 60 requests per minute.",
                        "param": null,
                        "type": "rate_limit_error"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "A per-account limit is exhausted: requests per minute or the daily token quota. Wait `Retry-After` seconds. The fail-closed `Rate limiter unavailable.` variant carries `Retry-After` but no `RateLimit-*` headers (nothing was computed).",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Internal server error.",
                    "param": null,
                    "type": "server_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "An unexpected failure. Retry with backoff, then contact support with the `X-Request-ID`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "example": {
                  "error": {
                    "code": null,
                    "message": "Method not allowed.",
                    "param": null,
                    "type": "invalid_request_error"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Any other error (for example 405 with an `Allow` header), in the same envelope.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              }
            }
          }
        },
        "summary": "Retrieve a model",
        "tags": [
          "Models"
        ],
        "x-scope": "chat"
      }
    }
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "servers": [
    {
      "description": "Production",
      "url": "https://api.chatbots.ecourtdate.com"
    }
  ],
  "tags": [
    {
      "description": "OpenAI-compatible chat completions, answered by a bot from its knowledge base with `[Source N]` citations. Supports streaming, tools, and structured output.",
      "name": "Chat"
    },
    {
      "description": "The names your key can send as `model`: your account's bots, their aliases, and `default`. OpenAI-compatible.",
      "name": "Models"
    },
    {
      "description": "Text embeddings (1024 dimensions) in the OpenAI shape, with an `input_type` extension for query versus document text.",
      "name": "Embeddings"
    },
    {
      "description": "Server-side conversations with persistent history: create one, post a message at a time, and the bot answers against the stored transcript and its knowledge base.",
      "name": "Conversations"
    },
    {
      "description": "The documents in your knowledge base (uploaded files and crawled pages): list with cursor pagination and filters, retrieve, and delete.",
      "name": "Documents"
    },
    {
      "description": "Upload files into the knowledge base as a background job, poll the job for its outcome, or receive the `ingest.completed` webhook.",
      "name": "Ingestion"
    },
    {
      "description": "Crawl a public website into the knowledge base as a background job, poll the job for its outcome, or receive the `crawl.completed` webhook.",
      "name": "Crawling"
    },
    {
      "description": "Unauthenticated liveness check for monitoring.",
      "name": "Status"
    }
  ],
  "webhooks": {
    "crawl.completed": {
      "post": {
        "description": "Sent when a crawl job started by `POST /v1/ingest/crawl` reaches `completed`, `completed_with_errors`, or `failed` (a crawl that times out ends `failed` with a single `crawl` error and still sends this event). The body mirrors `GET /v1/ingest/crawl/{crawlJobId}` plus `event` and `namespace`. Delivery: a `POST` with `Content-Type: application/json` to the webhook URL configured for your account, sent once per job when it reaches a terminal status. Respond with any `2xx` within 10 seconds; redirects are not followed. On any other outcome the delivery is retried twice more, 30 seconds and then 5 minutes later (3 attempts in total), with the same `X-ECD-Delivery-Id` and a fresh signature each time; after the third failure the event is dropped. Deduplicate on `X-ECD-Delivery-Id`, verify `X-ECD-Signature` before trusting the body, and treat the payload as a notification: the job status endpoint remains the source of truth.",
        "operationId": "crawlCompleted",
        "parameters": [
          {
            "description": "The event name, identical to the body's `event`.",
            "example": "crawl.completed",
            "in": "header",
            "name": "X-ECD-Event",
            "required": true,
            "schema": {
              "enum": [
                "crawl.completed"
              ],
              "type": "string"
            }
          },
          {
            "description": "A UUID identifying this event. Identical on every retry of the same event; use it to deduplicate.",
            "example": "0f6b1d2e-7c3a-4e9f-8b21-5d4c3a2b1e0f",
            "in": "header",
            "name": "X-ECD-Delivery-Id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "description": "`t=<unix seconds>,v1=<hex>`: `v1` is the hexadecimal HMAC-SHA256, keyed with your account's webhook signing secret, of the string `<t>.<body>`, where `<body>` is the raw request body exactly as sent. `t` and `v1` are recomputed on every delivery attempt; reject a timestamp more than a few minutes from now to defeat replays. Present only when a signing secret is configured for your account.",
            "example": "t=1787321022,v1=587f20b7dab0273ce3e1dd6318ce942e1bb146e1eb5deb479ac1eb420fc5cd4d",
            "in": "header",
            "name": "X-ECD-Signature",
            "required": false,
            "schema": {
              "pattern": "^t=[0-9]+,v1=[0-9a-f]{64}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "completed_with_errors": {
                  "value": {
                    "crawl_job_id": "2d6f8c0e-5b1a-4f3c-9e7d-0a1b2c3d4e5f",
                    "errors": [
                      {
                        "error": "The page could not be retrieved or contained no readable text.",
                        "url": "https://courts.example.gov/forms/fee-waiver"
                      }
                    ],
                    "event": "crawl.completed",
                    "namespace": "general",
                    "pages_crawled": 148,
                    "pages_indexed": 146,
                    "status": "completed_with_errors"
                  }
                },
                "failed": {
                  "summary": "The crawl itself failed",
                  "value": {
                    "crawl_job_id": "2d6f8c0e-5b1a-4f3c-9e7d-0a1b2c3d4e5f",
                    "errors": [
                      {
                        "error": "Crawl timed out.",
                        "url": "crawl"
                      }
                    ],
                    "event": "crawl.completed",
                    "namespace": "general",
                    "pages_crawled": 12,
                    "pages_indexed": 0,
                    "status": "failed"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/CrawlCompletedEvent"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any `2xx` acknowledges the delivery. Anything else, a timeout, or a connection failure triggers a retry."
          }
        },
        "security": [],
        "summary": "Crawl job completed",
        "tags": [
          "Crawling"
        ]
      }
    },
    "ingest.completed": {
      "post": {
        "description": "Sent when an ingest job started by `POST /v1/ingest/files` reaches `completed`, `completed_with_errors`, or `failed`. The body mirrors `GET /v1/ingest/jobs/{jobId}` plus `event` and `namespace`, so a handler usually needs no follow-up call. Delivery: a `POST` with `Content-Type: application/json` to the webhook URL configured for your account, sent once per job when it reaches a terminal status. Respond with any `2xx` within 10 seconds; redirects are not followed. On any other outcome the delivery is retried twice more, 30 seconds and then 5 minutes later (3 attempts in total), with the same `X-ECD-Delivery-Id` and a fresh signature each time; after the third failure the event is dropped. Deduplicate on `X-ECD-Delivery-Id`, verify `X-ECD-Signature` before trusting the body, and treat the payload as a notification: the job status endpoint remains the source of truth.",
        "operationId": "ingestCompleted",
        "parameters": [
          {
            "description": "The event name, identical to the body's `event`.",
            "example": "ingest.completed",
            "in": "header",
            "name": "X-ECD-Event",
            "required": true,
            "schema": {
              "enum": [
                "ingest.completed"
              ],
              "type": "string"
            }
          },
          {
            "description": "A UUID identifying this event. Identical on every retry of the same event; use it to deduplicate.",
            "example": "0f6b1d2e-7c3a-4e9f-8b21-5d4c3a2b1e0f",
            "in": "header",
            "name": "X-ECD-Delivery-Id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "description": "`t=<unix seconds>,v1=<hex>`: `v1` is the hexadecimal HMAC-SHA256, keyed with your account's webhook signing secret, of the string `<t>.<body>`, where `<body>` is the raw request body exactly as sent. `t` and `v1` are recomputed on every delivery attempt; reject a timestamp more than a few minutes from now to defeat replays. Present only when a signing secret is configured for your account.",
            "example": "t=1787321022,v1=d21e155da8e0a868e3b1940a7181ef1e8e8f95c4d5e85d0005335a416e3979eb",
            "in": "header",
            "name": "X-ECD-Signature",
            "required": false,
            "schema": {
              "pattern": "^t=[0-9]+,v1=[0-9a-f]{64}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "completed": {
                  "value": {
                    "completed_documents": 2,
                    "errors": [],
                    "event": "ingest.completed",
                    "failed_documents": 0,
                    "job_id": "bfcde661-9eec-4142-a790-ca9a62c8e0f8",
                    "namespace": "general",
                    "status": "completed",
                    "total_documents": 2
                  }
                },
                "completed_with_errors": {
                  "value": {
                    "completed_documents": 2,
                    "errors": [
                      {
                        "document_id": "4b1f0c8e-2a77-4c0f-9d3e-6f2a1b9c7d10",
                        "error": "No readable text was found in this document. Scanned or image-only files must be run through OCR before upload."
                      }
                    ],
                    "event": "ingest.completed",
                    "failed_documents": 1,
                    "job_id": "bfcde661-9eec-4142-a790-ca9a62c8e0f8",
                    "namespace": "general",
                    "status": "completed_with_errors",
                    "total_documents": 3
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/IngestCompletedEvent"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any `2xx` acknowledges the delivery. Anything else, a timeout, or a connection failure triggers a retry."
          }
        },
        "security": [],
        "summary": "Ingest job completed",
        "tags": [
          "Ingestion"
        ]
      }
    }
  }
}
