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.
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); });
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
- API reference — every endpoint these methods wrap.
- Webhook security — the verification model in depth.
- SDK overview — feature parity across runtimes.