TypeScript SDK
The @zoody/sdk package handles batching, deduplication, and retries for Node.js and TypeScript apps.
Install
npm install @zoody/sdkBasic usage
import { Zoody } from "@zoody/sdk";
const zoody = new Zoody({ apiKey: "zo_live_your_api_key" });
zoody.track({
eventName: "feature_used",
email: "user@example.com",
properties: { feature: "dashboard" },
});The SDK queues events and flushes them automatically every 5 seconds or when the batch reaches 50 events. track() never throws - it is fire-and-forget.
Configuration
const zoody = new Zoody({
// Required
apiKey: "zo_live_...",
// Optional
apiUrl: "https://api.zoody.io", // default
batchSize: 50, // flush at this size (max 100)
flushIntervalMs: 5000, // auto-flush every 5s
maxRetries: 3, // retry on network errors
debug: false, // log batch activity
});| Option | Type | Default | Description |
|---|---|---|---|
| apiKey | string | required | Starts with zo_live_ |
| apiUrl | string | https://api.zoody.io | API base URL |
| batchSize | number | 50 | Max 100 |
| flushIntervalMs | number | 5000 | Auto-flush interval (ms) |
| maxRetries | number | 3 | Network error retries |
| debug | boolean | false | Log batch activity |
API
zoody.track(event)
Queue a single event. See Quickstart for event field reference.
zoody.flush()
Send all queued events immediately. Returns a promise. Call this before your process exits, especially in serverless environments.
zoody.shutdown()
Flush remaining events and stop the auto-flush timer. Use on process exit.
Examples
import express from "express";
import { Zoody } from "@zoody/sdk";
const app = express();
const zoody = new Zoody({ apiKey: process.env.ZOODY_API_KEY! });
app.post("/api/reports/generate", (req, res) => {
// Track the event (non-blocking)
zoody.track({
eventName: "report_generated",
email: req.user.email,
properties: { reportType: req.body.type },
});
res.json({ ok: true });
});
// Flush on shutdown
process.on("SIGTERM", async () => {
await zoody.shutdown();
process.exit(0);
});
app.listen(3000);// app/api/track/route.ts
import { Zoody } from "@zoody/sdk";
const zoody = new Zoody({ apiKey: process.env.ZOODY_API_KEY! });
export async function POST(req: Request) {
const { email, action } = await req.json();
zoody.track({ eventName: action, email });
// Flush immediately in serverless (no persistent process)
await zoody.flush();
return Response.json({ ok: true });
}Error handling
try {
await zoody.flush();
} catch (err) {
if (err instanceof ZoodyError) {
switch (err.status) {
case 401: // Invalid or revoked API key
break;
case 402: // Monthly event limit exceeded
console.log(`Limit: ${err.body.limit}, Used: ${err.body.current}`);
break;
case 400: // Validation errors
console.log(err.body.errors);
break;
}
}
}Partial failures
A batch can partially succeed. Rejected events failed validation and are not retried.
const result = await zoody.flush();
// result.accepted = 48
// result.rejected = 2
// result.errors = [{ index: 3, message: "..." }, { index: 7, message: "..." }]Serverless tip
Always call await zoody.flush() before returning from your handler. Serverless runtimes freeze between invocations, so the auto-flush timer won't fire. Without an explicit flush, queued events may be lost.