Quickstart · TypeScript / JavaScript · ~5 min

TypeScript / JavaScript SDK

Behavioral intelligence for any JS runtime — Node 18+, Deno, Bun, and modern browsers. Ships full TypeScript types and a one-line wrap for any of 20+ providers (OpenAI, Anthropic, Gemini, Groq, Ollama, Bedrock, and more).

Just want monitoring? Lead with observe()
The fastest path to a signal is the one-line observe() call — state + risk, no model config, never blocks. The TS SDK below leads with process() (Humane generates the reply); for the observe-only loop use the Python SDK or the raw /observe endpoint.

1. Install

bash
npm i @humane-ai/sdk
# or pnpm add @humane-ai/sdk
# or bun add @humane-ai/sdk

2. Your first call

typescript
import { HumaneClient } from "@humane-ai/sdk";

const client = new HumaneClient({ apiKey: process.env.HUMANE_KEY! });

const r = await client.process({
  userId: "u_8f2",
  message: "honestly I don't know why I keep trying",
});

console.log(r.response);              // AI response shaped by behavioral context
console.log(r.user.mood);             // 0.28
console.log(r.safety.action);         // "PROCEED" | "HOLD" | "BLOCK"
console.log(r.analysis?.reasons);     // ["'struggling' → sadness (negative)"]
process() needs an LLM configured
process() generates a reply, so the tenant must have finished onboarding. If not, the call throws a HumaneError wrapping the 409 (code: "onboarding_required"). Catch it and fall back to your own model or the /observe endpoint, which never requires onboarding.

3. One-line wrap for OpenAI

Drop-in wrapper — every chat.completions.create() call now carries a .humane property with the full behavioral payload.

typescript
import OpenAI from "openai";
import { wrap } from "@humane-ai/sdk";

const client = wrap(new OpenAI(), { humaneKey: process.env.HUMANE_KEY! });

const resp = await client.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "I'm anxious" }],
  user: "u_8f2",
});

console.log(resp.choices[0].message.content);
console.log((resp as any).humane.user.mood);       // 0.31
console.log((resp as any).humane.safety.action);   // PROCEED | BLOCK
Blocked messages never hit OpenAI
If Humane's safety gate returns BLOCK, the wrapper short-circuits before calling OpenAI — no token spend on blocked messages.

4. Streaming

typescript
for await (const ev of client.stream({ userId: "u_8f2", message: "tell me more" })) {
  if (ev.type === "metadata") {
    console.log("mood:", ev.data.user.mood, "safety:", ev.data.safety.action);
  } else if (ev.type === "token") {
    process.stdout.write(ev.data.content);
  } else if (ev.type === "final") {
    // Full text + metadata — reconcile state here.
  }
}

5. Verifying webhook signatures

Webhook receivers MUST verify the X-Humane-Signatureheader. Import the helper from the sub-path @humane-ai/sdk/webhook:

typescript
import express from "express";
import { verifyWebhookSignature } from "@humane-ai/sdk/webhook";

const app = express();

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.header("X-Humane-Signature");
  if (!verifyWebhookSignature(process.env.HUMANE_WEBHOOK_SECRET!, req.body, sig)) {
    return res.status(401).end();
  }
  const event = JSON.parse(req.body.toString("utf8"));
  // handle event ...
  res.sendStatus(200);
});

6. Safe retries

typescript
await client.process({
  userId: "u_8f2",
  message: "hi",
  idempotencyKey: "req_8f3c9a1b4d",   // same key + same body → cached reply
});

7. Durable history

typescript
const history = await client.getHistory("u_8f2", 200);
// → [{ role, content, timestamp }, ...] from the platform DB

await client.clearHistory("u_8f2");  // GDPR right-to-erasure

8. Human takeover

The takeover lifecycle (operator steps into a live conversation, then hands back to the AI) is exposed in the Python SDK and over plain REST. From TypeScript, call the same four endpoints with fetch and your X-API-Key:

typescript
const base = "https://humaneai.vaarak.com/api/sdk";
const headers = { "X-API-Key": process.env.HUMANE_KEY!, "Content-Type": "application/json" };

// Open a session
await fetch(`${base}/takeover/u_8f2`, {
  method: "POST", headers,
  body: JSON.stringify({ operator_id: "op_1", operator_name: "Dana" }),
});
// Send an operator-authored turn (the AI is suppressed while active)
await fetch(`${base}/takeover/u_8f2/messages`, {
  method: "POST", headers,
  body: JSON.stringify({ body: "Hey, it's Dana — I'm here." }),
});
// Hand back to the AI
await fetch(`${base}/takeover/u_8f2/release`, { method: "POST", headers, body: "{}" });

Next steps