Manual installation
Install the SDK, set the ingest key and load it before your provider clients, without the installer.
On this page
The integration is one import. The SDK reads its own settings from the
environment and captures provider calls at the fetch boundary, so there is no
file to generate, no client to wrap and no option you must pass.
- Package
@tracehatch/sdk@
0.5.0- Runtime
- Node.js 18+, ESM and CommonJS
- Runtime dependencies
- None
- Where
- The server package that makes model calls
Create a project and a key
Create a project, then create or choose an ingest key in project setup — Project settings → API keys manages them too. A key selects both the project and the environment, so use a development key while you experiment. A new secret is shown once; save it then.
Keys for the production environment start with th_live_; every other
environment gets th_test_.
Install the SDK
Install it in the server package that makes your model calls, beside the
package.json your server actually runs.
npm install --save-exact @tracehatch/sdk@0.5.0Commit the updated lockfile. Bun runtime compatibility is unverified.
Save the key in the private server environment
TRACEHATCH_API_KEY=your-project-api-keyKeep the file ignored by Git and never expose the key to browser code — there is no public variant of this variable. Restart your server after changing it.
Load the SDK before your provider clients
Provider clients capture fetch when they are constructed, so the SDK has to be
loaded first. Pick whichever matches how your server starts.
node --import @tracehatch/sdk/auto server.jsThe preload flag runs before your entry file's imports, so clients created at import time are captured too. Keep the entry file and arguments you already run.
Restart and trigger an AI action
Keep your existing provider credentials, model and requests. Restart the server, use one AI feature you already have, then open Traces and confirm the run is the action you just performed.
Next.js
Next.js owns startup, so it loads the SDK from instrumentation.ts rather than a
preload flag.
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
await import("@tracehatch/sdk/auto")
}
}
Save it beside your app or pages directory — inside src when your app uses
src/app. Merge it into an existing register() function if you have one, keep
the key in .env.local, and use the Node.js runtime for AI routes. The Edge
runtime and browser code are not supported.
In short-lived route handlers, hand the final flush to after() so export
finishes after the response:
import { after } from "next/server"
import { flush } from "@tracehatch/sdk"
export async function POST(request: Request) {
const response = await answer(request)
after(() => flush())
return response
}More runtimes: Frameworks and runtimes.
Which providers are captured
- OpenAI
- Captured automatically: chat.completions, responses and embeddings, including streams and background responses. Keep your existing client and model.
- Anthropic
- Captured automatically: messages.create and messages.stream. Keep your existing client and Claude model.
- OpenAI-compatible endpoint
- Captured automatically for OpenAI-compatible hosts such as Azure OpenAI, Groq, Mistral, Together, OpenRouter, DeepSeek, xAI and Ollama. Provider-specific pricing is not guaranteed.
- Gemini / other providers or frameworks
- Frameworks that call a supported API shape through fetch are captured automatically. Other providers use a manual generation span around the real call, or the log endpoint from any language.
Support follows API methods and response shapes, not a fixed model list. A new or custom model is captured; whether its price is known is a separate question, and unpriced calls are counted but show no cost.
What you get without writing anything else
- Runs, generations, tool steps, sessions, end users and prompt versions — see Automatic capture.
- The run's
agentcomes from the nearestpackage.jsonname, and itsreleasefrom the deployment's commit (VERCEL_GIT_COMMIT_SHA,GITHUB_SHAand similar, then gitHEAD).
Add trace(), span() and tool() only where you want a
run named your way, or a step the provider never sees.
What is not captured
- Provider clients constructed before the SDK loaded — they kept the
original
fetch. - A client given its own
fetchoption. - Non-JSON request bodies, such as audio uploads.
- Endpoints outside the supported set.
If nothing arrives, Troubleshooting goes through the causes in order.