Webhooks

Receive real-time notifications when events occur in Zep

Webhooks allow your application to receive real-time notifications when specific events occur in Zep, such as when an episode finishes processing or a batch ingestion completes. Instead of polling for changes, Zep pushes event data directly to your server as HTTP POST requests.

Webhooks are only available on certain plans. See the Zep pricing page for details.

Why use webhooks

Webhooks enable event-driven architectures where your application reacts immediately to changes:

  • Episode processed notifications: Trigger downstream processing when new data is added to a graph
  • Batch completion alerts: Know when large data imports finish so you can start using the data
  • Reduced polling: Eliminate the need to continuously check for updates

Setting up webhooks

Webhooks are configured per project in the Zep Dashboard.

1

Enable webhooks for your project

Navigate to your projectโ€™s settings page and enable webhooks.

2

Create an endpoint

In the Webhooks tab, add a new endpoint by providing:

  • Endpoint URL: The HTTPS URL on your server that will receive webhook events
  • Subscribed events: Select which events you want to receive (e.g., episode.processed, ingest.batch.completed)
3

Save your signing secret

After creating an endpoint, youโ€™ll see a signing secret. Copy and securely store this secretโ€”youโ€™ll need it to verify that incoming webhooks are genuinely from Zep.

Available events

EventDescription
episode.processedFired when an episode finishes processing and is added to the graph
ingest.batch.completedFired when a batch ingestion operation completes, includes the list of episode UUIDs from the batch

Additional events will be added in the future.

Webhook payload schema

Every webhook payload includes the following fields:

FieldDescription
event_nameThe type of event (e.g., episode.processed)
account_idYour Zep account identifier
project_uidThe project where the event occurred
graph_idThe graph identifier*
user_idThe user identifier*
graph_typeEither user or graph, indicating the graph type

*Only one of graph_id or user_id will be present in a given payload, depending on the graph type.

Receiving webhooks

Your webhook endpoint must:

  • Accept HTTP POST requests
  • Return a 2xx status code (200-299) within 15 seconds to acknowledge receipt
  • Disable CSRF protection for the webhook route if your framework enables it by default
1from flask import Flask, request
2
3app = Flask(__name__)
4
5@app.route("/webhooks/zep", methods=["POST"])
6def handle_webhook():
7 payload = request.get_data(as_text=True)
8 headers = request.headers
9
10 # Verify the webhook signature (see next section)
11 # Process the event
12 event = request.json
13 event_name = event.get("event_name")
14
15 if event_name == "episode.processed":
16 # Handle episode processed event
17 pass
18 elif event_name == "ingest.batch.completed":
19 # Handle batch completion event
20 pass
21
22 return "", 200

Verifying webhook signatures

Verifying webhook signatures is highly recommended. Without verification, attackers could send fake HTTP POST requests to your endpoint, potentially causing your application to process fraudulent data.

Zep signs every webhook with a cryptographic signature using your endpointโ€™s signing secret. Verifying this signature ensures that:

  • The webhook genuinely came from Zep
  • The payload hasnโ€™t been tampered with in transit

Zep uses Svix to manage webhooks. The easiest way to verify signatures is with the Svix client libraries.

First, install the Svix library:

$pip install svix

Then verify incoming webhooks:

1from svix.webhooks import Webhook
2
3# Your signing secret from the Zep Dashboard
4WEBHOOK_SECRET = "whsec_..."
5
6def verify_webhook(payload: str, headers: dict) -> dict:
7 wh = Webhook(WEBHOOK_SECRET)
8
9 # This will raise an exception if verification fails
10 return wh.verify(payload, headers)
11
12# In your webhook handler:
13@app.route("/webhooks/zep", methods=["POST"])
14def handle_webhook():
15 payload = request.get_data(as_text=True)
16 headers = {
17 "svix-id": request.headers.get("svix-id"),
18 "svix-timestamp": request.headers.get("svix-timestamp"),
19 "svix-signature": request.headers.get("svix-signature"),
20 }
21
22 try:
23 event = verify_webhook(payload, headers)
24 # Process the verified event
25 return "", 200
26 except Exception as e:
27 print(f"Webhook verification failed: {e}")
28 return "", 400

The verification process requires the raw request body exactly as received. Many web frameworks automatically parse JSON bodies, which can break signature verification. Make sure to access the raw body before any parsing middleware runs.

Manual verification

If you prefer not to use the Svix libraries, you can verify signatures manually using HMAC-SHA256.

Every webhook includes three headers for verification:

  • svix-id: Unique message identifier
  • svix-timestamp: Unix timestamp (seconds since epoch)
  • svix-signature: Base64-encoded signatures (may include multiple, comma-separated)
1

Construct the signed content

Concatenate the svix-id, svix-timestamp, and the raw request body, separated by periods:

{svix-id}.{svix-timestamp}.{raw-body}
2

Calculate the expected signature

Use HMAC-SHA256 with your signing secret (base64-decoded, excluding the whsec_ prefix) to sign the content:

1const crypto = require('crypto');
2
3const signedContent = `${svixId}.${svixTimestamp}.${rawBody}`;
4const secret = "whsec_...";
5const secretBytes = Buffer.from(secret.split('_')[1], "base64");
6const expectedSignature = crypto
7 .createHmac('sha256', secretBytes)
8 .update(signedContent)
9 .digest('base64');
3

Compare signatures

The svix-signature header may contain multiple signatures prefixed with version numbers (e.g., v1,abc123). Remove the version prefix and compare against your calculated signature.

Use constant-time string comparison to prevent timing attacks.

4

Validate the timestamp

Compare the svix-timestamp against your serverโ€™s current time. Reject webhooks with timestamps more than 5 minutes old to prevent replay attacks.

For more details on manual verification, see the Svix documentation on manual verification.

Managing webhooks

The Webhooks tab in the Zep Dashboard provides several management features:

  • Disable/Enable: Temporarily stop receiving events without deleting your endpoint configuration
  • Activity logs: View the history of webhook deliveries and their status
  • Replay messages: Re-send failed webhooks for debugging or recovery
  • Rate limiting: Configure throttling to control the rate of incoming webhooks
  • Delete: Remove an endpoint entirely

Webhook configuration is project-specific. Each project has its own set of webhook endpoints and subscriptions. If you have multiple projects, youโ€™ll need to configure webhooks separately for each one.

Pricing

Webhook deliveries consume credits. See the Zep pricing page for costs.

Best practices

  • Always verify signatures: Treat unverified webhooks as potentially malicious
  • Respond quickly: Return a 2xx response within 15 seconds to avoid timeout retries
  • Process asynchronously: If handling takes longer than a few seconds, acknowledge receipt immediately and process the event in a background job
  • Handle duplicates: Webhooks may occasionally be delivered more than once; use the svix-id header to deduplicate
  • Monitor failures: Check the activity logs in the Dashboard to identify and fix delivery issues

Further reading

For additional information on consuming webhooks: