@nextengage/sdk
Official TypeScript SDK for the NextEngage marketing platform.
Install
pnpm add @nextengage/sdk
Usage
import { NextEngage } from "@nextengage/sdk";
const ne = new NextEngage();
await ne.contacts.upsert({
email: "ada@example.com",
attributes: { country: "IN", plan: "pro" },
});
await ne.contacts.track({ email: "ada@example.com", name: "purchased", properties: { amount: 49 } });
await ne.contacts.bulkImport([
{ email: "a@x.com" },
{ email: "b@x.com", attributes: { vip: true } },
]);
const tpl = await ne.templates.create({ name: "Promo", subject: "Hi {{ email }}", body: "<p>Sale!</p>" });
const campaign = await ne.campaigns.create({ name: "Spring", templateId: tpl.id, fromEmail: "news@acme.com" });
await ne.campaigns.send(campaign.id);
The API key is scoped to a single project; the SDK resolves the project id
automatically from /me.
Async ingest (upsert / track)
On the hosted API, contacts.upsert and contacts.track are accepted onto a
durable queue and persisted asynchronously — the request never waits on the
database, so it stays fast and resilient even under heavy load or a DB blip.
These calls resolve to { accepted: true } (HTTP 202) and do not return the
created contact or a contact id — the write happens a moment later. A
subsequent read (e.g. contacts.list) may briefly not reflect it yet (eventual
consistency).
import { NextEngage, isAccepted } from "@nextengage/sdk";
const result = await ne.contacts.upsert({ email: "ada@example.com" });
if (isAccepted(result)) {
} else {
console.log(result.id);
}
A self-hosted instance without an ingest queue persists synchronously and
returns the entity (Contact / { ok, contactId }) instead — which is why
those methods return a union. bulkImport is always synchronous.
Error handling
Failed requests throw NextEngageError with status and parsed body.