Docs

Two ways to route. The embed — one file inside your system, prompts never leave — or the HTTP API. Same engine, same price table, same savings math.

1 · The embed (recommended)

Add the file, wrap your existing model call. Classification runs in your process, the provider call uses your own account, and only savings numbers are reported. Works in Node 18+, serverless functions, edge runtimes and browsers.

import { SmartMem } from "https://getsmartmem.com/sdk/smartmem.js";

const sm = new SmartMem({ apiKey: "sm_live_..." });

// before: openai.chat.completions.create({ model: "gpt-5.6-sol", messages })
const reply = await sm.complete(
  { messages },
  (model) => openai.chat.completions.create({ model, messages })
);

Options

OptionTypeNotes
apiKeystring, requiredYour sm_live_ key. Used only to attribute savings to your dashboard; it never unlocks model access.
endpointstringWhere metrics are posted. Defaults to the origin the script was loaded from.
baselineModelstringWhat savings are measured against. Defaults to openai/gpt-5.6-sol.
fallbackModelstringRetry target if your provider call throws once. Defaults to the baseline model.
allowProvidersstring[]Restrict routing to providers you have keys for (openai, anthropic, google, xai).
qualityFloor0–1Raise the minimum capability beyond what the difficulty band implies.
maxLatencyMsintPrefer models that typically respond within this budget.
reportbooleanSet false for a fully offline embed — routes locally, never sends metrics.
adaptivebooleanDefault true. Learns from your real outcomes: models that keep failing your traffic get skipped, reliable cheap ones get more work. Tip: throw from your call when an answer is unusable so it counts as a miss.
scoutRatenumberDefault 0.05. Share of requests that re-test a cheaper model the router stopped using, so it notices when that model improves.

Methods

Routing only, fully offline

Set report: false and nothing ever leaves your infrastructure — the router still picks the model and prices the decision.

import { SmartMem } from "https://getsmartmem.com/sdk/smartmem.js";

const sm = new SmartMem({ apiKey: "sm_live_...", report: false });

const d = sm.route({ messages });
console.log(d.model, d.savings_pct, d.reason); // e.g. google/gemini-3.1-flash-lite 96.6 ...

What the embed sends us

Exactly one payload per completed call — numbers only:

POST https://getsmartmem.com/api/public/v1/track
Authorization: Bearer sm_live_...
Content-Type: application/json

{
  "routed_model": "google/gemini-3.1-flash-lite",
  "input_tokens": 28,
  "output_tokens": 40,
  "complexity": "simple",
  "task_kind": "extraction"
}

// -> { "ok": true, "savings_usd": 0.0004209, "savings_pct": 96.76 }

2 · HTTP API

Endpoint

POST /api/public/v1

Authenticate with Authorization: Bearer sm_live_…. Mint a key on your dashboard; it is shown once and stored only as a hash.

Request

curl -X POST https://getsmartmem.com/api/public/v1 \
  -H "Authorization: Bearer sm_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Classify this ticket as billing, bug or feature request: ...",
    "output_tokens": 40
  }'
FieldTypeNotes
promptstringThe task text. Classified in memory and discarded — never stored. Optional if you send input_tokens or complexity.
complexity"simple" | "moderate" | "complex"Skip automatic classification and set the band yourself.
task_kindstringcode, reasoning, writing, extraction, classification, chat. Breaks near-ties toward a model that suits the task.
input_tokensintOverrides the estimate from the prompt.
output_tokensintExpected completion length. Defaults to 80% of input.
quality_floor0–1Raise the minimum capability. Never lowers the floor the difficulty band already set.
max_latency_msintPrefer models that typically respond within this budget.
allow_providersstring[]Restrict to openai, anthropic, google and/or xai.
baseline_modelstringWhat savings are measured against. Defaults to openai/gpt-5.6-sol.

Response

{
  "model": "google/gemini-3.1-flash-lite",
  "provider": "google",
  "complexity": "simple",
  "complexity_score": 0.21,
  "task_kind": "extraction",
  "quality_floor": 0.7,
  "estimated": {
    "input_tokens": 28,
    "output_tokens": 40,
    "routed_cost_usd": 0.0000141,
    "baseline_model": "openai/gpt-5.6-sol",
    "baseline_cost_usd": 0.000435,
    "savings_usd": 0.0004209,
    "savings_pct": 96.76
  },
  "alternatives": [...],
  "reason": "Classified as simple (0.21) — needs capability >= 0.70 ...",
  "decision_ms": 3
}

Maximum privacy mode

If you would rather not send prompt text at all, classify on your side and send only the shape of the request:

// No prompt leaves your side at all:
{
  "complexity": "moderate",
  "task_kind": "writing",
  "input_tokens": 1200,
  "output_tokens": 600,
  "quality_floor": 0.85
}

Errors

What gets recorded

Difficulty band, task type, chosen model, baseline model, token counts, both cost figures and the decision time. Nothing else — no prompt text, no completions. The embed cannot send them even by accident: the metrics endpoint accepts numbers only and drops anything else.