Beta. The Java SDK is at
0.0.3. The wire contract against the Raindrop ingestion API is
stable and verified end-to-end against the live backend on every push, but the public API may
still change in minor ways before 0.1.0. Pin an exact version in your build file.Installation
The SDK requires Java 17+. The artifact coordinates areai.raindrop:raindrop-java.
Gradle:
Raindrop implements AutoCloseable, so try-with-resources flushes and stops the client for
short-lived programs:
Single-Shot Tracking (trackAi)
For simple request-response interactions, call trackAi() directly. At least one of input or
output is required, along with userId and event. It returns the event id (or null if the
event was invalid or the client is disabled):
For new code we recommend the
begin() → finish() interaction API below: it buffers a pending
event immediately and links any spans you create into the same trace.track() for non-AI product events:
Interactions
begin() opens an interaction (a trace) and immediately ships a pending event so it appears in the
Events tab right away. Update it as work progresses, then finish() records the final output:
setInput, setOutput, setModel, setProperty, and setError return the interaction, so they
chain. Call finish() exactly once; it finalizes the event and closes the root span.
Tracing
Tracing captures detailed execution information — multi-step pipelines, tool calls, and subagents — so you can visualize the full execution flow, debug prompt chains, and understand the intermediate steps behind a response.Tool and task spans
Spans created from an interaction inherit itsuserId, convoId, and event, so the dashboard
groups them under the same user, conversation, and event. Use startTool for tool calls and
startTask for other units of work:
setInput, setOutput, setError (which accepts a Throwable or a String), and setAttribute
all return the span for chaining. Always end() a span — a try/finally guarantees it even when
the work throws.
Nested spans & subagents
Spans are nestable: a span started from another span is parented to it, so subagent and tool trajectories form the correct tree in the trace view. UsestartSpan(SpanOptions) for a generic
span (for example a subagent with a custom operationId), and startTool / startTask for the
common cases:
raindrop.startToolSpan("name").
Signals
Signals capture quality ratings on AI events. UsetrackSignal() with the same event id returned
by begin() or trackAi():
Self-diagnostics
selfDiagnose() reports a signal that surfaces in the dashboard’s Self Diagnostics tab — useful
when your agent detects that it is stuck or degraded:
Identifying Users
Attachments
Attachments include extra context — documents, images, code, or embedded content — with an event. Add them to atrackAi() or track() event via the builder:
The dashboard’s attachment viewer renders
TEXT, IMAGE, and IFRAME attachments. CODE
attachments survive ingestion and are searchable, but are not currently displayed in the visual
attachments tab.Configuration
Call
raindrop.close() (or shutdown()) before your process exits to flush buffered events and
spans under a bounded deadline. If writeKey is empty and no local Workshop is configured, the
client is a no-op (zero HTTP calls) rather than an error.
Local Workshop mirroring is enabled when RAINDROP_LOCAL_DEBUGGER is set to a URL, when
RAINDROP_WORKSHOP is a truthy value or URL, or when the SDK can reach the default Workshop daemon
at http://localhost:5899/v1/.
Application Git provenance
raindrop.app.commit_sha, with optional raindrop.app.commit_dirty and
raindrop.app.branch. Application Git discovery runs once on a bounded daemon thread; telemetry,
flush, and shutdown do not wait, so early operations may omit metadata. Branch discovery is opt-in.
Java uses application injection, explicit environment, or the configured application source and
never dependency/JAR metadata as the application revision.
Use .appGit(false) to disable client enrichment without removing canonical properties explicitly
supplied on an operation. .autoDetect(false) or RAINDROP_GIT_AUTO_DETECT=false disables automatic
sources but retains explicit config, canonical operation properties, and RAINDROP_COMMIT_SHA,
RAINDROP_COMMIT_DIRTY, and RAINDROP_BRANCH. RAINDROP_GIT_SOURCE_DIRECTORY selects the AUT source;
RAINDROP_GIT_DETECT_BRANCH=true enables branch discovery. The explicit values are a full 40- or
64-character hexadecimal SHA, true or false dirty state, and an ordinary branch string,
respectively. Existing canonical properties always win,
including invalid values, and an overridden SHA does not acquire inferred branch or dirty state from
another source. The metadata is for reporting/filtering, not simulation checkout selection or
release/SDK identity.
Client config wins over explicit Raindrop environment values, which win over reliable deployment,
application Git, and provider-marked CI sources.
A non-empty .sourceDirectory(...) or RAINDROP_GIT_SOURCE_DIRECTORY is resolved and captured at
client initialization. It exclusively selects the automatic AUT checkout after explicit config and
RAINDROP_* identity values. If that repository is unavailable, metadata stays absent; ambient
build, deployment, and CI identity is not substituted. Discovery remains background work, so pending
operations omit it without blocking.
This API requires the SDK release containing Git reporting. No version is
documented until that release is published.
Projects
Pass.projectId(...) on the builder 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.
^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$). An invalid slug is ignored with a warning and no header is sent, so a typo never breaks ingestion.
Omitting .projectId(...) (or passing "default") sends to the default Production project, which is the existing behavior, so single-project orgs need nothing here. See Projects for isolation, archival, and the full behavior table.
Reliability
Raindrop is designed never to slow down or crash your application. The SDK is a strict no-op when Raindrop is unreachable, slow, rate-limited, or misconfigured:- All network I/O runs on a background daemon thread.
trackAi,track,trackSignal,identify,begin/finish, and span start/end only enqueue work and return immediately on the caller’s thread.flush()is non-blocking too. - Oversized payloads cost the cap, not the payload. Text fields and structured span/property
values are bounded before serialization, so a multi-MB input is
O(cap)on the caller. - Bounded waits everywhere. Connect/read timeouts, a capped retry count with clamped backoff,
and a bounded flush-on-shutdown deadline —
close()returns promptly even against a black-hole endpoint. - Exceptions never escape. Serialization or transport failures are swallowed and rate-limited-logged; your code path is unaffected.
Local development with Workshop
Workshop is the local-first trace debugger. Point the SDK at it (or just run onlocalhost with RAINDROP_WORKSHOP=1) and events + spans stream into the UI with no write key: