Chain events stream (SSE)
https://api.triport.io/v1/chains/eventsA live, one-way **Server-Sent Events** stream of chain health changes and metric ticks for the Triport network catalogue.
GET /v1/chains/events is a Server-Sent Events (SSE) endpoint, consumed in the
browser with the native EventSource
API. It is a long-lived, server-to-client, text/event-stream connection — it is
not a WebSocket, so there is no client-to-server channel and no upgrade
handshake. Open it once and the server pushes named events as chain health and
metrics change.
Two named event types are emitted:
chain_status— a chain's health transitioned (e.g.live→degraded).chain_metric_tick— a periodic metrics sample for a chain (p50 latency, block height).
There is no snapshot frame on connect. The stream only delivers changes that
occur after you subscribe. To render correct initial state, seed each chain from
GET /v1/chains/{id}/metrics on mount, then apply the
events as they arrive.
EventSource reconnects automatically on network drops. On reconnect the backend
resumes the connection with periodic keepalive comments and any future events; it
does not replay a backlog, so treat a reconnect like a fresh subscription and
re-seed from the metrics endpoint if you need a guaranteed-consistent baseline.
The broadcaster fans out events non-blocking: a subscriber that cannot keep up has events dropped rather than back-pressuring the server. Do not assume you receive every tick — design your UI to converge from whatever events arrive plus the periodic metrics read.
Parameters
This endpoint takes no path, query, or body parameters. Authentication is the
browser session cookie, which EventSource sends when opened with
withCredentials: true.
——optionalResponse
Content-Type: text/event-stream. Each event is a event:/data: pair; lines
beginning with : are keepalive comments. The data: payload is a JSON object.
chain_idstringethereum-mainnet).prevstringlive | degraded | down | scope_missing.nextstringlive | degraded | down | scope_missing.atstringErrors
SSE has no per-event error envelope; failures surface as the connection failing to
open or the EventSource error event firing (after which it auto-reconnects).
| Code | Meaning | When it happens |
|---|---|---|
401 | Unauthenticated | No valid session cookie — sign in first. |
404 | Not found | Chains vector not mounted on this deployment. |
For the standard JSON error envelope used by the REST endpoints, see errors.
Examples
JavaScript (fetch)
EventSource, not fetch, is the right primitive for SSE in the browser:
const es = new EventSource("https://api.triport.io/v1/chains/events", {
withCredentials: true,
});
es.addEventListener("chain_status", (e) => {
const ev = JSON.parse(e.data); // { chain_id, prev, next, at }
console.log(`${ev.chain_id}: ${ev.prev} → ${ev.next}`);
});
es.addEventListener("chain_metric_tick", (e) => {
const ev = JSON.parse(e.data); // { chain_id, latency_p50_ms, block_height, at }
if (ev.latency_p50_ms != null) {
console.log(`${ev.chain_id} p50=${ev.latency_p50_ms}ms`);
}
});
es.onerror = () => {
// EventSource reconnects on its own; no snapshot is replayed on resume.
};
// later: es.close();TypeScript SDK (@triport/sdk)
import { TriportClient, ChainEvent } from "@triport/sdk";
const client = new TriportClient({ apiKey: process.env.TRIPORT_API_KEY });
// Seed initial state, then subscribe for live updates.
const seed = await client.chains.metrics("solana-mainnet");
let p50 = seed.latency_p50_ms;
const stop = client.chains.subscribeEvents((ev: ChainEvent) => {
if (ev.type === "chain_metric_tick" && ev.chain_id === "solana-mainnet") {
if (ev.latency_p50_ms != null) p50 = ev.latency_p50_ms;
}
});
// stop(); // closes the stream and removes listenersPython (triport-sdk)
from triport import TriportClient
client = TriportClient(api_key="...") # use os.environ["TRIPORT_API_KEY"]
# Seed first, then stream live changes (the stream sends no snapshot frame).
seed = client.chains.metrics("solana-mainnet")
for event in client.chains.events():
if event.type == "chain_status":
print(f"{event.chain_id}: {event.prev} -> {event.next} @ {event.at}")
elif event.type == "chain_metric_tick":
if event.latency_p50_ms is not None:
print(f"{event.chain_id} p50={event.latency_p50_ms}ms")Notes
- Server-Sent Events, one direction only. There is no message channel from client to server; to issue requests use the REST endpoints.
- No snapshot on connect, no backlog on reconnect. Always seed from
GET /v1/chains/{id}/metricsand treat the stream as a delta feed. For historical trends useGET /v1/chains/{id}/metrics/series. - Lossy by design. Slow subscribers have events dropped (non-blocking fan-out), so reconcile periodically against the metrics endpoint rather than assuming a complete event log.
- Nulls are meaningful.
latency_p50_msandblock_heightarrive as JSONnullwhen unmeasured — check!= nullrather than truthiness, since a real0is a valid value. - Discover the chain catalogue with
GET /v1/chainsand per-chain detail withGET /v1/chains/{id}.