v3.6.1 · STABLE · NPM
SDKS · TYPESCRIPT

The TypeScript SDK.

Fully typed end to end, isomorphic across Node 20+ and the browser, shipped as ESM. List endpoints are async-iterable, errors are typed classes, and the same webhook verifier our services use is exported for your receiver.

@threatdefendr/sdk · 3.6.1 Node 20+ · browser ESM · typed Apache-2.0

Install

Published to npm as @threatdefendr/sdk. Types are bundled; no @types package needed.

SHELLinstall
$ pnpm add @threatdefendr/sdk # or: npm i @threatdefendr/sdk . yarn add @threatdefendr/sdk

Authenticate

Construct one client with a token and workspace. Keep the token server-side — the SDK is browser-safe for reads against a scoped token, but a privileged token never belongs in client code.

TYPESCRIPTclient.ts
import { Client } from "@threatdefendr/sdk"; const td = new Client({ token: process.env.TD_TOKEN!, // server-side only workspace: "acme-prod", });

Quickstart

Open a case and read a detection back. Inputs and outputs are fully typed, so the compiler catches a wrong field before it ships.

TYPESCRIPTopen a case
const { id } = await td.cases.create({ severity: "HIGH", title: "Service account from corp egress", detections: ["det_2vK4nT"], }); const det = await td.detections.get("det_2vK4nT"); console.log(id, det.title);

Pagination & streaming

List endpoints are async-iterable and page transparently — use for await and stop whenever you like. The live event tail is the same shape, backed by a server-sent stream.

TYPESCRIPTiterate + tail
// auto-paginating: every live detection for await (const det of td.detections.list({ state: "LIVE" })) { console.log(det.id, det.title); } // hold the cursor yourself const page = await td.events.search({ query: "severity:HIGH", limit: 100 }); console.log(page.items.length, page.nextCursor); // live tail for await (const ev of td.events.tail({ severity: "HIGH" })) { handle(ev); }

Retries, idempotency & webhooks

The client backs off on 429 and 5xx; writes take an idempotency key. The package also exports verifyWebhook so your receiver checks signatures exactly as our senders compute them.

TYPESCRIPTresilient writes
const td = new Client({ token: process.env.TD_TOKEN!, workspace: "acme-prod", maxRetries: 5, // backoff on 429 / 5xx timeoutMs: 30_000, }); await td.cases.create( { title: "oncall handoff", severity: "MEDIUM" }, { idempotencyKey: "oncall-2026-06-24" }, );
TYPESCRIPTverify a webhook (express)
import { verifyWebhook } from "@threatdefendr/sdk"; app.post("/td-webhook", (req, res) => { if (!verifyWebhook(SECRET, req.headers, req.rawBody)) { return res.status(401).end(); } res.status(202).end(); // ack fast, then process async queue.add(req.body); });
Verify over the raw body. Pass req.rawBody — the exact bytes received — not the parsed JSON. Re-serializing changes whitespace and key order and the signature will never match.

Errors

Failures throw typed subclasses of ApiError, so an instanceof check routes each case cleanly.

TYPESCRIPThandling failures
import { ApiError, AuthError, RateLimitError } from "@threatdefendr/sdk"; try { await td.contain.isolateHost({ hostId: "h_47193" }); } catch (e) { if (e instanceof RateLimitError) await sleep(e.retryAfter * 1000); else if (e instanceof AuthError) await refresh(); else if (e instanceof ApiError) log.error(e.status, e.requestId); else throw e; }

Where to go next

← PREV Python SDK NEXT → Go SDK