Skip to main content

Installation

Install with your package manager of choice:

Quick Start: Interaction API

The Interaction API uses a simple three-step pattern:
  1. begin() – Create an interaction and log the initial user input
  2. Update – Optionally call setProperty, setProperties, or addAttachments
  3. finish() – Record the AI’s final output and close the interaction
Using Vercel AI SDK? Check out our automatic integration to track AI events and traces with zero configuration.

Example: Chat Completion

Updating an Interaction

Update an interaction at any point using setProperty, setProperties, or addAttachments:

Resuming an Interaction

If you no longer have the interaction object returned from begin(), resume it with resumeInteraction():
Interactions are subject to a 1 MB event limit. Oversized payloads will be truncated. Contact us if you have custom requirements.

Single-Shot Tracking (trackAi)

For simple request-response interactions, you can use trackAi() directly:
We recommend using begin()finish() for new code to take advantage of partial-event buffering, tracing, and upcoming features like automatic token counts.

Tracking Signals (Feedback)

Signals capture quality ratings on AI events. Use trackSignal() with the same eventId from begin() or trackAi():

Self Diagnostics

Self Diagnostics lets your agent proactively report its own issues — capability gaps, missing context, persistent tool failures — back to your team. Signals appear in Raindrop’s Self Diagnostics dashboard. The easiest way to enable Self Diagnostics is by using wrap() with the Vercel AI SDK:
See the Vercel AI SDK docs for the full wrap() reference. For integrations that don’t use the Vercel AI SDK, or can’t use wrap(), the sections below cover standalone alternatives.

Self Diagnostics Tool

createSelfDiagnosticsTool() creates a tool your agent can call to report issues. It gives you:
  • A reusable execute() handler that tracks an agent signal
  • Adapter-specific tool definitions for Vercel AI SDK, OpenAI SDK, and Anthropic SDK
The four default categories are missing_context, repeatedly_broken_tool, capability_gap, and complete_task_failure. You can replace these with your own.
Self diagnostics requires an eventId to correlate the signal with an interaction. Pass eventId, interaction, or getEventId when creating the tool.
You don’t need to mention self diagnostics in your system prompt — the tool description generated by the SDK already tells the model when and how to call it. Just wire the tool into your LLM call using one of the adapters below. When the agent calls the tool, a signal is tracked on the same eventId:

Supported adapters

createSelfDiagnosticsTool() supports these adapters: Vercel AI SDK:
OpenAI SDK:
Anthropic SDK:

UI guidance

__raindrop_report is an internal tool. If your chat UI renders tool calls, hide this tool call from end users to avoid confusing them with internal diagnostics details.

Manual Reporting

If your agent already has its own way of detecting issues, use selfDiagnose() to report them directly — no LLM tool call needed.

Attachments

Attachments let you include additional context—documents, images, code, or embedded content—with your events. They work with both begin() interactions and trackAi() calls.

Feature Flags

Feature flags record which flags and experiment variants were active when an event happened, so you can compare AI quality across variants in Raindrop. Like properties, they attach to individual events — pass them wherever the event is created (begin() or trackAi()), or set them on a live interaction as your pipeline makes decisions. Two input forms are accepted:

With begin()

Pass the flags that shaped the request — typically the values you just read from your flag provider (LaunchDarkly, Statsig, PostHog, etc.):

Updating a live interaction

Use setFeatureFlag / setFeatureFlags when a flag decision happens mid-interaction — for example, a retrieval strategy chosen after begin():
Flags merge across calls; setting the same flag again overwrites its value.

With trackAi()


Identifying Users


PII Redaction

Read more about how Raindrop handles privacy and PII redaction here. Enable client-side PII redaction when initializing the SDK:

Error Handling

Exceptions are raised when errors occur while sending events to Raindrop. Handle these appropriately in your application.

Configuration

Call await raindrop.close() before your process exits to flush any buffered events.

Projects

Pass projectId to scope every event from a client to a specific project. Under the hood this sets the X-Raindrop-Project-Id header on each request.
Omitting projectId (or passing "default") sends to the default Production project, which is the existing behavior. See Projects for isolation, archival, and the full behavior table.

Multiple projects in one process

When one service runs several agents that should report to different projects — e.g. an Express app serving a support agent and a billing agent — create one Raindrop client per project and reuse them. The instance is the routing decision; there is no shared mutable project state to race on, so concurrent requests handled by different agents route independently.
Tracing across multiple projects. OpenTelemetry auto-instrumentation is shared per process: the first client that enables tracing initializes it, and later clients share the pipeline. begin() scopes every span produced in the current request/task to its client’s project, until the matching finish() (an interaction that is never finished releases its scope once garbage-collected). For LLM calls made outside an interaction, scope them explicitly with asCurrent():
One tracing API key per process: all auto-instrumented spans export under the first tracing-enabled client’s write key. Clients created with a different writeKey (a different workspace) get a warning, and from then on only spans scoped to a client via begin()/asCurrent() are exported — the other workspace’s spans and any unscoped spans are dropped at export rather than delivered to the wrong workspace. Run a second workspace’s tracing in its own process. Single-key processes (the normal case) are unaffected. useExternalOtel is unsupported when more than one write key is used in a single process. Manual events (trackAi, begin/finish, signals, identify) always ship with each client’s own key and are unaffected.
On runtimes without AsyncLocalStorage.enterWith — notably Cloudflare Workers (workerd) — begin() cannot scope auto-instrumented spans: it still records the interaction and its manual withSpan/withTool/events, but ambient span routing is skipped (so it can’t cross-contaminate concurrent requests). Use asCurrent() (or interaction.withSpan(...)) to route auto-instrumented spans there — those are built on AsyncLocalStorage.run, which Workers support.

Tracing

