Official TypeScript SDK for the NextEngage marketing platform.
pnpm add @nextengage/sdkimport { NextEngage } from "@nextengage/sdk";
// apiKey falls back to the NEXTENGAGE_API_KEY env var, so this works with no args:
const ne = new NextEngage();
// …or pass it explicitly (and optionally override the endpoint for local dev):
// const ne = new NextEngage({
// apiKey: "ne_...",
// baseUrl: "http://localhost:3001",
// });
// Upsert a contact (by email) — accepted for async persistence (see below).
// Attributes MERGE: this sets country/plan without touching other attributes;
// send `{ key: null }` to delete a key.
await ne.contacts.upsert({
email: "ada@example.com",
attributes: { country: "IN", plan: "pro" },
});
// Track a behavioral event (feeds dynamic segments)
await ne.contacts.track({ email: "ada@example.com", name: "purchased", properties: { amount: 49 } });
// Bulk import
await ne.contacts.bulkImport([
{ email: "a@x.com" },
{ email: "b@x.com", attributes: { vip: true } },
]);
// Create + send a campaign
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.
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)) {
// Hosted API: queued for async persistence — no entity returned.
} else {
// Self-hosted with no ingest queue: persisted synchronously, `result` is the Contact.
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.bulkImportis always synchronous.
Failed requests throw NextEngageError with status and parsed body.