> This page is for version v4 (default).
> For other versions, use one of these documentation indexes:
> - v4 (default): https://docs-beta.getzep.com/v4/llms.txt
> - v3: https://docs-beta.getzep.com/v3/llms.txt
> - v2: https://docs-beta.getzep.com/v2/llms.txt

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

# Build project, product, and domain context

> Create, populate, search, and govern a shared Context Graph for a project, product, or business domain.

An agent cannot complete a business task when project records, product
documentation, and operational updates are in separate systems. This quickstart
combines these sources in one shared Context Graph and retrieves the context
that is relevant to a task.

## Choose the graph scope

Create one Context Graph for each subject and access boundary. Zep gives each
graph a UUID. Store the graph UUID in your application, for example next to
your project ID, and use it in each later call. The graph `name` and
`description` describe the graph. They are not addresses.

| Scope           | Example graph name  | Example sources                                                     |
| --------------- | ------------------- | ------------------------------------------------------------------- |
| Project         | `project-argus`     | Plans, decisions, issues, and meeting notes                         |
| Product         | `product-atlas`     | Specifications, release notes, support content, and catalog records |
| Business domain | `incident-response` | Runbooks, incidents, service records, and policy documents          |

This guide uses a graph with the name `project-argus`. The project depends on a
product and follows an incident-response process, so the example also shows
product and business-domain context. If these sources have different access
requirements, put them in separate Context Graphs and retrieve only the
authorized graphs for each task.

## Install and initialize the SDK

#### Python

