> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tell.rs/llms.txt
> Use this file to discover all available pages before exploring further.

# Node.js SDK

> Server SDK for Node.js — analytics events and structured logging with batching and delivery.

```bash theme={null}
npm install @tell-rs/node
```

A server-side SDK — you create a client, pass a user ID on every call, and a background worker handles batching and delivery over HTTP + JSONL. Zero dependencies.

**Data collected server-side is trustworthy and tamper-proof.** Unlike the browser SDK, the Node.js SDK doesn't auto-collect device context — you control exactly what properties are sent.

## Quick start

```typescript theme={null}
import { Tell, production, Events } from "@tell-rs/node";

const tell = new Tell(production("a1b2c3d4e5f60718293a4b5c6d7e8f90"));

tell.track("user_123", "Page Viewed", { url: "/home" });
tell.identify("user_123", { name: "Jane", plan: "pro" });

await tell.close();
```

### Verify it works

In development mode (`logLevel: "debug"`), the SDK logs to the console when events are batched and flushed. You can also call `await tell.flush()` and check that no error is reported via `onError`.

## Configuration

Two presets are available:

```typescript theme={null}
// Production — default endpoint, batch 100, flush 10s, error-only logging
const tell = new Tell(production("YOUR_API_KEY"));

// Development — localhost:8080, batch 10, flush 2s, debug logging
const tell = new Tell(development("YOUR_API_KEY"));
```

For custom settings:

```typescript theme={null}
const tell = new Tell({
  apiKey: "YOUR_API_KEY",
  endpoint: "https://collect.internal",
  batchSize: 200,
  flushInterval: 5_000,
  maxRetries: 5,
  closeTimeout: 10_000,
  networkTimeout: 60_000,
  logLevel: "debug",
  gzip: true,
  onError: (e) => console.error("[Tell]", e),
});
```

## Tracking events

Every method takes `userId` as its first parameter. For details on each event type and when to use them, see [Events & Properties](/tracking/events-and-properties).

```typescript theme={null}
tell.track("user_123", "Feature Used", { feature: "dark_mode", enabled: true });

tell.identify("user_123", { name: "Jane", email: "jane@example.com" });

tell.group("user_123", "company_456", { plan: "enterprise" });

tell.revenue("user_123", 49.99, "USD", "order_789", { product: "annual_plan" });

tell.alias("anon_visitor_abc", "user_123");
```

Pass `undefined` when you don't need properties:

```typescript theme={null}
tell.track("user_123", "App Started");
```

### Standard event names

```typescript theme={null}
import { Events } from "@tell-rs/node";

tell.track("user_123", Events.PageViewed, { url: "/pricing" });
tell.track("user_123", Events.UserSignedUp, { source: "organic" });
tell.track("user_123", Events.OrderCompleted, { total: 99.00 });
tell.track("user_123", Events.FeatureUsed, { feature: "export" });
```

Constants cover user lifecycle, revenue, subscriptions, trials, shopping, engagement, and communication events. Custom string names always work too.

### Super properties

```typescript theme={null}
tell.register({ app_version: "2.1.0", env: "production" });

// This track call automatically includes app_version and env
tell.track("user_123", "Click", { button: "submit" });

tell.unregister("env");
```

Event-specific properties override super properties when keys conflict. See [Events & Properties](/tracking/events-and-properties) for more on property patterns.

## Structured logging

```typescript theme={null}
tell.logError("Payment failed", "billing", { error: "card_declined", amount: 9.99 });
tell.logInfo("User signed in", "auth", { method: "oauth" });
tell.logWarning("High memory usage", "api");
```

The `service` parameter defaults to `"app"`. Nine severity levels from `logEmergency` to `logTrace`. See [Logs](/tracking/logs) for the full level reference and when to use each.

Generic logging method:

```typescript theme={null}
tell.log("error", "Something broke", "worker", { job_id: "j_123" });
```

## Redaction & beforeSend

Transform or drop events before they're queued. Return `null` to drop an item. Supports a single function or an array of functions applied in order.

