Adding Fact Triplets

Manually specify fact/node triplets to add structured relationships to your graph

Overview

You can add manually specified fact/node triplets to the graph. You need only specify the fact, the target node name, and the source node name. Zep will then create a new corresponding edge and nodes, or use an existing edge/nodes if they exist and seem to represent the same nodes or edge you send as input. And if this new fact invalidates an existing fact, it will mark the existing fact as invalid and add the new fact triplet.

The add_fact_triple method returns a task_id that can be used to track the processing status of the operation. See the Check Data Ingestion Status recipe for how to poll task status.

Adding a Fact Triplet

1from zep_cloud.client import Zep
2
3client = Zep(
4 api_key=API_KEY,
5)
6
7result = client.graph.add_fact_triple(
8 user_id=user_id, # Optional: You can use graph_id instead of user_id
9 fact="Paul met Eric",
10 fact_name="MET",
11 target_node_name="Eric Clapton",
12 source_node_name="Paul",
13)
14
15# The result includes a task_id for tracking processing status
16task_id = result.task_id
17print(f"Fact triple task ID: {task_id}")

Retrieving Created UUIDs

Because fact triple creation is asynchronous, the UUIDs for the edge and nodes are not returned immediately. After the task completes, you can retrieve them by calling client.task.get() with the task_id returned from add_fact_triple. The task’s params field will contain edge_uuid, source_node_uuid, and target_node_uuid.

1from zep_cloud.client import Zep
2import time
3
4client = Zep(api_key=API_KEY)
5
6# Add a fact triple
7result = client.graph.add_fact_triple(
8 user_id=user_id,
9 fact="Paul met Eric",
10 fact_name="MET",
11 target_node_name="Eric Clapton",
12 source_node_name="Paul",
13)
14
15task_id = result.task_id
16
17# Poll until the task completes
18while True:
19 task = client.task.get(task_id=task_id)
20
21 if task.status == "succeeded":
22 break
23 elif task.status == "failed":
24 raise Exception(f"Task failed: {task.error}")
25
26 time.sleep(1)
27
28# Retrieve the created UUIDs from task.params
29edge_uuid = task.params.get("edge_uuid")
30source_node_uuid = task.params.get("source_node_uuid")
31target_node_uuid = task.params.get("target_node_uuid")
32
33print(f"Edge UUID: {edge_uuid}")
34print(f"Source Node UUID: {source_node_uuid}")
35print(f"Target Node UUID: {target_node_uuid}")

Advanced Options

Custom types and attributes

You can attach custom scalar attributes to nodes and edges, and optionally reference custom entity and edge types from your ontology to enforce a schema.

  • source_node_labels / target_node_labels — specify a single entity type name from your ontology.
  • fact_name — serves as both the relationship display name and the edge type name looked up in your ontology.
  • source_node_attributes / target_node_attributes / edge_attributes — custom scalar properties on those nodes and edges, usable for filtering in graph searches.

Attribute validation — specifying types is optional. If a label or fact_name is not found in your ontology, attributes pass through without validation:

SituationResult
Label not in ontologyAll node_attributes pass through — no error
fact_name not in ontologyAll edge_attributes pass through — no error
Label in ontology, matching type has no propertiesAll attributes pass through
Label in ontology and type HAS properties definedAttributes validated strictly

When validation activates, every attribute key and value type must match the schema — otherwise the call returns HTTP 400.

1from zep_cloud.client import Zep
2
3client = Zep(api_key=API_KEY)
4
5# Assumes your ontology defines:
6# - Entity type "Person" with properties: { "age": int, "role": text }
7# - Edge type "WORKS_AT" with properties: { "start_year": int, "department": text }
8
9result = client.graph.add_fact_triple(
10 user_id=user_id,
11 fact="Alice works at Acme Corp",
12 fact_name="WORKS_AT", # matches edge type in ontology
13 source_node_name="Alice",
14 source_node_labels=["Person"], # matches entity type "Person" in ontology
15 source_node_attributes={
16 "age": 30, # validated: must be int
17 "role": "Engineer", # validated: must be text
18 },
19 target_node_name="Acme Corp",
20 target_node_labels=["Organization"], # not in ontology — passes through freely
21 edge_attributes={
22 "start_year": 2021, # validated against WORKS_AT schema
23 "department": "Engineering",
24 },
25)

Attribute values must be scalar types: string, number, boolean, or null. Nested objects and arrays are not supported.

Specifying Node UUIDs

You can optionally specify source_node_uuid and/or target_node_uuid to control which nodes are used. The behavior depends on whether you provide a UUID:

ScenarioBehavior
UUID provided and node existsUses the existing node with that UUID
UUID provided but node doesn’t existReturns a 404 error. You must omit the UUID to create a new node.
UUID omittedSearches for an existing node by name. If no match is found, creates a new node with an auto-generated UUID.

See the associated SDK reference for complete details on all available parameters.

Field limits

FieldLimit
factMaximum 250 characters
fact_nameMaximum 50 characters; must be SCREAMING_SNAKE_CASE
source_node_name, target_node_nameMaximum 50 characters each
source_node_summary, target_node_summaryMaximum 500 characters each
valid_at, invalid_atRFC 3339 / ISO 8601 format (e.g., 2024-01-15T10:30:00Z)