---
title: "Read the org-wide events feed (union across the org's projects)"
method: GET
path: "/orgs/v1/{org_id}/events"
tags: ["Org"]
---

# Read the org-wide events feed (union across the org's projects)

`GET /orgs/v1/{org_id}/events`

## Request

- Base URL: `https://api.run402.com`
- URL: `https://api.run402.com/orgs/v1/{org_id}/events`
- Auth: one of:
  - API key in header `SIGN-IN-WITH-X`
  - HTTP bearer

Same envelope and cursor semantics as the per-project feed — both pagination modes, ascending catch-up and `order=desc` display reads — over every fact the organization owns. That is a superset of the per-project feeds: it also carries ORGANIZATION-level facts, which belong to the org and to no project and appear here with `project_id: null` (a project feed can never show them). Cursors are not interchangeable between the two feeds even though an event's `id` is the same in both — see `cursor`. Principal-only: any active org member may read; project service_keys are rejected (a deployed app must not read sibling projects' events). Read-only and never lifecycle-gated.

## Path parameters

- `org_id` string, uuid, required

## Query parameters

- `cursor` string
- `order` 'asc' | 'desc'
- `before` string
- `limit` integer
- `source` 'app' | 'platform'
- `event_type` string
- `min_severity` 'info' | 'notice' | 'warn' | 'critical'

## Headers

- `SIGN-IN-WITH-X` string, required

## Response `200`

Feed page

