npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

zep-sdk

v1.0.0

Published

A complete, production-grade TypeScript client for the Zep AI memory API (Threads, Users, Graph, Context, Batch, Project, Task) with zero runtime dependencies, full type safety, and strict lint/build verification.

Downloads

160

Readme

zep-sdk

A complete, production-grade TypeScript client for the Zep AI memory API — Threads, Users, Context Templates, the temporal Knowledge Graph, Batch ingestion, Projects, Tasks, and Webhook signature verification.

Zero runtime dependencies — built entirely on the standard fetch and Web Crypto APIs, so it works unmodified in Node 20+, browsers, and edge runtimes. Ships dual ESM/CJS builds with full type declarations.

Installation

npm install zep-sdk

Quick start

import { ZepClient } from "zep-sdk";

const client = new ZepClient({ apiKey: "z_..." }); // or set ZEP_API_KEY (Node only)

await client.user.add("user-1", { firstName: "Ada", lastName: "Lovelace" });
await client.thread.create("thread-1", "user-1");

await client.thread.addMessages("thread-1", [
  { role: "user", content: "Hi, I'm Ada. I love analytical engines." },
]);

const { context } = await client.thread.getUserContext("thread-1");
console.log(context);

Configuration

A ZepClient is built once and is safe to share across concurrent requests — it holds no mutable request-scoped state.

const client = new ZepClient({
  apiKey: "z_...",
  baseUrl: "https://api.getzep.com", // default
  maxRetries: 2, // default; 0 disables retries
  timeoutMs: 30_000, // default
  fetch: customFetch, // override for testing or polyfilling
});

If apiKey is omitted, the client falls back to the ZEP_API_KEY environment variable (Node only — browsers/edge runtimes must pass it explicitly, since process.env isn't available there).

Resources

| Property | Covers | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | client.thread | list, create, delete, get/add messages, user context, rolling summary, update message | | client.user | add, get, update, delete, listOrdered, getThreads, summary instructions | | client.context | context-rendering templates (list/get/create/update/delete) | | client.graph | create/get/update/delete/listAllGraphs, add, addBatch, addFactTriple, clone, search, ontology, pattern detection, cache warming | | client.graph.edge | individual fact/edge CRUD | | client.graph.episode | individual episode CRUD + streaming | | client.graph.node | individual node CRUD + connected edges | | client.graph.customInstructions | per-graph extraction instructions | | client.graph.observations | standalone timestamped graph notes | | client.graph.threadSummaries | thread summaries folded into a graph | | client.project | current project settings | | client.task | async task status + polling (await) | | client.batch | bulk ingestion jobs: create → add → process → poll/list | | verifyWebhook | verify incoming Zep webhook deliveries (HMAC-SHA256 / Svix) |

Pagination

Paginated list endpoints have a corresponding listAll/stream-style async generator, so you can use for await...of directly:

for await (const thread of client.thread.listAll({ pageSize: 100 })) {
  console.log(thread.threadId);
}

Error handling

Every rejected promise is either a ZepError (the API responded with a non-2xx status) or the underlying fetch error (network failure, abort, etc). Narrow with isZepError, or the reason-specific helpers:

import { isNotFound, isForbidden } from "zep-sdk";

try {
  await client.thread.getSummary("thread-1");
} catch (err) {
  if (isNotFound(err)) {
    // no summary yet
  } else if (isForbidden(err)) {
    // plan upgrade required
  } else {
    throw err;
  }
}

ZepError also exposes .reason (one of "bad_request", "unauthorized", "forbidden", "not_found", "conflict", "unprocessable_entity", "rate_limited", "internal_server_error", "service_unavailable", "unknown"), .status, .body, and .requestId. 429 and 5xx responses are retried automatically with exponential backoff (maxRetries, default 2); 4xx errors are not retried.

Batch ingestion

The Batch API is a three-step lifecycle — create an empty batch, add up to 500 items per call (up to 50,000 per batch), then start processing:

const batch = await client.batch.create({ metadata: { description: "Support backfill" } });

await client.batch.add(batch.batchId, [
  { type: "graph_episode", userId: "alice", data: "Alice upgraded to Pro.", dataType: "text" },
  {
    type: "thread_message",
    threadId: "alice-support-42",
    content: "Dashboard won't load.",
    role: "user",
    name: "Alice",
  },
]);

await client.batch.process(batch.batchId);
const final = await client.batch.await(batch.batchId);

client.graph.addBatch remains fine for small (≤20 episodes), same-graph, order-independent batches. For everything larger, or batches mixing thread messages and graph episodes across multiple targets, use client.batch. For batches with thousands of items, prefer subscribing to the ingest.batch.completed webhook over polling await. client.thread.addMessagesBatch is deprecated in favor of client.batch.

Webhooks

Zep signs webhook deliveries via Svix (HMAC-SHA256), verified here using the Web Crypto API — no node:crypto import, so this works isomorphically. Endpoint management (creating/rotating endpoints) happens in the Zep dashboard; verifyWebhook verifies deliveries your server receives:

import { verifyWebhook, ZepWebhookVerificationError } from "zep-sdk";

// Read the RAW body - many frameworks parse JSON before your handler
// runs, which breaks verification.
const rawBody = await request.text();

try {
  await verifyWebhook(
    rawBody,
    {
      svixId: request.headers.get("svix-id") ?? "",
      svixTimestamp: request.headers.get("svix-timestamp") ?? "",
      svixSignature: request.headers.get("svix-signature") ?? "",
    },
    signingSecret,
  );
} catch (err) {
  if (err instanceof ZepWebhookVerificationError) {
    return new Response("invalid signature", { status: 400 });
  }
  throw err;
}

const event = JSON.parse(rawBody);

Graph scoping

Every graph operation is scoped to exactly one of a user's graph or a standalone graph, via userScope/graphIdScope:

import { userScope, graphIdScope } from "zep-sdk";

await client.graph.add("text", "Ada loves math.", { scope: userScope("user-1") });
await client.graph.add("text", "Company policy update.", { scope: graphIdScope("company-docs") });

Passing neither or both throws ZepInvalidArgumentError synchronously, before any HTTP request is made.

Development

npm run typecheck
npm run lint
npm run test
npm run test:coverage
npm run build