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

# Request guardrails

> Catch secrets, personal data and prompt-injection attempts in API requests before they reach a model — in memory, at the gateway, with nothing stored.

Request guardrails check every text a model would see in a request —
messages, system prompt, tool arguments and tool results — against your
organization's policy before the request leaves the gateway. Each category
can count matches, rewrite them out of the request, or refuse the request
outright. The check runs in memory on the request path, adds about a
millisecond, and stores nothing: not the request, not the matches.

> [!NOTE]
>
> Guardrails are **off by default**. Nothing changes for an organization that
> has not configured a policy — no checks, no added latency. An organization
> admin turns them on from the portal's **Privacy & Data Controls** page, and
> a policy change applies to new requests within about a minute.

## Categories

| Category | What it detects |
| - | - |
| **Secrets** | AWS access keys (`AKIA…`, `ASIA…`) and the secret key beside them, Google Cloud service-account keys and `AIza…` API keys, GitHub tokens (`ghp_`, `gho_`, `ghu_`, `ghs_`, `ghr_`, `github_pat_`), Slack tokens (`xox[abprs]-…`) and webhooks, Stripe keys (`sk_live_`, `rk_live_`, `sk_test_`), OpenAI keys (`sk-…`, `sk-proj-…`), Anthropic keys (`sk-ant-…`), PEM private-key blocks, and signed JWTs (the header is decoded to confirm it is one; it is never logged). |
| **Personal data** | Email addresses, phone numbers (E.164 and common national formats), payment card numbers that pass the Luhn check (13–19 digits, with or without separators), and national IDs by locale — US Social Security numbers, UK National Insurance numbers, Canadian Social Insurance numbers (Luhn-checked). |
| **Prompt injection** | Attempts to override the model's instructions: "ignore previous instructions", "disregard your system prompt", "you are now…", "reveal your (system) prompt", jailbreak openers, data-exfiltration phrasing and override markers. Text is normalized first (fullwidth and math-alphabet characters, zero-width characters, combining marks, Cyrillic and Greek look-alike letters, spacing) so disguised variants match. Checked in user turns and tool results — never in your own system prompt or instructions. |

Every pattern is anchored and tested against a false-positive corpus (git
SHAs, UUIDs, base64 blobs, long hex strings, "sk-" in prose, order numbers
that look like phone numbers, 16-digit numbers that fail Luhn, timestamps),
so ordinary code and data pass untouched.

## Modes

Each category has its own mode:

| Mode | Effect |
| - | - |
| **off** | Not checked. |
| **audit** | Counts matches and records an event in the portal's Activity feed. The request is sent to the model unchanged. |
| **redact** | Replaces each match with `[REDACTED:secrets]` or `[REDACTED:pii]` before the model sees it, then sends the request. Not available for prompt injection — an attack phrase is not something to paraphrase. |
| **block** | Refuses the request with a `400` that names the category. Nothing is sent to a model, nothing is reserved from your wallet, nothing is billed. |

When several categories match one request, `block` wins: the request is
refused even if the other matching categories are set to `redact` or
`audit`. A served request has every `redact` category rewritten and every
`audit` category counted. Streaming is unaffected: a request is checked
once, before the stream opens, and a served stream is never interrupted.

## How a block looks

```json theme={"dark"}
{
  "success": false,
  "error": {
    "code": "GUARDRAIL_BLOCKED",
    "type": "guardrail_blocked",
    "category": "secrets",
    "mode": "block",
    "message": "Your organization's guardrail policy blocked this request. Remove the flagged content and try again, or contact your administrator.",
    "request_id": "req_…",
    "source": "client",
    "details": { "reason": "guardrail_blocked", "category": "secrets", "mode": "block" }
  }
}
```

`category` is one of `secrets`, `pii` or `injection`. The response never
carries the matched text — your integration can branch on `error.type` or
`error.code` and tell the user which kind of content to remove.

## What is recorded

Counts only, ever:

* **Usage record.** A served request that matched gets a flag on its usage
  row: `metadata.guardrails = { "<category>": { "mode": "audit" | "redact", "count": n } }`.
  Blocked requests write no usage row (nothing was served or billed); the
  refusal is recorded in the gateway's error log with the same per-category
  counts.
* **Activity feed.** Each hit raises a `client.guardrail.hit` event in the
  portal's Activity feed with the category, mode, count and request id,
  throttled to one event per category per organization per minute.
* **Analytics.** The organization's Analytics page shows hits per category
  per day, and the number of blocked requests.

The matched text, the request body and the redacted values are never
written anywhere — the evaluation is in memory and its result is a count.

## Allowlist

The prompt-injection category accepts a list of phrases that must never
count as an attack — wording your own product legitimately sends, such as
"ignore the previous ticket and start over". Matching is case-insensitive
and ignores extra spaces; any detector match that overlaps an allowlisted
phrase is dropped. Up to 50 phrases of up to 200 characters each.

## Coverage

| Endpoint | Checked text |
| - | - |
| `POST /v1/chat/completions` | `messages[].content` (strings and `text` parts), `tool_calls[].function.arguments`, `function_call.arguments`, `tool` / `function` role content. |
| `POST /v1/messages` | `system` (string or text blocks), `text` blocks, string leaves of `tool_use.input`, `tool_result` content. |
| `POST /v1/responses` | `instructions`, string `input`, message items (`text`, `input_text`, `output_text`), `function_call.arguments`, `function_call_output` / `custom_tool_call_output.output`, `custom_tool_call.input`. |

Binary and base64 parts (images, audio, files), tool definitions and
metadata fields are not scanned. The Bedrock Converse surface, embeddings,
media generation and realtime voice are not covered yet.

## Latency and availability

* **Budget.** All patterns are precompiled and each text is scanned once;
  the evaluator has no network calls. A 10,000-token (\~40 KB) request
  evaluates in about 1 ms, under a 2 ms p95 budget enforced by the test
  suite.
* **Policy reads.** The gateway reads your policy from the same 30-second
  per-organization settings cache it already uses for fallback chains — no
  extra database read per request.
* **If the policy cannot be read.** The last known policy keeps applying for
  up to five minutes. Past that, an organization whose last known policy
  enforced anything (`redact` or `block` in any category) gets `503` with
  `Retry-After: 30` and `details.reason = "guardrail_policy_unavailable"`
  rather than being served unchecked. Organizations that never configured
  guardrails, or only audit, are never refused on this path.

## Set it up

1. In the portal, open your organization's **Privacy & Data Controls** page
   and expand **Request guardrails**.
2. Pick a mode per category. Start with **audit** to see what would match;
   move to **redact** or **block** once the counts look right.
3. Use **Test a sample** to paste a prompt or tool result and see which
   categories match and what the current modes would do — the sample is
   evaluated server-side and discarded; only categories and counts come
   back.
4. Save. New requests follow the policy within about a minute.


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