Set up your Python project, ideally with [a virtual environment](https://medium.com/@vkmauryavk/managing-python-virtual-environments-with-uv-a-comprehensive-guide-ac74d3ad8dff), and then:

**`pip`**

```bash pip
pip install --upgrade "zep-cloud>=4.0.0a6"
```

**`uv`**

```bash uv
uv pip install "zep-cloud>=4.0.0a6"
```

#### TypeScript

Set up your TypeScript project and then:

**`npm`**

```bash npm
npm install @getzep/zep-cloud@preview
```

**`yarn`**

```bash yarn
yarn add @getzep/zep-cloud@preview
```

**`pnpm`**

```bash pnpm
pnpm install @getzep/zep-cloud@preview
```

#### Go

Set up your Go project and then:

```bash
go get github.com/getzep/zep-go/v4
```

After [creating a Zep account](https://app.getzep.com/), obtaining an API key, and setting the API key as an environment variable, initialize the client once at application startup and reuse it throughout your application.

#### Initialize Zep client

**`Python`**

```python Python
import os
from zep_cloud.client import Zep

API_KEY = os.environ.get('ZEP_API_KEY')

client = Zep(
    api_key=API_KEY,
)
```

**`TypeScript`**

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

const API_KEY = process.env.ZEP_API_KEY;

const client = new ZepClient({
  apiKey: API_KEY,
});
```

**`Go`**

```go Go
import (
    zepclient "github.com/getzep/zep-go/v4/client"
    "github.com/getzep/zep-go/v4/option"
)

client := zepclient.NewClient(
    option.WithAPIKey(os.Getenv("ZEP_API_KEY")),
)
```

#### .env

```
ZEP_API_KEY=your_api_key_here
```

## Create the Context Graph

Create the graph before you add data. Use the graph UUID that `graph.create`
returns for every operation in this quickstart.

**`Python`**

```python Python
graph = client.graph.create(
    name="project-argus",
    description="Plans, product dependencies, and incidents for Project Argus",
)

# Store the graph UUID next to your project ID
GRAPH_UUID = graph.uuid_
```

**`TypeScript`**

```typescript TypeScript
const graph = await client.graph.create({
  name: "project-argus",
  description: "Plans, product dependencies, and incidents for Project Argus",
});

// Store the graph UUID next to your project ID
const graphUuid = graph.uuid!;
```

**`Go`**

```go Go
zepGraph, err := client.Graph.Create(context.TODO(), &zep.CreateGraphRequest{
    Name:        zep.String("project-argus"),
    Description: zep.String("Plans, product dependencies, and incidents for Project Argus"),
})
if err != nil {
    log.Fatal(err)
}

// Store the graph UUID next to your project ID
graphUUID := *zepGraph.UUID
```

## Ingest the source data

Add each source as an episode. Use JSON for structured records, text for
documents, and message data for communications with identified speakers.
Source metadata supports filtering, source traceability, and source-based
access policies.

**`Python`**

```python Python
import json
import time

client.graph.episode.add(
    GRAPH_UUID,
    type="json",
    data=json.dumps({
        "project_id": "ARG-001",
        "name": "Project Argus",
        "status": "at risk",
        "product": "Atlas Gateway",
        "owner": "Platform Engineering",
    }),
    source_description="Project record from the project system",
    metadata={"source": "project_system", "record_id": "ARG-001"},
)

client.graph.episode.add(
    GRAPH_UUID,
    type="text",
    data=(
        "Atlas Gateway release 4.2 requires the regional failover runbook. "
        "The release cannot start until the backup region passes validation."
    ),
    source_description="Atlas Gateway release runbook",
    metadata={"source": "product_docs", "product": "atlas-gateway"},
)

incident_result = client.graph.episode.add(
    GRAPH_UUID,
    type="message",
    data=(
        "Nina (incident commander): The backup region failed validation. "
        "Move the Argus release review to Friday."
    ),
    source_description="Incident response channel",
    metadata={"source": "incident_channel", "incident_id": "INC-482"},
)
incident_episode_uuid = incident_result.episode.uuid_
```

**`TypeScript`**

```typescript TypeScript
await client.graph.episode.add(graphUuid, {
  type: "json",
  data: JSON.stringify({
    project_id: "ARG-001",
    name: "Project Argus",
    status: "at risk",
    product: "Atlas Gateway",
    owner: "Platform Engineering",
  }),
  sourceDescription: "Project record from the project system",
  metadata: { source: "project_system", record_id: "ARG-001" },
});

await client.graph.episode.add(graphUuid, {
  type: "text",
  data:
    "Atlas Gateway release 4.2 requires the regional failover runbook. " +
    "The release cannot start until the backup region passes validation.",
  sourceDescription: "Atlas Gateway release runbook",
  metadata: { source: "product_docs", product: "atlas-gateway" },
});

const incidentResult = await client.graph.episode.add(graphUuid, {
  type: "message",
  data:
    "Nina (incident commander): The backup region failed validation. " +
    "Move the Argus release review to Friday.",
  sourceDescription: "Incident response channel",
  metadata: { source: "incident_channel", incident_id: "INC-482" },
});
const incidentEpisodeUuid = incidentResult.episode!.uuid!;
```

**`Go`**

```go Go
projectRecord, err := json.Marshal(map[string]string{
    "project_id": "ARG-001",
    "name":       "Project Argus",
    "status":     "at risk",
    "product":    "Atlas Gateway",
    "owner":      "Platform Engineering",
})
if err != nil {
    log.Fatal(err)
}

_, err = client.Graph.Episode.Add(context.TODO(), graphUUID, &graph.AddEpisodeRequest{
    Type:              graph.V4AddEpisodeRequestTypeJSON.Ptr(),
    Data:              string(projectRecord),
    SourceDescription: zep.String("Project record from the project system"),
    Metadata: map[string]any{
        "source":    "project_system",
        "record_id": "ARG-001",
    },
})
if err != nil {
    log.Fatal(err)
}

_, err = client.Graph.Episode.Add(context.TODO(), graphUUID, &graph.AddEpisodeRequest{
    Type: graph.V4AddEpisodeRequestTypeText.Ptr(),
    Data: "Atlas Gateway release 4.2 requires the regional failover runbook. " +
        "The release cannot start until the backup region passes validation.",
    SourceDescription: zep.String("Atlas Gateway release runbook"),
    Metadata: map[string]any{
        "source":  "product_docs",
        "product": "atlas-gateway",
    },
})
if err != nil {
    log.Fatal(err)
}

incidentResult, err := client.Graph.Episode.Add(context.TODO(), graphUUID, &graph.AddEpisodeRequest{
    Type: graph.V4AddEpisodeRequestTypeMessage.Ptr(),
    Data: "Nina (incident commander): The backup region failed validation. " +
        "Move the Argus release review to Friday.",
    SourceDescription: zep.String("Incident response channel"),
    Metadata: map[string]any{
        "source":      "incident_channel",
        "incident_id": "INC-482",
    },
})
if err != nil {
    log.Fatal(err)
}
incidentEpisodeUUID := *incidentResult.Episode.UUID
```

For an existing corpus or a recurring import, use
[`zep-ingest`](/zep-ingest). For large application-managed imports, use the
[Batch API](/adding-batch-data).

## Wait until the context is searchable

Zep processes episodes asynchronously. Poll the last submitted episode, and
then retry the task query until the search index returns a result. The
[ingestion status guide](/check-data-ingestion-status) explains the processing
order and production alternatives to polling.

**`Python`**

```python Python
from zep_cloud import SearchFilters

query = "What blocks the Project Argus release, and what changed?"
deadline = time.monotonic() + 300

while True:
    episode = client.graph.episode.get(GRAPH_UUID, incident_episode_uuid)
    if episode.processed:
        break
    if time.monotonic() >= deadline:
        raise TimeoutError("The project context did not finish processing.")
    time.sleep(5)

search_deadline = time.monotonic() + 300
while True:
    indexed_results = client.graph.search_edges(
        GRAPH_UUID,
        query=query,
        filters=SearchFilters(episode_uuids=[incident_episode_uuid]),
        limit=10,
    )
    if indexed_results.items:
        break
    if time.monotonic() >= search_deadline:
        raise TimeoutError("The project context is not searchable.")
    time.sleep(5)

results = client.graph.search_edges(GRAPH_UUID, query=query, limit=10)
```

**`TypeScript`**

```typescript TypeScript
const query = "What blocks the Project Argus release, and what changed?";
const deadline = Date.now() + 300_000;
const sleep = (ms: number) =>
  new Promise((resolve) => setTimeout(resolve, ms));

let episode = await client.graph.episode.get(graphUuid, incidentEpisodeUuid);
while (!episode.processed) {
  if (Date.now() >= deadline) {
    throw new Error("The project context did not finish processing.");
  }
  await sleep(5_000);
  episode = await client.graph.episode.get(graphUuid, incidentEpisodeUuid);
}

const searchDeadline = Date.now() + 300_000;
let indexedResults = await client.graph.searchEdges(graphUuid, {
  limit: 10,
  body: { query, filters: { episodeUuids: [incidentEpisodeUuid] } },
});
while (indexedResults.data.length === 0) {
  if (Date.now() >= searchDeadline) {
    throw new Error("The project context is not searchable.");
  }
  await sleep(5_000);
  indexedResults = await client.graph.searchEdges(graphUuid, {
    limit: 10,
    body: { query, filters: { episodeUuids: [incidentEpisodeUuid] } },
  });
}

const results = await client.graph.searchEdges(graphUuid, {
  limit: 10,
  body: { query },
});
```

**`Go`**

```go Go
deadline := time.Now().Add(5 * time.Minute)
for {
    episode, err := client.Graph.Episode.Get(
        context.TODO(),
        graphUUID,
        incidentEpisodeUUID,
    )
    if err != nil {
        log.Fatal(err)
    }
    if episode.Processed != nil && *episode.Processed {
        break
    }
    if time.Now().After(deadline) {
        log.Fatal("the project context did not finish processing")
    }
    time.Sleep(5 * time.Second)
}

limit := 10
query := "What blocks the Project Argus release, and what changed?"
searchDeadline := time.Now().Add(5 * time.Minute)
searchFilters := zep.SearchFilters{
    EpisodeUUIDs: []string{incidentEpisodeUUID},
}
for {
    indexedResults, err := client.Graph.SearchEdges(
        context.TODO(),
        graphUUID,
        &zep.GraphSearchEdgesRequest{
            Limit: &limit,
            Body: &zep.SearchRequest{
                Query:   query,
                Filters: &searchFilters,
            },
        },
    )
    if err != nil {
        log.Fatal(err)
    }
    if len(indexedResults.Results) > 0 {
        break
    }
    if time.Now().After(searchDeadline) {
        log.Fatal("the project context is not searchable")
    }
    time.Sleep(5 * time.Second)
}

results, err := client.Graph.SearchEdges(context.TODO(), graphUUID, &zep.GraphSearchEdgesRequest{
    Limit: &limit,
    Body:  &zep.SearchRequest{Query: query},
})
if err != nil {
    log.Fatal(err)
}
```

## Retrieve context for a task

Search the same Context Graph with the task as the query. The result can contain
facts that connect the project record, product runbook, and incident update.

**`Python`**

```python Python
for edge in results.items or []:
    print(edge.fact, edge.episode_uuids)
```

**`TypeScript`**

```typescript TypeScript
for (const edge of results.data) {
  console.log(edge.fact, edge.episodeUuids);
}
```

**`Go`**

```go Go
for _, edge := range results.Results {
    if edge.Fact != nil {
        fmt.Println(*edge.Fact, edge.EpisodeUUIDs)
    }
}
```

This search confirms that the context is available. To ground a conversational turn in one low-latency call, use [`graph.get_context`](/searching-the-graph). For a complex task, an agent plans its retrieval and makes several targeted tool calls. See [Build an Agent with Zep](/build-an-agent-with-zep).

Treat the results as untrusted reference data when you add them to a model
request. Context supports task completion, but it does not guarantee the
model's output or authorize an action.

## Apply governance

Use [policy-based access control](/policy-based-access-control) to limit which
callers can retrieve the graph or its sources. Retain the episode UUIDs in each
retrieved edge when the application must [trace a fact to its source](/source-traceability).

Zep governs context and API access. Your application must authorize external
actions, such as changing the project status or starting a release.

## Next steps

* [Build an agent that uses Zep tools](/build-an-agent-with-zep).
* [Prepare data for ingestion](/prepare-data-for-ingestion).
* [Shape the graph for a domain](/customizing-graph-structure).
* [Build a custom Context Block](/advanced-context-block-construction).
* [Search a Context Graph](/searching-the-graph).