Sentinel API v1

最初のAPI呼び出しから本番運用まで

Five-minute quickstart

From a clean node to the first image event

This golden path needs no camera or cloud account. First use the deterministic mock provider to verify API-key auth, durable resources, JPEG ingest, Events, webhook signatures, and evidence download—then repeat with real RTSP and a real model.

What do you receive? Event output and fields

Watch sends event JSON by HTTP POST to your configured webhook URL. This shortened alert example shows common fields; IDs, time and content are illustrative.

{
  "id": "evt_example",
  "schema": "sentinel.event.v1",
  "type": "monitor.alert.raised",
  "severity": "critical",
  "state": "raised",
  "created_at": "2026-09-07T12:00:00Z",
  "source_id": "src_camera03",
  "monitor_id": "mon_restricted_area",
  "title": "Person entered restricted area",
  "summary": "Camera 03 detected a person entering the specified area.",
  "evidence": {
    "image_url": "/v1/events/evt_example/image"
  }
}
FieldMeaning
idEvent ID; state updates may share this ID.
typeWhat happened, such as monitor.alert.raised.
severitySeverity: safe, suspicious, or critical.
stateLifecycle: raised, updated, or cleared.
created_atEvent creation time (ISO 8601).
source_id / monitor_idSource and monitor IDs; may be null for system events.
title / summaryHuman-readable text; use typed fields for alarm logic.
evidence.image_urlEvidence image location, when available; requires an events:read API key.

Common types include monitor.alert.raised, monitor.alert.updated, monitor.alert.cleared, source.disconnected and inference.failed. Delivery depends on your webhook filters; critical + raised alone does not include all updates or cleared notifications.

Verify the signature, persist the event and return 2xx. Deduplicate retries using the X-Sentinel-Delivery-ID HTTP header, not just the event ID. Webhooks do not deliver every ordinary inference.

Full event format · Signatures and delivery

5–10 min

local wiring proof

0

cameras required

1 command

node stays running

Docker is mandatory—not optional—for this self-hosted API and packaged model-inference path. If it is not installed, complete the official OS-specific setup below and start the Linux-container engine first; the one-command quickstart does not change virtualization settings or install Docker for you.

0. Get the source kit

Run the commands below from a Sentinel Monitor source-kit checkout. There is not yet an anonymously downloadable image or public source repository; the evaluation kit includes the runtime, Compose files, examples, and these docs.

You do not paste the API key back into Sentinel. Quickstart creates a local bootstrap API key in deploy/api/.env and uses it for the first Event automatically. Only when Postman, your backend, or another integration calls /v1 do you retrieve a scoped key from that server's secret store and send it in the Authorization: Bearer header. It cannot sign in to the website or activate the desktop App.
Request evaluation access before continuing, so the install path and image entitlement are explicit.

Before running: install and start the prerequisites

Docker supplies the isolated API runtime, mock provider, and durable volumes; the quickstart does not start the optional UI. The macOS/Linux path also needs curl and Python 3 because the script verifies readiness, the first Event, and the evidence hash.

docker version
docker compose version
docker info --format '{{.OSType}}'
curl --version
python3 --version
docker version must show both Client and Server, OSType must be linux, and Compose must be v2. If a check fails, finish the official install, non-root/rootless setup, or engine startup first; do not run the quickstart as root or disable security controls to mask the failure.

One command to the first event

macOS, Linux, or Ubuntu/WSL

./deploy/api/quickstart.sh

Windows PowerShell

powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\deploy\api\quickstart.ps1

Run either command from the source-kit root. The quickstart locates its deployment files, creates private secrets when needed, starts the API-only runtime and deterministic mock provider, waits for readiness, executes the complete first-Event example, and leaves the node running. Neither starts the optional UI. On Windows, ExecutionPolicy applies only to that verified kit's child process; it does not change the user or machine policy.

Complete the Windows 11 first-run guide and verify the kit SHA-256/revision first. If enterprise Group Policy blocks it, stop and request administrator approval.

Advanced: walk the same path manually

1. Start the API-only runtime

