Beta. The Java SDK is at
0.0.1. 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():
| Field | Type | Description |
|---|---|---|
eventId | String | The id of the AI event you’re evaluating |
name | String | Signal name (e.g. "thumbs_up", "thumbs_down") |
type | Signal.Type | One of DEFAULT, STANDARD, FEEDBACK, EDIT, AGENT, AGENT_INTERNAL |
sentiment | Sentiment | POSITIVE or NEGATIVE |
comment | String | Merged into properties.comment (for FEEDBACK signals) |
after | String | Merged into properties.after (for EDIT signals) |
attachmentId | String | Optional attachment id to associate the signal with |
properties | Map<String,Object> | Additional metadata |
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:
| Field | Type | Description |
|---|---|---|
type | Attachment.Type | CODE, TEXT, IMAGE, or IFRAME (serialized as type on the wire) |
role | Attachment.Role | INPUT or OUTPUT |
name | String | Optional display name |
value | String | Content or URL |
language | String | Programming language (only meaningful for CODE attachments) |
attachmentId | String | Optional UUID. The backend auto-assigns one if empty; set it explicitly to round-trip with a signal’s attachmentId. |
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
| Builder method | Description | Default |
|---|---|---|
.writeKey(String) | Your Raindrop write key. Empty/missing → SDK becomes a no-op. | — |
.endpoint(String) | Override the ingest endpoint. | https://api.raindrop.ai/v1/ |
.bufferSize(int) | Flush once this many events are buffered. | 50 |
.bufferTimeoutMs(long) | Flush after this delay even if the buffer isn’t full. | 1000 |
.redactPii(boolean) | Redact emails, phone numbers, credit cards, SSNs, and secrets from AI text. | false |
.maxTextFieldChars(int) | Per-field character cap; oversized fields are truncated. | 1_000_000 |
.debugLogs(boolean) | Verbose logging to stderr. | false |
.localWorkshopUrl(String) | Mirror cloud-bound posts to a local Workshop daemon. | auto-detected localhost |
.disableLocalWorkshop() | Disable env/probe-based local Workshop mirroring. | — |
.disabled(boolean) | When true, the SDK sends nothing. | false |
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/.
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: