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

# Policies

> Check requests and responses against your own rules, and block what you don't allow.

A policy is a yes/no question about content, such as *"Does the response reveal a reusable password, API key, access token, or private key?"* For each request it applies to, a classifier model (the judge) answers the question, and Antara records a **decision**. In enforcing mode, a "yes" blocks the request or replaces the response.

## How a policy is set up

| Setting | Values | Meaning |
| - | - | - |
| `rule` | 1–20,000 characters, ending with `?` (or `？` / `؟`) | The question. "Yes" means the content breaks the policy |
| `direction` | `request`, `response`, `both` | What is checked: the conversation you send (its first and last 48 KiB), the model's output (its last 96 KiB, tool calls included), or both. Images are checked as `[image]` |
| `mode` | `monitoring` (default), `enforcing` | Monitoring only records decisions. Enforcing also blocks |
| `scope` | `organization`, `projects`, `unattached` | Every request in the organization, requests in the listed `project_ids`, or only requests that name it in `policy_ids` |
| `match_threshold_basis_points` | 1–9,999, or `null` for 7,000 | How sure the judge must be before it counts as a match: 7,000 means 70 %. Higher values need a stronger signal |

Manage policies in the console's **Policies** page or with the [Policy API](https://docs.antarah.dev/api-reference/introduction.md) and an Admin API key with `policies:write`. A policy starts as a **draft**; publishing it creates an immutable version and puts it to work. You can pause and resume it, and every edit is a new draft until you publish again. An organization can have up to 50 active (monitoring or enforcing) policies.

```bash theme={"dark"}
curl https://api.antarah.dev/v1/organization/policies \
  -H "Authorization: Bearer $ANTARA_ADMIN_KEY" \
  -H "Idempotency-Key: create-protect-credentials-1" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Protect credentials",
    "rule": "Does the response reveal a reusable password, API key, access token, or private key, excluding redacted values, placeholders, and clearly fictional examples?",
    "direction": "response",
    "scope": "organization",
    "mode": "monitoring"
  }'
```

Then publish it with `POST /v1/organization/policies/{policy_id}/publish` and `{"revision": 1}`. Changes take effect within seconds.

## Adding policies to one request

Policies with `scope: unattached` run only when a request names them. Add `policy_ids` to Chat Completions, Responses, or Messages (up to 20):

```python theme={"dark"}
client.chat.completions.create(
    model="a1",
    messages=[{"role": "user", "content": "..."}],
    extra_body={"policy_ids": ["pol_0193a6c2e7b47c3d9f1e2a4b5c6d7e8f"]},
)
```

An ID that isn't a published, unpaused policy of your organization gets `400 unknown_policy`.

## What a blocked request looks like

When an enforcing policy matches, you still get a `200` in the endpoint's normal shape, with this text:

* Request direction: `The "<policy name>" policy doesn't allow this request.`
* Response direction: `The "<policy name>" policy doesn't allow this response.`

The judge's reasoning is never included. Detect a refusal with the in-band marker, which is always present:

| Endpoint | Marker |
| - | - |
| Chat Completions | `finish_reason: "content_filter"` and a non-null `message.refusal` (the text is in `content` too) |
| Responses | `response.metadata.antara_policy_action = "blocked"` |
| Messages | `stop_reason: "refusal"` |

The header `x-antara-policy-action: blocked` is added when the decision comes before the response headers: every request-direction refusal, and response-direction refusals on non-streaming requests. A streamed response-direction refusal has already sent its headers, so read the in-band marker.

## Timing and streaming

| Policies | When the judge runs | Effect on latency |
| - | - | - |
| Enforcing, request | Before the model is called | Adds the judge's time to the request |
| Monitoring, request | Alongside the model | None |
| Enforcing, response | After the model finishes; the output is held until then | Streams receive [keepalives](https://docs.antarah.dev/guides/streaming.md#keepalives) while held, then the whole output (or the refusal) at once |
| Monitoring, response | After the response is delivered | None |

Each evaluation has 8 seconds.

## Billing

| Case | Billed |
| - | - |
| Blocked by a request policy | No: the model was never called |
| Blocked by a response policy | Yes: the model did generate the output |
| Judge calls | Never billed to you |

## When the judge can't answer

If an enforcing policy can't be checked (the judge times out or fails), Antara fails closed and doesn't bill the request. A request check, or a response check on a non-streaming request, answers `503 policy_evaluation_unavailable`. A response check on a stream ends the stream with a `policy_evaluation_unavailable` error event, because the `200` has already been sent. Retrying is safe. Monitoring policies never block; their decision is recorded with `check_status` `failed` or `timed_out`, and `result: null`.

## Decisions

Every evaluation is recorded as a decision: policy and version, direction, mode, result, action (`allowed`, `flagged`, or `blocked`), check status, and latency. Decisions never contain your prompts, outputs, or the judge's reasoning. See them in the console's **Decisions** page, or [export them](https://docs.antarah.dev/guides/decision-export.md) to your own systems. They're kept for 90 days.

## Starter policies

The console offers ready-made policies to start from:

| Name | Direction | Rule |
| - | - | - |
| Agent tool safety | response | Does the response propose a tool call that would delete, overwrite, or change permissions on files or system settings when the user did not explicitly ask for that change? |
| Block harassment, hate, and threats | request | Does the request ask for content that harasses a specific person or group, attacks people for a protected characteristic, or threatens physical harm? |
| Prevent password, API key, and token leaks | response | Does the response reveal a reusable password, API key, access token, or private key, excluding redacted values, placeholders, and clearly fictional examples? |

> [!NOTE]
>
> A judge is a classifier, not a guarantee. Write rules as specific questions, test them in monitoring mode first, and tune `match_threshold_basis_points` before you enforce.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.