Skip to main content
SONZAI

Migrating to Sonzai

Bring users, chat history, memories, and documents from your existing AI stack into Sonzai's memory constellation. One overview page plus a tutorial for every common source system.

Why migrate

Most memory systems store strings. Sonzai builds a memory constellation: every incoming piece of content is passed through a fact-extraction pipeline, deduplicated against what the agent already knows, linked into a graph, and surfaced automatically during future conversations. Per-user mood, relationship state, and proactive wakeups all key off that constellation.

If you're coming from a raw transcript store, a RAG index, or a memory-as-a-service vendor, you get three things by migrating:

  1. Automatic fact extraction + supersession — no more manually curating which memories matter.
  2. Per-user state — mood, relationship depth, goals, and habits tracked separately for every user of the same agent.
  3. One import, many agents — migrated users are usable by any agent in the project, not tied to a single thread.

The import surfaces

Sonzai exposes three endpoints for bringing data in. Pick based on shape:

You have...UseEndpoint
Many users at once (chat histories, CRM contacts, memory exports)Batch importPOST /api/v1/agents/{agentId}/users/import
One user with rich metadata + contentPrime userPOST /api/v1/agents/{agentId}/users/{userId}/prime
More content for a user already in SonzaiAdd contentPOST /api/v1/agents/{agentId}/users/{userId}/content
Documents (PDF / DOCX / MD / TXT) for the knowledge baseUpload documentPOST /api/v1/projects/{projectId}/knowledge/documents

Full field-level reference for the priming endpoints lives in API → Priming. The tutorials below show how to shape data from each source system into these endpoints.

How the pipeline works

Every call to an import endpoint is asynchronous and returns a job_id.

Your data
 |
 v
+----------------------+
| Metadata fields      | --> facts generated synchronously (fast)
+----------------------+
+----------------------+      NATS queue
| Content blocks       | ---------------> LLM fact extraction
+----------------------+                       |
                                             v
                                  Memory constellation
                                  (dedup + supersession)
  • Metadata (display_name, company, title, email, phone, custom) is converted into facts on the spot. The response tells you how many.
  • Content blocks are queued to NATS; a worker runs LLM extraction against them and merges the results into the user's memory tree.
  • Job status is pollable at GET /api/v1/agents/{agentId}/users/import/{jobId} for batch imports (or .../users/{userId}/prime/{jobId} for single-user primes). When status is completed, extraction is done.

This means a big batch import returns quickly (HTTP 202 Accepted) and then keeps processing in the background.

Content block shape

Every content-carrying payload uses the same block shape:

{ "type": "chat_transcript", "body": "User: Hi\nAgent: Hey Mia, how's the hiking been?" }
  • type — a free-form label. Common values: "text", "chat_transcript", "note", "bio". The extractor uses the label as a hint but doesn't reject unknown values.
  • body — raw text. Split long transcripts across multiple blocks if you prefer, or pack one session per block. Either works.

The source field

All three priming endpoints accept an optional top-level source string. It is purely provenance metadata — the backend stores it on the import job and the resulting facts. It does not change behaviour. Pick whatever is meaningful to you:

{ "source": "openai_assistants", "users": [ ... ] }

Suggested values used throughout the tutorials below: openai_assistants, zep, mem0, letta, langchain, character_ai, replika, custom_json, crm.

Pick your migration path

A minimal end-to-end example

If you just want to see the whole loop once before diving into a source-specific guide:

import { Sonzai } from "@sonzai-labs/agents";

const client = new Sonzai({ apiKey: process.env.SONZAI_API_KEY! });
const AGENT_ID = "agent_abc";

// 1. Import
const job = await client.agents.priming.batchImport(AGENT_ID, {
source: "custom_json",
users: [
  {
    user_id: "user_123",
    display_name: "Mia Tanaka",
    metadata: { email: "[email protected]", company: "Acme" },
    content: [
      { type: "chat_transcript", body: "User: I'm allergic to peanuts.\nAgent: Noted." },
    ],
  },
],
});
console.log(job.job_id, job.total_users, job.facts_created);

// 2. Poll for completion
let status = await client.agents.priming.getImportStatus(AGENT_ID, job.job_id);
while (status.status !== "completed" && status.status !== "failed") {
await new Promise(r => setTimeout(r, 2000));
status = await client.agents.priming.getImportStatus(AGENT_ID, job.job_id);
}
console.log(status);

Once the job completes, call GET /api/v1/agents/{agentId}/users to confirm the user is there with the extracted facts attached.

What happens to the data

After a successful import you have, per user:

  • An entry in the agent's user list (GET /api/v1/agents/{agentId}/users)
  • A priming metadata record (GET /api/v1/agents/{agentId}/users/{userId}/metadata)
  • Extracted facts in the memory constellation (GET /api/v1/agents/{agentId}/memory/facts?user_id=...)
  • An initial mood state derived from the agent's Big5 baseline
  • Read access to the agent granted automatically

You can now chat with the agent as that user and every extracted fact is available for retrieval.

On this page