TypeScript SDK
Use the TypeScript SDK when your agent runs in Node.js, a Next.js route handler, or a worker. Instrument the boundary of a request once, and every step recorded inside it stays connected. Node.js is required, because the SDK uses async context propagation to keep concurrent runs in separate traces.
Install and configure
npm install tracify-sdk
The SDK reads TRACIFY_API_KEY from the environment. Pass options when you need to override it:
import { traceAgent } from "tracify-sdk";
const runAgent = traceAgent(async (question: string) => callModel(question), {
name: "support-agent",
apiKey: process.env.TRACIFY_API_KEY,
region: "eu",
environment: "production",
release: "2026-10-15",
});
Every option is optional. These are the ones you will use most:
name: the trace name shown in the dashboard. Defaults to the function name.region:"eu"or"us". Defaults to theTRACIFY_REGIONvariable, then"eu". You can also sethostorTRACIFY_HOSTfor a self-hosted deployment.environment,release,deploymentId: compare runs across deployments.sessionId,endUserId,tags: group related traces and find one customer's runs.
A key belongs to one region. If the key and the configured region disagree, the client throws Tracify API key region mismatch when it is created, instead of sending data to the wrong place.
Record steps with span and observe
Inside a traceAgent function, span records a block of work and observe wraps a function. Steps started inside another step become its children, so the trace graph shows which step called which and how loops repeat.
import { observe, span, traceAgent } from "tracify-sdk";
const searchDocs = observe(
async function searchDocs(query: string) {
return search(query);
},
{ type: "tool" },
);
export const answer = traceAgent(async (question: string) => {
for (let turn = 0; turn < 5; turn++) {
const reply = await span(
"think",
() =>
span(
"chat",
async (step) => {
const reply = await callModel(question);
step.set({
model: "your-model-id",
inputTokens: reply.usage.input,
outputTokens: reply.usage.output,
});
return reply;
},
{ type: "llm", input: question },
),
{ type: "agent" },
);
if (reply.done) return reply.text;
await searchDocs(reply.query);
}
});
type is one of agent, llm, tool, retrieval or span. A step ends when its function settles. Its return value becomes the output unless you call step.setOutput(value) first. If the function throws, the error type and message are recorded and the error is rethrown unchanged.
step.set records what the step measured: model, inputTokens, outputTokens and costUsd. Any other key you pass is stored as metadata on the span.
Record finished calls directly
When you already have the numbers, for example from a custom provider client, record a finished call without wrapping it:
import { llmCall, toolCall } from "tracify-sdk";
await llmCall({
input: question,
output: reply.text,
modelId: "your-model-id",
inputTokens: reply.usage.input,
outputTokens: reply.usage.output,
costUsd: 0.002,
latencyMs: 800,
});
await toolCall({
toolName: "web_search",
input: { q: question },
output: results,
latencyMs: 1200,
});
Both calls attach to the step they were made in. Cost is whatever you pass: Tracify does not guess prices. See Track model cost and latency.
Wrap provider clients
If you use the OpenAI or Anthropic SDK, or LangGraph, wrap them once and your existing calls are recorded with model, tokens, latency and errors. See OpenAI, Anthropic and LangGraph.
What the SDK sends
Inputs and outputs are serialized, have obvious secrets removed (bearer tokens, API keys, JWTs and password= style values), and are capped at 10,000 characters. This is a safety net, not a guarantee: do not record anything you would not want stored. Telemetry is sent in the background, and a failed request logs a warning instead of throwing into your agent.
Feedback, scores and prompts
Pass projectId when you construct a TracifyClient to attach end-user feedback and scores to a trace. See Feedback and scores. To load a prompt version without redeploying, see Prompt deployment.