Tables and fields
events
AI event records (user message + agent response), with signals, properties, tool calls, errors, token usage, and user context. Recent reprojections can temporarily expose multiple physical versions until background merges settle.project_id(string): Owning project id — selectable and filterable inside the server-injected read scopeevent_id(string): Internal Raindrop database event id — accepted by get_event; use for exact counts and correlating separate event/trace queriestimestamp(datetime): Event time (UTC)event_name(string): Instrumented event name (e.g. chat_message)user_id(string): End-user idconvo_id(string): Conversation idcustom_event_id(string): Caller-supplied public event id — shown as id by get_event when presentai_input(string): User input textai_output(string): AI response textai_model(string): Model that produced the responseai_input_language(string): Detected language of the user inputsignals(array(string)): Signal ids attached to this event — use has(signals, ’…’) or arrayJoin(signals)properties(map(string)): Custom event properties — access with properties.my_keyuser_traits(map(string)): User traits at event time — access with user_traits.planfeature_flags(map(string)): Feature flags at event time — access with feature_flags.my_flaguser_plan(string): User plan at event timeuser_created_at(datetime): When the user was first seenis_internal(boolean): Marked as internal trafficis_excluded_user(boolean): User is on the exclusion listinput_words(number): Word count of ai_inputoutput_words(number): Word count of ai_outputtool_calls(array(json)): Tool calls made during the event (JSON), newest 500 onlytool_names(array(string)): Names of tools called during the event. Stored list keeps only the newest 500 calls; for giant events use the traces tabletool_call_count(number): Number of tool calls in the event, from a stored list that keeps only the newest 500. A value of 500 may be a floor: count TOOL_CALL spans in the traces table for the full totalerror_spans(array(json)): Error spans recorded on the event (JSON)error_names(array(string)): Span names that errored during the eventerror_count(number): Number of error spans on the eventhas_errors(boolean): Event has at least one error spantoken_usage(array(json)): Per-span token usage (JSON)input_tokens(number): Total input tokens across the event’s LLM callsoutput_tokens(number): Total output tokens across the event’s LLM callstotal_tokens(number): Total tokens (input + output) across the event’s LLM calls
traces
OTel span records — LLM calls, tool executions, and agent steps. Correlate with events using event_id in separate queries; RQL does not support JOIN. Recent reprojections can temporarily expose multiple physical versions until background merges settle.project_id(string): Owning project id — selectable and filterable inside the server-injected read scopeevent_id(string): Owning event idtrace_id(string): OTel trace idspan_id(string): OTel span idparent_span_id(string): Parent span idspan_type(string): Span type: LLM_GENERATION, LLM_GENERATION_STREAM, TOOL_CALL, or INTERNAL — filter LLM calls with span_type LIKE ‘LLM%’span_name(string): Span namestatus(string): Span status (OK / ERROR)timestamp(datetime): Span start time (UTC)end_timestamp(datetime): Span end time (UTC)duration_ms(number): Span duration in millisecondsinput_payload(string): Span input payloadoutput_payload(string): Span output payloadinput_tokens(number): Input tokens (LLM spans)output_tokens(number): Output tokens (LLM spans)provider(string): LLM provider (e.g. openai.chat)model(string): LLM model iduser_id(string): End-user id (from the owning event when the span doesn’t carry one)convo_id(string): Conversation id (from the owning event when the span doesn’t carry one)attributes(map(string)): String span attributes — access with attributes.my_attrattributes_num(map(number)): Numeric span attributes — access with attributes_num.my_attr. LLM spans carry their USD cost as attributes_num.raindrop.pricing.cost_usd(absent when the call could not be priced or was ingested before cost stamping)
users
One row per project and end-user from the maintained user-activity rollup — activity, plan, traits, and signals.project_id(string): Owning project iduser_id(string): End-user idevent_count(number): Approximate distinct events from this userfirst_seen(datetime): First event timelast_seen(datetime): Most recent event timeactive_days(number): Distinct days with activityplan(string): Latest known plantraits(map(string)): Latest user traits — access with traits.my_traitsignals(array(string)): All signal ids seen across the user’s events
user_days
One row per project, end-user, and UTC day from the maintained user-activity rollup — daily per-user event counts. The query time range bounds day unless WHERE filters day.project_id(string): Owning project iduser_id(string): End-user idday(datetime): UTC day (date)event_count(number): Approximate distinct events from this user on that day
conversations
One row per project and conversation from the maintained conversation rollup — anchor event, user, activity window, message count, and signals.project_id(string): Owning project idconvo_id(string): Conversation idevent_id(string): Most recent event id, used as the conversation’s clickable anchoruser_id(string): User id on the most recent eventstart_ts(datetime): First event timeend_ts(datetime): Most recent event timemessage_count(number): Rollup event rows; use uniqExact(event_id) on events for an exact distinct countduration_seconds(number): Seconds between the first and most recent eventsignals(array(string)): All materialized signal ids seen in the conversation
Functions
count()(aggregate): Number of rowscountIf(cond)(aggregate): Rows matching a conditioncountDistinct(x)(aggregate): Exact distinct count (alias of uniqExact)uniq(x)(aggregate): Approximate distinct countuniqIf(x, cond)(aggregate): Approximate distinct count for rows matching a conditionuniqExact(x)(aggregate): Exact distinct countuniqExactIf(x, cond)(aggregate): Exact distinct count for rows matching a conditionsum(x)(aggregate): SumsumIf(x, cond)(aggregate): Conditional sumavg(x)(aggregate): AverageavgIf(x, cond)(aggregate): Conditional averagemin(x)(aggregate): MinimumminIf(x, cond)(aggregate): Conditional minimummax(x)(aggregate): MaximummaxIf(x, cond)(aggregate): Conditional maximummedian(x)(aggregate): Approximate medianquantile(0.95)(x)(aggregate): Approximate quantilequantiles(0.5, 0.9, 0.99)(x)(aggregate): Multiple quantilesany(x)(aggregate): Any value in the groupanyIf(x, cond)(aggregate): Any value among matching rowsargMin(x, order)(aggregate): x at the minimum of orderargMax(x, order)(aggregate): x at the maximum of ordergroupArray(x)(aggregate): Collect values into an arraygroupUniqArray(x)(aggregate): Collect distinct values into an arraygroupArrayIf(x, cond)(aggregate): Collect matching values into an arraygroupUniqArrayIf(x, cond)(aggregate): Collect distinct matching values into an arrayargMinIf(x, order, cond)(aggregate): x at the minimum of order among matching rowsargMaxIf(x, order, cond)(aggregate): x at the maximum of order among matching rowsquantileIf(0.95)(x, cond)(aggregate): Approximate quantile over matching rowstopK(10)(x)(aggregate): Approximate top-K most frequent valueswindowFunnel(3600)(toDateTime(timestamp), cond1, cond2, ...)(aggregate): Deepest step reached in order within the window (seconds) per group, 0 to N; wrap traces timestamps in toDateTime()retention(cond1, cond2, ...)(aggregate): Array of 0/1 per condition: cond1 met, and each later condition met along with cond1, per groupsequenceMatch('(?1).*(?2)')(toDateTime(timestamp), cond1, cond2)(aggregate): 1 when the group’s rows match the step pattern in time order; wrap traces timestamps in toDateTime()sequenceCount('(?1).*(?2)')(toDateTime(timestamp), cond1, cond2)(aggregate): Number of non-overlapping pattern matches in time order; wrap traces timestamps in toDateTime()toStartOfMinute(t)(date): Truncate to minutetoStartOfFiveMinutes(t)(date): Truncate to 5-minute buckettoStartOfFifteenMinutes(t)(date): Truncate to 15-minute buckettoStartOfInterval(t, INTERVAL 30 MINUTE)(date): Truncate to an intervaltoStartOfHour(t)(date): Truncate to hourtoStartOfDay(t)(date): Truncate to daytoStartOfWeek(t)(date): Truncate to weektoStartOfMonth(t)(date): Truncate to monthtoDate(t)(date): Cast to datetoDateTime('2026-01-31 00:00:00', 'UTC')(date): Cast a value to a whole-second UTC datetime; direct datetime-column string comparisons are converted automaticallytoDateTime64('2026-01-31 00:00:00.123', 3, 'UTC')(date): Cast a value to a UTC datetime with fractional-second precisiontoHour(t)(date): Hour of day (0-23)toDayOfWeek(t)(date): Day of week (1=Mon)now()(date): Current timestamptoday()(date): Current dateyesterday()(date): Yesterday’s datedateDiff('hour', a, b)(date): Difference between two datetimesdateAdd('day', 1, t)(date): Add an interval — or just use t + INTERVAL 1 DAYdateSub('day', 1, t)(date): Subtract an interval — or just use t - INTERVAL 1 DAYformatDateTime(t, '%Y-%m-%d')(date): Format a datetimetoUnixTimestamp(t)(date): Datetime to unix secondstoUnixTimestamp64Milli(t)(date): DateTime64 to unix millisecondsfromUnixTimestamp(n)(date): Unix seconds to datetimeage('hour', a, b)(date): Full units between datetimeslower(s)(string): Lowercaseupper(s)(string): Uppercaselength(x)(string): String or array lengthcityHash64(x)(string): Deterministic 64-bit hash, for stable pseudo-random ordering or samplingsubstring(s, from, len)(string): Substring (1-based)substr(s, from, len)(string): Alias of substring (1-based)concat(a, b, ...)(string): Concatenate stringstrim(s)(string): Strip whitespacesplitByChar(',', s)(string): Split into an arrayreplaceAll(s, from, to)(string): Replace all occurrencesreplaceRegexpAll(s, re, to)(string): Regex replaceextract(s, re)(string): First regex captureextractAll(s, re)(string): All regex matches as an arraymatch(s, re)(string): Regex testposition(haystack, needle)(string): 1-based substring position (0 = absent)positionCaseInsensitive(haystack, needle)(string): Case-insensitive positionstartsWith(s, prefix)(string): Prefix testendsWith(s, suffix)(string): Suffix testempty(x)(string): True when emptynotEmpty(x)(string): True when not emptyhas(arr, x)(array): Array contains valuepattern_match('<experiment id>', '<pattern key>' [, '<comparison group key>'])(misc): Whether the event matched the pattern in the latest saved sample (events table only). Optionally restrict to its saved comparison group. Includes every saved match; it does not judge events outside the sample.answered('<question id>')(misc): Whether Jev answered yes to a saved question on the event (events table only). Only events a count judged have answers.hasAny(arr, [a, b])(array): Array contains any of the valueshasAll(arr, [a, b])(array): Array contains all of the valuesarrayJoin(arr)(array): Explode an array into rowsarrayCount(x -> cond, arr)(array): Count matching elementsarrayFilter(x -> cond, arr)(array): Filter elementsarrayMap(x -> expr, arr)(array): Transform elementsarrayExists(x -> cond, arr)(array): Any element matchesarrayDistinct(arr)(array): Distinct elementsarrayFlatten(arr)(array): Flatten an array of arraysarrayConcat(a, b, ...)(array): Concatenate arraysarraySort(arr)(array): Sort elementsarraySlice(arr, from, len)(array): Slice an arrayarrayElement(arr, n)(array): Element at index (1-based)arrayStringConcat(arr, sep)(array): Join array to stringindexOf(arr, x)(array): 1-based index of value (0 = absent)JSONExtractString(json, 'key')(json): Extract a string fieldJSONExtractInt(json, 'key')(json): Extract an integer fieldJSONExtractFloat(json, 'key')(json): Extract a float fieldJSONExtractBool(json, 'key')(json): Extract a boolean fieldJSONExtractRaw(json, 'key')(json): Extract raw JSONJSONExtractArrayRaw(json)(json): JSON array to array of raw JSONJSONExtractKeys(json)(json): Keys of a JSON objectJSONHas(json, 'key')(json): JSON field existstoJSONString(x)(json): Serialize a value to JSONmapKeys(m)(map): Keys of a mapmapValues(m)(map): Values of a mapmapContains(m, 'key')(map): Map has keyif(cond, then, else)(conditional): ConditionalmultiIf(c1, r1, c2, r2, else)(conditional): Multi-branch conditionalcoalesce(a, b, ...)(conditional): First non-null valueifNull(x, alt)(conditional): Fallback when nullnullIf(a, b)(conditional): Null when equalassumeNotNull(x)(conditional): Treat as non-nullisNull(x)(conditional): Is nullisNotNull(x)(conditional): Is not nullround(x, n)(math): Roundfloor(x)(math): Floorceil(x)(math): Ceilingabs(x)(math): Absolute valuegreatest(a, b, ...)(math): Largest argumentleast(a, b, ...)(math): Smallest argumentexp(x)(math): e^xlog(x)(math): Natural loglog2(x)(math): Base-2 logsqrt(x)(math): Square rootpow(x, y)(math): x^yintDiv(a, b)(math): Integer divisiontoString(x)(type): Cast to stringtoInt64(x)(type): Cast to integertoFloat64(x)(type): Cast to floattoInt64OrZero(x)(type): Cast to integer, 0 when not numerictoInt32OrZero(x)(type): Cast to integer, 0 when not numerictoInt64OrNull(x)(type): Cast to integer, NULL when not numerictoFloat64OrZero(x)(type): Cast to float, 0 when not numerictoFloat64OrNull(x)(type): Cast to float, NULL when not numerictuple(a, b)(misc): Tuple constructorcityHash64(x)(misc): 64-bit hash of a value, e.g. for samplingbar(x, min, max, width)(misc): Unicode bar chart of a valueformatReadableQuantity(n)(misc): 1234567 -> 1.23 millionformatReadableTimeDelta(sec)(misc): Seconds -> human duration