NEWMEN

SDKs

TypeScript SDK

@newmen-ai/sdk · ESM and CJS · Node 20+, Bun, Deno, browsers, edge.

The TypeScript SDK is hand-written for ergonomics, with response types generated from the OpenAPI spec so it cannot drift from the wire format. It ships at @newmen-ai/sdk on npm.

Do you even need the SDK?

Note.Probably not to start. Newmen is a drop-in for the OpenAI and Anthropic SDKs — most people just set OPENAI_BASE_URL (or ANTHROPIC_BASE_URL) and keep their existing client. See the migration guide. Reach for @newmen-ai/sdk only when you want first-class helpers for the Pro reliability loop — operations, feedback, datasets, and evaluators — in one typed client.

Install

bashpnpm add @newmen-ai/sdk
# or: npm install @newmen-ai/sdk
# or: yarn add @newmen-ai/sdk
# or: bun add @newmen-ai/sdk

Initialize

typescriptimport Newmen from "@newmen-ai/sdk";

const client = new Newmen({
  apiKey: process.env.NEWMEN_API_KEY,
  // baseURL defaults to https://api.newmen.ai/v1
});

The constructor accepts apiKey, baseURL, timeout, maxRetries, and defaultHeaders.

operation_id (Pro)

Note.Optional. metadata.operation_id is what unlocks the Pro reliability loop — per-operation routing, eval-gated auto-refund, datasets, and training. Calls without one are recorded and billed normally and still get pay-as-you-go pricing and thumbs-down refunds; they just aren’t grouped into an operation.

Passing a new operation_id in metadata auto-registers a lightweight entry. You can start tagging traffic immediately. To attach evaluators, ship gates, or an output schema — which are required for datasets and training — formally define the operation via the console or API. See Operations.

Chat completions

typescriptconst res = await client.chat.completions.create({
  model: "atlas-1",
  messages: [{ role: "user", content: "Summarise this ticket." }],
  metadata: { operation_id: "summarize_ticket" },
});

console.log(res.choices[0].message.content);
console.log("call_id:", res.id); // save this to attach feedback

Streaming

typescriptfor await (const chunk of client.chat.completions.stream({
  model: "atlas-1",
  messages,
  metadata: { operation_id: "summarize_ticket" },
})) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

Streaming returns an async iterator with full AbortController support. The full response is recorded server-side after the stream closes.

Schema version

When you change the output_schema on an operation — adding a field, removing one, changing a type — pass schema_version alongside operation_id so traffic is bucketed by the schema it was generated against. Datasets and evaluators are then scoped to a single version; old and new shapes never pollute the same training set.

typescript// When you change output_schema on an operation, bump schema_version
// so production traffic is bucketed by the schema it was generated against.
await client.chat.completions.create({
  model: "atlas-1",
  messages,
  metadata: {
    operation_id: "summarize_ticket",
    schema_version: "2",           // bumped after adding the "confidence" field
  },
});

Do not bump schema_version for requirement changes — when the quality bar shifts but the output structure stays the same. Instead, update the evaluator rubric and re-run evaluation against the existing dataset. See Requirement changes.

Resources

The SDK exposes the full reliability loop through typed resource classes.

typescript · feedback// Attach a correction to a production call by its call_id.
await client.feedback.create({
  call_id: res.id,
  rating: "thumbs_up",
  correction: "Better output here.",
  tags: ["accuracy:good"],
});
typescript · operations// Formally define an operation before configuring evaluators or ship gates.
const op = await client.operations.create({
  key: "summarize_ticket",
  name: "Support ticket summary",
  description: "One-sentence summary fed into routing. Must not contain PII.",
  output_schema: {
    type: "object",
    properties: {
      summary:  { type: "string", maxLength: 200 },
      priority: { type: "string", enum: ["low", "medium", "high"] },
    },
    required: ["summary", "priority"],
  },
  ship_gates: [
    { evaluator_id: "ev_regex_pii",     min_score: 1.0  },
    { evaluator_id: "ev_judge_quality", min_score: 0.88 },
  ],
});
typescript · datasets// Create a dataset scoped to the operation.
const ds = await client.datasets.create({
  operation_id: "summarize_ticket",
  name: "Tickets v3 — golden candidate",
});

// Bulk-add items from tagged production calls.
await client.datasets.items.add(ds.id, [
  {
    source_call_id: "chatcmpl-abc123",
    input: { messages: [{ role: "user", content: "..." }] },
    expected_output: { summary: "Billing error on invoice #4421.", priority: "high" },
  },
]);

// Promote — throws ShipGatesUnmetError if any gate fails.
await client.datasets.promote(ds.id);

Additional resources — client.evaluators, client.evaluations, client.calls — follow the same resource-class pattern with typed .list(), .get(), .create(), and .update() methods.

Errors

typescriptimport {
  NumenAPIError,
  AuthenticationError,
  ShipGatesUnmetError,
} from "@newmen-ai/sdk";

try {
  await client.datasets.promote(ds.id);
} catch (e) {
  if (e instanceof ShipGatesUnmetError) {
    console.error("gates failed:", e.failedGates);
    return;
  }
  if (e instanceof AuthenticationError) {
    // re-issue key, do not retry
    throw e;
  }
  if (e instanceof NumenAPIError) {
    // base class — http status, body, request id all attached
    throw e;
  }
  throw e;
}

Every SDK error extends NumenAPIError and carries the originating HTTP status, response body, and request id. The SDK retries 429 and 5xx responses twice by default with exponential backoff; opt out via maxRetries: 0.