> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.lakera.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.lakera.ai/_mcp/server.

# Configuring policies in Self-hosted AI Guardrails

Enterprise self-hosting customers can configure and dynamically update the Check Point
AI Guardrails policies and guardrails used for screening by individual apps and
integrations. This is done using a policy file hosted in an S3 bucket (or S3-compatible
storage) within your own environment.

The policy file is used to specify and update both your [projects](/docs/projects) and
[policies](/docs/policies) configurations. It also specifies the assignment mapping
between projects and policies.

> **Note**
>
> Check Point provides a starter policy file containing recommended policies developed
> by Check Point security experts. You receive this file during onboarding.

Each project must have one policy assigned. The same policy can be assigned to multiple
projects.

Policies can be checked for validity via the
[policy linter in the AI Guardrails platform](https://platform.lakera.ai/policy-linter)
or via the [`/policies/lint`](/api-reference/lakera-api/policies-linter) or
[`/policies/health`](/api-reference/lakera-api/policies-health) endpoints.

Policies can either be shared through an S3-compatible bucket or through a local
filesystem that is mounted in the running containers.

For information on how to set up and self-host AI Guardrails, please refer to the
[Self-Hosting documentation](./../selfhosting)

## Policy file setup

### Set up requirements for S3

First create a bucket in S3, or an S3 compatible alternative, in your environment. Add a
directory in the bucket to contain the policies. Policies must reside in a directory
rather then in the root of the bucket.

Create one or more JSON policy files (details on the file contents are outlined below).
The file can be called anything as long as it ends with `.json`. Upload the policy file
to the S3 bucket in the directory that was created.

Then, set up the AI Guardrails container via the environment variables:

```bash
export POLICY_BUCKET_NAME=[Name of the bucket, e.g. policies]
export POLICY_ENDPOINT_URL=[S3 URL including region, e.g. https://s3.eu-central-1.amazonaws.com]
export POLICY_PREFIX=[Path to the policy within the bucket, e.g. the directory where the policies reside]
export AWS_ACCESS_KEY_ID=$YOUR_ACCESS_KEY
export AWS_SECRET_ACCESS_KEY=$YOUR_SECRET_ACCESS_KEY
export LAKERA_GUARD_LICENSE=$YOUR_LICENSE
```

The final S3 URI is `${POLICY_ENDPOINT_URL}/${POLICY_BUCKET_NAME}/${POLICY_PREFIX}/`,
for example, `https://s3.eu-central-1.amazonaws.com/policies/prod/*.json`.

If you are using a non-AWS S3 provider, the access key environment variables
`S3_ACCESS_KEY_ID` and `S3_SECRET_ACCESS_KEY` can be used as variable names instead of
the AWS ones.

### Set up requirements for a local filesystem

A local filesystem which contains policy files can be mounted into a self-hosted
container to share policy files through local filesystem storage. This filesystem can
read-only from the container's perspective allowing for the same policy filesystem to be
shared across multiple containers.

Create one or more JSON policy files (details on the file contents are outlined below).
The file can be called anything as long as it ends with `.json`. Store the policy file
in the filesystem which is mounted to the self-hosted container.

The policy files must reside in a directory pointed to by the `LOCAL_POLICY_DIR`
environment variable.

Then, set up the AI Guardrails container via the environment variables:

```bash
export LOCAL_POLICY_DIR=[The directory that is mounted in the Guard container]
export FORCE_POLICY_MANAGER=fs
export LAKERA_GUARD_LICENSE=$YOUR_LICENSE
```

With `FORCE_POLICY_MANAGER=fs`, AI Guardrails reads policies from the mounted directory
rather than from S3. Setting it is recommended for every filesystem-based deployment.

> **Note**
>
> `LOCAL_POLICY_DIR` on its own is enough in a standard self-hosted container, because
> that container already runs with a self-hosted deployment type.
> `FORCE_POLICY_MANAGER=fs` makes the choice explicit, so the configuration keeps
> working if the container's deployment type ever changes. If both are set,
> `FORCE_POLICY_MANAGER` takes precedence.

#### Kubernetes and OpenShift

Mount the directory from a ConfigMap and point `LOCAL_POLICY_DIR` at the mount path. The
volume can be read-only, so the same ConfigMap can be shared across containers:

```yaml
volumes:
  - name: policies
    configMap:
      name: policy-config

volumeMounts:
  - name: policies
    mountPath: /policies
    readOnly: true

env:
  - name: LOCAL_POLICY_DIR
    value: /policies
  - name: FORCE_POLICY_MANAGER
    value: "fs"
```

If you deploy with the `lakera-guard` Helm chart, use the `guard.policies` value
instead. The chart creates the ConfigMap, mounts it read-only, and sets
`LOCAL_POLICY_DIR` for you:

```yaml
guard:
  policies:
    - name: "custom-policies.json"
      data: |
        {
          "schema_version": 1,
          ...
        }
```

To keep the policy in its own file rather than inline in your values, pass it at install
time with `--set-file`:

```bash
helm upgrade --install lakera-guard . --namespace lakera-guard \
  --set 'guard.policies[0].name=custom-policies.json' \
  --set-file 'guard.policies[0].data=./custom-policies.json'
```

### Docker command

Include the environment variables when you run the Docker command. For example:

```bash
docker run \
  -e POLICY_BUCKET_NAME \
  -e POLICY_PREFIX \
  -e POLICY_ENDPOINT_URL \
  -e AWS_ACCESS_KEY_ID \
  -e AWS_SECRET_ACCESS_KEY \
  -e LAKERA_GUARD_LICENSE \
  -p 8000:8000 \
  registry.gitlab.com/lakeraai/llm-guard/guard:latest
```

Depending on your AWS account type it may be necessary to also include
`-e AWS_SESSION_TOKEN`.

To mount a local policy directory instead, add `-v ./policies:/policies:ro`,
`-e LOCAL_POLICY_DIR=/policies` and `-e FORCE_POLICY_MANAGER=fs`.

### Confirming the policy file was loaded

On startup the container logs which policy source it selected. For a filesystem-based
setup you should see:

```
using policy manager override: local
trying to load from filesystem
getting policies from local
```

If instead the container reads from S3, or reports the AI Guardrails Default Policy, the
policy directory was not picked up. Check that `LOCAL_POLICY_DIR` points at the mount
path and that the files inside it end in `.json`.

To confirm that a specific project resolves to the policy you expect, send a screening
request with `"breakdown": true` and check the returned `policy_id` and detector list:

```bash
curl -X POST "http://localhost:8000/v2/guard" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role": "user", "content": "test"}],
    "project_id": "project-chatbot",
    "breakdown": true
  }'
```

The `breakdown` array lists exactly the detectors your policy mapped to that project.
The [`/v2/policies/health`](/api-reference/lakera-api/policies-health) endpoint
additionally reports whether the project uses a custom policy or the AI Guardrails
Default Policy.

### Troubleshooting

1. **The container crashes with
   `UNLEASH_API_URL and UNLEASH_INSTANCE_ID are required for SaaS`.** The container's
   `DEPLOYMENT` environment variable is set to a value AI Guardrails does not recognize.
   Unrecognized values are treated as a SaaS deployment, so the container expects
   SaaS-only infrastructure and ignores the policy directory. The logs show
   `Unknown deployment_env, falling back to prod` right before the crash. Leave
   `DEPLOYMENT` unset so the container's own default applies, or set it explicitly to
   `onprem`.
2. **Screening requests return the AI Guardrails Default Policy instead of your
   policy.** The policy file was not loaded. Check the startup logs for the lines above,
   confirm `LOCAL_POLICY_DIR` matches the mount path, and confirm the file is visible
   inside the container with `ls $LOCAL_POLICY_DIR`. Also check that the project ID in
   the request matches an entry in the `projects` section exactly.
3. **A request returns `project with policy not found`.** The `project_id` in the
   request does not exist in the policy file. AI Guardrails does not fall back to the
   default policy for an unknown project ID. Add the project to the `projects` section,
   or omit `project_id` to use the AI Guardrails Default Policy.

### Technical considerations

The container crashes if the policy file is invalid. When this happens, it logs errors
that explain the issues with the policy file.

AI Guardrails updates policies automatically when you change the policy file. The update
happens on the next screening request for that policy. Note that it may take a couple of
minutes for policy updates to take effect.

The policy file can be stored in a folder or prefix in the S3 bucket or S3 compatible
storage.

To change the location of the policy file or update the credentials, change the
environment variable and restart the container.

## Policy schema

The policy schema is made up of three sections:

1. **`projects`**: this defines the different [projects](./../projects), defines their
   ID name, and assigns them to their policy.
2. **`policies`**: this defines the different [policies](./../policies), defines their
   ID name, and maps them to their detectors.
3. **`detectors`**: this defines the different [detectors](./../defenses), defines their
   ID name, their detector type and their confidence threshold level for flagging.

Note that project, policy, and detector ID names can be whatever you want as long as
they follow the naming convention set out for each section below. Clear and descriptive
names are recommended, so you can recognize them in logs and API responses.

The policy file should always start with specifying the schema version:
`"schema_version": 1`.

### Projects section

Project IDs must start with `project-`.

Each project can only map to one policy. Multiple projects can map to the same policy.
This is specified by `policy_id`.

Each project can also set its project mode with the optional `action` field.
`"action": "enforce"` corresponds to Enforce mode and `"action": "detect"` to Detect
mode. When `action` is omitted, the project uses Enforce mode. The policy linter rejects
any other value. See [Project Mode](/docs/projects#project-mode) for how each mode
behaves.

For information on how to use projects to set, configure, track, understand, and compare
the security and threat profiles of each of your GenAI applications and components,
please refer to the [Projects documentation](./../projects).

#### `projects` example:

```json
"projects": [
    {
      "id": "project-chatbot",
      "policy_id": "policy-strict-defense",
      "action": "enforce"
    },
    {
      "id": "project-internal-RAG",
      "policy_id": "policy-just-hate-speech",
      "action": "detect"
    }
  ],
```

### Policies section

Policy IDs must start with `policy-`.

Each policy can map to multiple detectors of different types. This is specified via
lists of detector IDs:

* `detectors` - these are detectors that should be run on all screening requests using
  this policy, regardless of whether they're user input, reference materials passed to
  the LLM, or a response coming from the LLM.
* `input_detectors` - these are detectors that should be run when screening LLM inputs,
  that is, only when the `role` is set as `user` in the API request.
* `output_detectors` - these are detectors that should be run when screening LLM
  outputs, that is, only when the `role` is set as `assistant` in the API request.

Note that a policy can't be set to use multiple detectors of the same detector type for
an input or an output screening request.

For convenience, you can map a policy to use a whole defense category rather than
listing all of the individual detectors. For example you can map a policy to a detector
with type `pii`, which then runs all of AI Guardrails' personally identifiable
information (PII) detectors when screening.

Note that you can't map a policy to both a defense category detector and also one of its
underlying individual detectors. For example you cannot map to both a
`moderated_content` type detector and a `moderated_content/hate` detector in the same
policy.

#### Policy mode

Policies that have a single defense configuration for all content, regardless of whether
it's LLM input or output are called **unified policies** in AI Guardrails. These
policies define the detectors only through the `detectors` list.

Within a policy, you can also map different detector configurations to be run on LLM
inputs vs outputs. For example, you could screen inputs for prompt attacks, outputs for
unknown links, and both inputs and outputs for moderated content. This is called an
**Input & Output Policy** in AI Guardrails, and can have detectors mapped via
`detectors`,`input_detectors` and/or `output_detectors` lists.

The policy mode is specified as **unified** or **input & output** via the `mode`
property. The default value is `unified` and the `mode` property is optional for unified
policies.

To specify the policy mode as an input & output policy set the property `"mode": "IO"`.
Also specify the input and/or output detectors via `input_detectors` and
`output_detectors` respectively.

You can opt to screen only the LLM input or only the output. If either `input_detectors`
or `output_detectors` is specified then it is optional to include a unified `detectors`
list as well. For `IO` policies, empty lists are recommended if you opt to do either of
these.

In screening API requests, the content is specified as being LLM input or output via the
`user` or `assistant` role respectively. API requests with the `system` role are not
screened as they are considered trusted content. Messages with the `tool` role are
screened as untrusted content, so detectors in the unified `detectors` list also run on
the responses coming back from an agent's tools. Please refer to the
[`guard` API documentation](/api-reference/lakera-api/guard) for details.

#### `policies` example

```json
"policies": [
    {
      "id": "policy-strict-defense",
      "detectors": [
        "detector-strict-prompt-attack",
        "detector-moderation"
      ]
    },
    {
      "id": "policy-chatbot-defense",
      "mode": "IO",
      "detectors": [
        "detector-moderation"
      ],
      "input_detectors": [
        "detector-prompt-attack",
        "detector-pii-credit-card"
      ],
      "output_detectors": [
        "detector-unknown-link"
      ]
    }
  ],
```

### Detectors section

Detector IDs must start with `detector-`.

The flagging sensitivity is controlled via `threshold`, which specifies the lowest
confidence level at which the detector returns `detected = true`. The accepted values
are:

1. `l1_confident`
2. `l2_very_likely`
3. `l3_likely`
4. `l4_less_likely`

The thresholds are inclusive, for example, setting the threshold to `l2_very_likely`
means the detector triggers when its confidence level is either `l2_very_likely` or
`l1_confident`. For more details, please refer to the
[documentation on fine-tuning detectors](./../defenses).

`type` specifies the detector type. The accepted values are:

1. `prompt_attack`
2. `moderated_content` - this runs all seven Content Moderation detectors and flags
   based on the max confidence level returned by any of them
3. `moderated_content/crime`
4. `moderated_content/hate`
5. `moderated_content/profanity`
6. `moderated_content/self-harm`
7. `moderated_content/sexual`
8. `moderated_content/violence`
9. `moderated_content/weapons`
10. `moderated_content/custom` - this is for custom regular expression based detectors
11. `pii` - this runs all eight PII detectors and flags based on the max confidence level
    returned by any of them
12. `pii/name`
13. `pii/phone_number`
14. `pii/email`
15. `pii/ip_address`
16. `pii/address`
17. `pii/credit_card`
18. `pii/iban_code`
19. `pii/us_social_security_number`
20. `pii/custom` - this is for custom regular expression based detectors
21. `unknown_links` - this can be customized by specifying allowed domains
22. `override_allow` - creates an allow list that prevents content from being detected
23. `override_deny` - creates a deny list that forces content to be detected

#### Custom detectors

You can define your own custom
[regular-expression](https://www.lakera.ai/ml-glossary/regular-expression-regex) based
detectors for Content Moderation or PII. You can use these to add custom defenses,
preventing your GenAI talking about unwanted topics or screening for additional types of
sensitive data by detecting specific words, strings, or text patterns when screening.

For example, you could create a custom PII detector for internal employee IDs, or other
forms of national ID. Or you could have a custom Content Moderation detector that
triggers any time one of your competitors' names are mentioned to avoid your GenAI
application being tricked into talking about them.

Custom detectors are created by defining a detector with type `moderated_content/custom`
or `pii/custom`.

Detectors of this type are specified by a `custom_matchers` list of objects containing a
`label` and a list of `regexes`. When the screened contents contain a text string that
matches any of the regexes, the custom detector triggers.

AI Guardrails uses [Perl-Compatible Regular Expressions (PCRE)](https://www.pcre.org/)
syntax.

For example, you can replace `<deny list word>` in the below regex with a word or phrase
and AI Guardrails detects when it appears in contents, in any casing, and as a
standalone word rather than part of another word.

```json
"custom_matchers": [
    {
        "label": "Deny list",
        "regexes": ["(?i)\\b<deny list word>\\b"]
    }
]
```

> **Note**
>
> When writing regular expressions in the policy file, make sure to escape backward
> slashes, for example, use `\\b` and `\\s` etc., otherwise it's interpreted on the JSON
> string level.

It's best to test custom detectors carefully to make sure the regular expressions are
correct.

Note that you can't fine-tune custom detectors, so set their threshold level to
`l1_confident`.

For more information on regular expressions plus guides to creating your own please
refer to [this useful website](https://www.regular-expressions.info/quickstart.html), or
ask your Check Point representative for help.

#### Allowed domains for unknown links

The [Unknown Links detector](./../unknown-links) detects URLs that aren't in the top one
million of the most popular domains. It's used to prevent malicious links being returned
to users by an LLM, for example, through data poisoning where an indirect prompt
injection is hidden in text passed to the LLM that tricks the AI into sharing phishing
links.

In the policy, you can specify a custom list of safe domains. The Unknown Links detector
won't flag URLs in the domains added to the allow list. These domains might not be
included in the million most popular domains but are safe to appear in LLM interactions.
As a result, AI Guardrails should never flag them. Usually this list includes your own
domain.

To add domains to the allowed domains list, add an `allowed_domains` list when defining
a detector of type `unknown_links`. For example:

```json
{
  "id": "detector-phishing-links",
  "type": "unknown_links",
  "threshold": "l1_confident",
  "allowed_domains": ["my-trusted-domain.com", "another-safe-domain.com"]
}
```

> **Warning**
>
> Do not include prefixes, for example, `http://` or `www.`, or a subdomain, for
> example, `platform.lakera.ai`, when defining your allowed domains as these are not
> supported. Please only include the domain name and top-level domain.

#### Allow and deny list detectors

AI Guardrails supports custom allow and deny lists to temporarily override model
decisions. These special detector types help you address false positives or false
negatives that affect critical workflows while waiting for model improvements.

> **Warning**
>
> Override AI Guardrails carefully to avoid creating security loopholes. For
> misclassification patterns, allow and deny lists are best kept as a temporary measure
> while Check Point updates threat models for a permanent fix. For prompt template issues,
> please consult with Check Point to ensure allow lists are used securely.

For details, see the [Allow and Deny Lists documentation](/docs/allow-deny-lists).

To implement these overrides, use the following detector types:

1. `override_allow` - Creates an allow list that prevents content from being detected
2. `override_deny` - Creates a deny list that forces content to be detected

Both detector types require a list of strings in the `override_list` parameter and must
use the `l1_confident` threshold level.

##### Example of allow and deny list detectors

```json
{
  "id": "detector-ban-soft-drinks",
  "type": "override_deny",
  "threshold": "l1_confident",
  "override_list": [
    "coke",
    "pepsi",
    "fanta",
    "redbull"
  ]
},
{
  "id": "detector-allow-dad-jokes",
  "type": "override_allow",
  "threshold": "l1_confident",
  "override_list": [
    "That mineral water was fanta-stic!"
  ]
}
```

In this example, any content containing references to soft drinks would be detected, but
the specific (and terrible) dad joke about mineral water would be allowed, even though
it contains `fanta`, which is in the deny list.

#### `detectors` example

```json
"detectors": [
    {
      "id": "detector-moderation",
      "threshold": "l3_likely",
      "type": "moderated_content/hate"
    },
    {
      "id": "detector-cc",
      "threshold": "l2_very_likely",
      "type": "pii/credit_card"
    },
    {
      "id": "detector-competitor",
      "type": "moderated_content/custom",
      "threshold": "l1_confident",
      "custom_matchers": [
        {
          "label": "Competitors",
          "regexes": ["(?i)\\bacme corp\\b", "(?i)\\bgeneric AI startup name\\b"]
        }
      ]
    },
    {
      "id": "detector-employee-ids",
      "type": "pii/custom",
      "threshold": "l1_confident",
      "custom_matchers": [
        {
          "label": "Old ID",
          "regexes": ["(AG|AI|AR|BE|BL|BS|...|ZG|ZH)\\s*[-.•]?\\s*[0-9]{1,6}"]
        },
        {
          "label": "New ID",
          "regexes": ["[A-Z0-9]*CD[A-Z0-9]"]
        }
      ]
    },
    {
        "id": "detector-phishing-links",
        "type": "unknown_links",
        "threshold": "l1_confident",
        "allowed_domains": [
            "my-trusted-domain.com",
            "another-safe-domain.com"
        ]
    }
]
```

## Checking the policy file is valid

### Policy linter interface

To check that your policy file is valid, use the
[policy linter tool in the AI Guardrails platform](https://platform.lakera.ai/policy-linter).
This editor provides basic linting for your policy. It checks for common errors and
suggests improvements.

The tool is run locally in the browser, so any policies or JSON entered in it are not
saved anywhere. Note this also means that the contents are lost if you close the page.

### Policy linter endpoint

Alternatively, you can use the
[`/v2/policies/lint`](/api-reference/lakera-api/policies-linter) endpoint within the
container to send a group of JSON files and get them checked for correctness. It returns
whether the policy passed `true` or `false` and gives a list of any errors or warnings.

### Policy health check endpoint

You can use the [`/v2/policies/health`](/api-reference/lakera-api/policies-health)
endpoint to check the validity of the policy configuration for a given project.

If the policy is syntactically correct, the endpoint returns an `ok` status and marks
the linter as passed. If the policy linter reports any warnings or errors, the response
includes them.

You need to specify the desired project's ID explicitly in the request. If the project
ID isn't passed then the health endpoint reports the status of the AI Guardrails Default
Policy. If the project ID does not exist, or has been deleted, the health endpoint
returns an error status.

The health check also reports whether the project is using the AI Guardrails Default
Policy, or a custom policy is defined and assigned to it in the policy file.

If you have tried to configure a custom policy for the project, make sure the health
check doesn't say it's using the AI Guardrails Default Policy. Otherwise the status
report is not reflective of your policy configuration but the health of AI Guardrails'
Default Policy instead.

## Audit history

Self-hosting customers must set up, track, and manage the audit history of the policy
file. The AI Guardrails container does not maintain an audit history of policies.

## Policy examples

### Defense category example

In this example, only the Content Moderation defense is used for screening requests
marked as for the project `project-chatbot-output`. This project has been assigned to
the policy `policy-just-moderation` and the policy mapped to the moderated content
detector with confidence threshold of `l3_likely`.

This means if any of the Content Moderation detectors (crime, hate speech, profanity,
self-harm, sexual content, violence, and weapons) identifies something that looks
suspiciously like a violation in the user's input or the chatbot's output, it triggers a
`guard` response of "flagged" (as long as [Enforce mode](/docs/projects#project-mode) is
enabled).

```json
{
  "schema_version": 1,
  "projects": [
    {
      "id": "project-chatbot",
      "policy_id": "policy-just-moderation"
    }
  ],
  "policies": [
    {
      "id": "policy-just-moderation",
      "detectors": ["detector-moderation"]
    }
  ],
  "detectors": [
    {
      "id": "detector-moderation",
      "threshold": "l3_likely",
      "type": "moderated_content"
    }
  ]
}
```

### Individual detector example

In this example, only the individual hate detector within the Content Moderation defense
category is used for the specified project's screening requests. The confidence
threshold is set to `l2_very_likely`, which means a balanced risk tolerance.

```json
{
  "schema_version": 1,
  "projects": [
    {
      "id": "project-chatbot-output",
      "policy_id": "policy-only-hate"
    }
  ],
  "policies": [
    {
      "id": "policy-only-hate",
      "detectors": ["detector-hate"]
    }
  ],
  "detectors": [
    {
      "id": "detector-hate",
      "threshold": "l2_very_likely",
      "type": "moderated_content/hate"
    }
  ]
}
```

### Multiple detectors example

In this example, AI Guardrails screens both LLM inputs and outputs for moderated content
of any kind. It also screens user inputs for prompt attacks, credit card numbers, and
(via a custom detector) internal employee IDs.

```json
{
  "schema_version": 1,
  "projects": [
    {
      "id": "project-chatbot",
      "policy_id": "policy-pinj-moderation-cc"
    }
  ],
  "policies": [
    {
      "id": "policy-pinj-moderation-cc",
      "mode": "IO",
      "detectors": ["detector-moderation"],
      "input_detectors": [
        "detector-pinj",
        "detector-credit-card",
        "detector-employee-ids"
      ],
      "output_detectors": []
    }
  ],
  "detectors": [
    {
      "id": "detector-moderation",
      "threshold": "l2_very_likely",
      "type": "moderated_content"
    },
    {
      "id": "detector-pinj",
      "threshold": "l4_less_likely",
      "type": "prompt_attack"
    },
    {
      "id": "detector-credit-card",
      "threshold": "l1_confident",
      "type": "pii/credit_card"
    },
    {
      "id": "detector-employee-ids",
      "type": "pii/custom",
      "threshold": "l1_confident",
      "custom_matchers": [
        {
          "label": "Old Company ID",
          "regexes": ["(AG|AI|AR|BE|BL|BS|...|ZG|ZH)\\s*[-.•]?\\s*[0-9]{1,6}"]
        },
        {
          "label": "New Company ID",
          "regexes": ["[A-Z0-9]*CD[A-Z0-9]"]
        }
      ]
    }
  ]
}
```

### Multiple policies

In this example, there are three apps: an external chatbot, an internal chatbot, and an
internal Q\&A tool. The customer chatbot has its own policy with separate detector
configuration for screening the LLM input and output. The internal apps both use the
same policy.

```json
{
  "schema_version": 1,
  "projects": [
    {
      "id": "project-customer-chatbot",
      "policy_id": "policy-strict-chatbot"
    },
    {
      "id": "project-internal-chatbot",
      "policy_id": "policy-internal"
    },
    {
      "id": "project-internal-q&a",
      "policy_id": "policy-internal"
    }
  ],
  "policies": [
    {
      "id": "policy-strict-chatbot",
      "mode": "IO",
      "detectors": [],
      "input_detectors": ["detector-pinj"],
      "output_detectors": ["detector-moderation", "detector-credit-card"]
    },
    {
      "id": "policy-internal",
      "detectors": ["detector-moderation", "detector-pii"]
    }
  ],
  "detectors": [
    {
      "id": "detector-moderation",
      "threshold": "l2_very_likely",
      "type": "moderated_content"
    },
    {
      "id": "detector-pinj",
      "threshold": "l3_likely",
      "type": "prompt_attack"
    },
    {
      "id": "detector-credit-card",
      "threshold": "l2_very_likely",
      "type": "pii/credit_card"
    },
    {
      "id": "detector-pii",
      "threshold": "l1_confident",
      "type": "pii"
    }
  ]
}
```