-
Notifications
You must be signed in to change notification settings - Fork 3
REST API Reference
Docker Sentinel exposes a JSON REST API on the same port as the web dashboard (default :8080). All API endpoints return JSON unless stated otherwise.
When authentication is enabled (the default after first-run setup), every API request must include one of:
-
Session cookie, set automatically by the browser after
POST /login. -
API bearer token, created on the My Account page or via
POST /api/auth/tokens.
Authorization: Bearer sntkn_abc123...
Token-authenticated requests are CSRF-exempt. Session-authenticated requests must include the CSRF token from the sentinel_csrf cookie in the X-CSRF-Token header.
When authentication is disabled, all endpoints are accessible without credentials.
The webhook endpoint (POST /api/webhook) uses its own secret-based auth instead of sessions/tokens.
10 granular permissions grouped into 3 built-in roles:
| Permission | Admin | Operator | Viewer |
|---|---|---|---|
containers.view |
yes | yes | yes |
containers.update |
yes | yes | no |
containers.manage |
yes | yes | no |
containers.approve |
yes | yes | no |
containers.rollback |
yes | yes | no |
history.view |
yes | yes | yes |
logs.view |
yes | yes | yes |
settings.view |
yes | yes | no |
settings.modify |
yes | no | no |
users.manage |
yes | no | no |
All error responses use a consistent JSON envelope:
{"error": "description of the problem"}| Code | Meaning |
|---|---|
400 |
Bad request / validation failure |
401 |
Not authenticated |
403 |
Insufficient permissions or self-protection |
404 |
Resource not found |
409 |
Conflict (e.g. setup already complete, username taken) |
429 |
Rate limited (login attempts) |
500 |
Internal server error |
502 |
Registry check failed (upstream error) |
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/containers |
containers.view |
List all containers |
GET |
/api/containers/{name} |
containers.view |
Container detail (history, snapshots) |
GET |
/api/containers/{name}/versions |
containers.view |
Available semver versions from registry |
GET |
/api/containers/{name}/tags |
containers.view |
All tags for the container's image |
GET |
/api/containers/{name}/logs |
containers.view |
Container log output |
GET |
/api/containers/{name}/row |
containers.view |
HTML partial + stats (for SSE row refresh) |
GET |
/api/containers/{name}/ghcr |
containers.view |
GHCR alternative image info |
GET |
/api/stats |
containers.view |
Dashboard stat card counts |
GET |
/api/last-scan |
containers.view |
Timestamp of last completed scan |
GET |
/api/ghcr/alternatives |
containers.view |
All known GHCR alternatives |
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/containers[
{
"id": "abc123def456...",
"name": "nginx",
"image": "nginx:1.27.4",
"policy": "auto",
"state": "running",
"maintenance": false,
"stack": "webstack"
}
]curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/containers/nginx{
"id": "abc123def456...",
"name": "nginx",
"image": "nginx:1.27.4",
"policy": "auto",
"state": "running",
"maintenance": false,
"history": [
{
"timestamp": "2025-01-15T14:30:00Z",
"container_name": "nginx",
"type": "update",
"old_image": "nginx:1.27.3",
"new_image": "nginx:1.27.4",
"outcome": "success",
"duration": 12500000000
}
],
"snapshots": [
{
"container_name": "nginx",
"timestamp": "2025-01-15T14:29:58Z"
}
]
}| Parameter | Type | Default | Description |
|---|---|---|---|
lines |
int | 50 | Number of lines (max 500) |
host |
string | Cluster host ID for remote containers |
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/api/containers/nginx/logs?lines=100"{
"logs": "2025-01-15 10:30:00 ...\n...",
"lines": 100,
"remote": false
}Returns lightweight counts for the dashboard stat cards.
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/stats{
"total": 25,
"running": 23,
"pending": 2
}{"last_scan": "2025-01-15T14:00:00Z"}Returns {"last_scan": null} if no scan has run yet.
All action endpoints accept an optional ?host=<hostID> query parameter to target remote cluster containers. Actions on the Sentinel container itself (identified by sentinel.self=true label) return 403 Forbidden.
| Method | Path | Permission | Description |
|---|---|---|---|
POST |
/api/containers/{name}/restart |
containers.manage |
Restart a container |
POST |
/api/containers/{name}/stop |
containers.manage |
Stop a container |
POST |
/api/containers/{name}/start |
containers.manage |
Start a container |
POST |
/api/update/{name} |
containers.update |
Trigger update (pull + recreate) |
POST |
/api/check/{name} |
containers.update |
Check registry for updates |
POST |
/api/containers/{name}/rollback |
containers.rollback |
Rollback to last snapshot |
POST |
/api/containers/{name}/switch-ghcr |
containers.update |
Migrate from Docker Hub to GHCR |
POST |
/api/containers/{name}/update-to-version |
containers.update |
Update to a specific tag |
POST |
/api/scan |
containers.update |
Trigger a full scan cycle |
POST |
/api/self-update |
settings.modify |
Self-update Sentinel |
Triggers a container update. If a newer version was found during scanning, the container is recreated with the new image. Runs asynchronously.
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/update/nginx{
"status": "started",
"name": "nginx",
"message": "update started for nginx"
}Recreates a container with an explicit image tag. For Sentinel containers, routes through the self-updater helper.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"tag": "1.27.5"}' \
http://localhost:8080/api/containers/nginx/update-to-version{
"status": "started",
"name": "nginx",
"message": "Updating nginx to nginx:1.27.5"
}Runs a synchronous registry check for a single container. If an update is found, it is added to the queue and SSE events are emitted. Ignored versions are filtered out.
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/check/nginxUpdate available:
{
"status": "update_available",
"name": "nginx",
"message": "Update available for nginx",
"newer_versions": ["1.27.5"]
}Up to date:
{
"status": "up_to_date",
"name": "nginx",
"message": "nginx is up to date"
}Rolls back to the most recent pre-update snapshot. If a rollback_policy setting is configured (manual or pinned), the container's policy is changed after rollback to prevent the next scan from retrying the same update.
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/containers/nginx/rollback{
"status": "started",
"name": "nginx",
"message": "rollback started for nginx"
}Triggers an immediate full scan of all containers. Runs asynchronously.
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/scan{"message": "Scan started"}Triggers a self-update via an ephemeral helper container. Sentinel will restart.
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/self-update{
"status": "started",
"message": "Self-update to v2.1.0 initiated -- Sentinel will restart shortly"
}| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/queue |
containers.view |
List pending updates (with release notes URLs) |
GET |
/api/queue/count |
containers.view |
Pending update count |
POST |
/api/approve/{key} |
containers.approve |
Approve and execute a pending update |
POST |
/api/reject/{key} |
containers.approve |
Reject and remove a pending update |
POST |
/api/ignore/{key} |
containers.approve |
Ignore a specific version |
The {key} is the container name for local containers, or hostID::name for remote cluster containers.
Returns pending updates enriched with release notes URLs when available.
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/queue[
{
"container_name": "nginx",
"current_image": "nginx:1.27.4",
"newer_versions": ["1.27.5"],
"resolved_current_version": "1.27.4",
"resolved_target_version": "1.27.5",
"release_notes_url": "https://github.com/nginx/nginx/releases/tag/1.27.5"
}
]Lightweight count without release notes enrichment.
{"count": 3}Approves a pending update and triggers the update asynchronously. For remote containers, dispatches to the cluster agent.
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/approve/nginx{
"status": "approved",
"name": "nginx",
"message": "update started for nginx"
}Ignores the top version in the pending update. That version will be excluded from future queue entries.
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/ignore/nginx{
"status": "ignored",
"name": "nginx",
"version": "1.27.5",
"message": "version 1.27.5 ignored for nginx"
}Removes the pending update from the queue without ignoring the version. It may reappear on the next scan.
{
"status": "rejected",
"name": "nginx",
"message": "update rejected for nginx"
}| Method | Path | Permission | Description |
|---|---|---|---|
POST |
/api/containers/{name}/policy |
containers.manage |
Set policy override |
DELETE |
/api/containers/{name}/policy |
containers.manage |
Remove policy override (revert to label) |
POST |
/api/bulk/policy |
containers.manage |
Bulk policy change |
Valid policies: auto, manual, pinned.
For remote containers, add ?host=<hostID> to scope the override.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"policy": "manual"}' \
http://localhost:8080/api/containers/nginx/policy{
"status": "ok",
"name": "nginx",
"policy": "manual",
"message": "policy set to manual for nginx"
}Supports two modes: preview (default) shows what would change, confirm applies the changes. Self-protected containers are blocked.
Preview:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"containers": ["nginx", "redis"], "policy": "pinned"}' \
http://localhost:8080/api/bulk/policy{
"mode": "preview",
"changes": [{"name": "nginx", "from": "auto", "to": "pinned"}],
"blocked": [],
"unchanged": [{"name": "redis", "reason": "already pinned"}]
}Confirm:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"containers": ["nginx", "redis"], "policy": "pinned", "confirm": true}' \
http://localhost:8080/api/bulk/policy{
"mode": "executed",
"applied": 1,
"blocked": 0,
"unchanged": 1
}| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/settings |
settings.view |
All settings (env + runtime overrides) |
GET |
/api/about |
settings.view |
Version, uptime, stats, channels |
GET |
/api/ratelimits |
containers.view |
Registry rate limit status |
GET |
/api/release-sources |
settings.view |
Release note source mappings |
Returns all configuration values merged from environment variables and runtime overrides stored in BoltDB.
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/settings{
"SENTINEL_POLL_INTERVAL": "6h",
"poll_interval": "6h",
"default_policy": "auto",
"paused": "false"
}{
"version": "v2.0.1",
"commit": "a1b2c3d",
"go_version": "go1.24.4",
"data_directory": "/data",
"uptime": "3d 12h 5m",
"started_at": "2025-01-12T08:00:00Z",
"poll_interval": "6h",
"last_scan": "2025-01-15T14:00:00Z",
"containers": 25,
"updates_applied": 42,
"snapshots": 150,
"channels": [{"name": "Gotify", "type": "gotify"}],
"registries": ["docker.io", "ghcr.io"]
}{
"health": "ok",
"registries": [
{
"registry": "docker.io",
"remaining": 95,
"limit": 100,
"reset": "2025-01-15T15:00:00Z"
}
]
}All setting modification endpoints accept JSON request bodies and require settings.modify permission. They return a status message on success.
| Method | Path | Body | Description |
|---|---|---|---|
POST |
/api/settings/poll-interval |
{"interval": "6h"} |
Poll interval (5m to 24h) |
POST |
/api/settings/default-policy |
{"policy": "auto"} |
Default policy (auto/manual/pinned) |
POST |
/api/settings/grace-period |
{"duration": "30s"} |
Grace period (0 to 10m) |
POST |
/api/settings/pause |
{"paused": true} |
Pause/unpause scanning |
POST |
/api/settings/latest-auto-update |
{"enabled": true} |
Auto-update :latest containers |
POST |
/api/settings/filters |
{"patterns": ["test-*"]} |
Scan exclusion filters |
POST |
/api/settings/image-cleanup |
{"enabled": true} |
Remove old images after update |
POST |
/api/settings/image-backup |
{"enabled": true} |
Backup images before update |
POST |
/api/settings/schedule |
{"schedule": "0 3 * * *"} |
Cron schedule (empty to disable) |
POST |
/api/settings/hooks-enabled |
{"enabled": true} |
Toggle lifecycle hooks |
POST |
/api/settings/hooks-write-labels |
{"enabled": true} |
Toggle hook label persistence |
POST |
/api/settings/dependency-aware |
{"enabled": true} |
Dependency-aware update ordering |
POST |
/api/settings/rollback-policy |
{"policy": "manual"} |
Policy after rollback (none/manual/pinned) |
POST |
/api/settings/version-scope |
{"scope": "minor"} |
Default version scope |
POST |
/api/settings/dry-run |
{"enabled": true} |
Dry-run mode |
POST |
/api/settings/pull-only |
{"enabled": true} |
Pull-only mode (no recreate) |
POST |
/api/settings/update-delay |
{"delay": "1h"} |
Delay before applying updates |
POST |
/api/settings/show-stopped |
{"enabled": true} |
Show stopped containers on dashboard |
POST |
/api/settings/remove-volumes |
{"enabled": true} |
Remove volumes on container recreate |
POST |
/api/settings/scan-concurrency |
{"concurrency": 4} |
Concurrent registry checks |
POST |
/api/settings/maintenance-window |
{"window": "..."} |
Maintenance window |
POST |
/api/settings/stack-order |
{"order": ["a", "b"]} |
Dashboard stack display order |
POST |
/api/settings/dashboard-columns |
{"columns": ["image","status"]} |
Visible dashboard columns |
POST |
/api/settings/compose-sync |
{"enabled": true} |
Compose sync |
POST |
/api/settings/ha-discovery |
{"enabled": true, ...} |
HA discovery settings |
POST |
/api/settings/switch-role |
{"role": "server"} |
Switch instance role |
POST |
/api/settings/docker-tls |
{"ca":"..","cert":"..","key":".."} |
Docker TLS cert paths |
POST |
/api/settings/docker-tls-test |
(same as above) | Test Docker TLS connection |
POST |
/api/settings/general |
{"key": "web_port", "value": "9090"} |
General settings (web_port, tls_mode, log_format) |
PUT |
/api/release-sources |
[{"image_pattern":"..","github_repo":".."}] |
Replace release note source mappings |
Example:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"interval": "4h"}' \
http://localhost:8080/api/settings/poll-interval{
"status": "ok",
"interval": "4h0m0s",
"message": "poll interval updated to 4h0m0s"
}| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/settings/notifications |
settings.view |
List channels (secrets masked) |
PUT |
/api/settings/notifications |
settings.modify |
Save channels |
POST |
/api/settings/notifications/test |
settings.modify |
Send test notification |
GET |
/api/settings/notifications/event-types |
settings.view |
Available event types for filtering |
GET |
/api/settings/notifications/templates |
settings.view |
Custom notification templates |
PUT |
/api/settings/notifications/templates |
settings.modify |
Save a custom template |
DELETE |
/api/settings/notifications/templates/{type} |
settings.modify |
Delete template (revert to default) |
POST |
/api/settings/notifications/templates/preview |
settings.modify |
Preview template with sample data |
Saves the full channel list. Secrets from previously saved channels are restored if the value is masked in the request.
curl -X PUT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '[{
"id": "ch1",
"name": "Gotify",
"type": "gotify",
"enabled": true,
"settings": {"url": "http://gotify:8080", "token": "abc123"}
}]' \
http://localhost:8080/api/settings/notifications{"status": "ok", "message": "notification settings saved"}Test a specific channel by ID, or the entire notification chain if no ID is provided.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"id": "ch1"}' \
http://localhost:8080/api/settings/notifications/test{"status": "ok", "message": "test notification sent to Gotify"}curl -X PUT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"event_type": "update_available", "template": "Update: {{.ContainerName}}"}' \
http://localhost:8080/api/settings/notifications/templatescurl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"event_type": "update_available", "template": "Update: {{.ContainerName}}"}' \
http://localhost:8080/api/settings/notifications/templates/preview{"preview": "Update: sentinel-test"}| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/containers/{name}/notify-pref |
settings.view |
Get notification mode |
POST |
/api/containers/{name}/notify-pref |
settings.modify |
Set notification mode |
GET |
/api/settings/container-notify-prefs |
settings.view |
All per-container preferences |
DELETE |
/api/notify-states |
settings.modify |
Clear all notification dedup states |
Valid modes: default, every_scan, digest_only, muted.
For remote containers, add ?host=<hostID>.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"mode": "muted"}' \
http://localhost:8080/api/containers/nginx/notify-pref{"status": "ok", "mode": "muted"}| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/settings/digest |
settings.view |
Get digest settings |
POST |
/api/settings/digest |
settings.modify |
Save digest settings |
POST |
/api/digest/trigger |
settings.modify |
Trigger immediate digest |
GET |
/api/digest/banner |
containers.view |
Pending digest banner info |
POST |
/api/digest/banner/dismiss |
containers.view |
Dismiss the banner |
All fields are optional; only provided fields are updated.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"digest_enabled": true, "digest_time": "09:00", "digest_interval": "24h"}' \
http://localhost:8080/api/settings/digest| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/settings/registries |
settings.view |
List credentials (masked) with rate limit status |
PUT |
/api/settings/registries |
settings.modify |
Save credentials |
POST |
/api/settings/registries/test |
settings.modify |
Test a credential |
DELETE |
/api/settings/registries/{id} |
settings.modify |
Delete a credential |
Secrets ending in **** are restored from previously saved values.
curl -X PUT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '[{"id":"r1","registry":"docker.io","username":"myuser","secret":"dckr_pat_abc"}]' \
http://localhost:8080/api/settings/registries{"status": "ok", "message": "registry credentials saved"}curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"registry":"docker.io","username":"myuser","secret":"dckr_pat_abc"}' \
http://localhost:8080/api/settings/registries/test{"success": true, "message": "Credentials valid"}| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/hooks/{container} |
settings.view |
List hooks for a container |
POST |
/api/hooks/{container} |
settings.modify |
Create or update a hook |
DELETE |
/api/hooks/{container}/{phase} |
settings.modify |
Delete a hook |
Valid phases: pre-update, post-update. For remote containers, add ?host=<hostID>.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"phase":"pre-update","command":["sh","-c","pg_dump > backup.sql"],"timeout":60}' \
http://localhost:8080/api/hooks/postgres{
"container_name": "postgres",
"phase": "pre-update",
"command": ["sh", "-c", "pg_dump > backup.sql"],
"timeout": 60
}| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/deps |
containers.view |
Full dependency graph |
GET |
/api/deps/{container} |
containers.view |
Dependencies for one container |
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/deps{
"containers": [
{"name": "app", "dependencies": ["db"], "dependents": []},
{"name": "db", "dependencies": [], "dependents": ["app"]}
],
"order": ["db", "app"],
"has_cycles": false
}{
"name": "app",
"dependencies": ["db"],
"dependents": []
}Available when Docker is running in Swarm mode.
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/services |
containers.view |
List all Swarm services |
GET |
/api/services/{name}/detail |
containers.view |
Service detail with tasks |
POST |
/api/services/{name}/update |
containers.update |
Update service image |
POST |
/api/services/{name}/rollback |
containers.rollback |
Native Swarm rollback |
POST |
/api/services/{name}/scale |
containers.manage |
Scale replicas |
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"replicas": 3}' \
http://localhost:8080/api/services/web/scale{"status": "scaled", "previous_replicas": 1}Maximum 100 replicas. Scaling to 0 saves the previous count so "scale up" can restore it.
All cluster endpoints require settings.modify permission.
| Method | Path | Description |
|---|---|---|
GET |
/api/cluster/hosts |
List enrolled hosts with connection status |
POST |
/api/cluster/enroll-token |
Generate enrolment token |
DELETE |
/api/cluster/hosts/{id} |
Remove a host |
POST |
/api/cluster/hosts/{id}/revoke |
Revoke a host's certificate |
POST |
/api/cluster/hosts/{id}/pause |
Pause a host |
GET |
/api/settings/cluster |
Get cluster settings |
POST |
/api/settings/cluster |
Save cluster settings |
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/cluster/enroll-token{"token": "enroll_abc123...", "id": "host-uuid"}| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/portainer/endpoints |
containers.view |
List Portainer endpoints |
GET |
/api/portainer/endpoints/{id}/containers |
containers.view |
Containers for an endpoint |
POST |
/api/settings/portainer-enabled |
settings.modify |
Toggle Portainer integration |
POST |
/api/settings/portainer-url |
settings.modify |
Set Portainer URL |
POST |
/api/settings/portainer-token |
settings.modify |
Set Portainer API token |
POST |
/api/settings/portainer-test |
settings.modify |
Test connection |
| Method | Path | Permission | Description |
|---|---|---|---|
POST |
/api/settings/npm-enabled |
settings.modify |
Toggle NPM integration |
POST |
/api/settings/npm-url |
settings.modify |
Set NPM URL |
POST |
/api/settings/npm-credentials |
settings.modify |
Set NPM login credentials |
POST |
/api/settings/npm-test |
settings.modify |
Test connection |
POST |
/api/settings/npm-sync |
settings.modify |
Sync proxy host mappings |
GET |
/api/settings/npm-mappings |
settings.view |
Get cached mappings |
GET /api/settings/npm-mappings supports ?grouped=true for mappings grouped by forward host.
Custom URL overrides per container port. Used for dashboard port link chips.
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/containers/{name}/port-config |
containers.view |
Get port overrides |
POST |
/api/containers/{name}/port-config/{port} |
settings.modify |
Set a port override |
DELETE |
/api/containers/{name}/port-config/{port} |
settings.modify |
Remove a port override |
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://app.example.com"}' \
http://localhost:8080/api/containers/nginx/port-config/8080| Method | Path | Description |
|---|---|---|
POST |
/login |
Login (form or JSON) |
POST |
/setup |
First-run admin setup |
POST |
/logout |
End session |
POST |
/api/auth/totp/verify |
Complete 2FA login |
POST |
/api/auth/passkeys/login/begin |
Begin passkey login |
POST |
/api/auth/passkeys/login/finish |
Finish passkey login |
GET |
/api/auth/passkeys/available |
Check if passkeys are configured |
GET |
/api/auth/oidc/login |
Initiate OIDC login flow |
GET |
/api/auth/oidc/callback |
OIDC callback handler |
GET |
/api/auth/oidc/available |
Check if OIDC is configured |
| Method | Path | Description |
|---|---|---|
GET |
/api/auth/me |
Current user info and permissions |
POST |
/api/auth/change-password |
Change own password |
GET |
/api/auth/sessions |
List own sessions |
DELETE |
/api/auth/sessions/{token} |
Revoke a session |
DELETE |
/api/auth/sessions |
Revoke all other sessions |
POST |
/api/auth/tokens |
Create API bearer token |
DELETE |
/api/auth/tokens/{id} |
Delete API token |
POST |
/api/auth/passkeys/register/begin |
Begin passkey registration |
POST |
/api/auth/passkeys/register/finish |
Finish passkey registration |
GET |
/api/auth/passkeys |
List own passkeys |
DELETE |
/api/auth/passkeys/{id} |
Delete a passkey |
POST |
/api/auth/totp/setup |
Begin 2FA setup (returns secret + QR) |
POST |
/api/auth/totp/confirm |
Confirm 2FA (returns recovery codes) |
POST |
/api/auth/totp/disable |
Disable 2FA (requires password) |
GET |
/api/auth/totp/status |
2FA status |
| Method | Path | Description |
|---|---|---|
GET |
/api/auth/users |
List all users |
POST |
/api/auth/users |
Create user |
DELETE |
/api/auth/users/{id} |
Delete user |
POST |
/api/auth/settings |
Toggle auth on/off |
GET |
/api/settings/oidc |
Get OIDC settings |
POST |
/api/settings/oidc |
Save OIDC settings |
curl -X POST -H "Content-Type: application/json" \
-d '{"username":"admin","password":"secret"}' \
http://localhost:8080/loginSuccess:
{"redirect": "/"}2FA required:
{"totp_required": true, "totp_token": "pending_abc123"}curl -X POST -H "Content-Type: application/json" \
-d '{"totp_token":"pending_abc123","code":"123456"}' \
http://localhost:8080/api/auth/totp/verify{"redirect": "/"}curl -X POST -H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "CI Token"}' \
http://localhost:8080/api/auth/tokens{
"id": "tok_abc123",
"name": "CI Token",
"token": "sntkn_full_token_shown_once"
}The plaintext token is returned only once at creation time.
Valid roles: admin, operator, viewer.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"username":"operator1","password":"strongpass123","role_id":"operator"}' \
http://localhost:8080/api/auth/users{"id": "usr_abc123", "username": "operator1"}{
"id": "usr_abc123",
"username": "admin",
"role_id": "admin",
"permissions": ["containers.view", "containers.update", "..."],
"auth_enabled": true
}| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/api/webhook |
Webhook secret | Receive push events from registries or CI |
POST |
/api/settings/webhook-enabled |
Session/token | Toggle webhooks |
POST |
/api/settings/webhook-secret |
Session/token | Regenerate secret |
GET |
/api/settings/webhook-info |
Session/token | Webhook config (secret masked) |
Uses webhook secret authentication (not session/token). Pass the secret as a query parameter or X-Webhook-Secret header.
curl -X POST "http://localhost:8080/api/webhook?secret=your_secret" \
-H "Content-Type: application/json" \
-d '{"push_data":{"tag":"latest"},"repository":{"repo_name":"myuser/myapp"}}'{
"status": "accepted",
"image": "myuser/myapp",
"tag": "latest",
"source": "dockerhub"
}Supported payload formats: Docker Hub, GHCR, and generic. Unrecognised payloads trigger a full scan as a fallback.
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/history |
history.view |
Recent update history |
GET |
/api/history/export |
history.view |
Export all history |
GET |
/api/logs |
logs.view |
Activity log entries |
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
int | 50 | Max records (up to 200) |
before |
string | RFC3339 timestamp for cursor-based pagination |
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/api/history?limit=10"[
{
"timestamp": "2025-01-15T14:30:00Z",
"container_name": "nginx",
"type": "update",
"old_image": "nginx:1.27.3",
"new_image": "nginx:1.27.4",
"outcome": "success",
"duration": 12500000000,
"host_id": "",
"host_name": ""
}
]| Parameter | Type | Default | Description |
|---|---|---|---|
format |
string | json |
json or csv
|
curl -H "Authorization: Bearer $TOKEN" -o history.csv \
"http://localhost:8080/api/history/export?format=csv"CSV columns: timestamp, container, type, old_image, new_image, outcome, duration_s, error, host_id, host_name.
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/images |
containers.view |
List all Docker images |
POST |
/api/images/prune |
containers.manage |
Remove dangling images |
DELETE |
/api/images/{id} |
containers.manage |
Remove a specific image |
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/images/prune| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/config/export |
settings.modify |
Download full config backup |
POST |
/api/config/import |
settings.modify |
Import config from backup (max 5 MB) |
GET |
/api/grafana-dashboard |
settings.modify |
Download Grafana dashboard JSON |
By default, secrets are replaced with ***REDACTED***. Add ?secrets=true to include them.
curl -H "Authorization: Bearer $TOKEN" -o sentinel-config.json \
http://localhost:8080/api/config/export{
"version": "1",
"exported_at": "2025-01-15T14:00:00Z",
"settings": {"poll_interval": "6h", "default_policy": "auto"},
"notifications": [],
"registries": []
}Redacted values (***REDACTED***) are skipped. Unknown setting keys are rejected.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @sentinel-config.json \
http://localhost:8080/api/config/import{
"message": "Imported 15 settings, 2 notification channels, 1 registry credentials",
"settings_imported": 15,
"notifications_imported": 2,
"registries_imported": 1,
"redacted_skipped": 3,
"warnings": []
}| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/metrics |
None | Prometheus metrics |
Standard Prometheus exposition format. Only available when SENTINEL_METRICS=true is set.
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/events |
containers.view |
Real-time event stream |
The SSE endpoint keeps a long-lived connection open and pushes events as they occur. The web dashboard uses this for live updates without polling.
curl -N -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/eventsOn connect, the server sends an initial event:
event: connected
data: {}
| Event Type | Fired When |
|---|---|
container_update |
Update started, completed, or failed |
container_state |
Container started, stopped, or restarted |
queue_change |
Queue item added or removed |
scan_complete |
Full scan finished |
policy_change |
Policy override changed |
digest_ready |
Digest notification ready |
settings_change |
Settings modified |
rate_limits |
Rate limit status changed |
ghcr_check |
GHCR alternative detected |
service_update |
Swarm service update event |
cluster_host |
Cluster host connected, disconnected, or enrolled |
All events share a common JSON structure:
{
"type": "container_update",
"container_name": "nginx",
"message": "Update available for nginx",
"host_id": "",
"host_name": "",
"timestamp": "2025-01-15T14:30:00Z"
}host_id and host_name are populated for cluster events. They are empty for local events.
event: connected
data: {}
event: scan_complete
data: {"type":"scan_complete","message":"Scan complete","timestamp":"2025-01-15T14:00:00Z"}
event: container_update
data: {"type":"container_update","container_name":"nginx","message":"Update available","timestamp":"2025-01-15T14:00:01Z"}
event: queue_change
data: {"type":"queue_change","container_name":"nginx","message":"Added to queue","timestamp":"2025-01-15T14:00:01Z"}
Getting Started
Using Sentinel
Multi-Host
Security
Reference