Core SDK
@cohorly/core is the transport-agnostic client that every JavaScript SDK is built on. It owns the event queue, batching, flush scheduling, identity, and super properties. Storage and network transport are injected, so the package has zero DOM, Node, or React Native dependencies. Use it directly only when you are building a new platform SDK; most apps should reach for a platform package instead.
Install
bashpnpm add @cohorly/core
Quickstart
You supply a CohorlyStorage (persist the queue and identity) and a
CohorlyTransport (send batches over the network). The package ships
fetchTransport for environments with a global fetch.
typescriptimport { CohorlyClient, fetchTransport } from "@cohorly/core";import type { CohorlyStorage } from "@cohorly/core";const storage: CohorlyStorage = {get: (key) => myStore.get(key) ?? null,set: (key, value) => myStore.set(key, value),remove: (key) => myStore.delete(key),};const client = new CohorlyClient({// apiHost defaults to the hosted Cohorly API; set it to target a different deployment.// Project token: attached to every event's properties and to every// /engage and /alias body so the server routes data to your project.token: "YOUR_PROJECT_TOKEN",storage,transport: fetchTransport, // or a custom CohorlyTransport});client.track("Signed Up", { plan: "pro" });await client.identify("user_123");await client.people.set({ name: "Ada Lovelace" });
API
| Member | Notes |
|---|---|
new CohorlyClient(options) | Options: storage is required, apiHost defaults to the hosted API; transport, token, flushIntervalMs, batchSize, debug, lib, maxQueueSize (1000), maxRetryDelayMs (10 min) are optional. |
track(event, properties?) | Enqueues an event and auto-flushes once batchSize is reached. Returns the TrackedEvent. |
identify(id) | Switches the distinct id synchronously, then sends /alias the first time if the user was previously anonymous - the switch is applied before the network call, so a slow or offline /alias request never delays it or any track() issued right after. The POST is best effort: a failure neither throws nor blocks, since the server also links $device_id/$user_id implicitly from the next event either id emits (see Identity). |
reset() | Assigns a fresh anonymous distinct id. Call this on logout, especially on shared devices. |
register(props) / unregister(key) | Super properties merged into every event. |
people.set / setOnce / increment / unset / delete | Profile updates sent to /engage. |
flush(transportOverride?) | Manually flush the queue; always forces an attempt. |
getDistinctId / isAnonymous / getApiHost | Accessors. |
stop() | Stops the auto-flush timer. |
Delivery and retries
Non-2xx responses surface as a TransportError (status plus optional
retryAfterMs parsed from Retry-After) that drives the retry contract: 429
and 5xx or network errors keep the queue and enter exponential backoff (base
2s, doubling, 10 min cap, +/-20% jitter, honoring Retry-After); 413 halves
the effective batch size and retries smaller without dropping events; 400
drops the rejected batch; 401 keeps the queue at max backoff. A success
resets the failure counter. The persisted queue is bounded at
maxQueueSize (oldest dropped first).
Building for the browser, React, Next.js, or React Native? Use the platform
packages (@cohorly/web, @cohorly/react, @cohorly/nextjs,
@cohorly/react-native), which wire up storage and transport for you.