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

# Content policies

> Zep content policies: define categories and rules for a project, bind the policy to new graphs, read the policy state of each episode, control whether flagged episodes appear in episode lists, and audit dropped artifacts.

> **Info**
>
> Available to [Enterprise Plan](https://www.getzep.com/pricing) customers only.

## Overview

A content policy tells Zep what derived information must not enter a Context Graph. You define the categories and the rules. Zep evaluates every artifact that ingestion derives from an episode against the rules of the graph before the artifact is published. An artifact that matches a rule is dropped. The source episode stays stored.

Use a content policy when your application must keep a class of information out of agent memory, for example health conditions in a banking assistant or compensation in a support agent. The policy applies to the graph content that Zep derives. The policy does not filter, redact, or rewrite the text you send.

A content policy separates three surfaces:

* **The project policy** is a set of categories and rules that you write at project scope. Each write creates a new immutable revision.
* **The bound policy** is the policy of one graph. Zep copies the current project revision when the graph is created. The bound policy never changes after creation.
* **The episode state** is the `content_policy` object on each episode of a protected graph. It records the evaluation status and what Zep dropped.

## What Zep evaluates

Zep evaluates the artifacts that ingestion derives from an episode: entities, entity attributes, entity summaries, facts, thread summaries, document summaries, and observations. Zep does not evaluate the source episode text.

A classifier that Zep operates answers one question per artifact and rule: does the artifact state or imply the prohibited meaning about a named or identifiable person? Zep converts each answer into one of three decisions.

| Decision  | Effect on the artifact                                                 | Effect on the episode                           |
| --------- | ---------------------------------------------------------------------- | ----------------------------------------------- |
| Match     | Zep drops the artifact.                                                | Zep flags the episode and records the category. |
| Uncertain | Zep drops the artifact.                                                | The episode is not flagged.                     |
| No match  | The artifact is published when every other rule also returns no match. | None                                            |

Zep never rewrites, redacts, or replaces a dropped artifact. A drop is complete:

* A match on a fact drops the fact. The entities at each end stay under evaluation on their own.
* A match on an entity drops the entity and every fact of the same episode that connects to that entity.
* A match on one entity attribute drops that attribute only.
* A match on an entity summary drops the summary. The entity is published without a summary update.
* An entity that the episode creates is dropped when a match drops its summary and no published edge connects to it. An entity that existed before the episode stays.
* An invalidation, expiration, or contradiction that depends on a dropped fact is not applied. Existing facts keep their state.

Permitted artifacts of the same episode are published. One episode can publish permitted facts and drop prohibited ones.

An artifact over 4000 characters is not truncated. Zep drops the artifact without a flag. A summary over 4000 characters is evaluated in segments, and a match on any segment drops the whole summary.

> **Note**
>
> Source episodes stay stored as written, under the existing [retention and deletion](/deleting-data-from-the-graph) behavior and BYOK controls. A content policy is not a promise that prohibited text never reaches Zep. It prevents the derived graph content. An application that must filter source text does so before it sends data to Zep.

## The policy model

A policy set has categories and rules.

* A **category** has a `key` and an optional `description`. The key is a customer-defined code that matches `^[a-z][a-z0-9_]{1,63}$` and is unique in the policy set. You filter episodes and audit events by key. The description has at most 500 characters. Zep does not send the description to the classifier.
* A **rule** belongs to one category through `category_key`. The rule has a `description` of at most 1000 characters that states the prohibited meaning and the subject scope. It has 1 to 8 `prohibited_examples` and 0 to 8 `permitted_examples`. Each example has at most 500 characters. The description and the examples are classifier inputs.

A policy set has at most 16 categories and at most 32 rules. A category without a rule is valid. A rule whose `category_key` names no category in the same body is rejected. An empty policy set has zero categories and zero rules and means "no content policy".

Zep rejects a write with `400 invalid_request` when a key repeats, a key does not match the pattern, a `category_key` names no category, a list or a string exceeds its limit, or `prohibited_examples` is empty. The `param` field of the error names the failing path, for example `rules[2].prohibited_examples`.

Write rules as you would brief a careful reviewer. Name the subject scope, such as "a named or identifiable person". Write prohibited examples that read as extracted facts, and permitted examples that are close to the rule but do not state the prohibited meaning.

## The project policy

The project policy is the default for every graph that you create later. The `ContentPolicy` response carries the server-owned fields `uuid`, `revision`, `categories[].id`, `rules[].id`, and `created_at`. A write body omits them. A write that echoes them is accepted, and Zep ignores the echoed values.

> **Note**
>
> The content policy methods are in the v4 SDKs after this release. The current v3 SDKs do not include them. The examples below use the v4 method names.

### Set the project policy

`PUT /project/content-policy` replaces the full policy set and returns the new revision with `200`. There is no partial update. A body that equals the current revision still creates a new revision.

**`Python`**

```python Python
from zep_cloud import Zep

client = Zep(api_key="YOUR_API_KEY")

policy = client.project.set_content_policy(
    categories=[
        {
            "key": "health_condition",
            "description": "Physical or mental health conditions of a person",
        }
    ],
    rules=[
        {
            "category_key": "health_condition",
            "description": "Statements that a named or identifiable person has, had, or was treated for a health condition.",
            "prohibited_examples": [
                "Jordan told the agent about a chronic back condition.",
                "The customer takes medication for anxiety.",
            ],
            "permitted_examples": [
                "The customer asked which pharmacies are open late.",
                "The user prefers text messages to phone calls.",
            ],
        }
    ],
)

print(policy.revision)
```

**`TypeScript`**

```typescript TypeScript
import { ZepClient } from "@getzep/zep-cloud";

const client = new ZepClient({ apiKey: "YOUR_API_KEY" });

const policy = await client.project.setContentPolicy({
  categories: [
    {
      key: "health_condition",
      description: "Physical or mental health conditions of a person",
    },
  ],
  rules: [
    {
      categoryKey: "health_condition",
      description:
        "Statements that a named or identifiable person has, had, or was treated for a health condition.",
      prohibitedExamples: [
        "Jordan told the agent about a chronic back condition.",
        "The customer takes medication for anxiety.",
      ],
      permittedExamples: [
        "The customer asked which pharmacies are open late.",
        "The user prefers text messages to phone calls.",
      ],
    },
  ],
});

console.log(policy.revision);
```

**`Go`**

```go Go
import (
    "context"
    "fmt"

    zep "github.com/getzep/zep-go/v4"
    zepclient "github.com/getzep/zep-go/v4/client"
    "github.com/getzep/zep-go/v4/option"
)

client := zepclient.NewClient(option.WithAPIKey("YOUR_API_KEY"))

policy, err := client.Project.SetContentPolicy(context.TODO(), &zep.ContentPolicyRequest{
    Categories: []*zep.ContentPolicyCategoryRequest{
        {
            Key:         zep.String("health_condition"),
            Description: zep.String("Physical or mental health conditions of a person"),
        },
    },
    Rules: []*zep.ContentPolicyRuleRequest{
        {
            CategoryKey: zep.String("health_condition"),
            Description: zep.String("Statements that a named or identifiable person has, had, or was treated for a health condition."),
            ProhibitedExamples: []string{
                "Jordan told the agent about a chronic back condition.",
                "The customer takes medication for anxiety.",
            },
            PermittedExamples: []string{
                "The customer asked which pharmacies are open late.",
                "The user prefers text messages to phone calls.",
            },
        },
    },
})
if err != nil {
    return err
}
fmt.Println(*policy.Revision)
```

The response is the new `ContentPolicy`:

```json
{
  "uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "revision": 3,
  "categories": [
    {
      "id": "cat_01j9z4x2",
      "key": "health_condition",
      "description": "Physical or mental health conditions of a person"
    }
  ],
  "rules": [
    {
      "id": "rule_01j9z4x9",
      "category_key": "health_condition",
      "description": "Statements that a named or identifiable person has, had, or was treated for a health condition.",
      "prohibited_examples": [
        "Jordan told the agent about a chronic back condition.",
        "The customer takes medication for anxiety."
      ],
      "permitted_examples": [
        "The customer asked which pharmacies are open late.",
        "The user prefers text messages to phone calls."
      ]
    }
  ],
  "created_at": "2026-09-22T10:00:00Z"
}
```

`categories[].id` and `rules[].id` are stable across revisions. Zep matches a category across revisions by `key`, and a rule by the pair `(category_key, description)`. A category or rule that does not change keeps its identifier.

To clear the project policy, write the empty policy set. No body, `{}`, and `{"categories": [], "rules": []}` are the same write. Graphs that you create later bind the empty set unless the create body adds rules. Graphs that already exist keep their bound policy.

### Read the project policy and its revisions

`GET /project/content-policy` returns the current revision. A project without a write returns revision `0`, the empty policy set.

`GET /project/content-policy/revisions` lists revisions in descending `revision` order with `limit` and `cursor`. The list carries `total_size`. Zep stores every revision that a graph binds until the last graph that binds it is deleted. A revision that no graph binds and that is not current can be removed by retention, so a client accepts a gap in the list.

`GET /project/content-policy/revisions/{revision_uuid}` returns one revision, or `404 not_found`.

**`Python`**

```python Python
current = client.project.get_content_policy()

revisions = client.project.list_content_policy_revisions(limit=20)
for revision in revisions.items:
    print(revision.revision, revision.uuid, revision.created_at)

one = client.project.get_content_policy_revision(revision_uuid=current.uuid)
```

**`TypeScript`**

```typescript TypeScript
const current = await client.project.getContentPolicy();

const revisions = await client.project.listContentPolicyRevisions({ limit: 20 });
for (const revision of revisions.items) {
  console.log(revision.revision, revision.uuid, revision.createdAt);
}

const one = await client.project.getContentPolicyRevision(current.uuid);
```

**`Go`**

```go Go
current, err := client.Project.GetContentPolicy(context.TODO())
if err != nil {
    return err
}

revisions, err := client.Project.ListContentPolicyRevisions(context.TODO(), &zep.ProjectListContentPolicyRevisionsRequest{
    Limit: zep.Int(20),
})
if err != nil {
    return err
}
for _, revision := range revisions.Results {
    fmt.Println(*revision.Revision, *revision.UUID, *revision.CreatedAt)
}

one, err := client.Project.GetContentPolicyRevision(context.TODO(), *current.UUID)
```

## Bind a policy at graph creation

A graph copies the current project revision when you create it. `graph.create` and `user.create` accept an optional `content_policy` object with three fields:

* `project_revision` is optional. When present, the create fails with `409 conflict` and the code `content_policy_revision_stale` if the current project revision is different. Send it when the policy that an operator reviewed must be the policy that the graph binds.
* `categories` and `rules` add to the project revision. A `key` that repeats a project category adds rules to that category. A rule that duplicates a project rule by `(category_key, description)` is ignored.
* The combined set must stay inside the limits of the policy model. A combined set over a limit is `400 invalid_request` with `param` set to `content_policy.rules`.

The create body cannot remove a project rule. Additions are additive only.

**`Python`**

```python Python
graph = client.graph.create(
    graph_id="support-emea",
    content_policy={
        "project_revision": 3,
        "categories": [{"key": "union_membership"}],
        "rules": [
            {
                "category_key": "union_membership",
                "description": "Statements about a person's trade union membership.",
                "prohibited_examples": ["The user is a shop steward."],
            }
        ],
    },
)
```

**`TypeScript`**

```typescript TypeScript
const graph = await client.graph.create({
  graphId: "support-emea",
  contentPolicy: {
    projectRevision: 3,
    categories: [{ key: "union_membership" }],
    rules: [
      {
        categoryKey: "union_membership",
        description: "Statements about a person's trade union membership.",
        prohibitedExamples: ["The user is a shop steward."],
      },
    ],
  },
});
```

**`Go`**

```go Go
graph, err := client.Graph.Create(context.TODO(), &zep.CreateGraphRequest{
    GraphID: zep.String("support-emea"),
    ContentPolicy: &zep.GraphContentPolicyRequest{
        ProjectRevision: zep.Int(3),
        Categories: []*zep.ContentPolicyCategoryRequest{
            {Key: zep.String("union_membership")},
        },
        Rules: []*zep.ContentPolicyRuleRequest{
            {
                CategoryKey:        zep.String("union_membership"),
                Description:        zep.String("Statements about a person's trade union membership."),
                ProhibitedExamples: []string{"The user is a shop steward."},
            },
        },
    },
})
```

The create and the binding are one atomic operation. The `201` response carries the graph, and the bound policy is readable from that moment. A create that cannot persist the binding writes no graph and returns `503 service_unavailable`.

A graph is never created unprotected when a policy exists. A create on an account without the content policy entitlement, when the project revision or the create body has at least one rule, returns `403 content_policy_not_entitled` and writes no graph.

### The bound policy is read-only

There is no update and no delete for the bound policy of a graph. A later write to the project policy does not change a graph that already exists. When a graph needs a different policy, create a new graph.

`GET /graphs/{graph_uuid}/content-policy` returns the bound set as a `ContentPolicy` with two more fields:

```json
{
  "uuid": "…",
  "revision": 3,
  "categories": [ … ],
  "rules": [ … ],
  "created_at": "…",
  "project_revision": 3,
  "bound_at": "2026-09-22T10:05:00Z"
}
```

`revision` and `project_revision` are the number of the project revision that the bound set includes, and they are always equal. The bound set has its own `uuid` when the graph adds a category or a rule, and `uuid` equals the `uuid` of that project revision when the graph adds nothing. Project categories and rules keep the project identifiers. Graph additions get their own identifiers.

**`Python`**

```python Python
bound = client.graph.get_content_policy(graph_uuid="graph_uuid")
print(bound.revision, bound.bound_at)
```

**`TypeScript`**

```typescript TypeScript
const bound = await client.graph.getContentPolicy("graph_uuid");
console.log(bound.revision, bound.boundAt);
```

**`Go`**

```go Go
bound, err := client.Graph.GetContentPolicy(context.TODO(), "graph_uuid")
if err != nil {
    return err
}
fmt.Println(*bound.Revision, *bound.BoundAt)
```

[Cloning a graph](/cloning-graphs) binds the source graph's policy to the clone. The clone operation does not accept `content_policy`. The clone copies each source episode with its `content_policy` object unchanged. It does not copy pending or failed episodes.

## Episode state

Every episode of a protected graph carries a `content_policy` object. An episode of a graph without a bound rule has no `content_policy` object.

```json
{
  "uuid": "…",
  "graph_uuid": "…",
  "processed": true,
  "content_policy": {
    "status": "complete",
    "revision": 3,
    "violated": true,
    "category_keys": ["health_condition"],
    "retained_count": 4,
    "dropped_count": 2
  }
}
```

| Field            | Meaning                                                                                                                                                                 |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`         | `pending`, `complete`, or `failed`. `failed` means that the ingestion retry policy is exhausted.                                                                        |
| `revision`       | The bound revision of the graph.                                                                                                                                        |
| `violated`       | `true` when at least one artifact matched a rule. It never returns to `false` under the same bound revision.                                                            |
| `category_keys`  | The category keys of every match. It is empty when `violated` is `false`. It only lists keys of the bound set.                                                          |
| `retained_count` | The count of published artifacts. It is `0` while `status` is `pending` or `failed`, because no artifact is published before every evaluation of the episode completes. |
| `dropped_count`  | The count of dropped artifacts. It grows as evaluation persists decisions, in every status.                                                                             |

`processed` keeps its existing meaning. An episode is `processed` only after `content_policy.status` reaches `complete`. A `pending` or `failed` evaluation leaves `processed: false`.

`violated`, `category_keys`, and `dropped_count` can grow after `complete`. Zep sometimes extracts one artifact from several episodes at once. When that artifact matches a rule, Zep attributes the match to every episode in the group, including an episode that is already complete. `status` and `processed` do not change in that case.

Ingestion evaluates every artifact of an episode before it publishes any of them. Retrieval never observes a protected graph state where an artifact is published before its evaluation completed.

### Pending and failed episodes

An episode stays `pending` while ingestion evaluates it. When the classifier fails, Zep retries under the existing ingestion retry policy, and the episode stays `pending`. When the retry policy is exhausted, `status` becomes `failed` and `processed` stays `false`. A match that was persisted before the failure stays in `violated` and `category_keys`.

No artifact of a failed episode is published. A failure is never recorded as "no match". A `pending` or `failed` episode is excluded from every summary and observation, and from context assembly.

## Task results

The `graph.add`, `thread.add_messages`, and `batch.process` tasks report the policy outcome in `result`. A policy violation is an expected outcome, and the task reaches `succeeded`.

```json
{
  "uuid": "…",
  "type": "graph.add",
  "status": "succeeded",
  "result": {
    "episode_uuid": "…",
    "content_policy": {
      "status": "complete",
      "violated": true,
      "category_keys": ["health_condition"],
      "retained_count": 4,
      "dropped_count": 2
    }
  }
}
```

* `graph.add` carries one `episode_uuid` and one `content_policy` object.
* `thread.add_messages` carries `episodes`, an array of `{episode_uuid, content_policy}` with one entry per message.
* `batch.process` carries the aggregate `violated_count` and `failed_count` in `result.content_policy`. Each batch item carries the episode `content_policy` object once the item completes.

The task stays `processing` while the episode is `pending`, including while Zep retries a classifier failure. A classifier failure that exhausts the retry policy is a processing failure. The task reaches `failed` with `error.code` set to `content_policy_evaluation_failed`. The error message names no source text, no artifact text, and no rule text. A `thread.add_messages` or `batch.process` task where some episodes completed and some failed is `partial`, and `result` carries the completed episodes.

The [ingestion status](/check-data-ingestion-status) recipe covers task and episode polling.

## Flagged episodes in retrieval

A flagged episode is an episode where at least one artifact matched a rule. The project setting `include_policy_violating_episodes` on `GET /project` and `PATCH /project` controls whether four operations return flagged episodes. The default is `false`.

| Operation                         | Setting `false`        | Setting `true`            |
| --------------------------------- | ---------------------- | ------------------------- |
| `graph.episode.list`              | Omits flagged episodes | Includes flagged episodes |
| `graph.search_episodes`           | Omits flagged episodes | Includes flagged episodes |
| `graph.episode.list_for_document` | Omits flagged episodes | Includes flagged episodes |
| `thread.list_episodes`            | Omits flagged episodes | Includes flagged episodes |

In both settings:

* The four operations return `pending` and `failed` episodes with `processed: false`. Such an episode has no published artifact, so the list exposes only the source episode.
* `graph.episode.get` returns a flagged episode. A direct read is an explicit request for one known identifier.
* `graph.search_episodes` matches on source text.

The setting never changes the result of `graph.get_context`, `thread.get_context`, `agent.get_context`, summaries, or observations. Context assembly excludes flagged, pending, and failed episodes from source text and from evidence expansion. A permitted fact from a mixed episode stays searchable, and default responses do not expand it to the source episode text. A flagged episode is never an input to a thread summary, a document summary, a user summary, an entity summary, or an observation. When an episode is flagged after a summary that used it was published, Zep marks that summary unavailable and rebuilds it from eligible inputs.

> **Note**
>
> Zep does not derive observations on a protected graph in this release. The observation writer does not evaluate its output against the bound policy, so it does not run.

**`Python`**

```python Python
client.project.update(include_policy_violating_episodes=True)
```

**`TypeScript`**

```typescript TypeScript
await client.project.update({ includePolicyViolatingEpisodes: true });
```

**`Go`**

```go Go
_, err := client.Project.Update(context.TODO(), &zep.PatchProjectRequest{
    IncludePolicyViolatingEpisodes: zep.Bool(true),
})
```

### Filter by violation

`graph.episode.list` and `graph.search_episodes` accept `content_policy_violated` as an exact-match filter in `filters`. The filter is supported only while `include_policy_violating_episodes` is `true`. While the setting is `false`, the filter is rejected with `400 unsupported_filter`.

### Cursors

The value of `include_policy_violating_episodes` is part of the pagination cursor of the four operations. A cursor that was issued under one value and used under the other value returns `400 invalid_cursor`. Restart the page walk after you change the setting.

## Audit events

`POST /graphs/{graph_uuid}/content-policy/events/list` lists one event per dropped artifact and one per flagged episode. Events carry identifiers only. An event never contains source text, artifact text, spans, probabilities, questions, person names, or entity names.

```json
{
  "items": [
    {
      "id": "cpe_01j9z5a0",
      "episode_uuid": "…",
      "revision": 3,
      "artifact_kind": "edge",
      "category_keys": ["health_condition"],
      "rule_ids": ["rule_01j9z4x9"],
      "drop_reason": "match",
      "decided_at": "2026-09-22T10:05:00Z"
    }
  ],
  "next_cursor": "…",
  "total_size": 12
}
```

| Field           | Meaning                                                                                                                                                                                                                                 |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`            | The event identifier.                                                                                                                                                                                                                   |
| `episode_uuid`  | The episode that the artifact was derived from.                                                                                                                                                                                         |
| `revision`      | The bound revision under which Zep decided.                                                                                                                                                                                             |
| `artifact_kind` | `entity`, `entity_attribute`, `entity_summary`, `edge`, `thread_summary`, `document_summary`, `observation`, or `episode`.                                                                                                              |
| `category_keys` | The category keys of the matched rules.                                                                                                                                                                                                 |
| `rule_ids`      | The rule identifiers. Project rules and graph rules have different identifiers, so you can separate them.                                                                                                                               |
| `drop_reason`   | `match`, `indeterminate`, `oversize`, or `dependent`. `indeterminate` is an uncertain decision. `oversize` is an artifact over 4000 characters. `dependent` is an artifact that was dropped because it connected to a dropped artifact. |
| `decided_at`    | The decision time.                                                                                                                                                                                                                      |

An episode-level event has `artifact_kind: "episode"`, `drop_reason: "match"`, and every matched key.

The list orders by `decided_at` descending and paginates with `limit` and `cursor`. It carries `total_size`. `filters` accepts `episode_uuid` and `drop_reason` as exact-match filters. Any other filter returns `400 unsupported_filter`. The operation is available on an unprotected graph and returns an empty list. Events follow the retention of the audit store, so a client accepts that old events are absent.

**`Python`**

```python Python
events = client.graph.list_content_policy_events(
    graph_uuid="graph_uuid",
    filters={"drop_reason": "match"},
    limit=50,
)
for event in events.items:
    print(event.episode_uuid, event.artifact_kind, event.category_keys)
```

**`TypeScript`**

```typescript TypeScript
const events = await client.graph.listContentPolicyEvents("graph_uuid", {
  filters: { dropReason: "match" },
  limit: 50,
});
for (const event of events.items) {
  console.log(event.episodeUuid, event.artifactKind, event.categoryKeys);
}
```

**`Go`**

```go Go
events, err := client.Graph.ListContentPolicyEvents(context.TODO(), "graph_uuid", &graph.ListContentPolicyEventsRequest{
    Filters: &graph.ContentPolicyEventFilters{DropReason: zep.String("match")},
    Limit:   zep.Int(50),
})
if err != nil {
    return err
}
for _, event := range events.Items {
    fmt.Println(event.EpisodeUUID, event.ArtifactKind, event.CategoryKeys)
}
```

## Permissions

Policy reads map to the ABAC action `project.get` at project scope and `graph.get` at graph scope. `PUT /project/content-policy` and the `include_policy_violating_episodes` field of `PATCH /project` map to `project.update`. `content_policy` in a create body needs `graph.create` or `user.add`, the same as the create itself. An ingestion-only API key cannot write a policy or change the retrieval setting, and receives `403 permission_denied`. [Policy-based access control](/policy-based-access-control) describes how actions attach to API keys.

## Entitlement and availability

Content policies need the content policy entitlement on your account. Without the entitlement:

* A `PUT /project/content-policy` with at least one rule returns `403 content_policy_not_entitled`.
* A create whose bound policy, the current project revision plus the `content_policy` in the body, has at least one rule returns `403 content_policy_not_entitled` and writes no graph. A body that adds categories only does not need the entitlement.
* A `PUT` of the empty policy set is accepted. A project can always clear its policy and create graphs again.

A project that loses the entitlement keeps existing bound graphs protected. A new graph on that account is rejected until the project policy is cleared or the entitlement returns. Removing protection from an existing graph is never a side effect of a plan change.

The classifier call sends the derived text and the rule text to a language model that Zep operates through its model gateway. Zep selects the model, and you cannot change it. In this release, an account under a strict data residency policy and a BYOC deployment cannot receive the entitlement. The [Security & Compliance](/security-compliance) overview describes the deployment models. Contact [Zep sales](https://www.getzep.com/pricing) for the entitlement.

## Error codes

| Code                               | Status     | Meaning                                                                                                                     |
| ---------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------- |
| `invalid_request`                  | 400        | A body limit or shape violation. `param` names the failing path.                                                            |
| `unsupported_filter`               | 400        | The `content_policy_violated` filter while `include_policy_violating_episodes` is `false`, or an unsupported events filter. |
| `invalid_cursor`                   | 400        | A cursor issued under the other value of `include_policy_violating_episodes`.                                               |
| `permission_denied`                | 403        | The key lacks `project.update`, `graph.create`, or `user.add`.                                                              |
| `content_policy_not_entitled`      | 403        | The account has no content policy entitlement.                                                                              |
| `not_found`                        | 404        | An unknown revision or graph.                                                                                               |
| `content_policy_revision_stale`    | 409        | `project_revision` in a create body is not the current project revision.                                                    |
| `service_unavailable`              | 503        | The binding could not persist. No graph was written.                                                                        |
| `content_policy_evaluation_failed` | task error | The classifier failed after the ingestion retry policy was exhausted. The episode is `failed`.                              |

## Related

* [Create Graph](/create-graph) and [Users and User Graphs](/users-and-user-graphs) describe the create operations that accept `content_policy`.
* [Episodes](/episodes) describes how episodes are stored and retrieved.
* [Cloning Graphs](/cloning-graphs) describes how a clone binds the source policy.
* [Policy-based access control](/policy-based-access-control) describes the ABAC actions that policy operations map to.