TypeScript SDK
Best for Node services, workers, and — via a server proxy — browser apps. Requires Node 18+; ESM and CJS builds are both shipped.
1. Setup client
npm install plexus-typescriptCreate a client with your API key, a stable sourceId, and a kind. Plexus
registers the source automatically on first send.
import { Plexus } from "plexus-typescript";
const px = new Plexus({
apiKey: "YOUR_API_KEY",
sourceId: "checkout-api",
kind: "service",
});kind declares what this source is — "service", "web-app", or "worker" —
so the app labels it correctly and shows the right template instead of treating
it as a bare device. The declaration is best-effort after the first successful
send; if it fails, the source just stays generic.
In production, set PLEXUS_API_KEY as an environment variable instead of
hardcoding the key — the SDK picks it up automatically:
const px = new Plexus({ sourceId: "checkout-api", kind: "service" });Get your API key from API Keys in the dashboard.
2. Send metrics
// Single metric
await px.send("request_latency_ms", 42);
// With tags — slice by queue, region, version, etc.
await px.send("queue_depth", 17, { tags: { queue: "emails" } });
// Batch — all points land in one network call
await px.sendBatch([
["temperature", 22.4],
["humidity", 58.1],
["pressure", 1013.2],
]);value accepts numbers, strings, booleans, objects, and arrays. Numbers become
metric points; everything else becomes an event point.
3. Send events
Use event() for things that happen rather than things you measure —
faults, state transitions, deploys. They show up as markers on charts, not
time-series lines.
await px.event("fault", "E-stop triggered");
await px.event("state_change", { from: "IDLE", to: "RUNNING" });
await px.event("deploy", { sha: "abc123" });4. Reliability
Points are sent immediately. On failure the SDK retries with exponential
backoff (4 attempts total by default); when retries are exhausted, points go
into an in-memory buffer (default capacity 10,000, oldest dropped first) and
are prepended to the next send. flush() drains the buffer explicitly;
close() is a final best-effort flush on shutdown.
send() resolves false rather than rejecting on delivery failure.
Telemetry calls are routinely fire-and-forget, so a network blip never becomes
an unhandled rejection. It rejects only on programmer error (invalid metric or
source) or authentication failure (AuthenticationError on 401/403).
const delivered = await px.send("engine_rpm", 3450); // true, or false if buffered
// On shutdown:
await px.close();Retry behavior is configurable via new Plexus({ retry: { maxRetries, baseDelayMs, ... } }).
5. Timestamps
Wire timestamps are milliseconds since epoch. Omit timestamp and the SDK
uses Date.now().
await px.send("temp", 72.5); // now
await px.send("temp", 72.5, { timestamp: new Date("2026-07-01") }); // Date accepted
await px.send("temp", 72.5, { timestamp: 1751328000 }); // seconds — auto-scaled to msNumbers below 1e12 are treated as seconds and auto-scaled — the same rule the
gateway applies, so second-resolution timestamps from other systems land
correctly.
6. Browser apps
Browsers must never hold a plx_ API key: the key grants org-wide write
access, and anything shipped to the browser is public. The pattern is a
two-piece proxy — the page fires fire-and-forget beacons at a first-party
route, and that route (which holds the key in server env) forwards to the
gateway. The key never ships to the browser.
Server side (Next.js route handler shown; works with anything that speaks
web-standard Request/Response — Remix, Hono, Bun, Deno):
// app/api/plexus/route.ts
import { createIngestProxy } from "plexus-typescript/server";
export const POST = createIngestProxy({
sourceId: "web-frontend",
allowMetrics: ["page_view", "signup", "read_seconds"],
});Browser side:
import { createBrowserClient } from "plexus-typescript/browser";
const plexus = createBrowserClient(); // posts to /api/plexus
plexus.track("page_view", 1, { page: "/pricing" });
plexus.event("signup", { plan: "team" });The browser client uses navigator.sendBeacon (survives page unload) with a
keepalive fetch fallback, and never throws. The proxy route is public, so it
sanitizes what it forwards — set allowMetrics to pin the metric names you
expect. It always responds 204; telemetry must never break the calling page.
Environment variables
| Variable | Description | Default |
|---|---|---|
PLEXUS_API_KEY | API key (required) | none |
PLEXUS_GATEWAY_URL | HTTP ingest URL | https://gateway.plexus.company |
PLEXUS_ENDPOINT | Product app (runs + kind declaration) | https://app.plexus.company |
Resolution order: explicit option > environment > ~/.plexus/config.json > default.