API
The REST API and WebSocket.
API
This document describes the Vigil API. It covers the REST API, the WebSocket, and the MediaMTX hooks.
The API contract is in server/api/openapi.yaml. It is an OpenAPI 3.1 document. The base URL is /api/v1.
The contract drives two code generators. oapi-codegen generates the Go server stubs. openapi-typescript generates the TypeScript client types.
Authentication
The API uses session tokens. The security scheme is cookieAuth. The cookie name is nvr_session.
The backend accepts the session in three forms:
- The
nvr_sessioncookie. - The
Authorization: Bearerheader. - The
X-Session-Tokenheader.
The cookie takes precedence when more than one form is present.
A session expires after 30 days. The session token is required for most endpoints.
Authorization roles
The API has three roles:
admin: full access.operator: camera management and probing.viewer: read-only access.
The authorization rules are:
- User administration, settings writes, and Drive administration require
admin. - Camera create, update, delete, discovery, and probe require
operatororadmin. - Camera reads, live, snapshots, recordings, events, and system reads require any authenticated user.
The REST endpoints
System
| Method | Path | Purpose |
|---|---|---|
| GET | /health |
The health of the service. |
| GET | /system/version |
The version and commit. |
| GET | /system/disk |
The disk usage. |
| GET | /system/status |
The system status. |
Authentication
| Method | Path | Purpose |
|---|---|---|
| GET | /auth/status |
If setup is required and the session is valid. |
| POST | /auth/setup |
Create the first administrator. |
| POST | /auth/login |
Log in. |
| POST | /auth/logout |
Log out. |
| GET | /auth/me |
The current user. |
Cameras
| Method | Path | Purpose |
|---|---|---|
| GET | /cameras |
List the cameras. |
| POST | /cameras |
Create a camera. |
| POST | /cameras/discover |
Discover and authenticate RTSP/ONVIF cameras using transient credentials. |
| POST | /cameras/discover/streams |
Detect RTSP streams for a selected ONVIF camera. |
| POST | /cameras/probe |
Probe a camera stream. |
| GET | /cameras/{id} |
Get a camera. |
| PATCH | /cameras/{id} |
Update a camera. |
| DELETE | /cameras/{id} |
Delete a camera. |
| POST | /cameras/{id}/live |
Get a live stream session. |
| GET | /cameras/{id}/snapshot |
Get a camera snapshot. |
| GET | /cameras/{id}/recordings |
List the recordings in a range. |
| POST | /cameras/{id}/playback |
Get a playback session. |
| GET | /recordings/{id}/content |
Stream a tokenized Drive archive with byte ranges. |
Events
| Method | Path | Purpose |
|---|---|---|
| GET | /events |
List the events. |
| POST | /events/{id}/acknowledge |
Acknowledge an event. |
Settings
| Method | Path | Purpose |
|---|---|---|
| GET | /settings |
Get the settings. |
| PATCH | /settings |
Update the settings. |
Google Drive storage
| Method | Path | Purpose |
|---|---|---|
| GET | /storage/gdrive/status |
Get the Drive status. |
| PUT | /storage/gdrive/configuration |
Set the Drive configuration. |
| POST | /storage/gdrive/connect |
Begin the OAuth connection. |
| GET | /storage/gdrive/callback |
The OAuth callback. |
| DELETE | /storage/gdrive/disconnect |
Disconnect Drive. |
| POST | /storage/gdrive/archive |
Run the archive now. |
The archive response reports uploaded, deleted, deleteFailed, failed,
and skipped. deleted counts local MP4s removed after a durable Drive
upload. deleteFailed means the Drive upload succeeded but local cleanup needs
another reconciliation pass; it is not an upload failure.
Archived playback uses GET /recordings/{id}/content?token=.... It is normally
opened by the native video element from a playback session and supports HTTP
Range requests; clients should not construct its token themselves.
Users
| Method | Path | Purpose |
|---|---|---|
| GET | /users |
List the users. |
| POST | /users |
Create a user. |
| DELETE | /users/{id} |
Delete a user. |
The live stream session
The live endpoint returns a live stream session. The session has:
cameraId: the camera ID.hlsUrl: the HLS URL.whepUrl: the WHEP URL.token: the short-lived stream token.expiresAt: the token expiry time.
The client appends the token to the HLS and WHEP URLs. The token expires after about 60 seconds. The client refreshes the session before the token expires.
The playback session
The playback endpoint returns a playback session. The session has:
cameraId: the camera ID.recordingId: the selected recording ID.playbackUrl: a MediaMTX URL for local video or a Vigil URL for Drive video.token: the short-lived playback token.expiresAt: the token expiry time.source:localorgdrive.startOffsetSec: the seek position inside a Drive segment (zero for local playback).nextRecordingStart: the next indexed segment start. The field is omitted when there is no following segment. Drive clients use it to continue across one-minute files.
The playback request has a start time and an optional durationSec. The default duration is 60 seconds.
The WebSocket
The WebSocket is at GET /api/v1/ws. It is not in the OpenAPI contract. It is mounted directly in the entry point.
The WebSocket is server-push only. Commands go over the REST API. The server authenticates the request, upgrades the connection, and subscribes to the event bus.
The server sends JSON text frames. The frame shape is:
{
"type": "event",
"data": {}
}
The server writes with a five-second timeout. A reader goroutine drains incoming frames to detect disconnects.
The dashboard and mobile app do not use the WebSocket. They poll the REST API. Refer to the dashboard and mobile documents.
The MediaMTX hooks
The MediaMTX hooks are internal. They are not part of the public API.
The auth hook
POST /internal/mediamtx/auth
MediaMTX calls this hook to validate a stream request. The handler accepts the MediaMTX external-auth JSON. It validates the token from the password, the token field, or the token query string. It returns 200 on success and 401 on failure.
The segment-complete hook
POST /internal/mediamtx/segment-complete
MediaMTX calls this hook when a recording segment completes. The handler accepts JSON or form data. It tolerates varied field names. It indexes the segment in SQLite.
Error responses
Errors return a JSON object with an error string and an optional code. The HTTP status codes are standard:
400: bad request.401: unauthenticated.403: forbidden.404: not found.409: conflict.504: timeout.
The generated client
The TypeScript client is in packages/api-client. The gen script runs openapi-typescript on the contract. The output is src/generated/schema.ts.
The client wraps openapi-fetch. The createApiClient function creates a client for a base URL. It defaults credentials to include.
Refer to development for the code generation commands.