The examples are labelled
ts but use no type annotations, so they are valid
JavaScript exactly as written. Swap the import for a require() if your
project is CommonJS; see module format.
The one TypeScript-only section is exported types.Quick start
Every method returns a
Promise. Call initialize() once before your first operation and shutdown() when your process exits. shutdown() is safe to call more than once.API surface: namespaced and flat
The client exposes two call styles against the same instance. Namespaced methods mirror the Python SDK one to one, so the same call shapes work in both languages. They return the raw snake_case response.customer_id depends on your instance. On a B2B instance
(user_context_isolation = strict) it is required. On a B2C instance
(equals_customer) it is not accepted: the user_id is the whole
identity, and a call carrying a customer_id is rejected. The SDK checks this
at the call site once initialize() has learned which mode you are on, so the
mistake surfaces immediately rather than as a silently empty fetch. See
B2C vs B2B.Cross-scope fetch
fetch() queries every scope you supply an identifier for, in parallel, deduplicates by item id, and returns a formatted_context string ready for prompt injection.
InvalidInputError is the exception: a malformed request surfaces instead of degrading to an empty result.
As an LLM tool
as_tool() returns a tool definition with the scope identifiers closed over, so the model chooses the query but never whose memory to read.
Client options
Pass these tonew SynapClient({ ... }):
string
Your Synap API key. Falls back to
SYNAP_API_KEY when omitted.string
API base URL. Falls back to
SYNAP_BASE_URL, then the Synap Cloud default. Set it when you run a self-hosted deployment.string
Optional. Resolved from the API key during
initialize() when omitted. Falls back to SYNAP_INSTANCE_ID.object
{ connect, read, write } in seconds. Defaults to 5s connect and 30s read.object
{ maxAttempts, backoffBase, backoffMax, backoffJitter }. Defaults to 3 attempts with jittered exponential backoff.boolean
default:"false"
Keep the connection warm with a periodic health ping. Worth it for a long-lived process, pointless in serverless where the container is frozen between requests.
function
Supply your own
fetch. Useful for testing or for routing through a proxy.The anticipation stream
The stream lets the server push context bundles ahead of time, which the SDK then serves locally instead of making a billed retrieval. It is opt-in.How it differs from the Python SDK
The two SDKs share one behaviour contract and the same method set, so a Python example translates call for call. Seven things still differ, and every one of them has bitten someone.Method names stay snake_case. There are no camelCase aliases.
Method names stay snake_case. There are no camelCase aliases.
wait_for_completion, record_message, stop_listening, batch_create,
get_context_for_prompt, create_from_file. All of them keep Python’s
spelling so a snippet ports without renaming.memories.waitForCompletion is undefined, not an alias, so the mistake
surfaces as is not a function at the call site rather than at import.{ user_id } and { userId } are equally valid.Context collections can be absent, so default them before reading
Context collections can be absent, so default them before reading
Python returns a Pydantic model whose Reading
facts, preferences, episodes,
emotions and temporal_events always exist, empty at worst. The
namespaced JavaScript surface returns the raw JSON, where a collection the
server omitted is undefined.context.facts.length on a response with no facts throws.The local cache does not survive a restart
The local cache does not survive a restart
Python caches to SQLite (
cache_backend defaults to "sqlite"), so a
restarted process keeps its cache. JavaScript caches in memory.This is a billing difference, not only a latency one: a cache miss is a
metered retrieval. Long-lived servers are barely affected. Short-lived
processes and serverless functions will see more cloud fetches than the
equivalent Python deployment.Branch on error.code, not instanceof
Branch on error.code, not instanceof
Every error carries a stable
.code. With a dual ESM/CJS dependency graph a
consumer can end up holding two copies of the same error class, and
instanceof then fails against the copy it was not built from.instanceof is made to work across copies as well, but .code is the
documented contract and the one to rely on at a package boundary.Five configure() options are accepted and ignored
Five configure() options are accepted and ignored
storage_path, cache_backend, session_timeout_minutes, log_level and
logger exist in ConfigureOptions so a config object can be shared
between the two SDKs without a type error. They do nothing here: there is no
SQLite backend and no global logger to replace.Timeouts and the retry policy are real, and their defaults match Python
exactly: connect 5s, read 30s, write 10s, 3 attempts, backoff base 1s
capped at 10s.flattenContextItems normalises the text field
flattenContextItems normalises the text field
Each memory type names its text differently: facts, preferences and
temporal events use Python has no free-function equivalent. Its
content, episodes use summary, emotions use
context. Concatenating the collections and reading .content across
them is wrong in both languages.JavaScript ships a helper that flattens every collection and normalises the
text into .memory:format_for_prompt is a method
on the cross-scope sdk.fetch() result, not on a scoped fetch.Only the anticipation stream is Node-only
Only the anticipation stream is Node-only
Every HTTP path (ingestion, retrieval, profiles, credits) runs on Edge,
Workers and in the browser.
listen() is gRPC, so it needs Node and the two
optional peers. Importing the SDK in an Edge route stays safe; only the
stream is unavailable there. See
Where it runs.Exported types
Option and response types are exported for annotating your own functions:Typed error handling
Error classes are real classes, soinstanceof narrowing works, and each carries a stable .code and a .transient flag: