@cowliss/sdk
v0.21.0
Published
TypeScript client for the Cowliss ingestion API: identify, track and batch.
Readme
@cowliss/sdk
The TypeScript client for the Cowliss ingestion API. Send identify and
track from your app, and Cowliss keeps the profiles, the events, the
segments, and the journeys that follow from them.
Runs anywhere fetch does: Node 22 and up, Bun, Deno, edge runtimes and
browsers. Zero dependencies, ESM only, fully typed.
Install
npm install @cowliss/sdkUse it
import { Cow } from "@cowliss/sdk";
const cow = new Cow({
apiKey: process.env.COW_API_KEY!,
sourceId: process.env.COW_SOURCE_ID!,
baseUrl: "https://api.cowliss.com",
});
await cow.identify({
identifiers: { userId: "u_1", email: "[email protected]" },
traits: { plan: "pro" },
});
await cow.track({
identifiers: { userId: "u_1" },
event: "checkout.completed",
properties: { amount: 4900, currency: "usd" },
});Send a message from your own code, with a template you pushed or a body written inline. Address an email address for mail nobody can turn off, such as a receipt, or a profile for mail its recipient can, such as a weekly report:
await cow.emails.send({
to: "[email protected]",
from: "Acme <[email protected]>",
template: { key: "receipt", props: { orderId: "A-2291" } },
});
await cow.emails.send({
to: { userId: "shop_1" },
from: "Acme <[email protected]>",
template: { key: "weekly-report", props: { visits: 120 } },
});It resolves once the message has been handed over, and answers with the
delivery id. A message to a profile goes to its email trait, skips anyone
who turned that purpose off, and carries an unsubscribe link. Mail that
follows a rule or a wait belongs in a journey.
Render a pushed template with your own props, exactly as a send to an address would, without sending it:
const { subject, html } = await cow.templates.preview({ key: "receipt", props: { name: "Ada" } });Send someone to their hosted email preferences page from your own "Manage email preferences" button. The link works for an hour, so ask for it on the click:
const { url, expiresAt } = await cow.preferences.link({ to: { userId: "shop_1" } });Every call carries identifiers, a map from kind to value. Cowliss generates
the profile id and resolves the map to it, so a call that carries two
identifiers is what links two profiles. There is no alias call.
identify returns the profile, track returns the stored event, and
batch imports many of both in one request for backfills. Each call names
the source it arrives through: set sourceId once on the client, or pass one
per call when a single process writes into more than one app.
Retries are handled for you. A call that fails to reach Cowliss, or that
Cowliss could not complete, backs off and tries twice more; a generated
messageId travels with every track, so a retry after a lost response never
writes the event twice. Each attempt has a deadline of its own (timeoutMs,
ten seconds by default, thirty for a batch), so a stalled Cowliss cannot
hold up your code. A
call Cowliss rejected is not retried: it throws a CowError carrying the
reason.
You do not have to wait for a call. await cow.track(...) throws on failure,
as it always has; cow.track(...) on its own returns immediately and can
never take your process down with an unhandled rejection. Pass onError to
see the failures a call you did not await would otherwise swallow.
Docs
License
MIT
