REST API
Fleet Monitor
On this page
Live topology and health for a fleet — a group of agents running together. Unlike trace endpoints, which are historical, the fleet API is near-real-time and backed by a streaming WebSocket.
List fleets
GET /v1/observ/orgs/{org}/workspaces/{ws}/fleet/fleets
Auth: API key or JWT · Role: viewer
Response 200 — not schema-modelled
{
"fleets": [
{
"fleet_id": "checkout-fleet",
"agent_count": 12,
"status": "active",
"last_seen": "2026-09-01T14:23:05Z",
"open_flag_count": 2
}
]
}
Examples
fl = evigauge.get("/v1/observ/orgs/{org}/workspaces/{ws}/fleet/fleets")
for f in fl["fleets"]:
print(f"{f['fleet_id']:20} {f['agent_count']} agents {f['open_flag_count']} flags")
const fl = await evigauge.get("/v1/observ/orgs/{org}/workspaces/{ws}/fleet/fleets");
fl.fleets.forEach((f: any) =>
console.log(`${f.fleet_id.padEnd(20)} ${f.agent_count} agents ${f.open_flag_count} flags`));
Fleet graph
GET /v1/observ/orgs/{org}/workspaces/{ws}/fleet/{fleet_id}/graph
The fleet's agent topology — nodes and the call edges between them, with token and latency rollups per node.
Auth: API key or JWT · Role: viewer
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
org | string | ✅ | Organization slug |
ws | string | ✅ | Workspace slug |
fleet_id | string | ✅ | Fleet identifier |
Response 200 — not schema-modelled
{
"fleet_id": "checkout-fleet",
"nodes": [
{ "id": "router", "kind": "agent", "call_count": 8120, "p95_latency_ms": 210, "total_tokens": 410233, "status": "ok" },
{ "id": "pricing", "kind": "agent", "call_count": 6402, "p95_latency_ms": 4900, "total_tokens": 992010, "status": "flagged" }
],
"edges": [{ "from": "router", "to": "pricing", "call_count": 6402 }]
}
Examples
g = evigauge.get(f"/v1/observ/orgs/{{org}}/workspaces/{{ws}}/fleet/{fleet_id}/graph")
for n in g["nodes"]:
mark = "⚠" if n["status"] != "ok" else " "
print(f"{mark} {n['id']:15} p95={n['p95_latency_ms']}ms tokens={n['total_tokens']:,}")
const g = await evigauge.get(
`/v1/observ/orgs/{org}/workspaces/{ws}/fleet/${fleetId}/graph`);
g.nodes.forEach((n: any) =>
console.log(`${n.status !== "ok" ? "⚠" : " "} ${n.id.padEnd(15)} p95=${n.p95_latency_ms}ms tokens=${n.total_tokens}`));
Fleet flags
GET /v1/observ/orgs/{org}/workspaces/{ws}/fleet/{fleet_id}/flags
Health flags raised on the fleet. Flags auto-resolve: after 3 consecutive
clean spans, a resolve event is emitted and the flag leaves the active set.
Auth: API key or JWT · Role: viewer
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
status | string | ➖ | active | active — one row per still-open flag episode; all — the raw append-only event log (fires and resolves), newest first |
Examples
flags = evigauge.get(
f"/v1/observ/orgs/{{org}}/workspaces/{{ws}}/fleet/{fleet_id}/flags",
params={"status": "active"},
)
print(flags)
const flags = await evigauge.get(
`/v1/observ/orgs/{org}/workspaces/{ws}/fleet/${fleetId}/flags`, { status: "active" });
console.log(flags);
Pause / resume / clear a fleet
POST /v1/observ/orgs/{org}/workspaces/{ws}/fleet/{fleet_id}/pause
POST /v1/observ/orgs/{org}/workspaces/{ws}/fleet/{fleet_id}/resume
POST /v1/observ/orgs/{org}/workspaces/{ws}/fleet/{fleet_id}/clear
Auth: API key or JWT · Role: admin · Request body: none
| Action | Effect |
|---|---|
pause | Stops fleet monitoring and flag evaluation. Spans still ingest. |
resume | Resumes monitoring from live state. |
clear | Discards accumulated fleet state and starts a fresh topology. |
pausedoes not stop your agents. Evigauge never controls your runtime — it pauses monitoring only. Andclearis destructive to accumulated topology state; the underlying spans are untouched.
Examples
base = f"/v1/observ/orgs/{{org}}/workspaces/{{ws}}/fleet/{fleet_id}"
evigauge.post(f"{base}/pause")
evigauge.post(f"{base}/resume")
evigauge.post(f"{base}/clear") # destructive: resets topology state
const base = `/v1/observ/orgs/{org}/workspaces/{ws}/fleet/${fleetId}`;
await evigauge.post(`${base}/pause`);
await evigauge.post(`${base}/resume`);
await evigauge.post(`${base}/clear`); // destructive: resets topology state
Mint a WebSocket ticket
POST /v1/observ/orgs/{org}/workspaces/{ws}/fleet/ws-ticket
Mints a short-lived, single-use ticket for the fleet WebSocket. Browsers
cannot set an Authorization header on a native WebSocket, so the ticket rides
the query string instead.
Auth: API key or JWT · Role: viewer · Request body: none
Response 200 — not schema-modelled
{ "ticket": "wst_01J8XYZ...", "expires_in": 60 }
The ticket is consumed on first use. Mint a fresh one for every connection — including every reconnect after a drop.
Fleet WebSocket stream
WSS wss://api.opexia.dev/ws/fleet/{workspace_id}?ticket={ticket}
Streams live fleet delta frames. Note the parameter is {workspace_id} — a
UUID, not the slug used elsewhere in the fleet API. The ticket is validated
before the connection is accepted.
| Close code | Meaning |
|---|---|
1008 | Ticket missing, expired, already used, or bound to a different workspace |
Examples
import json, httpx, websockets # pip install websockets
def mint_ticket():
return evigauge.post("/v1/observ/orgs/{org}/workspaces/{ws}/fleet/ws-ticket")["ticket"]
async def stream(workspace_uuid: str):
# A ticket is single-use: mint a new one for every connect and reconnect.
url = f"wss://api.opexia.dev/ws/fleet/{workspace_uuid}?ticket={mint_ticket()}"
async with websockets.connect(url) as ws:
async for raw in ws:
frame = json.loads(raw)
print(frame["type"], frame.get("nodes", []))
async function mintTicket(): Promise<string> {
const { ticket } = await evigauge.post(
"/v1/observ/orgs/{org}/workspaces/{ws}/fleet/ws-ticket");
return ticket;
}
async function stream(workspaceUuid: string) {
// A ticket is single-use: mint a new one for every connect and reconnect.
const ws = new WebSocket(
`wss://api.opexia.dev/ws/fleet/${workspaceUuid}?ticket=${await mintTicket()}`);
ws.onmessage = (e) => {
const frame = JSON.parse(e.data);
console.log(frame.type, frame.nodes);
};
ws.onclose = (e) => {
if (e.code === 1008) console.error("Ticket rejected — mint a fresh one.");
else setTimeout(() => stream(workspaceUuid), 1000); // reconnect with a new ticket
};
}