> ## Documentation Index
> Fetch the complete documentation index at: https://docs.knoxcall.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Model & Output Policy

> Constrain which models an agent may call (allowlist, denylist, rewrite map, default model) and validate the shape of what comes back against a JSON Schema — block, retry, or warn.

# Model & Output Policy

Two agent-level controls that bracket the call: **model policy** decides what may
go upstream, **output validation** decides whether what came back is usable.

## Model policy

Four fields on the agent, all optional:

| Field             | Type      | Effect                                                |
| ----------------- | --------- | ----------------------------------------------------- |
| `default_model`   | string    | Substituted when the request body carries no `model`. |
| `model_rewrite`   | map       | Exact source-model → target-model substitution.       |
| `model_denylist`  | string\[] | Patterns that are refused.                            |
| `model_allowlist` | string\[] | When non-empty, the model must match one.             |

### Evaluation order

The order matters, because a rewrite changes what the lists see:

1. **No `model` in the body?** If `default_model` is set it is written into the
   body and the request proceeds. If it isn't, nothing is enforced — the request
   passes through and the upstream decides (most reject a missing model).
2. **`model_rewrite`** — an *exact* key match on the requested name substitutes the
   target. The rewritten name is what the lists then match, so you cannot smuggle
   a denied model in by rewriting to it.
3. **`model_denylist`** — a match refuses the request. **Denylist beats allowlist**;
   a model on both lists is denied.
4. **`model_allowlist`** — when the list is non-empty, a model that matches nothing
   in it is refused. An empty allowlist means "no allowlist", not "allow nothing".

### Patterns

Allowlist and denylist entries are globs: `*` matches any run of characters,
including dashes, anywhere in the name — `claude-*` matches every Claude model,
`*-opus-*` matches by family. `?` is a **literal question mark**, not a wildcard.
`model_rewrite` is not a pattern language: its keys are exact model names.

Patterns are compiled with the platform's linear-time regex engine, so a pathological
pattern cannot stall a worker.

<Note>
  **Both lists fail closed, in opposite directions.** A pattern the engine refuses to
  compile counts as a **match** on the denylist (so an unreadable deny rule blocks
  rather than silently stopping denying) and as a **non-match** on the allowlist (so
  an unreadable allow rule grants nothing). Either way the request is refused, never
  quietly let through. Check your patterns on save rather than discovering this in
  production.
</Note>

### Responses

A refused model gets a `403` naming the reason and the patterns it could have
matched:

```json theme={"dark"}
{
  "error": "model_denylisted",
  "error_description": "Requested model is not permitted by this agent's policy",
  "requested_model": "gpt-4o",
  "allowed": ["claude-sonnet-*", "claude-haiku-*"]
}
```

`error` is `model_denylisted` or `model_not_allowed`. `allowed` is `null` when the
refusal came from the denylist rather than the allowlist. Every data-plane refusal
uses this `{ error, error_description }` shape — it is **not** the `{data, meta}`
envelope the `/v1` management API returns, because the data plane speaks your
provider SDK's protocol, not KnoxCall's. A rewrite or a default
substitution is silent to the caller — the request succeeds — but is recorded on
the usage row, so the model you are billed for is the model that actually ran.

### Day-zero models

Model policy is opt-in. With no allowlist an agent will proxy any model its
upstream accepts, which is what makes a new model usable the day it ships — see
[Providers & day-zero models](/ai-gateway/day-zero-models).

## Output validation

Set an agent's `output_schema` to a JSON Schema and the gateway validates the
model's answer against it before the response leaves the building.

### What is validated

The gateway extracts the assistant's text content from the provider response,
parses it as JSON and checks it. The supported schema subset is deliberately
small — `type`, `required`, `properties`, `items`, `enum` — which covers "return
this exact object shape" without a full JSON-Schema engine.

Two cases pass without complaint by design:

* a response with no extractable text content (nothing to validate);
* content that is not JSON, **unless** the schema's top-level `type` is `object`
  or `array` — then non-JSON is a violation.

### Actions

`output_validation_action` decides what happens on a violation. Default is `warn`.

| Action  | Behaviour                                                                                                                                                                                                                                                                                                                    |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `warn`  | The response is returned to the caller unchanged, with `X-Knox-AI-Output-Warning` carrying the first errors (truncated to 200 characters).                                                                                                                                                                                   |
| `block` | The caller gets `422 output_schema_violation` with the first few errors in `error_description`. The upstream call already happened and is still billed.                                                                                                                                                                      |
| `retry` | The gateway re-calls the upstream **once**, appending the model's own answer plus a corrective instruction naming the schema errors. If the retry succeeds it becomes the response and `X-Knox-AI-Retry: output_schema` is set. If it fails or violates again, the original response is returned **with the `warn` header**. |

<Note>
  A `warn` violation is reported to the caller in a response header only. It does not
  write an audit row or a queryable event, so "how often does this agent break its
  schema" has no answer on the server side today — read the header in your client if
  you need to count them.
</Note>

A `retry` costs a second upstream call, charged like any other. It is worth it for
a flaky structured-output model and not worth it for a model that never gets the
shape right — fix the prompt instead.

<Warning>
  **Output validation runs on the buffered path only.** A streaming request
  (`stream: true`) is forwarded token by token and there is no complete document to
  validate until the client already has it, so `output_schema` is not applied. If
  the response shape is load-bearing for your application, turn streaming off for
  that agent (`streaming_enabled: false`) rather than assuming the schema is being
  enforced.
</Warning>

A retried response goes through exactly the same downstream stages as a first
response — [PII redaction and detokenization](/ai-gateway/pii-redaction), the
canary scan, the cache-store gate — so a retry cannot be a way around a control
that applied to the first answer.
