Node SDK
@genie-os/sdk — typed TypeScript client. ESM + CJS. Node 18+, Next.js, Cloud Functions, edge runtimes.
Install
npm install @genie-os/sdkpnpm add @genie-os/sdkyarn add @genie-os/sdkbun add @genie-os/sdkdeno add npm:@genie-os/sdkimport { GenieOS } from '@genie-os/sdk';
// Keys: gos_live_* (production) or gos_test_* (sandbox)
const gos = new GenieOS({ apiKey: process.env.GENIEOS_API_KEY! });The SDK is isomorphic: it works in Node 18+, edge runtimes (Cloudflare
Workers, Vercel Edge), and any browser-shaped environment that exposes
fetch. It has no native dependencies and ships both ESM and CJS builds.
Quick reference
gos.workspace.get();
gos.keys.list();
gos.keys.get(id);
gos.templates.list();
gos.templates.get('welcome');
gos.templates.schema('welcome');
gos.templates.create({ name: 'Welcome', key: 'welcome' });
gos.templates.compose({ prompt: 'Warm welcome for new Glow subscribers' });
gos.templates.render('welcome', { variables: { ... } });
gos.templates.send('welcome', { to: '…', variables: { ... } });
gos.sequences.list();
gos.sequences.get(keyOrId);
gos.sequences.listRuns(keyOrId);
gos.sequences.enroll(keyOrId, { contact, variables });
gos.sequences.runs.get(runId);
gos.sequences.runs.cancel(runId);
gos.events.emit({ type, contact, metadata });
gos.webhooks.list();
gos.webhooks.get(id);
gos.webhooks.create({ url, events });
gos.webhooks.update(id, { ... });
gos.webhooks.delete(id);
gos.brand.list();
gos.brand.get(id);
gos.pages.list();
gos.pages.get(idOrSlug);
gos.pages.compose(idOrSlug, { prompt });
gos.pages.publish(idOrSlug);
gos.pages.unpublish(idOrSlug);
gos.messaging.kit();
gos.messaging.catalog();
gos.messaging.send({ templateKey, to, variables });
gos.messaging.listDeliveries({ templateKey });
gos.social.listNetworks();
gos.social.refreshNetworks();
gos.social.list({ status });
gos.social.get(postId);
gos.social.create({ ... });
gos.social.schedule(postId, { scheduledAt });
gos.social.publish(postId);
gos.social.delete(postId);
gos.social.analytics(postId);
gos.marketing.strategy({ detail: 'summary' });
gos.marketing.patchStrategy({ ... });
gos.marketing.listIcps();
gos.marketing.getIcp(icpId);
gos.marketing.creationDefaults();
gos.marketing.setCreationDefaults({ ... });
gos.creations.list();
gos.creations.get(creationId);
gos.creations.spawn({ brief });
gos.creations.approveStrategy(creationId);
gos.lists.list();
gos.lists.get(listId);
gos.lists.create({ name });
gos.lists.addMembers(listId, contactIds);
gos.approvals.listPolicies();
gos.approvals.listPending();
gos.approvals.managePolicy(surfaceKind, body);
gos.approvals.decide(requestId, { decision });
gos.links.list();
gos.links.get(linkId);
gos.links.utmSuggestions({ field: 'source' });
gos.links.create({
destinationUrl: 'https://…',
utm: { source: 'newsletter', medium: 'email', campaign: 'summer' },
password: 'optional-gate',
});
gos.links.update(linkId, { destinationUrl: 'https://…/v2' });
gos.links.analytics({ linkId, days: 30 });
gos.qr.create({ encodes: { kind: 'shortLink', linkId } });
gos.qr.render(qrId, { format: 'pdf', saveToAssets: true });
gos.connectors.catalog();
gos.connectors.list();
gos.audit.list({ limit: 50 });Every mutating method takes a second options argument:
gos.templates.send(params, {
idempotencyKey: 'order:123',
signal: abortController.signal,
headers: { 'X-My-Trace': traceId },
maxRetries: 3,
});Configuration
import { GenieOS } from '@genie-os/sdk';
const gos = new GenieOS({
apiKey: process.env.GENIEOS_API_KEY!, // gos_live_* or gos_test_*
baseUrl: 'https://api.genieos.pro', // override for self-hosted / staging
maxRetries: 5, // 0 disables retries
initialBackoffMs: 250, // doubled per attempt with jitter
timeoutMs: 30_000, // applies per attempt
fetch: globalThis.fetch, // override (e.g. node-fetch, undici)
userAgent: 'my-app/1.4.2', // appended to the SDK\u2019s UA
});Pass the bearer explicitly (apiKey). Convention across GenieOS tools:
| Var | Purpose |
|---|---|
GENIEOS_API_KEY | Bearer token (gos_live_* or gos_test_*) |
GENIEOS_API_URL | Override the API host (CLI / Python also) |
Idempotency
If idempotencyKey isn\u2019t supplied, the SDK generates one by hashing
the request body. Pass your own for stable, domain-shaped keys:
await gos.templates.send(
{ to, template: 'order_confirm', variables },
{ idempotencyKey: `order:${order.id}:confirm` },
);See Idempotency.
Errors
import {
GenieOS,
GenieOSError,
GenieOSAuthError,
GenieOSValidationError,
GenieOSNotFoundError,
GenieOSRateLimitError,
GenieOSServerError,
GenieOSNetworkError,
} from '@genie-os/sdk';
try {
await gos.templates.send(params);
} catch (err) {
if (err instanceof GenieOSValidationError) {
err.fields.forEach((f) => console.warn(f.path, f.code));
} else if (err instanceof GenieOSRateLimitError) {
await sleep(err.retryAfterSeconds * 1000);
} else {
throw err;
}
}Every error includes requestId, httpStatus, and code. See Errors.
Webhooks
import { verifyWebhook, GenieOSWebhookVerificationError } from '@genie-os/sdk';
import express from 'express';
const app = express();
app.post(
'/genieos',
express.raw({ type: 'application/json' }),
(req, res) => {
try {
const event = verifyWebhook({
payload: req.body, // Buffer
header: req.header('GenieOS-Signature')!,
secret: process.env.GENIEOS_WEBHOOK_SECRET!,
});
handle(event);
res.sendStatus(204);
} catch (err) {
if (err instanceof GenieOSWebhookVerificationError) {
res.sendStatus(400);
} else {
res.sendStatus(500);
}
}
},
);verifyWebhook accepts an optional toleranceSeconds (default 300) to
widen or tighten the replay window.
Typed templates via OpenAPI
There is no built-in codegen binary in @genie-os/sdk yet. Generate
types from the live OpenAPI document instead:
curl -s https://api.genieos.pro/v1/openapi.json > openapi.json
npx openapi-typescript openapi.json -o ./src/api.types.tsOr call gos.templates.schema('welcome') at runtime for the
schema contract of a single template.
Edge runtimes
The SDK works on Vercel Edge, Cloudflare Workers, Deno, and Bun. There\u2019s no Node-specific code in the request path; the only thing that varies is how you supply the API key (env vars vs bindings).
// Cloudflare Worker
export default {
async fetch(req: Request, env: { GENIEOS_API_KEY: string }) {
const gos = new GenieOS({ apiKey: env.GENIEOS_API_KEY });
await gos.events.emit({ type: 'page.viewed', contact: { external_id: '...' } });
return new Response(null, { status: 204 });
},
};Tree-shaking
The SDK is structured by resource — gos.templates, gos.sequences,
etc. — so bundlers can drop the resources you don\u2019t use. A typical
app importing only templates.send ships ~3 KB gzipped.