cd Sentinel-Monitor/deploy/api
# If .env already exists, keep it; bootstrap refuses to overwrite secrets.
./bootstrap.sh mock
docker compose up -d --build

export SENTINEL_API_KEY="$(python3 ../../docs/api/examples/read_dotenv.py   .env SENTINEL_API_KEY)"
export SENTINEL_BASE_URL=http://localhost:8000

curl --fail "$SENTINEL_BASE_URL/readyz"
curl --fail "$SENTINEL_BASE_URL/v1/system" \
  -H "Authorization: Bearer $SENTINEL_API_KEY"
If bootstrap.sh has already run, skip that line; it intentionally refuses to overwrite .env. read_dotenv.py treats .env only as data; never source a Compose dotenv file. The API binds to 127.0.0.1 and the default Compose has no UI.

2. Create a test Source

SOURCE_JSON="$(curl --fail -X POST "$SENTINEL_BASE_URL/v1/sources" \
  -H "Authorization: Bearer $SENTINEL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: quickstart-source-1" \
  -d '{
    "name": "Quickstart JPEG",
    "input": {"type": "frame"}
  }')"
export SOURCE_ID="$(printf '%s' "$SOURCE_JSON" |
  python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')"
printf 'Created %s\n' "$SOURCE_ID"

3. Create a Monitor

MONITOR_JSON="$(python3 -c 'import json,os
print(json.dumps({
  "name": "After-hours loading dock",
  "source_id": os.environ["SOURCE_ID"],
  "prompt": "Notify when a person enters the loading area after hours.",
  "enabled": True,
}))' |
  curl --fail -X POST "$SENTINEL_BASE_URL/v1/monitors" \
    -H "Authorization: Bearer $SENTINEL_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: quickstart-monitor-1" \
    --data-binary @-)"
export MONITOR_ID="$(printf '%s' "$MONITOR_JSON" |
  python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')"
printf 'Created %s\n' "$MONITOR_ID"

4. Optional: register and test a Webhook

: "${SENTINEL_WEBHOOK_URL:?Export a reachable HTTPS receiver URL first}"

WEBHOOK_JSON="$(python3 -c 'import json,os
print(json.dumps({
  "name": "My receiver",
  "url": os.environ["SENTINEL_WEBHOOK_URL"],
  "secret_env": "SENTINEL_WEBHOOK_SIGNING_SECRET",
  "enabled": True,
  "filters": {"event_types": ["sdk.test"]},
}))' |
  curl --fail -X POST "$SENTINEL_BASE_URL/v1/webhook-endpoints" \
    -H "Authorization: Bearer $SENTINEL_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: quickstart-webhook-1" \
    --data-binary @-)"
export WEBHOOK_ID="$(printf '%s' "$WEBHOOK_JSON" |
  python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')"

curl --fail -X POST \
  "$SENTINEL_BASE_URL/v1/webhook-endpoints/$WEBHOOK_ID/test" \
  -H "Authorization: Bearer $SENTINEL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: quickstart-webhook-test-1" \
  -d '{
    "severity": "suspicious",
    "title": "Sentinel webhook test",
    "summary": "Verify receipt and signature."
  }'

The automated smoke already verifies exact-body HMAC with a local receiver. Run this manual step only when you have a reachable receiver, and keep it running to receive the sdk.test Event below.

5. Send actual JPEG bytes

export TEST_JPEG="${TMPDIR:-/tmp}/sentinel-quickstart-frame.jpg"

python3 -c 'import base64,pathlib,sys
pathlib.Path(sys.argv[2]).write_bytes(base64.b64decode(pathlib.Path(sys.argv[1]).read_text()))' \
  ../../docs/api/assets/sample-frame.jpg.b64 "$TEST_JPEG"

curl --fail -X POST "$SENTINEL_BASE_URL/v1/sources/$SOURCE_ID/frames" \
  -H "Authorization: Bearer $SENTINEL_API_KEY" \
  -H "Content-Type: image/jpeg" \
  -H "Idempotency-Key: quickstart-frame-1" \
  --data-binary "@$TEST_JPEG"

frames is a bounded test/custom-ingest path, not a video-storage API. The runtime validates media type, decoded dimensions, and size limits before inference.

