API reference
Live contract, pulled from the route handlers

Three endpoints,
one continuous loop.

Every sample below is rendered from src/lib/contracts/* and the route handlers under src/app/api/v1/*. The same literal JSON is what your code parses — if a field changes, this page updates in the same change.

Auth required
Every endpoint below expects a better-auth.session_token cookie. Read the auth guide.
Streams

Three streams endpoints, pulled from /openapi/streams.yaml.

Methods, intents, and request/response samples rendered directly from /openapi/streams.yaml. The curl and the JSON byte-for-byte match what the route handlers emit — for constraints, every field, and error envelopes, jump to /docs/api/streams.

POST
/api/v1/streams
Auth required

Register a stream

Upsert a stream by its sourceUrl. The handler validates the body with zod, runs the SSRF guard against sourceUrl, then upserts by sourceUrl — POST is idempotent, so re-posting the same URL returns the existing record instead of 409.

A fresh row is initialized with agentState: 'healthy', status: 'watching', and uptimePct: 100 as a placeholder until the first probe lands. Returns 201 with the full row.

Request
curl -X POST https://streamwake.polsia.io/api/v1/streams \
  -H "content-type: application/json" \
  -b "better-auth.session_token=<your-session-cookie>" \
  -d '{
      "sourceUrl": "https://example.com/manifest.m3u8"
    }'
201 Response — StreamItem
{
  "id": "ckq3xstreamabc123",
  "sourceUrl": "https://example.com/manifest.m3u8",
  "name": "Primary CDN — eu-west",
  "targetRegion": "eu-west",
  "createdAt": "2026-08-03T18:24:11.000Z",
  "agentState": "healthy",
  "status": "watching",
  "lastAction": "registered",
  "lastCheckedAt": null,
  "uptimePct": 100
}
GET
/api/v1/streams
Auth required

List monitored streams

Returns the 50 most recently created streams, ordered by createdAt descending. Each row carries the same shape as the POST response (StreamItem), with uptimePct computed from the last 24 hours of StreamProbe rows. Rows with no probes in the window render as 100%.

Request
curl https://streamwake.polsia.io/api/v1/streams \
  -b "better-auth.session_token=<your-session-cookie>"
200 Response — StreamList
{
  "items": [
    {
      "id": "ckq3xstreamabc123",
      "sourceUrl": "https://example.com/manifest.m3u8",
      "name": "Primary CDN — eu-west",
      "targetRegion": "eu-west",
      "createdAt": "2026-08-03T18:24:11.000Z",
      "agentState": "healthy",
      "status": "watching",
      "lastAction": "registered",
      "lastCheckedAt": "2026-08-03T18:25:00.000Z",
      "uptimePct": 100
    },
    {
      "id": "ckq3xstreamdef456",
      "sourceUrl": "https://backup.example.com/manifest.m3u8",
      "name": "Backup CDN — eu-west",
      "targetRegion": "eu-west",
      "createdAt": "2026-08-02T11:02:08.000Z",
      "agentState": "degraded",
      "status": "degraded",
      "lastAction": "raised the latency budget and re-probed to confirm the trend",
      "lastCheckedAt": "2026-08-03T18:25:00.000Z",
      "uptimePct": 96
    }
  ]
}
GET
/api/v1/streams/:id
Auth required

Read a single stream's status

Returns the live StreamStatus for one stream. The uptimePct is computed from the same 24-hour probe window as the list endpoint, so the numbers always agree. Returns 404 if the id is unknown.

Request
curl https://streamwake.polsia.io/api/v1/streams/ckq3xstreamabc123 \
  -b "better-auth.session_token=<your-session-cookie>"
200 Response — StreamStatus
{
  "id": "ckq3xstreamabc123",
  "sourceUrl": "https://example.com/manifest.m3u8",
  "agentState": "healthy",
  "uptimePct": 100,
  "lastAction": "registered",
  "lastCheckedAt": "2026-08-03T18:25:00.000Z"
}
Streams in depth

Want every field pulled straight from the spec?

The dedicated streams reference renders every endpoint, parameter table, request body, response schema, and error envelope directly from /openapi/streams.yaml. Edit the spec and the page re-renders in the same change.

Open the full streams reference
GET
/api/v1/agents
Auth required

Recent agent activity

Returns the most recent 100 StreamProbe rows joined with their stream. Three transformations happen on the way out:

  • severity healthy → info, degraded → warn, failing → critical.
  • anomalyClass no anomaly for healthy; slow response or client error for degraded; network / timeout or 5xx server error for failing.
  • fix / action — when a probe is failing AND the stream has no subsequent healthy probe, both fields flip to investigating; otherwise fix echoes Stream.lastAction and action falls through one of four actionFor branches by status and latency.
Request
curl https://streamwake.polsia.io/api/v1/agents \
  -b "better-auth.session_token=<your-session-cookie>"
200 Response — AgentEventList
{
  "items": [
    {
      "id": "ckq3xprobe123",
      "streamId": "ckq3xstreamabc123",
      "sourceUrl": "https://example.com/manifest.m3u8",
      "streamName": "Primary CDN — eu-west",
      "severity": "info",
      "anomalyClass": "no anomaly",
      "fix": "registered",
      "action": "no action needed — stream healthy",
      "createdAt": "2026-08-03T18:25:00.000Z",
      "latencyMs": 142
    },
    {
      "id": "ckq3xprobe124",
      "streamId": "ckq3xstreamdef456",
      "sourceUrl": "https://backup.example.com/manifest.m3u8",
      "streamName": "Backup CDN — eu-west",
      "severity": "warn",
      "anomalyClass": "slow response",
      "fix": "raised the latency budget and re-probed to confirm the trend",
      "action": "raised the latency budget and re-probed to confirm the trend",
      "createdAt": "2026-08-03T18:25:00.000Z",
      "latencyMs": 1840
    }
  ]
}
Errors

Every body, verbatim.

Consolidated across all four endpoints — grep this table when an error code comes back and you want the exact bytes the handler emitted.

StatusBodyWhen
400
{
  "errors": {
    "sourceUrl": "Must be a valid URL"
  }
}
Zod validation failed on the request body, or the SSRF guard refused sourceUrl (loopback / RFC 1918 / link-local).
401
{
  "error": "Unauthorized"
}
No session cookie, or the cookie is expired. Re-auth via /api/auth/sign-in/email and retry.
500
{
  "error": "Internal Server Error"
}
Unexpected server error — safe to retry.
404
{
  "error": "Not Found"
}
Returned by GET /api/v1/streams/<id> when the id is unknown.
400
{
  "errors": {
    "name": "String must contain at most 120 character(s)"
  }
}
Zod validation failed on the request body, or the SSRF guard refused sourceUrl (loopback / RFC 1918 / link-local).
Ready to run it?

From zero,
to a watched stream.

The quickstart walks every endpoint above in order — register a stream, read it back, then open /app/streams and watch the first probe land.

Also see the player SDK reference for the npm/CDN install and the TelemetryEvent wire format → /docs/sdk/web.