Sentinel API v1

From your first API call to production

Sources

Connect RTSP or ingest test frames

A Source is a durable video input with desired state and observable runtime state. List/get responses never return passwords or unredacted RTSP URLs.

Allow the camera destination first

RTSP is default-deny. Before creating a Source, add an exact IP, host:port, or CIDR to deploy/api/.env, then recreate only the API service. Wildcards are not supported.

SENTINEL_RTSP_ALLOWED_HOSTS=192.168.10.25:554
SENTINEL_RTSP_ALLOWED_CIDRS=192.168.10.0/24

docker compose config --quiet
docker compose up -d --no-deps --force-recreate api
DNS-hostname mode additionally requires the documented firewall and residual-risk flags. Prefer a fixed IP or exact CIDR for first setup.

Create an RTSP Source

curl -X POST http://localhost:8000/v1/sources \
  -H "Authorization: Bearer $SENTINEL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: site-a-loading-dock" \
  -d '{
    "name": "Loading dock",
    "input": {
      "type": "rtsp",
      "url": "rtsp://user:password@camera.local/stream2"
    }
  }'

Submit input.url only when creating or rotating credentials. Responses expose credentials_configured and sanitized connection information, never the URL/password. Prefer a dedicated read-only camera account and low-resolution substream.

Create an image Source without a camera

POST /v1/sources

{
  "name": "Integration test",
  "input": {"type": "frame"}
}

Test and frame ingest

POST /v1/sources/{source_id}/test

POST /v1/sources/{source_id}/frames
Content-Type: image/jpeg
Idempotency-Key: frame-20260829-001

<raw JPEG bytes>

frames is a bounded test/custom-ingest path, not a video-storage API. The server checks Content-Type, byte size, JPEG decoding, and image dimensions before inference.

Retrieve the current image

GET /v1/sources/{source_id}/image?view=preview
GET /v1/sources/{source_id}/image?view=inference

Both return binary image/jpeg, require the same Source authorization, and use Cache-Control: no-store. A Source with no current frame returns 503. Do not treat current images as immutable Event evidence.

Bounded live preview

GET /v1/sources/{source_id}/stream?max_fps=15&max_height=720
Authorization: Bearer $SENTINEL_API_KEY

Content-Type: multipart/x-mixed-replace; boundary=frame

This endpoint returns a bounded MJPEG preview, not a recording or Event-evidence download. It requires sources:read; max_fps defaults to 15 and accepts 1–30, while max_height defaults to 0 (keep source size) and accepts 0–2160. The stream is not stored and uses Cache-Control: no-store.

Runtime states

pending | connecting | online | degraded | offline | stopped

The API reports runtime_state, last_frame_at, and sanitized status_code/status_message so integrations can distinguish connecting, offline, and stream-error conditions. Disabling/re-enabling does not change the src_* ID.

If a Source response, log, or error exposes a complete RTSP URL/password, treat it as a security defect and stop using that build.

Next: create a Monitor for the Source →