6. Create a deterministic Event with image evidence

EVENT_JSON="$(python3 -c 'import base64,json,os,pathlib
print(json.dumps({
  "source_id": os.environ["SOURCE_ID"],
  "monitor_id": os.environ["MONITOR_ID"],
  "type": "sdk.test",
  "severity": "suspicious",
  "summary": "Quickstart evidence event",
  "image_base64": base64.b64encode(pathlib.Path(os.environ["TEST_JPEG"]).read_bytes()).decode(),
}))' |
  curl --fail -X POST "$SENTINEL_BASE_URL/v1/events/test" \
    -H "Authorization: Bearer $SENTINEL_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: quickstart-event-1" \
    --data-binary @-)"
export EVENT_ID="$(printf '%s' "$EVENT_JSON" |
  python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')"
printf 'Created %s\n' "$EVENT_ID"

image_base64 exists only on the controlled test-event endpoint so a camera-free setup can validate evidence. Normal Events and webhooks return image_url and do not inline Base64 by default.

7. Query the Event and download the JPEG

curl --fail "$SENTINEL_BASE_URL/v1/events/$EVENT_ID" \
  -H "Authorization: Bearer $SENTINEL_API_KEY"

curl --fail "$SENTINEL_BASE_URL/v1/events/$EVENT_ID/image" \
  -H "Authorization: Bearer $SENTINEL_API_KEY" \
  --output event.jpg

file event.jpg

Ordinary inference text and live preview

curl --no-buffer --fail \
  "$SENTINEL_BASE_URL/v1/analyses/stream?monitor_id=$MONITOR_ID" \
  -H "Authorization: Bearer $SENTINEL_API_KEY" \
  -H "Accept: text/event-stream"

curl --fail \
  "$SENTINEL_BASE_URL/v1/sources/$SOURCE_ID/stream?max_fps=15&max_height=720" \
  -H "Authorization: Bearer $SENTINEL_API_KEY" \
  --output preview.mjpeg

The first stream emits every completed ordinary inference, including safe results; the second is the bounded MJPEG preview. Use /v1/events, /v1/events/stream, or signed Webhooks for alerts, warnings, and errors. Closing the desktop window does not stop these API outputs.

Python: run the same tested flow

The source kit includes a complete Python 3 standard-library example. It creates a Source and Monitor, ingests a JPEG, creates a test Event, and verifies the downloaded evidence SHA-256.

cd deploy/api
export SENTINEL_BASE_URL=http://127.0.0.1:8000
export SENTINEL_API_KEY="<your-server-side-key>"
python3 ../../docs/api/examples/first_event.py

JavaScript: query and binary image

import { writeFile } from "node:fs/promises";

const base = "http://localhost:8000";
const headers = { Authorization: `Bearer ${process.env.SENTINEL_API_KEY}` };

const events = await fetch(`${base}/v1/events?limit=10`, { headers })
  .then(async (response) => {
    if (!response.ok) throw await response.json();
    return response.json();
  });

const event = events.data[0];
if (!event) throw new Error("No events returned");
const image = await fetch(new URL(event.evidence.image_url, base), { headers });
if (!image.ok) throw new Error(`image download failed: ${image.status}`);
await writeFile("event.jpg", Buffer.from(await image.arrayBuffer()));

Move to real input

The mock quickstart already created deploy/api/.env. Do not rerun bootstrap over it. Preserve the generated API/Webhook secrets, replace only the reviewed provider settings, validate Compose, and recreate the required API/provider services.
  1. Select local_* or private_openai_compatible from the model guide; use cloud_gemini only after its documented release gate passes.
  2. Configure the exact RTSP host/host:port or CIDR allowlist first, then create a Source with input.type=rtsp.
  3. Call Source test and verify a decodable frame or sanitized failure code.
  4. Create a new Monitor linked to the RTSP Source, then trigger real inference with representative footage; the mock Monitor remains linked to the frame Source.

Next: complete Source guide →

Done means: you can query the Event with a Bearer key, download a decodable JPEG from its protected URL, and the automated smoke has verified a signed webhook.