Install and configure
One client, five calls: identify, track, batch, emails.send and preferences.link.
npm install @cowliss/sdkimport { Cow } from "@cowliss/sdk";
const cow = new Cow({ apiKey: process.env.COW_KEY!, sourceId: process.env.COW_SOURCE_ID!, baseUrl: process.env.COW_API_URL,});| Option | Default | Meaning |
|---|---|---|
apiKey |
required | Your org’s API key, the Bearer credential on every request. One key serves every app. |
sourceId |
none | The source calls arrive through when they do not name one. Set it once per app. |
baseUrl |
https://api.cowliss.com |
The origin your Cowliss API is served at, with no trailing slash. |
maxRetries |
2 |
Retries after the first attempt, on network errors and 5xx only. |
timeoutMs |
10000 |
How long one call may take before it is abandoned and retried. |
batchTimeoutMs |
30000 |
The same for one batch, which carries up to 500 rows and is slower by design. |
onError |
none | Called with the CowError of any failed call. This is where a call you do not await gets logged. |
fetch |
global | An injectable fetch, for tests and runtimes that do not have one. |
The two ids you hold
Section titled “The two ids you hold”| Id | Looks like | You use it for |
|---|---|---|
| Source id | src_01j2x8q7… |
Every call. It goes in the client config and on every HTTP body. |
| App id | app_my-app |
Segment definitions, automation triggers, filtering the event feed. |
You send the source; you filter on the app. A process writing into two apps names sourceId per call, or holds one client per app. See Apps and sources.
Calling without waiting
Section titled “Calling without waiting”// You need the result: await it, and handle the error.const profile = await cow.identify({ identifiers: { userId: "user_1" } });
// You do not: let it run, and keep serving the request.cow.track({ identifiers: { userId: "user_1" }, event: "checkout_started" });A call you do not wait for never rejects into your process. If Cowliss is unreachable it gives up on its own, within timeoutMs per attempt, and reports through onError:
const cow = new Cow({ apiKey: process.env.COW_KEY!, sourceId: process.env.COW_SOURCE_ID!, onError: (error) => logger.warn({ err: error }, "cowliss write dropped"),});Wait when the next thing you do depends on the write: a profile you are about to read back, or a segment you expect the event to move.
Errors
Section titled “Errors”A 4xx surfaces as a typed CowError carrying the API’s error code, and is never retried. Network errors, timeouts and 5xx retry with exponential backoff.
error.code is the API’s code and error.status the HTTP status it came with. status is 0 when no answer arrived (network_error), or when the client refused the call before sending it, such as one with no sourceId.
import { CowError } from "@cowliss/sdk";
try { await cow.track({ identifiers: { userId: "user_1" }, event: "checkout_started" });} catch (error) { if (error instanceof CowError && error.code === "over_quota") { // out of credit: the write did not land }}An unknown sourceId, an inactive source, a reserved event name and a future timestamp are all refused rather than quietly dropped. Every code and its status is in API conventions.
A retry never writes or sends twice: track and emails.send carry an idempotency key on every attempt, and identify matches on its identifiers. A retried send whose first attempt went through answers with that send’s deliveryId.
Versions
Section titled “Versions”The package’s CHANGELOG.md lists every version after 0.16.0; the Changelog page is the CLI’s. Before that, 0.15.0 added the source_inactive and version_outdated error codes and 0.15.1 made baseUrl default to https://api.cowliss.com; 0.14.0 and 0.16.0 changed nothing in the SDK.
Without the SDK
Section titled “Without the SDK”Requests and responses use the { data } envelope, and the endpoints are documented in the API reference.
curl -X POST $COW_API_URL/v1/identify \ -H "Authorization: Bearer $COW_KEY" \ -H "Content-Type: application/json" \ -d '{ "data": { "sourceId": "'"$COW_SOURCE_ID"'", "identifiers": { "userId": "user_1" }, "traits": { "email": "[email protected]", "plan": "pro" } } }'A raw client sends its own messageId on a track.