Tracing captures detailed execution information from your AI pipelines—multi-model interactions, chained prompts, and tool calls. This helps you:
  • Visualize the full execution flow of your AI application
  • Debug and optimize prompt chains
  • Understand the intermediate steps that led to a response

Getting Started

Wrap your code with withSpan or withTool on an interaction, and LLM calls inside are automatically captured:
Next.js users: Add raindrop-ai to serverExternalPackages in your config:

Using withSpan

Use withSpan to trace tasks or operations. Any LLM calls within the span are automatically captured:

Using withTool

Use withTool to trace agent actions—memory operations, web searches, API calls, and more:

Manual Tool Tracking

For more control over tool span tracking, use trackTool or startToolSpan.

trackTool – Retroactive Logging

Use trackTool to log a tool call after it has completed:

startToolSpan – Real-Time Tracking

Use startToolSpan to track a tool as it executes:

Module Instrumentation

In some environments, automatic instrumentation may not work due to module loading order or bundler behavior. Use instrumentModules to explicitly specify which modules to instrument:
Anthropic users: You must use a module namespace import (import * as ...), not the default export.
Supported modules: openAI, anthropic, cohere, bedrock, google_vertexai, google_aiplatform, pinecone, together, langchain, llamaIndex, chromadb, qdrant, mcp. For Bedrock, use a namespace import — same pattern as Anthropic above. Cross-region inference profile model IDs (e.g. us.anthropic.claude-*) are supported.

OpenTelemetry Integration

If you already have an OpenTelemetry setup (Sentry, Datadog, Honeycomb, etc.), integrate Raindrop alongside it using useExternalOtel:
Without instrumentModules, getInstrumentations() returns instrumentations for all supported AI libraries. Specify instrumentModules to instrument only specific libraries.

Direct tool spans in stateless handlers

bypassOtelForTools: true changes how tool spans are shipped, but they still read their parent from the active @opentelemetry/api context. trace.setSpanContext() with context.with() is sufficient when the handler, Sentry, and Raindrop share the same OpenTelemetry API singleton and context manager. If a stateless handler cannot rely on ambient context, pass the propagated W3C hexadecimal IDs directly. Both values must be supplied together:
Invalid or incomplete overrides are ignored, and the SDK falls back to the active OpenTelemetry context.

Detached sub-agents (Beta)

A detached sub-agent runs asynchronously in another process, worker, or machine. Unlike resumeInteraction(), which joins the parent’s event, it reports as its own event. The parent mints the child event and sends a hand-off carrier; the worker resumes that event and reports its result.
Beta. The detached sub-agent API surface may change in a minor release. Requires raindrop-ai 0.3.0 or later.
1

Dispatch from the parent

Each process creates its own Raindrop instance. The parent allocates the child event before sending the job:
2

Send the hand-off

Send the job with all of dispatch.headers:
childEventId is allocated before dispatch, so the link is resolvable while the job is queued. The dispatch span is named launch_subagent by default; use toolName to choose another name.interaction.subagent() returns:
  • childEventId — the child event id, allocated before dispatch
  • name — the sub-agent name
  • parentEventId — the parent event id
  • dispatchSpanId — the dispatch span id, when tracing is enabled
  • headers — the native carrier headers
  • langsmithHeaders — the carrier in LangSmith’s header shape
  • carrier — the parsed carrier for non-HTTP transports
Send all returned headers, not only traceparent. The carrier includes traceparent, baggage with raindrop-* fields, and x-raindrop-handoff. The hand-off header wins if standard propagation headers are rewritten or replaced.
With disabled: true, childEventId is still allocated, but carrier is null and both header objects are empty. There is no dispatch span or carrier link.
3

Resume in the worker

The carrier travels with the job. It is JSON-serialisable, so a queue, file, or another process can carry it. The worker’s request.headers below means the received header bag:
For a non-HTTP transport, the first argument accepts the received TraceCarrier too:
A short-lived worker must await raindrop.close() before exit to flush buffered events.
4

Report the outcome

withSubagentRun() wraps the same worker flow:
It resolves with the callback’s return value. If the callback returns without reporting, the SDK aborts the run. If it throws, the SDK reports the failure and rethrows the error.
run.interaction is the child’s ordinary interaction object. It supports withSpan, withTool, trackTool, and the other interaction APIs. Use the object form: withTool({ name: "search_corpus" }, fn). While the run is open, its reverse reference is bound to the execution context. Every span the child emits—including auto-instrumented spans—is linked back to the dispatch. cancel() emits no span, so a cancelled child has no reverse reference on a child span.

Reported outcomes

  • finish({ output }) reports a successful result; the caller supplies the output.
  • fail(reason) marks telemetry as errored and synthesizes output from the reason.
  • cancel(reason) writes a terminal event with raindrop.handoff.terminal=cancelled and synthesized output, but emits no span or errored span.
Status comes from the child’s telemetry, so a failed run needs an errored span. Only a child that reports no outcome at all produces no event, leaving its launcher queued.

Carrier rules

The carrier is authoritative. SubagentResumeOptions values (name, eventId, userId, convoId, event, input, model, or properties) are fallbacks only when no carrier is available. A missing carrier is valid: the child still reports its own event, with run.linked === false and run.parent === null. When a carrier resolves, run.linked === true and run.parent describes the launching context, including the parent event id and dispatch span id. The child’s run.eventId is the childEventId allocated by the parent.
Only accept carriers inside your own trust boundary; an untrusted carrier can attribute a child to another tenant’s event. For non-HTTP transports, pass the carrier as the first argument or use resumeSubagent(undefined, { parent: carrier }). Passing { parent: carrier } as the first argument does not link the run.

Dispatch options


That’s it! You’re ready to explore your events in the Raindrop dashboard. Ping us on Slack or email us if you get stuck!