# GET /api/v2/events

List events

- MCP id: events.index
- Scope: api:read:events
- Permission: events::manage_self
- Rate cost: 1 unit, read bucket. `view=detail` makes it 3. Adds: `with_total=1` +2, each include +1, `q` +2.
- Cache-Control: private, no-store

Returns agenda events, latest `start` first.

A caller sees only their own events unless they hold `events::manage_for_others`, and `filter[user_id]` defaults to the calling user even then; pass `filter[user_id]=me,<id>` to widen it, which requires that permission. Fetch one window of the calendar with `filter[start][gte]` and `filter[start][lte]`. A recurring master is one row: its occurrences are generated from `recurrence_frequency`, `recurrence_interval`, `selected_weekdays` and `recurrence_exceptions` and are not returned individually, except where plnnrly has separated one into a row of its own (those rows carry `parent_event_id`). `meta.page.total` is only computed when you ask for it with `with_total=true`. Deleted events are excluded unless you pass `trashed=with` or `trashed=only`.

## Parameters
- view (string, optional): Which field set to render.
- per_page (integer 1 to 100, optional): Rows per page. At most 25 when `include` names a to-many relation; a larger value is 400 invalid-query-parameter.
- cursor (string, optional): Opaque keyset cursor from a previous page's links.next. Never construct one by hand.
- with_total (boolean, optional): Whether meta.page.total is counted for this request.
- filter[created_at] (timestamp (YYYY-MM-DDThh:mm:ssZ), optional): Creation timestamp. Pair with `sort=id` for a stable full export. Operators: = gte lt.
- filter[end] (timestamp (YYYY-MM-DDThh:mm:ssZ), optional): When the event ends. Operators: = gte lte.
- filter[is_all_day] (boolean, optional): Whether the event occupies whole days rather than a time range.
- filter[is_recurring] (boolean, optional): Whether the event is the master of a recurring series.
- filter[occurrence_date] (date (YYYY-MM-DD), optional): The series date a separated occurrence replaces. Operators: = gte lte null.
- filter[parent_event_id] (integer, optional): Master event of a separated occurrence. `null` selects everything that is not a separated occurrence. Operators: = in null.
- filter[recurrence_end] (timestamp (YYYY-MM-DDThh:mm:ssZ), optional): Last moment a series may generate an occurrence. `null` selects the open-ended series. Operators: = gte lte null.
- filter[recurrence_frequency] (string, optional): The unit a recurring series repeats in. Operators: = ne in.
- filter[recurrence_pattern] (string, optional): How a monthly or yearly series positions its occurrence within the month. Operators: = ne in.
- filter[recurrence_start] (timestamp (YYYY-MM-DDThh:mm:ssZ), optional): First moment a series may generate an occurrence. Operators: = gte lte null.
- filter[start] (timestamp (YYYY-MM-DDThh:mm:ssZ), optional): When the event starts. Combine gte and lte to fetch one window of the agenda. Operators: = gte lte.
- filter[task_id] (integer, optional): Kanban task the event is planned against. `null` selects the events with no task, which are the ones no time can be logged on. Operators: = in null.
- filter[updated_at] (timestamp (YYYY-MM-DDThh:mm:ssZ), optional): Last-write timestamp. Use for incremental sync. Operators: = gte lt.
- filter[user_id] (integer, optional): Whose agenda the event is on. Accepts the literal `me`, and defaults to the calling user when omitted. Naming another user requires the `events::manage_for_others` permission. Operators: = in.
- q (string, optional): Free-text search over `title`. At most one substring match per request: `q` together with a `contains` filter is 400 invalid-query-parameter.
- sort (string, optional): Up to three keys, comma-separated, each optionally prefixed with `-` for descending.
- include (string, optional): Comma-separated names to inline, at most five.
- trashed (string, optional): Whether soft-deleted rows are included.
- X-Request-Id (header, optional): A correlation id of your own. It is recorded in our logs next to the server's request id but never returned: the response header always carries the server's id. A value that does not match the pattern is discarded silently, never rejected.
- If-None-Match (header, optional): The ETag a read is conditional on. A match is answered 304 with no body.

## Response 200
- id (integer, required): Unique identifier.
- user_id (integer, required, server-assigned): Whose agenda the event is on. Always the authenticated user at creation time; not writable.
- task_id (integer or null, required): Kanban task this event is planned against, or `null` for a plain calendar block. Only an event with a task can have time logged on it.
- parent_event_id (integer or null, required, server-assigned): Master event, when this row is a separated occurrence of a recurring series. Set by the server; not writable.
- occurrence_date (date (YYYY-MM-DD) or null, required, server-assigned): The date of the series occurrence this row replaces. Set by the server; not writable.
- start (timestamp (YYYY-MM-DDThh:mm:ssZ), required): When the event starts. RFC 3339 UTC.
- end (timestamp (YYYY-MM-DDThh:mm:ssZ), required): When the event ends. RFC 3339 UTC.
- title (string ≤ 255, required): Event title, as shown on the calendar tile.
- description (html or null, required, detail only): Free-text description, authored in the rich editor. HTML, sanitised on the way in and on the way out; `@`-mention spans are preserved, and changing them is what notifies the people mentioned.
- is_all_day (boolean, required): Whether the event occupies whole days rather than a time range. An all-day event is measured against the owner's working week rather than against start and end.
- is_recurring (boolean, required): Whether this row is the master of a recurring series. When `true`, the recurrence fields describe how occurrences are generated.
- recurrence_interval (integer or null, required): How many frequency units pass between occurrences: 2 with a weekly frequency is every other week.
- recurrence_frequency (string or null, required): The unit the series repeats in.
- recurrence_pattern (string or null, required): How a monthly or yearly series positions its occurrence within the month. Ignored for a daily or weekly frequency.
- recurrence_start (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, required): First moment the series may generate an occurrence.
- recurrence_end (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, required): Last moment the series may generate an occurrence; `null` for an open-ended series.
- selected_weekdays (array of string or null, required, detail only): Weekdays a recurring event falls on. Applies to a weekly frequency; ignored otherwise.
- recurrence_exceptions (array of string or null, required, detail only): Dates on which this recurrence does not occur. plnnrly adds one whenever a single occurrence is separated or deleted.
- created_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Creation timestamp.
- updated_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Last-write timestamp; use for incremental sync.
- deleted_at (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, required): When the event was deleted, or `null` while it is live. Reach a deleted event with `trashed=with` and undo it with POST `events/{event}/actions/restore`.
- user (Ref or UserSummary, required): The user whose agenda the event is on. Renders as an `{"id": N}` reference unless expanded.
- links (EventLinks, required)
  - self (url, required): The canonical URI of this record.

## Errors
- 403 forbidden-by-policy | insufficient-permission | insufficient-token-scope: Forbidden.
- 304 : Not modified.
- 400 idempotency-key-required | invalid-cursor | invalid-query-parameter | malformed-request | unsupported-parameter: Bad request.
- 401 token-expired | unauthenticated: Unauthenticated.
- 404 endpoint-not-found | resource-not-found: Not found.
- 406 not-acceptable: Not acceptable.
- 429 rate-limit-exceeded: Too many requests.
- 500 internal-error: Server error.
- 503 tenant-unavailable: Service unavailable.

## Example
```bash
curl -G \
  'https://<your-url>/api/v2/events' \
  --data-urlencode 'filter[start][gte]=2026-03-02T00:00:00Z' \
  --data-urlencode 'filter[start][lte]=2026-03-08T23:59:59Z' \
  --data-urlencode 'sort=start' \
  -H 'Authorization: Bearer <token>' \
  -H 'Accept: application/json'
```
