# 3pm-backend route `eventsQuery`

Paths are relative to the backend's origin (https://fleet.edgecr4ft.net, or your own 3pm-backend), not to this site. Index: https://3pm.edgecr4ft.net/docs/backend/api.md

---

tokens:
  claude: 1232
  o200k: 824
# POST /api/buckets/{clusterId}/events/query (eventsQuery)

The persisted events of the bucket, newest first: a range, exact filters (kind, app, level, event_name…), a limit.

- auth: an account — Authorization: Bearer 3pmu_<token>, or the dashboard's session cookie
- body: EventsQuery (JSON)
- answer: EventsResult (JSON)

## Details

Body `{ range, filters?, limit? }`. `range` is the metrics query's, minus `step` (there is no grid): `from` / `to` as epoch milliseconds or `now-<duration>`. `filters` are exact matches, AND-ed, on the fields the store writes — `kind` (`process`, `daemon_shutdown`, `app`, `action`, `shell`), `node`, `app`, `instance`, `src`, `level`, `event_name`, `fingerprint`, `call` (every line of one action or shell session) — no operators, no regex. `limit` is 1 to 1000 (default 200).

Answer `{ events, truncated, took_ms }`, newest first, each event the record as stored (`_time`, `_msg`, `kind`, and the fields above); `truncated` says more matched than `limit`. Bounds: `limits.eventsQuery` of `GET /api/v1/routes`.

Example — the last 50 error-level application events of one app over a day:

{"range":{"from":"now-24h"},"filters":{"kind":"app","app":"api","level":"error"},"limit":50}

## Bounds

- default_limit: 200
- max_body_bytes: 16384
- max_limit: 1000
- max_range_ms: 2678400000
- max_value_len: 256

## Types

```ts
/** `POST /api/buckets/{id}/events/query`: the persisted events, newest first. */
interface EventsQuery {
  /** `step` is ignored. */
  range: TimeRange;
  filters?: EventFilters;
  /** ≤ 1000, default 200. */
  limit?: number;
}

interface EventsResult {
  /** The stored fields as they are (`_time`, `_msg`, `kind`, `app`, `level`, `a.*`, `exception.*`, `fingerprint`, `repeat`…). */
  events: Record<string, unknown>[];
  /** More than `limit` matched. */
  truncated: boolean;
  took_ms: number;
}

interface TimeRange {
  from: TimeValue;
  /** Default `now`. */
  to?: TimeValue;
  /** `30s`, `5m`, `1h` or milliseconds (what `step_ms` answers); omitted ⇒ derived from `options.max_points`. Both ends are aligned to it. */
  step?: DurationValue;
}

/** Exact matches on the stored fields. */
interface EventFilters {
  /** `process`, `daemon_shutdown`, `app`, `action`, `shell`. */
  kind?: string;
  node?: NodeId;
  app?: string;
  instance?: number;
  src?: string;
  level?: string;
  event_name?: string;
  fingerprint?: string;
  /** A call id: every line of one action or shell session. */
  call?: string;
}

/** Epoch milliseconds, or `now` / `now-<duration>` (`now-1h`, `now-7d`). */
type TimeValue = EpochMs | string;

/** Milliseconds, or `30s` / `5m` / `1h` / `7d` / `2w`. */
type DurationValue = number | string;

/** `3pmn_…` — a node. */
type NodeId = string;

/** Milliseconds since the Unix epoch. */
type EpochMs = number;
```
