Skip to main content
The Moda SDK for Node.js (moda-ai) auto-instruments the openai and @anthropic-ai/sdk client libraries: after await Moda.init(...), chat completions and messages — including streamed responses and tool calls — are captured and sent to Moda without changes to your provider code. This page covers installation, configuration, provider coverage, context APIs, manual capture, and running alongside an existing OpenTelemetry setup.

Prerequisites

  • Node.js 18 or later (TypeScript 5.0 or later for type definitions)
  • A Moda API key, created at Settings → Ingestion keys (see Authentication)
  • The openai (4.0+) or @anthropic-ai/sdk (0.18+) package your app already uses

Set up

1

Install the SDK

2

Initialize and make a call

Call Moda.init(apiKey, options) once at startup and await it. Create your provider clients after init.
app.ts
3

Verify in the dashboard

Open Conversations in the dashboard. Within minutes you should see a conversation with ID session_8f2a containing the user message and the assistant response, with the model name and token counts attached.
Moda.init() is async and must be awaited, and it must run before you create provider clients. If init is not awaited, calls made before initialization completes are not captured. The API key argument is required — unlike the Python SDK, Moda.init() does not read MODA_API_KEY from the environment, and it throws [Moda] API key is required if the key is missing.

Configuration

Moda.init() options

Lifecycle

Calling Moda.init() a second time while initialized is a no-op. flush() and shutdown() are safe no-ops before init.

What gets instrumented

The SDK instruments exactly two client libraries:
  • openaichat.completions.create, including streaming and tool calls.
  • @anthropic-ai/sdkmessages.create and messages.stream, including tool use and extended thinking.

Anthropic example

claude.ts

OpenRouter and OpenAI-compatible endpoints

Any endpoint reached through the openai client with a custom baseURL is captured — requests, responses, streaming, and token usage. The SDK records these calls with vendor openai.
openrouter.ts

Conversation and user context

Set a conversation ID to group related calls into one conversation, and a user ID to attribute them to a user.

Global properties and setters

Global context applies to every call in the process. In servers that handle concurrent requests, use the scoped functions instead.

Scoped context

withConversationId, withUserId, and withContext run a callback with context stored in AsyncLocalStorage: the scoped value overrides the global one, follows the callback across await boundaries, is isolated from parallel requests, restores the previous value on exit, and can be nested.
server.ts

Reading context

Set the conversation ID before the first model call of a session — from your session, thread, or run ID — so every call in the session lands in one conversation. Set Moda.conversationId or wrap the run in withConversationId.

Manual capture with withLLMCall

For providers that are not auto-instrumented (direct HTTP calls, custom gateways), wrap the call with Moda.withLLMCall({ vendor, type }, callback). Call span.reportRequest before the call and span.reportResponse after it; errors thrown in the callback propagate normally.
manual.ts

Vercel AI SDK

Pass Moda.getVercelAITelemetry() as experimental_telemetry on AI SDK calls:
Conversation and user context are snapshotted at the moment getVercelAITelemetry() is called. Set Moda.conversationId (or enter withConversationId) before calling it, not after.
See Vercel AI SDK for streaming, structured output, tools, and the full option list (recordInputs, recordOutputs, functionId, metadata).

Using with an existing OpenTelemetry setup

If another SDK has already registered a global TracerProvider — for example Sentry v8+ or dd-trace — Moda.init() detects it and adds Moda’s span processor to that provider instead of creating its own. Both pipelines receive the same spans, and Moda.shutdown() removes only Moda’s processor, leaving the other SDK untouched. Initialize the other SDK first, then Moda:
sentry.ts

Escape hatches

If the external provider samples out or filters Moda’s spans, create a standalone Moda provider that bypasses it:
createModaProvider accepts apiKey, baseUrl, debug, batchSize, and flushInterval. For fully custom OpenTelemetry setups, createModaSpanProcessor(options) returns a span processor (same options) that you can add to your own TracerProvider.

Flushing and serverless

Spans are batched and exported every flushInterval milliseconds (default 5000). Unexported spans are lost when the process exits or a serverless runtime freezes, so:
  • Serverless functionsawait Moda.flush() before each invocation returns:
    handler.ts
  • Long-running servers — flush and shut down on termination:

Troubleshooting

Next steps

  • Vercel AI SDK — full guide for apps built on the ai package.
  • OpenRouter — provider-specific setup details.
  • Reliability — delivery guarantees, limits, and error handling.
  • Data model — how captured messages become conversations and signals.