Streams — full reference
Streamwake /api/v1/streams v1.0.0

Four endpoints,
one YAML to drive them.

Every field, parameter, response shape, and error envelope below is rendered from /openapi/streams.yaml — edit the spec and this page re-renders in the same change. Auth uses the cookie set by better-auth at /api/auth/sign-up/email; swap in better-auth.session_token=<your-session-cookie> and the curls below land on your local instance.

Auth required
Server: https://streamwake.app Read the auth guide.
Endpoints

Four operations, straight from the spec.

Section order matches /openapi/streams.yaml#paths. Each card below carries the method, endpoint template, summary, and description pulled directly from the spec.

POST
/streams
Auth: cookie
streams

Register a stream (idempotent by sourceUrl)

Upsert a stream by its sourceUrl. The handler validates the body with zod (StreamCreate), 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", lastAction "registered", and uptimePct 100 (a placeholder until the first probe lands).

Request body

POST body, field by field

Content type: application/json — schema: StreamCreate

Example body
{
  "sourceUrl": "https://example.com/manifest.m3u8",
  "name": "Primary CDN — eu-west",
  "targetRegion": "eu-west"
}
Curl — copy/paste
curl -X POST https://streamwake.polsia.io/streams \
  -H "content-type: application/json" \
  -b "better-auth.session_token=<your-session-cookie>" \
  -d '{
      "sourceUrl": "https://example.com/manifest.m3u8",
      "name": "Primary CDN — eu-west",
      "targetRegion": "eu-west"
    }'
Responses
201
Returns the full StreamItem row for the upserted stream. A fresh row carries agentState "healthy", status "watching", lastAction "registered", and uptimePct 100; an existing row reflects the latest probe state.
{
  "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
}
400
Zod validation failed on the request body, or the SSRF guard refused sourceUrl (loopback / RFC 1918 / link-local, or the URL could not be parsed / did not resolve).
{
  "errors": {
    "sourceUrl": "Must be a valid URL"
  }
}
401
No session cookie, or the cookie is expired. Re-auth via /api/auth/sign-in/email and retry.
{
  "error": "Unauthorized"
}
500
Unexpected server error — safe to retry.
{
  "error": "Internal Server Error"
}
GET
/streams
Auth: cookie
streams

List the 50 most recently created streams

Returns the 50 most recently created streams, ordered by createdAt descending. Each row carries the StreamItem shape used by POST, with uptimePct computed server-side from the last 24 hours of StreamProbe rows. Rows with no probes in the window render as 100 — the placeholder the dashboard's StreamRowCard uses.

Curl — copy/paste
curl https://streamwake.polsia.io/streams \
  -b "better-auth.session_token=<your-session-cookie>"
Responses
200
A list of StreamItem rows, newest first.
{
  "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
    }
  ]
}
401
No session cookie, or the cookie is expired. Re-auth via /api/auth/sign-in/email and retry.
{
  "error": "Unauthorized"
}
500
Unexpected server error — safe to retry.
{
  "error": "Internal Server Error"
}
GET
/streams/{id}
Auth: cookie
streams

Read one stream's status

Returns the live StreamStatus for one stream. The uptimePct value is computed from the same 24-hour probe window the list endpoint uses, so the detail and list numbers always agree. Returns 404 with { "error": "Not Found" } if the id is unknown.

Parameters
NameInRequiredTypeDescription
idpath
yes
stringStable row identifier (cuid).
Curl — copy/paste
curl https://streamwake.polsia.io/streams/<id> \
  -b "better-auth.session_token=<your-session-cookie>"
Responses
200
The stream's current status snapshot.
{
  "id": "ckq3xstreamabc123",
  "sourceUrl": "https://example.com/manifest.m3u8",
  "agentState": "healthy",
  "uptimePct": 100,
  "lastAction": "registered",
  "lastCheckedAt": "2026-08-03T18:25:00.000Z"
}
401
No session cookie, or the cookie is expired. Re-auth via /api/auth/sign-in/email and retry.
{
  "error": "Unauthorized"
}
404
Returned when the id is unknown.
{
  "error": "Not Found"
}
500
Unexpected server error — safe to retry.
{
  "error": "Internal Server Error"
}
PATCH
/streams/{id}
Auth: cookie
streams

Update a stream

Update the mutable fields of an existing stream — name and targetRegion. sourceUrl is the stable identity of the row, so it cannot be changed here (re-register the new URL as a separate row). PATCH semantics: fields omitted from the body are left unchanged on the row. Idempotent — repeated PATCHes with the same body return the same row. Returns the updated StreamItem.

Parameters
NameInRequiredTypeDescription
idpath
yes
stringStable row identifier (cuid).
Request body

PATCH body, field by field

Content type: application/json — schema: StreamCreate

Example body
{
  "name": "Primary CDN — eu-west (renamed)",
  "targetRegion": "eu-central"
}
Curl — copy/paste
curl -X PATCH https://streamwake.polsia.io/streams/<id> \
  -H "content-type: application/json" \
  -b "better-auth.session_token=<your-session-cookie>" \
  -d '{
      "name": "Primary CDN — eu-west (renamed)",
      "targetRegion": "eu-central"
    }'
Responses
200
The updated StreamItem row.
{
  "id": "ckq3xstreamabc123",
  "sourceUrl": "https://example.com/manifest.m3u8",
  "name": "Primary CDN — eu-west (renamed)",
  "targetRegion": "eu-central",
  "createdAt": "2026-08-03T18:24:11.000Z",
  "agentState": "healthy",
  "status": "watching",
  "lastAction": "registered",
  "lastCheckedAt": "2026-08-03T18:25:00.000Z",
  "uptimePct": 100
}
400
Zod validation failed on the request body.
{
  "errors": {
    "name": "String must contain at most 120 character(s)"
  }
}
401
No session cookie, or the cookie is expired. Re-auth via /api/auth/sign-in/email and retry.
{
  "error": "Unauthorized"
}
404
Returned when the id is unknown.
{
  "error": "Not Found"
}
500
Unexpected server error — safe to retry.
{
  "error": "Internal Server Error"
}
Errors

Every body, pulled from the spec.

Consolidated across all four endpoints — one row per distinct (status, body-shape) pair declared in /openapi/streams.yaml. Bodies are emitted by the route handlers in production — copy these bytes when you write your error handler.

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).
Continue the tour

Wire it up,
or skim the multi-endpoint view.

The multi-endpoint reference keeps streams alongside the agents feed on a single page; the quickstart walks the same flow end to end.