- ProjectEventFeedPage: One page of the cursored events feed — oldest-first by default (catch-up), newest-first with `order=desc` (display). Store `cursor` and pass it as `?cursor=` next time to catch up in one call; a desc page walks older history via `before_cursor` instead. Events become visible within seconds (a short visibility watermark orders concurrent commits, identically in both orders) and are never lost after it.
  - `events` ProjectEvent[], required
    - `id` string: Opaque event cursor (`evc_...`). Never parse it; store and echo it as `?cursor=`.
    - `event_type` string: Flat snake_case event name, e.g. `deploy_activated`, `error_fingerprints_observed`, `platform_incident`, `mailbox_suspended`, `project_transfer_initiated`, `organization_past_due`, `verification_failed`, `webhook_disabled` (platform vocabulary) or an app-chosen name such as `signature_completed` (app-events-emit-lane, `source: "app"`). The vocabulary is the platform notification event set plus feed-native events plus whatever an app's own deployed code has emitted via `POST /projects/v1/:project_id/events`. `platform_incident` is an ATTRIBUTED platform-fault event: it lands on an affected project's feed when a platform incident resolves, with a compact payload `{ incident_id, subsystem, severity, scope, status, started_at, resolved_at, summary, impact: { count } }` — the real number of that project's invocations the platform, not the app, caused to fail. Global (unattributed) incidents ride the page-level `platform_incidents[]` overlay instead.
    - `severity` 'info' | 'notice' | 'warn' | 'critical': event-attention-rules: how significant the event is, stamped at write and never updated. An app event carries what its emitter declared (`info` when it declared nothing); a platform event carries the level registered for its type. States what happened, not how important the class is: `verification_succeeded` is `notice`, `mailbox_suspended` is `warn`.
    - `class` string: Event class stamped at write time: `lifecycle`, `threshold`, `security`, `recovery`, `billing_critical`, `destructive_lifecycle`, `verification`, `platform_incident`, `digest`, or `app` (app-events-emit-lane). Mandatory classes (`security`, `recovery`, `billing_critical`, `destructive_lifecycle`, `verification`, `platform_incident`) are retained 365 days; everything else — including `app`, which is NEVER a mandatory class — 90 days. `platform_incident` is mandatory for the retention floor only — the platform's confession log is not the first thing swept — and is never delivered as a preference-gated notification.
    - `source` string: Producer discriminator. `"app"` for a row written by a project's own deployed code via `POST /projects/v1/:project_id/events`; any other value (`gateway`, `apply`, `email-lambda-events`, …) is a platform producer — the internal split among them is an implementation detail. Consumers key on `(source, event_type)` to tell app facts from platform facts; filter with `?source=app` / `?source=platform` on both feed GETs.
    - `occurred_at` string, date-time
    - `payload` object: Compact fact: resource ids + verdict fields (never bodies or secret material). Oversize payloads carry `payload_truncated: true` + `dropped_keys[]`; the referenced domain resources hold full detail.
    - `next_actions` object[]: Platform-synthesized drill-downs for this event — the highest-probability next call, identical across HTTP/SDK/CLI/MCP.
      - `type` string
      - `method` string
      - `path` string
      - `command` string
      - `why` string
  - `cursor` string, required: High-water mark: the last served event's cursor (present even when `events` is empty — an empty page echoes your own `cursor` unchanged). On `order=desc` pages it is the LIVE high-water mark — the same value a caught-up ascending read with the same filters would return — so a display surface hands off to catch-up polling without a discovery call.
  - `has_more` boolean, required: True when more events are immediately available past `cursor` — poll again right away. On `order=desc` pages: true when OLDER rows remain past this page (within any active `source`/`event_type` filter) — follow `before_cursor`.
  - `before_cursor` string: `order=desc` pages only, and only when older rows remain (`has_more: true`): pass as `?order=desc&before=` for the next OLDER page. Absent on the oldest retained page — its absence IS the end-of-history signal, so you never fetch a trailing empty page. Same opaque cursor encoding as `cursor`.
  - `reset` boolean, required: True when the supplied position was unusable — an ascending `cursor` that was malformed or older than the retention floor (swept rows can no longer be proven seen), or a malformed desc `before`. The page restarts (ascending: from the earliest retained event; desc: from the newest page) and `earliest_cursor` is provided — never a bare 4xx, never a silent skip.
  - `earliest_cursor` string: Present only when `reset` is true: a cursor positioned just before the earliest retained event for this scope.
  - `platform_incidents` object[]: Sidecar overlay of GLOBAL (unattributed) platform incidents — open incidents plus incidents resolved after the caller's cursor position. It is a sidecar, NOT interleaved into `events[]`: the numeric cursor stays monotonic and uncorrupted. Each entry carries a stable `id` so a client dedups across reads. Attributed (per-project) incidents are NOT here — they arrive as `platform_incident` entries inside `events[]`. Present only when at least one such global incident applies; omitted otherwise.
    - `id` string, required: Stable incident id (`inc_…`) — the dedup key across feed reads.
    - `subsystem` string, required
    - `severity` 'info' | 'minor' | 'major', required
    - `scope` 'global', required: Always `global` in the overlay — attributed incidents ride `events[]`.
    - `status` 'ongoing' | 'resolved', required
    - `summary` string
    - `started_at` string, date-time, required
    - `resolved_at` string, date-time, nullable: Null while the incident is ongoing.
  - `platform_status` 'degraded': Present and equal to `degraded` only while an open platform incident is global or affects this caller's projects; omitted when clear. A health-assessment rider (same field as on GET /agent/v1/me/status and GET /tiers/v1/status), never added to unrelated 200s.

## Other responses

- `400`: Invalid `source` (must be app or platform), `event_type` (must match the flat snake_case grammar), or `order` (must be asc or desc); or a nonsensical pagination combination — `before` without `order=desc`, or `cursor` with `order=desc`
- `401`: Missing or invalid authentication. Run402-originated JSON bodies use the canonical ErrorEnvelope.
- `403`: Not a member of this org (FORBIDDEN)

---

[API](https://skmtc.dev/run402/apis/run402-api.md) · [All operations](https://skmtc.dev/run402/apis/run402-api/llms.txt) · [OpenAPI document](https://skmtc.dev/run402/apis/run402-api/revisions/893607557f50?raw)