### Drop health-check events

```typescript theme={null}
const tell = new Tell({
  apiKey: "YOUR_API_KEY",
  beforeSend: (event) => {
    if (event.properties?.url === "/health") return null;
    return event;
  },
});
```

### Scrub user PII from traits

```typescript theme={null}
const tell = new Tell({
  apiKey: "YOUR_API_KEY",
  beforeSend: (event) => {
    if (event.traits?.email) {
      return { ...event, traits: { ...event.traits, email: "[REDACTED]" } };
    }
    return event;
  },
  beforeSendLog: [scrubPII, addTimezone], // pipeline of functions
});
```

### `redact()` utility

For common patterns, use the built-in `redact()` factory:

```typescript theme={null}
import { Tell, redact, redactLog, SENSITIVE_PARAMS } from "@tell-rs/node";

const tell = new Tell({
  apiKey: "YOUR_API_KEY",
  beforeSend: redact({
    dropRoutes: ["/health", "/readyz"],
    stripParams: SENSITIVE_PARAMS,
    redactKeys: ["email", "phone", "ssn"],
  }),
  beforeSendLog: redactLog({
    redactKeys: ["password", "credit_card"],
  }),
});
```

You can combine `redact()` with custom hooks in an array:

```typescript theme={null}
const tell = new Tell({
  apiKey: "YOUR_API_KEY",
  beforeSend: [
    redact({ stripParams: SENSITIVE_PARAMS }),
    myCustomHook,
  ],
});
```

<Tip>
  Server-side redaction prevents sensitive data from ever leaving your infrastructure. For defense in depth, also configure [pipeline redaction](/pipeline/transforms/redact) to catch anything that slips through.
</Tip>

## Lifecycle

```typescript theme={null}
await tell.flush();  // Force-send all queued events
tell.resetSession();  // Rotate session ID
await tell.close();   // Flush + shut down
```

Always call `close()` before your process exits to avoid losing buffered events.

## Error handling

Tracking calls (`track`, `identify`, `group`, `revenue`, `alias`) and logging calls never throw. Validation errors and network failures route through the `onError` callback:

```typescript theme={null}
const tell = new Tell({
  apiKey: "YOUR_API_KEY",
  onError: (err) => console.error("[Tell]", err.message),
});
```

Constructor and lifecycle methods (`flush`, `close`) can throw or reject — handle these in your application code.

## Retry behavior

On HTTP send failure (5xx or network error), the SDK retries with exponential backoff: 1-second base delay, 1.5x multiplier, 20% jitter, capped at 30 seconds. After `maxRetries` attempts (default: 3), the batch is dropped and reported via `onError`.

4xx errors (except 413) are not retried — they indicate a client-side issue (e.g. invalid API key). On 413 (payload too large), the SDK automatically halves the batch size for future sends.

## Configuration reference

| Parameter        | Production                 | Development             | Description                          |
| ---------------- | -------------------------- | ----------------------- | ------------------------------------ |
| `endpoint`       | `https://collect.tell.app` | `http://localhost:8080` | HTTP collector URL                   |
| `batchSize`      | 100                        | 10                      | Events per batch before auto-flush   |
| `flushInterval`  | 10,000 ms                  | 2,000 ms                | Time between auto-flushes            |
| `maxRetries`     | 3                          | 3                       | Retry attempts on send failure       |
| `closeTimeout`   | 5,000 ms                   | 5,000 ms                | Max wait on `close()`                |
| `networkTimeout` | 30,000 ms                  | 30,000 ms               | HTTP request timeout                 |
| `maxQueueSize`   | 1,000                      | 1,000                   | Max queued items before oldest drops |
| `gzip`           | false                      | false                   | Compress payloads with gzip          |
| `source`         | `os.hostname()`            | `os.hostname()`         | Log source identifier                |
| `logLevel`       | `error`                    | `debug`                 | SDK log verbosity                    |
| `disabled`       | false                      | false                   | Drop all events locally              |
