@getnexorai/sdk
v0.1.44
Published
Official JavaScript SDK for Nexor — AI-led omni-channel contactability for your leads. REST client + embeddable chat widget.
Readme
@getnexorai/sdk
Official JavaScript SDK for Nexor — AI-led omni-channel contactability for your leads. One package, two things:
- REST client.
nexor.init({ apiKey })thennexor.createLead(...),nexor.getLead(...),nexor.sendMessage(...), etc. Works in Node 18+ and modern browsers. - Embeddable chat widget.
nexor.initChat({ workflowId })drops a chat bubble in the bottom-right of any web page. Visitors chat; you get a real Nexor lead + AI-driven conversation; cadence follow-ups happen on WhatsApp/email/voice as usual.
Full API reference: https://docs.getnexor.ai
Install
npm install @getnexorai/sdk
# or
pnpm add @getnexorai/sdk
# or
yarn add @getnexorai/sdkOr use it script-tag-style with no build:
<script src="https://unpkg.com/@getnexorai/sdk/dist/nexor.iife.js"></script>
<script>
Nexor.init({ apiKey: "nxr_live_…" });
</script>Quick start — push a lead from Node
import nexor from "@getnexorai/sdk";
nexor.init({ apiKey: process.env.NEXOR_API_KEY });
const { lead, workflow_run } = await nexor.createLead({
first_name: "Ada",
last_name: "Lovelace",
email: "[email protected]",
phone: "+1 555 0100",
workflow_id: "your-workflow-uuid",
metadata: { plan: "trial", utm_source: "google" },
});
console.log("Created lead:", lead.id);
console.log("Workflow run:", workflow_run?.id);That's it. Nexor's AI agent will now reach out on the channels configured in the workflow.
Quick start — embed a chat widget
<script src="https://unpkg.com/@getnexorai/sdk/dist/nexor.iife.js"></script>
<script>
Nexor.init({ apiKey: "nxr_live_…" });
Nexor.initChat({
workflowId: "your-workflow-uuid",
title: "Talk to our team",
accentColor: "#4f46e5",
systemPrompt: "You are a friendly sales assistant for Acme Corp.",
capture: { fields: ["first_name", "email"] },
});
</script>A chat bubble appears in the bottom-right. Visitors fill the (configurable) capture form, chat with your AI agent, and become real leads in your Nexor account — primed for follow-up on WhatsApp/email/voice.
API
Initialisation
nexor.init({
apiKey: string; // required
baseUrl?: string; // default "https://api.getnexor.ai"
timeoutMs?: number; // default 30_000
maxRetries?: number; // default 2 (retries on 429 + 5xx + network errors)
fetch?: typeof fetch; // override (e.g. for tests / Node <18)
userAgentSuffix?: string;
turnstile?: string | { siteKey: string }; // bot protection for the chat widget — see below
});For multi-tenant / server-side use where a singleton doesn't fit, instantiate the client directly:
import { NexorClient } from "@getnexorai/sdk";
const client = new NexorClient({ apiKey });
await client.createLead({ first_name: "Ada", … });Leads
nexor.createLead(input)
nexor.createLeadsBulk(inputs) // up to 1,000 per call
nexor.updateLead(leadId, updates) // metadata is shallow-merged server-side
nexor.getLead(leadId) // lead + collected variables + per-channel engagement
nexor.getLeadHistory(leadId, { channel, limit, offset })
nexor.syncLeadTags({ lead_id | email, tags }) // tags param is the full active set; [] clears
nexor.isLeadPaused(leadId, { workflow_id })
nexor.stopAutomation(leadId, { workflow_id }) // human takeover
nexor.resumeAutomation(leadId, { workflow_id, resume_cadence })
nexor.listLeadMeetings(leadId, { status })createLead accepts an optional force_first_message: { channel, content, subject?, sender_id? } that pushes the very first message synchronously (WhatsApp or email) rather than waiting for cadence.
Workflows / Campaigns / Templates
nexor.listWorkflows()
nexor.listCampaigns()
nexor.listTemplates({ status, category })Messages
nexor.sendMessage({
lead_id, workflow_id,
channel: "whatsapp" | "email" | "call",
content?: string, // free-form text (whatsapp/email)
html?: string, // email body
subject?: string, // email subject
template_id?: string, // whatsapp template (alternative to content)
components?: unknown, // whatsapp template parameters
begin_message?: string, // first utterance for outbound calls
});Meetings
nexor.createMeeting({ lead_email, title, starts_at, duration_minutes, … });
nexor.createMeetingNotes({ lead_email, notes, transcript_text, summary, action_items, … });Chat widget
nexor.initChat({
// Required
workflowId: string;
// Branding
title?: string; // header text
subtitle?: string; // small line below header
greeting?: string; // first bot message
locale?: string; // UI + timestamp locale; defaults to browser
accentColor?: string; // hex, default "#111827"
accentTextColor?: string; // hex, default "#ffffff"
showBranding?: boolean; // "Powered by Nexor" footer, default true
// Behaviour
position?: "bottom-right" | "bottom-left";
openOnLoad?: boolean;
container?: HTMLElement; // default document.body
// Speed-dial channels (optional) — hover (desktop) or tap (mobile) the
// launcher and it fans out round buttons instead of opening chat right away.
// Only the channels you supply are shown, in the order listed. On phones the
// chat panel opens full-screen with a clear close button in the header.
channels?: {
whatsapp?: string; // "+56912345678" → opens wa.me
whatsappText?: string; // pre-filled WhatsApp message
call?: string; // "+56912345678" → tel:
email?: string; // → mailto:
instagram?: string; // handle "nexor.ai" or full URL
chat?: boolean; // include an "open chat" button, default true
order?: ("whatsapp" | "call" | "email" | "instagram" | "chat")[];
labels?: Partial<Record<"whatsapp"|"call"|"email"|"instagram"|"chat", string>>;
};
// Per-widget prompt overrides — forwarded to the agent on every turn
systemPrompt?: string;
clientPrompt?: string;
// Lead linking
lead?: { first_name?, last_name?, email?, phone?, metadata? }; // pre-known visitor → skips capture
capture?: {
fields?: ("first_name" | "last_name" | "email" | "phone")[]; // default ["first_name", "email"]
mode?: "before" | "skip"; // default "before"
label?: string;
submitLabel?: string;
};
metadata?: Record<string, unknown>; // attached to every turn (page URL, UTM, …)
// Callbacks
onOpen?: () => void;
onClose?: () => void;
onMessage?: ({ role, text }) => void;
onLeadCaptured?: (leadId) => void;
onError?: (err) => void;
});Built-in widget copy follows locale (or navigator.language when omitted).
Spanish-family locales such as es, es-CL, and es-MX automatically use
Spanish labels, placeholders, validation errors, and accessibility text.
Explicit copy options (title, greeting, capture.label, etc.) always win.
Returns a handle:
const chat = nexor.initChat({ … });
chat.open();
chat.close();
chat.toggle();
await chat.send("Hello from JS"); // programmatic message
chat.destroy();
chat.getSessionId(); // stable per-browser id (persisted to localStorage)How it works under the hood
Each turn POSTs to /api/public/chat with:
{
"workflow_id": "…",
"session_id": "sess_…", // generated + persisted by the SDK
"message": "user text",
"system_prompt": "…", // optional override
"client_prompt": "…", // optional override
"lead": { "first_name": "Ada", "email": "[email protected]" },
"metadata": { "page_url": "…" }
}The server matches/creates a web-chat lead, runs the workflow's AI agent, and returns:
{ "success": true, "reply": "…", "session_id": "…", "lead_id": "…", "workflow_run_id": "…" }Subsequent turns reuse the same session and lead — including across page reloads.
Bot protection (Turnstile)
Nothing but a public API key stands between a script and your agent, so automated traffic can drive the widget's flow at machine speed. Pass a Cloudflare Turnstile sitekey to init() and the SDK attaches a fresh, single-use X-Turnstile-Token to every chat turn, which the API verifies before it runs the agent.
<script>
Nexor.init({
apiKey: "nxr_live_…",
turnstile: "0x4AAAAAA…", // or { siteKey: "0x4AAAAAA…" } to tune the widget
});
Nexor.initChat({ workflowId: "your-workflow-uuid" });
</script>Nexor issues the sitekey — it's paired with the secret key held by the API, so it has to be the one Nexor gives you, not one from your own Cloudflare account. Like every Turnstile sitekey it's a public value, meant to ship in client-side code.
What visitors see: normally nothing. The widget runs in interaction-only mode, so it stays invisible and solves in the background. Only when Cloudflare decides a visitor looks automated does it escalate to a checkbox, centred on top of the page; once ticked, the message sends and the widget goes quiet again.
When it can't run: a browser extension blocking challenges.cloudflare.com, an offline network, or a Cloudflare outage all resolve to no token rather than an error — the SDK never throws or hangs on Turnstile. The turn is still sent and the API decides what to do with it, so a blocked visitor degrades to the server's policy instead of a chat that silently stops working. Tokens are single-use, so an automatic retry mints a new one.
It's opt-in. Omit turnstile and behaviour is byte-for-byte what it was: no script loaded, no extra header, no new failure modes. Outside the browser (Node, SSR) the module is a silent no-op regardless of configuration.
Errors
All API failures throw a NexorAPIError (or one of its subclasses):
import { NexorAPIError, NexorAuthError, NexorValidationError, NexorNetworkError } from "@getnexorai/sdk";
try {
await nexor.createLead({ first_name: "" });
} catch (err) {
if (err instanceof NexorValidationError) {
console.error("Bad input:", err.message);
} else if (err instanceof NexorAuthError) {
console.error("API key rejected");
} else if (err instanceof NexorAPIError) {
console.error(`HTTP ${err.status}: ${err.message}`, err.requestId);
} else if (err instanceof NexorNetworkError) {
console.error("Network problem:", err.cause);
}
}429 and 5xx responses are automatically retried with exponential backoff (configurable via maxRetries). Honors Retry-After.
Examples
examples/node-create-lead.mjs— push leads from a Node script.examples/browser-chat.html— embed the chat widget via script tag.
Building from source
npm install
npm run typecheck
npm run build # outputs dist/{index.js,index.cjs,chat.js,chat.cjs,nexor.iife.js} + .d.tsReleasing
Push to main auto-publishes to npm (.github/workflows/publish.yml): bumps the patch, builds, and npm publishes over OIDC — no token, no manual step. Embeds load unpkg.com/@getnexorai/sdk unpinned, so it reaches every widget.
- Ship: merge to
main. - Test without publishing: Actions → publish → Run workflow,
dry_run= true. - Don't
npm publishby hand.
License
MIT
