Events

Event feed, event detail, deduped group lookup, and the public SSE stream.

GET /v1/events

List DeFi events with filters, cursor-style metadata, and full-text search.

Query Parameters

ParameterTypeDefaultDescription
pagenumber1Page number
limitnumber20Results per page
eventTypestring-Event type such as Incident or DepositabilityChange
severitystring-Critical, Warning, or Info
protocolIdstring-Protocol ID
curatorIdstring-Curator ID
chainIdstring-Chain: integer id or slug (e.g. 1, solana)
incidentSeveritystring-minor or major
remediationStatusstring-resolved or unresolved
searchstring-Full-text search on title and description
excludeSourcestring-Exclude a source such as peg-monitor
sortBystringoccurred_atSort field
sortOrderstringdescasc or desc
daysBackstringadaptiveLookback window, capped at 730 days

Example

curl "https://api.philidor.io/v1/events?severity=Critical&limit=5"

Response

{
  "data": [
    {
      "id": "42",
      "event_type": "Incident",
      "severity": "Critical",
      "title": "Vault incident",
      "description": "Incident summary",
      "occurred_at": "2026-07-01T12:00:00Z",
      "protocol_id": "morpho",
      "protocol_name": "Morpho",
      "chain_id": 1,
      "chain_name": "Ethereum",
      "curator_id": "gauntlet",
      "curator_name": "Gauntlet",
      "incident_severity": "major",
      "loss_amount_usd": null,
      "remediation_status": "unresolved",
      "links": [],
      "occurrence_count": 1,
      "group_key": "incident:morpho:42"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 5,
    "hasMore": false
  }
}

GET /v1/events/{id}

Get one event with affected vaults.

Path Parameters

ParameterTypeDescription
idstringEvent ID

Example

curl https://api.philidor.io/v1/events/42

Response

{
  "data": {
    "id": "42",
    "event_type": "Incident",
    "severity": "Critical",
    "title": "Vault incident",
    "affected_vaults": [
      {
        "vault_id": "morpho-1-0x...",
        "vault_name": "Gauntlet USDC Core",
        "tvl_usd": 125000000,
        "chain_id": 1,
        "address": "0x..."
      }
    ]
  }
}

GET /v1/events/group/{groupKey}

Return the current representative for a deduped event group. This helps clients recover when a grouped event is retracted.

Example

curl https://api.philidor.io/v1/events/group/rating-change:morpho:2026-06-22

Response

Same shape as a single event list item.


GET /v1/events/stream

Open a Server-Sent Events stream. Requires a Data-plan key. Browser EventSource cannot send an Authorization header — connect through a server-side relay or pass the key on the underlying request as shown in API Access and Plans.

The stream flushes ready immediately, then delivers immutable operation snapshots named published, promoted, or retracted. Each operation has a versioned delivery cursor such as id: v2:42. The event row's data.id remains its event ID. Republication of the same event produces a new cursor and a new captured body.

Heartbeat comments arrive every 20 seconds. Connection caps are 200 per key and 5 per end viewer when a keyed relay reports X-Philidor-Client-Ip. Anonymous connections retain a 5-per-IP cap during a configured rollout window. Rejected admissions return finite JSON with a correlated request ID, Cache-Control: no-store, and a Retry-After hint; the connection-cap hint is 10 seconds.

Reconnecting and checkpoints

Send the last delivered v2:<cursor> as Last-Event-ID, or as ?since= when opening a replacement relay connection. Last-Event-ID takes precedence. Delivery cursors are 64-bit decimal strings; do not convert them to JavaScript numbers.

Live delivery and replay consume the same durable operation journal. Polling recovers committed operations even when a notification is missed. Snapshot bodies preserve the operation as captured, rather than hydrating the event's current state on reconnect. Current publication restrictions and chain access gates still apply.

Replay is bounded to a 24-hour delivery window and 1000 journal operations per connection. Reconnect delivery is at least once; deduplicate repeated delivery cursors. An event ID and operation name are not a sufficient deduplication key because the same event can be retracted and published again.

A cursor control frame advances the replay checkpoint over filtered operations without emitting their bodies. Persist its id just as you persist an operation's id. For a new connection without a cursor, the checkpoint starts at the current journal tail; load the initial event list through REST.

The version 2 ready frame reports the supplied cursor and limits:

{
  "vaultId": null,
  "version": 2,
  "replay": { "cursor": "v2:42", "window_seconds": 86400, "max_rows": 1000 }
}

A gap frame explicitly reports an incomplete range. It has no new id line.

reasonMeaningRecovery
queue_overflowThe client exhausted its bounded 100-frame queue. Queued frames precede the gap, then the connection closes.Reconnect from the last delivered cursor. The gap also carries the last queued checkpoint.
replay_truncatedThe 1000-operation replay limit was reached before the journal tail. The connection closes after its checkpoint and gap.Reconnect using the gap's cursor to continue the remaining range.
cursor_legacyAn unversioned event ID or timestamp belongs to the previous cursor contract.Refetch REST state. The server also replays the retained journal window; pre-journal operations cannot be reconstructed.
cursor_unknown, cursor_invalidThe supplied cursor is not a valid retained delivery position.Refetch REST state and accept the retained-window checkpoint.
cursor_expiredThe cursor precedes the 24-hour replay window.Refetch older state; the retained window is replayed from its floor.
replay_failedThe journal could not be read. The connection closes.Retry from the last delivered cursor with backoff and refetch REST state if recovery remains unavailable.

The journal begins with this release; it does not backfill past event mutations. Previously issued numeric and timestamp cursors remain recognized as legacy positions and produce an explicit gap. The API's reviewed stream handoff sends draining with retry: 1000; replay and checkpoint handling are required for reconnect continuity within the retained window.

Query Parameters

ParameterTypeDefaultDescription
vaultIdstring-Optional vault filter
sincestring-Delivery cursor v2:<64-bit decimal string>. Legacy numeric/timestamp positions produce an explicit gap. Last-Event-ID wins when both are present.

Example

// This relay attaches a server-held Data-plan API key.
const stream = new EventSource('/api/philidor/events-stream');

stream.addEventListener('published', (event) => {
  const row = JSON.parse(event.data);
  console.log(row.title);
});

Response

event: ready
data: {"vaultId":null,"version":2,"replay":{"cursor":null,"window_seconds":86400,"max_rows":1000}}

id: v2:42
event: published
data: {"id":42,"title":"Vault incident"}

On this page

Raw