OOtis Docs

Reference

Customization

Configuration, beforeSend, debug logging, error handling, and SDK exports.

Configuration reference

const otis = initOtis({
  apiKey: "sk-otis-xxx",         // Required (or OTIS_API_KEY env var on server)
  serviceName: "my-app",         // Required
  disabled: false,               // Silently drop all spans (default: false)
  endpoint: "https://...",       // Default: https://ingest.runotis.com
  serverless: true,              // Flush immediately (Lambda, Vercel, Workers)
  beforeSend: (span) => span,    // Pre-send hook for filtering / enrichment
  identifierHashing: true,       // HMAC-SHA256 hashing for all identifiers (default: true)
  browser: {                     // Browser identity via cookies
    autoAnonymousUserId: true,
    autoSessionId: true,
  },
  debug: true,                   // Debug logging
  piiRedaction: {                // PII redaction (enabled by default)
    enabled: true,
    disabledPatterns: ["ipv4"],
  },
});
OptionEnv varDefaultDescription
apiKeyOTIS_API_KEYrequired unless disabledAuthentication token
serviceNamerequired unless disabledService name for span attribution
endpointOTIS_ENDPOINThttps://ingest.runotis.comCollector URL
disabledfalseAll spans silently dropped, no network requests
serverlessfalseImmediate flush for Lambda / Vercel / Workers
beforeSendPre-send hook for filtering / enrichment
debugfalseDebug logging
identifierHashingtrueHMAC identifier pseudonymization with salt derived from API key (see Privacy)
browserBrowser identity config
piiRedaction{ enabled: true }Client-side PII redaction (see Privacy)

Browser note: OTIS_API_KEY and OTIS_ENDPOINT env vars are not available in browsers. Pass them explicitly. The browser initOtis always flushes immediately (equivalent to serverless: true).

beforeSend hook

Full control over every span before export. Runs after PII redaction, so the span you receive has already been scrubbed.

Return the span (optionally modified) to send it, or null to drop it.

const otis = initOtis({
  apiKey: "sk-otis-xxx",
  serviceName: "my-app",
  beforeSend: (span) => {
    // Drop noisy spans
    if (span.name.startsWith("internal.healthcheck")) return null;

    // Add required attributes to every span
    span.attributes = {
      ...span.attributes,
      "deploy.env": "production",
      "app.version": "1.2.3",
    };

    return span;
  },
});

Filter to AI spans only

import { initOtis, isAISpan } from "@runotis/sdk";

const otis = initOtis({
  serviceName: "my-app",
  beforeSend: (span) => isAISpan(span) ? span : null,
});

isAISpan() recognizes these prefixes:

PrefixSource
ai.*otis.wrap() spans (Vercel AI SDK)
gen_ai.*OpenAI, Anthropic direct SDK instrumentations
llm.*LangChain, LlamaIndex instrumentations
otis.*Otis-namespaced spans

Event spans bypass beforeSend

Event spans bypass beforeSend

Spans produced by identifyUser, setUserProperties, setGroupProperties, sendEvent, and sendFeedbackSignal are always forwarded; they bypass beforeSend entirely. These carry user identity and feedback data that's needed regardless of filtering. You can safely return null for everything in beforeSend without losing identity or feedback data.

Debug logging

initOtis({ serviceName: "my-app", debug: true });             // all activity
initOtis({ serviceName: "my-app", debug: "traced,wrap" });    // specific types
initOtis({ serviceName: "my-app", debug: "verbose" });        // full payloads
initOtis({ serviceName: "my-app", debug: "verbose:filter" }); // verbose for specific types
TypeWhat it logs
identifyUseruser ID, group count
setUserPropertiesuser ID, property count
setGroupPropertiesgroup type, group ID, property count
sendEventevent name, attribute count
sendFeedbackSignalevent ID, signal name
tracedfunction name, arg count
wrapwrapped function name
filterspan name, kept/dropped, reason
exportbatch size; verbose: span names

Output format: console.debug("[otis:<type>]", summary, payload?)

Sample output when using beforeSend with debug: "filter":

[otis:filter] { span: 'ai.generateText', kept: true, reason: 'beforeSend' }
[otis:filter] { span: 'http.request', kept: false, reason: 'beforeSend_drop' }
[otis:filter] { span: 'otis_identifyUser', kept: true, reason: 'otis_event' }

Error handling

SDK export errors

By default, errors during span export are logged to console.error. Register a listener for programmatic handling:

otis.on("error", (err) => {
  myErrorReporter.captureException(err);
});

Application errors

See sendException() for surfacing app errors as their own span.

Already using OpenTelemetry?

If your app already has OpenTelemetry instrumentation you want to keep, or you're emitting OTel spans from a non-wrapped AI library, see the OpenTelemetry integration guide. It covers OtisSpanProcessor, OtisExporter, the GenAI attribute conventions Otis recognizes, and how otis.wrap() coexists with an existing tracer provider.

Key types & exports

import {
  // Initialization
  initOtis,
  getOtisInstance,
  shutdownOtis,

  // Core class
  Otis,

  // Wrapping
  type WrapContext,
  type WrapOptions,
  type OtisOptions,
  extractUserTextContent,

  // Chat request helpers
  contextFromChatRequest,
  type ChatRequestContextOptions,
  OTIS_SESSION_COOKIE,    // "__otis_session"
  OTIS_SESSION_HEADER,    // "x-otis-session-id"

  // Span
  OtisSpan,
  getSpanId,

  // Event helpers
  identifyUser,
  setUserProperties,
  setGroupProperties,
  sendEvent,
  sendFeedbackSignal,

  // Context inspection
  currentSpan,
  withCurrent,

  // Application errors
  sendException,

  // Per-call metadata
  eventMetadata,

  // Browser identity
  CookieIdentityManager,
  type BrowserIdentityConfig,

  // Hashing
  isHashedUserId,
  isHashedSessionId,
  isHashedGroupId,

  // Filtering
  isAISpan,

  // PII redaction defaults
  DEFAULT_SCAN_ATTRIBUTES,
  DEFAULT_SCAN_ATTRIBUTE_PREFIXES,

  // Escape hatch
  OtisSpanProcessor,
  OtisExporter,
} from "@runotis/sdk";

// Next.js client
import {
  OtisProvider,
  useOtis,
  OtisPageView,
  withOtisConfig,
} from "@runotis/sdk/next";

// Next.js server
import {
  createOtisInstrumentation,
  getServerOtis,
} from "@runotis/sdk/next/server";

On this page