← Back to home

TypeScript SDK

The @zoody/sdk package handles batching, deduplication, and retries for Node.js and TypeScript apps.

Install

npm install @zoody/sdk

Basic 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
});
OptionTypeDefaultDescription
apiKeystringrequiredStarts with zo_live_
apiUrlstringhttps://api.zoody.ioAPI base URL
batchSizenumber50Max 100
flushIntervalMsnumber5000Auto-flush interval (ms)
maxRetriesnumber3Network error retries
debugbooleanfalseLog 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

Express / Node.js
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);
Next.js API Route (serverless)
// 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.