# OpenAI-Compatible Chat Integration This is the outbound wire contract for Scriptorium's OpenAI-compatible chat-completions client. ## Endpoint And Method Scriptorium uses the request endpoint override when present; otherwise it uses the configured client base URL. It removes a trailing slash and sends `POST /chat/completions`. For example, `http://localhost:8000/v1` becomes `http://localhost:8000/v1/chat/completions`. ## Request Payload The payload always contains `model` and rendered `messages`. It additionally contains these fields when applicable: | Field | Inclusion | | --- | --- | | `session_id` | Non-empty rendered prompt session ID. | | `temperature` | Non-zero effective value or an explicit zero override. | | `max_tokens` | Non-zero effective value or an explicit zero override. | | `top_p` | Non-zero effective value or an explicit zero override. | | `service_tier` | Any non-empty configured value. | | `reasoning_effort` | Any non-empty configured value. | | `response_format` | Structured output is requested. | | provider-specific fields | Flattened from `extra_params`. | `service_tier` and `reasoning_effort` are forwarded without a provider value catalog; the selected backend decides which values it supports. `extra_params` are top-level JSON fields, not a nested object. Keys cannot be empty or collide with `model`, `session_id`, `messages`, `temperature`, `max_tokens`, `top_p`, `service_tier`, `reasoning_effort`, or `response_format`. Values must be JSON-serializable. A rendered `session_id` is sent as a top-level JSON field, not as a header. Empty values are omitted. The maximum length is 256 Unicode code points. Messages without cache control use string `content`. A message with cache control uses one text block: ```json { "role": "system", "content": [{ "type": "text", "text": "rendered text", "cache_control": {"type": "ephemeral", "ttl": "1h"} }] } ``` When the prompt omits cache-control `ttl`, the payload omits `ttl`. Structured JSON Schema output is sent as: ```json { "response_format": { "type": "json_schema", "json_schema": { "name": "schema name", "strict": true, "schema": {"type": "object"} } } } ``` ## Authentication And Timeout When a direct request API key is present, Scriptorium sends `Authorization: Bearer ` and does not read `api_key_env`. Otherwise, it resolves the configured non-empty `api_key_env` at request time and sends the same header. If neither mechanism supplies a key, it sends no `Authorization` header. The configured client timeout applies by default. A positive effective `timeout_seconds` replaces it. An explicit request override of zero disables the HTTP-client timeout; negative values are rejected before a request is sent. ## Response Subset And Failures A successful provider response must supply non-empty `choices[0].message.content`. Scriptorium reads these optional or required usage fields when present: - `usage.prompt_tokens` - `usage.completion_tokens` - `usage.total_tokens` - `usage.prompt_tokens_details.cached_tokens` - `usage.cache_write_tokens` Missing cache usage is reported as zero. Invalid JSON, an empty choices array, or empty first-choice content is a malformed provider response. Network and request-construction failures, non-2xx responses, and malformed responses fail the outbound call. Provider response bodies are not exposed by this client. The client does not implement built-in retries, tool calls, top-level `cache_control`, or multi-request payload modes. ## Related References Prompt schema preparation and runner orchestration are described in [runner internals](../internal/runner.md). Prompt and profile configuration is defined by the [configuration reference](../config.md).