Projects

Read the project events feed (cursored catch-up or newest-first display order)

The durable, ordered, per-project feed of operationally significant facts — deploy activations, mailbox suspensions, transfers, lifecycle cliffs, verification outcomes. Answers "what happened to my project since cursor C" in one call. Read-only and never lifecycle-gated: a frozen project's feed stays readable (that is when you most need it). Authorize-before-reveal: unauthorized callers get 403, never a 404 existence oracle. After every deploy, the apply/promote success response's next_actions[] hands you this route with a cursor positioned at your own deploy_activated event. Two pagination modes, disjoint by construction: the default ascending catch-up (cursor — "what's new since") and the display-order read order=desc (+ before for older pages — "latest N, then the rest of history"). A desc response still carries the live high-water cursor, so a display surface flips to catch-up polling without a discovery call. Each event's next_actions[] entries carry kind: pointer (a read — safe to follow whenever, no judgment implied) or remediation (a change the platform is suggesting). A remediation may also carry precondition, the machine-checkable condition under which it is still warranted — renew_tier states one, because it spends money and is moot the moment the organization renews by another route. Treat an unqualified remediation as advice, not a live claim. Every event carries project_id; on this feed it is always this project, and it is null only on the organization feed, where facts that belong to no project also appear. A fact OUTLIVES the project it describes — deleting a project no longer erases its history, so a row may name a project that no longer exists.

get/projects/v1/{project_id}/events

Request

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

Path parameters

project_idstring required

Run402 project ID. Public IDs are not UUIDs.

Example:prj_1741340000_0042

Target project id. A service_key reads only its OWN project's feed; principals and delegates authorize via project.read.

Query parameters

cursorstring

Opaque cursor from a prior response's page-level cursor. Returns events strictly after it. Omit on first contact to start from the earliest retained event. Ascending catch-up only — it does not compose with order=desc (400); display reads page older via before. A cursor is bound to the PROJECTION that issued it (its scope and its source/event_type filters) and is not portable: replaying a project feed's cursor against the org feed, or an unfiltered cursor against a filtered read, returns reset: true + earliest_cursor rather than silently skipping the rows the other projection omitted. An event id is NOT a cursor — the same fact carries the same id in every feed, which is exactly why it cannot also encode a position; passing one back resets.

order'asc' | 'desc'

console-project-events-feed: desc selects the display-order read — the newest N visible events first (the visibility watermark applies identically), with before_cursor in the response when older rows remain. asc is the default's explicit alias, byte-identical to omitting the param. Any other value is rejected 400.

beforestring

console-project-events-feed: opaque cursor (a prior desc response's before_cursor) bounding the page from above, exclusive — returns the next OLDER page. Requires order=desc (400 otherwise). Projection-bound like cursor: one issued for a different scope or filter set is unusable here. A malformed or foreign value follows the reset semantics: 200 with reset: true, earliest_cursor, and a restart from the newest page — never a bare 4xx.

limitinteger

Page size (default 50, max 200).

source'app' | 'platform'

app-events-emit-lane: filter to just the app lane (source='app') or just platform-emitted rows (source<>'app' — the platform's internal producer split, e.g. gateway/apply/email-lambda-events, is an implementation detail; the public dichotomy is app vs. platform). Composes with cursor/limit (and order/before) unchanged: cursor stays the raw unfiltered high-water mark and has_more reflects the filtered view. Omit to see both.

event_typestring

app-events-emit-lane: comma-separated event_type names to filter to, e.g. signature_completed,booking_created. Each name must match ^[a-z][a-z0-9_]{2,63}$ or the request is rejected 400. Composes with source/cursor/limit (and order/before) unchanged.

min_severity'info' | 'notice' | 'warn' | 'critical'

event-attention-rules: only events at or above this severity (info < notice < warn < critical). Composes with source/event_type/cursor/limit (and order/before). Joins the page cursor's projection exactly as source and event_type do: a cursor from a filtered read replayed on a read with different filters takes the reset path, never a silent partial page. info filters nothing and is treated as absent. Any other value is 400 VALIDATION_FAILED.

Headers

SIGN-IN-WITH-Xstring required

SIWX auth header — base64-encoded JSON payload containing a signed CAIP-122 (EIP-4361) message. See https://docs.x402.org/extensions/sign-in-with-x.

Response

Feed page

cursorstring 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_moreboolean 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_cursorstring

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.

resetboolean 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_cursorstring

Present only when reset is true: a cursor positioned just before the earliest retained event for this scope.

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.

Example response

{
  "platform_incidents": [
    {
      "id": "inc_9b21fa0c7d4e15a3",
      "subsystem": "gateway"
    }
  ]
}

Changes

No recorded changes to this operation across all 1 revision of this API.