# Text-to-image rendering `llm_connect.images` renders one square image from a prompt. It does not choose a model or a budget. The caller passes both. Credentials stay in the provider client and never appear on a receipt. ## Request - Prompt: non-empty, at most 16 000 characters. - Size: `WIDTHxHEIGHT`, square, edge from 1024 through 4096. Default `1024x1024`. - Format: `png` or `jpeg`. A file suffix `.png`, `.jpg`, or `.jpeg` must match the format. - An existing output file is left untouched unless `force` is set. ## Receipt Each successful render returns image bytes and a receipt: - `provider`, `model` - `prompt_digest` — SHA-256 of the UTF-8 prompt. The prompt text is not stored. - `cost_usd`, `cost_eur`, `cost_source` (`provider`, `quote`, or `unpriced`) - `width`, `height`, `media_type`, `output_format`, `byte_length` - `output_path` when a file was written - `within_cap` — false when the provider cost exceeded a cap that was checked before the call. The file is still written, because the provider already charged it, and the batch stops. ## Spend gate `ImageSpendPolicy` takes `max_cost_usd`, `max_batch_cost_usd`, and `quote_usd`. A cap requires a quote. The quote is reserved before the provider call. The provider-reported `usage.cost` replaces it on the receipt when present. A failed HTTP call releases the reservation. Parallel `/images` requests share one gate for the life of the server process. ## Provider OpenRouter only, via `POST {api_base}/images` (default `https://openrouter.ai/api/v1/images`). The request sends `n=1`, `aspect_ratio=1:1`, an explicit pixel `size`, and `output_format`. The key comes from the explicit argument, `OPENROUTER_API_KEY`, or `apikey-openrouter.txt`. Only `b64_json` is accepted. A URL in the response is not fetched. There is no default image model on `image render`. `image render-hall` renders one named entry, the seat about to be published, using the 2026-10-08 decision tree in [`docs/image-model-selection.md`](../../docs/image-model-selection.md). It does not walk the corpus. A hall portrait that does not ask for vector output, editing, readable text, or a brand system uses `google/gemini-nano-banana-2.1` with quote 0.04 USD and a per-call cap of 0.08 USD. `image plan` without `--entry` only inventories missing portraits. That inventory is not a render queue. Existing seats are historic when a later publisher drips them, and carry a historic remark on the post. Seats written after that practice starts wait a short cooldown, then publish; the portrait is rendered at that moment if the file is still absent. The cooldown scheduler is not part of this library. ## CLI ```bash llm-connect image render \ --prompt "..." \ --model "" \ --output visuals/name.jpg \ --max-cost-usd 0.15 \ --max-batch-cost-usd 15 \ --quote-usd 0.08 llm-connect image render --batch renders.jsonl --model "" \ --max-cost-usd 0.15 --max-batch-cost-usd 15 --quote-usd 0.08 ``` A batch file is JSONL. Each object has `prompt` and `output_path`. The receipt JSON goes to stdout. Image bytes stay in the output file. Exit `2` means a cap stopped the batch after any already-paid files were written. ## HTTP `POST /images` exists when the server is started with `--image-model`, `--image-max-cost-usd`, and `--image-quote-usd`. Without those flags the path is not found. The body is `prompt`, optional `size`, and optional `output_format`. The response is `receipt` plus `image_base64`. Caps are the server's, not the caller's. `/execute` is unchanged. Usage rows are appended only when `LLM_CONNECT_USAGE_LEDGER` is set (server) or unless `--no-ledger` is passed (CLI). The row's tags carry `kind=image` and the prompt digest.