# Zep v4: the Context API for agents

By Patrick Kemp · Oct 14, 2026 · https://www.getzep.com/blog/zep-api-for-agents/ · Tags: Product updates

Zep v4 is the Context API for agents: graph navigation, Ingestion Traces and stable error codes in the API, plus a step-by-step path from v3.

**Key takeaways**

- **Zep v4 is a Context API built for agents that call it.** Agents search a graph, read the results, and choose the next call. v4 gives them tools to navigate the graph by connection and to see how Zep built it.
- **Agents can navigate the graph by connection.** v4 lists nodes by degree, lists the neighbors of a node, and returns a bounded subgraph, so an agent needs fewer calls to reach the nodes that it wants.
- **Debug logs and Ingestion Traces are available through the API.** A developer or an agent can read how Zep processed an episode and why it extracted a fact.
- **Every resource has a UUID that Zep generates, and every error has a stable code.** The SDK retries a create call without making a duplicate, and an agent can read the error code to decide its next step.

## Why Zep v4

Business knowledge is scattered across systems, and agents piece it together to make decisions. Zep combines business data, documents, and conversations into [Zep graphs](https://www.getzep.com/platform/agent-knowledge-graph/) that agents use as context.

Agents now use the Zep API in two ways. [Coding agents](https://www.getzep.com/blog/coding-agents-can-design-your-zep-implementation/) write integration code for developers, and run-time agents call Zep as they work: they search a graph, read the results, and decide which call to make next.

## What's new in v4

- **Hyperedges.** A hyperedge stores a fact about three or more entities as one fact, so an agent does not find one pair of the fact and miss the rest. See [Hyperedges](https://help.getzep.com/v4/hyperedges).
- **Hierarchical entity types.** Custom entity types can have parent types and subtypes. The ontology can record that one type is a kind of another. The feature is in closed beta for select accounts. See [Entity type hierarchy](https://help.getzep.com/v4/customizing-graph-structure#entity-type-hierarchy).
- **Documents.** A `document_id` groups the chunks of one file, so Zep extracts each chunk with the earlier chunks as context. See [Documents](https://help.getzep.com/v4/documents).
- **Identity and protected properties.** Zep matches entities on the values that identify them, such as an account ID, so one customer does not become two nodes. Ingestion does not replace a protected value that you imported from a system of record. `strict_ontology: true` makes the graph conform to the entity and edge types that you defined. See [Customize graph structure](https://help.getzep.com/v4/customizing-graph-structure).
- **Event time.** `reference_time` records when an event occurred, separate from the time that Zep received the data, so a backfill keeps the correct order of events. See [Preserve original event time](https://help.getzep.com/v4/prepare-data-for-ingestion#preserve-original-event-time).
- **Also in v4.** One call can [add up to 100 nodes or 100 edges](https://help.getzep.com/v4/adding-fact-triplets). Each entity node lists the [episodes that mention it](https://help.getzep.com/v4/graph-overview) in `episode_uuids`. [Auto search](https://help.getzep.com/v4/searching-the-graph#relative-time-in-auto-search) can turn relative calendar language into a time window in the time zone of the user or graph.

We'll be posting throughout the week to cover these changes in more detail.

## Navigate the graph by connection

[Zep v4: Navigate the Graph by Connection for Your AI Agents](https://www.youtube.com/embed/vqZ0F3EtO6Q?feature=oembed)

The three navigation calls on a sales graph, first in Python and then in the dashboard's graph assistant.

Search returns what matches a query. Many agent questions are about structure instead: which accounts connect to this person, or what is two hops from this deal. With search alone, an agent answers them with a chain of calls, and it cannot tell when it has seen the full neighborhood. v4 adds three navigation calls:

- `graph.node.list` with `order_by="degree"` lists the most connected nodes first. Each node has a `degree` field: the number of entity edges that touch the node.
- `graph.node.list_neighbors` returns each node that connects to an anchor node, with every edge that connects the two.
- `graph.get_subgraph` expands from up to 20 seed nodes, to a depth of up to three hops. When the expansion reaches the node limit or the edge limit, the response sets `truncated` and names the limit in `truncation_reason`. An agent cannot mistake a partial neighborhood for a complete one.

```python
import os

from zep_cloud.client import Zep

client = Zep(api_key=os.environ["ZEP_API_KEY"])
graph_uuid = "YOUR_GRAPH_UUID"

hubs = client.graph.node.list(
    graph_uuid,
    order_by="degree",
    order="desc",
    page_size=5,
).items
if not hubs:
    raise SystemExit("The graph has no nodes yet.")
anchor = hubs[0]

neighbors = client.graph.node.list_neighbors(graph_uuid, anchor.uuid_, page_size=25).items
for neighbor in neighbors:
    print(neighbor.node.name, len(neighbor.edges))

subgraph = client.graph.get_subgraph(
    graph_uuid,
    seed_node_uuids=[anchor.uuid_],
    depth=2,
)
if subgraph.truncated:
    print("Stopped by:", subgraph.truncation_reason)
```

Agents that connect through the [Context MCP Server](https://help.getzep.com/v4/context-mcp-server) get the same navigation as tools. `get_node_neighbors` lists the entities that connect directly to one entity, with the connecting facts. `get_subgraph` explores a bounded, multi-hop neighborhood around seed entities.

In the Zep dashboard, the [graph visualizer assistant](https://help.getzep.com/v4/graph-visualizer-assistant) answers questions about the graph that you have open, such as how two entities connect. The assistant is read-only, and it shows each retrieval step as it runs.

## See how Zep built each episode

A wrong fact in the graph has a cause in the episode text or in an extraction decision. Without a record of what each extraction step saw and decided, a fix is guesswork. In v4, debug logs and Ingestion Traces are available through the API and the SDKs, and a developer or an agent can read them in code.

`debug_log.enable` turns on debug mode for the project for a limited window: 60 minutes for standard accounts and 24 hours for Enterprise accounts. Zep captures workflow logs for the episodes that it processes during the window. On Flex, Flex Plus, and Enterprise plans, the same call also enables Ingestion Traces.

```python
enabled = client.debug_log.enable()

# Add episodes, and then read the logs and traces for one episode.
logs = client.graph.episode.get_debug_logs(graph_uuid, episode_uuid)
traces = client.graph.episode.list_ingestion_traces(
    graph_uuid,
    episode_uuid,
    page_size=50,
)
```

`graph.episode.get_debug_logs` returns the status, the start and completion times, the error, and the log entries for one episode. `graph.episode.list_ingestion_traces` returns a page of trace records, oldest first. Each record has `step`, `status`, `input`, `output`, `error`, and `created_at`.

Ingestion Traces explain why each traced extraction step made its decision. A trace connects each decision to the episode context, prior episodes, ontology, or deduplication candidates that informed it. A developer can find the cause of a wrong fact in the trace before they change the ontology or the data.

Debug mode applies only to episodes that Zep processes while the window is active. Zep keeps workflow logs for 12 hours, or 7 days for Enterprise accounts, and keeps Ingestion Traces for 30 days. Logs and traces hold episode content, so handle them as you handle the source data. For more information, see [Debug mode](https://help.getzep.com/v4/debug-mode).

## An API that agents can call safely

An agent that calls an API in a loop has to retry failed calls and react to errors without a person to help.

### Every resource has a UUID that Zep generates

v4 addresses every user, thread, and graph by a UUID that Zep generates. A create call returns the UUID, and every later call takes it. The v3 `user_id`, `thread_id`, and `graph_id` values become legacy names. Each UUID is opaque and holds no tenant data. An identifier in a URL does not put your customer names or internal IDs into access logs, proxy logs, referrer headers, or error reports. Store the Zep UUID next to your own record, and pass it on each call.

```python
from datetime import datetime, timezone

from zep_cloud import AddMessage

zep_user = client.user.create(
    email="jane.smith@example.com",
    first_name="Jane",
    last_name="Smith",
)
# Store this value with the user record in your application database.
zep_user_uuid = zep_user.uuid_

zep_thread = client.thread.create(user_uuid=zep_user_uuid)

client.thread.add_messages(
    zep_thread.uuid_,
    messages=[
        AddMessage(
            reference_time=datetime.now(timezone.utc).isoformat(),
            name="Jane Smith",
            role="user",
            content="Which plan is my team on?",
        )
    ],
)
```

### Retries that do not make a second resource

A v4 create call carries no identifier, so each call makes a new resource. The SDK sends an idempotency key on its own retries, so a retried request does not make a second user, thread, or graph. When your application can repeat a create call, for example in a repair job, you can also pass your own `Idempotency-Key`. In Python, the option is `idempotency_key`.

### Errors with stable codes

Every v4 error response has an `error` object with the same fields: `code`, `message`, `param`, `details`, and `request_id`. The `code` is a stable value that your code can compare. When one request field causes the error, `param` names that field.

| HTTP status | Example code | What the caller can do |
|---|---|---|
| 400 | `invalid_identifier` | Pass a UUID, not a legacy name. |
| 400 | `invalid_request` | Correct the field that `param` names. |
| 404 | `not_found` | Stop, or check the UUID. The resource does not exist. |
| 429 | `rate_limited` | Wait, and then send the request again. |

Examples of v4 error codes and the action for each.

An agent does not have to parse a message to decide its next step. Every v4 response also has an `X-Zep-Request-ID` header with the same value as `request_id`.

### One search method for each scope, and one pagination form

Each search scope has its own method: `graph.search_edges`, `graph.search_nodes`, `graph.search_episodes`, `graph.search_observations`, and `graph.search_thread_summaries`. `graph.get_context` returns a Context Block. One method for each scope gives each call one purpose and one response type. A run-time agent can expose each method as a separate tool.

List methods return a pager that follows an opaque cursor. You do not increment a page number. Lists and search results default to 50 items. A delete returns a task. Poll the task until it reaches `succeeded`, `partial`, or `failed`.

## Get started and migrate from v3

The Python, TypeScript, and Go SDKs support v4. Install the v4 SDK for your language:

```bash
pip install --upgrade "zep-cloud>=4.0.0"
npm install @getzep/zep-cloud@^4
go get github.com/getzep/zep-go/v4
```

For more information, see the [Zep documentation](https://help.getzep.com/v4/overview).

v3 and v4 serve the same project data at the same time, so you can move your integration to v4 in steps. First, add a column for the Zep UUID to each table that refers to a Zep resource. Then store the UUID of each new resource while your code still uses v3. The v3 response already contains the UUID that v4 uses. Next, give the existing rows their UUIDs with a backfill job.

The [migration guide](https://help.getzep.com/v4/migrating-from-v3) gives each step, the method map, and the changes that need more than a rename. If you use a coding agent, the [Build with Zep plugin](https://help.getzep.com/v4/implement-zep-with-agents) for Claude Code, Codex, and Cursor includes a skill that helps your agent migrate v3 code to v4.

## FAQ

### When does the v3 API retire?

No retirement date is set. The v3 API will be deprecated on April 13, 2027, and it keeps working after that date. v3 and v4 serve the same project data at the same time, so you can move your integration to v4 in steps: store the Zep UUID with each new record while your code still uses v3, and then backfill the existing rows.

### Do my existing users get new UUIDs?

No. User and thread UUIDs have the same value in v3 and v4. For a graph, v4 uses the value that v3 returns as `canonical_graph_uuid`, not the v3 `Graph.uuid`. v3 already returns these values, so you can store them in your own database before you move any calls to v4. Use v3 SDK version 3.30.0 or later.

### Is lookup part of the long-term API?

No. `lookup` is a migration tool, and it will become deprecated when the v3 API retires. A later major version will remove it. Use `lookup` only in the backfill job that gives your existing rows their UUIDs. `lookup.batch` resolves up to 1,000 legacy names in one request. After the migration, read the UUID from your own database.

### Which plans include Ingestion Traces?

Ingestion Traces are available on the Flex, Flex Plus, and Enterprise plans. On those plans, `debug_log.enable` turns on traces along with debug mode, and Zep traces selected extraction steps for the episodes that it processes during the window: 60 minutes, or 24 hours for Enterprise accounts. On other plans, debug mode captures workflow logs only. Zep keeps traces for 30 days.
