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

# Zscaler AI Guard guardrail for prompts and responses

> Inspect prompts, model responses, and MCP tool calls with Zscaler AI Guard on TrueFoundry AI Gateway, then block the request or continue with masked text.

Use [Zscaler AI Guard](https://www.zscaler.com/products-and-solutions/ai-guardrails) with TrueFoundry AI Gateway to inspect prompts before they reach the model, model responses before they reach the caller, and MCP tool arguments and results. The gateway sends the text to Zscaler's detection API and applies the action that comes back.

## How the integration works

On each hook you attach the guardrail to, the gateway joins the texts for that hook into one string and sends it to Zscaler. The API key is sent as `Authorization: Bearer`.

| Hook | Direction | Text sent |
| - | - | - |
| Input | `IN` | The prompt, including the system prompt |
| Output | `OUT` | The response text only. The prompt is not included |
| MCP tool call | `IN` | The tool arguments |
| MCP tool result | `OUT` | The tool result alone |

The host is **Base URL** when that field is set. Otherwise it is `https://api.us1.zseclipse.net`.

* When **Policy ID** is set, the gateway calls `POST /v1/detection/execute-policy`.
* When **Policy ID** is empty, the gateway calls `POST /v1/detection/resolve-and-execute-policy`. That call sends `user` from the authenticated session: the user's slug.

Zscaler returns an `action`, and may return `maskedContent`:

| Zscaler result | Mutate | Validate |
| - | - | - |
| `ALLOW`, `DETECT`, or any other action when `maskedContent` is missing or unchanged | Pass the payload through unchanged | Same |
| `BLOCK` | Violation | Violation |
| `maskedContent` differs from the scanned text | Rewrite that hook's messages, or the MCP arguments or result, from `maskedContent`, and continue | Violation. The payload is not changed. |

In Mutate, a mask that cannot be applied, or a rebuilt payload that is unchanged, fails closed. That is a violation, not a vendor error, so Enforce But Ignore On Error still rejects the request.

A violation is handled by the enforcing strategy. When the request is rejected, the caller receives a `400` with type `guardrail_checks_failed`. The message names the detectors that triggered and the entity types or code languages they found, such as `pii: CREDIT_CARD` or `code: Python`. It does not include the scanned text. `maskedContent`, offsets, raw detector `details`, and `errorMsg` are not returned.

<Note>
  Output inspection is skipped for streamed responses. Tool calls in a model response are not inspected.
</Note>

## Prerequisites

* An AI Guard API key. The gateway sends it as `Authorization: Bearer` to the detection API.
* The base URL of your AI Guard API if it is not `https://api.us1.zseclipse.net`.
* Network access from the gateway to that host.
* A detection policy in Zscaler AI Guard. Leave **Policy ID** empty to let Zscaler resolve the policy, or set it to run one policy.

## Add the Zscaler AI Guard guardrail

<Steps>
  <Step title="Select Zscaler AI Guard">
    In TrueFoundry, go to **AI Gateway → Guardrails**, create or open a guardrail group, and select **Zscaler AI Guard** under **External Providers**.
  </Step>

  <Step title="Configure the guardrail">
    * **Name**: A name such as `zscaler-ai-guard`.
    * **API Key**: The AI Guard API key. TrueFoundry stores it as a secret and sends it as `Authorization: Bearer`.
    * **Base URL**: Optional. The AI Guard API base URL, for example `https://api.us1.zseclipse.net`. When empty, the gateway uses `https://api.us1.zseclipse.net`. Trailing slashes are ignored.
    * **Policy ID**: Optional. An integer greater than 0. When set, the gateway calls `POST /v1/detection/execute-policy` for that policy. When empty, it calls `POST /v1/detection/resolve-and-execute-policy`.
    * **Operation**:
      * **Validate** (default) never changes the payload. `BLOCK`, or a `maskedContent` value that differs from the scanned text, is a violation. Validate guardrails run in parallel with the model request.
      * **Mutate** rewrites that hook's messages, or the MCP arguments or result, when `maskedContent` differs from the scanned text, and the request continues. On a hook with several mutate guardrails, lower **Priority** values run first. If the mask cannot be applied, or the rebuilt payload is unchanged, the check fails closed.
    * **Enforcing Strategy**: See [Enforcing strategy](#enforcing-strategy).
    * **Priority**: Order among other mutate guardrails. Lower values run first. Applies to Mutate only.
  </Step>

  <Step title="Attach the guardrail">
    Save the guardrail group, then attach it to the LLM input hook, the LLM output hook, or MCP tool hooks through a [guardrail policy](/docs/ai-gateway/guardrails-configuration).
  </Step>
</Steps>

## Enforcing strategy

| Strategy | Violation | Vendor unreachable or error |
| - | - | - |
| **Enforce** | Request is rejected | Request is rejected |
| **Enforce But Ignore On Error** | Request is rejected | Request continues |
| **Audit** (Validate only) | Recorded, request continues | Request continues |

Vendor errors include an unreachable host, a non-2xx response, and an error reported in the response body, such as a policy ID that Zscaler does not find.

A mask that cannot be applied, or a rebuilt payload that is unchanged, is not a vendor error. It is a violation, so the request is rejected even for Enforce But Ignore On Error.

<Warning>
  Validate guardrails run in parallel with the model request. When a Validate guardrail rejects a request, the prompt has already been sent to the model. To make the guardrail finish before the model is called, turn off **Run Guardrails in Parallel with Model Request** in the gateway guardrail settings, or use Mutate.
</Warning>

## Verify the integration

Select the guardrail on a request with the `X-TFY-GUARDRAILS` header. The selector format is `<group>/<guardrail-name>`. MCP tool hooks are selected by the guardrail policy, not by this header.

```bash theme={"dark"}
curl -X POST "https://<your-gateway-url>/chat/completions" \
  -H "Authorization: Bearer ${TFY_API_KEY}" \
  -H "Content-Type: application/json" \
  -H 'X-TFY-GUARDRAILS: {"llm_input_guardrails":["my-group/zscaler-ai-guard"],"llm_output_guardrails":["my-group/zscaler-ai-guard"]}' \
  -d '{
    "model": "my-account/my-model",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "Summarize the quarterly plan in three bullets."}
    ]
  }'
```

What comes back depends on the policy configured in Zscaler AI Guard.

**Zscaler returns `ALLOW`, `DETECT`, or any other action with no mask change.** The response is a normal completion. The payload is unchanged.

```json theme={"dark"}
{
  "guardrail_checks": {
    "input_guardrails": [
      {
        "guardrail_integration": "my-group/zscaler-ai-guard",
        "result": "passed",
        "data": { "decision": "allow", "action": "ALLOW", "detectors": [] },
        "transformed": false
      }
    ]
  }
}
```

**`maskedContent` differs, and the guardrail is Mutate.** The hook's messages, or the MCP arguments or result, are rewritten from `maskedContent`, and the check reports `transformed: true`. `maskedContent` is not included on the check.

```json theme={"dark"}
{
  "guardrail_integration": "my-group/zscaler-ai-guard",
  "result": "passed",
  "transformed": true
}
```

**Zscaler returns `BLOCK`.** The request is rejected with `400`. The message names the detectors that triggered and their categories, and never the scanned text. The check data includes `decision`, `action`, `severity`, `policyName`, `detectors`, and `message`. Each detector includes `name`, `triggered`, `action`, `severity`, and `categories`. Raw `details`, offsets, `errorMsg`, and `maskedContent` are not returned.

```json theme={"dark"}
{
  "status": "failure",
  "message": "Input Guardrail checks failed for integrations: [my-group/zscaler-ai-guard] - Details: Zscaler AI Guard policy violation (<detector>: <category>)",
  "error": { "type": "guardrail_checks_failed", "code": "400" },
  "error_origin_level": "guardrails_input",
  "guardrail_checks": {
    "input_guardrails": [
      {
        "guardrail_integration": "my-group/zscaler-ai-guard",
        "result": "failed",
        "data": {
          "decision": "block",
          "action": "BLOCK",
          "severity": "<severity>",
          "policyName": "<policy-name>",
          "detectors": [
            {
              "name": "<detector>",
              "triggered": true,
              "action": "BLOCK",
              "severity": "<severity>",
              "categories": ["<category>"]
            }
          ],
          "message": "Zscaler AI Guard policy violation (<detector>: <category>)"
        },
        "transformed": false
      }
    ]
  }
}
```

In Validate, a `maskedContent` value that differs from the scanned text produces the same kind of failed check. The payload is not rewritten.

## Identity

On `resolve-and-execute-policy`, the gateway sends `user` from the authenticated session: the user's slug, not the personal access token name. Request headers are not forwarded. A caller cannot choose the user or the policy id. **Policy ID** is taken only from the guardrail configuration.

## Limits

* Output scans include the response text only. They do not include the prompt.
* Streamed responses are not scanned.
* Validate runs in parallel with the model request. See the warning above.
* Tool calls in a model response are not inspected.
* MCP tool hooks are selected by gateway rules, not by request headers.


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