HTTP API¶
An optional, token-authenticated HTTP control API exposes sabon's operations — show config, list jobs and snapshots, trigger backup/check/prune/restore, and browse run history.
It is off by default. Set SABON_API_ADDR
to enable it and SABON_API_TOKEN to a
bearer token every request must carry. The token is optional but strongly
recommended — leaving it empty disables authentication (sabon warns at
startup). It is privileged — a restore overwrites live data — so bind it
to loopback and put TLS in front of it (a reverse proxy), the same way you would
expose anything else.
services:
sabon:
environment:
SABON_API_ADDR: ":8080"
SABON_API_TOKEN: ${SABON_API_TOKEN}
ports:
- "127.0.0.1:8080:8080" # loopback; front with a TLS reverse proxy
Authentication¶
Every request must send the token as a bearer:
A missing or wrong token returns 401 (when a token is configured).
Endpoints¶
All routes are under /api/v1. Responses are JSON unless noted.
Reads¶
| Method | Path | Description |
|---|---|---|
GET |
/config |
Configured targets (secrets/credentials omitted). |
GET |
/jobs |
Discovered backup jobs (app, sources, targets, snapshot mode). |
GET |
/apps/{app}/snapshots?target=<name> |
restic snapshots for the app in a target (target required). |
Triggers¶
Mutating operations are fire-and-forget: the POST returns 202 Accepted
immediately and the work runs in the background. Watch it under
run history. target is optional for backup/check/prune (empty
= all of the app's targets).
| Method | Path | Description |
|---|---|---|
POST |
/apps/{app}/backup?target=<name> |
Back up the app. |
POST |
/apps/{app}/check?target=<name> |
restic check (repo integrity). |
POST |
/apps/{app}/prune?target=<name> |
restic prune (reclaim space). |
POST |
/apps/{app}/restore |
Restore (JSON body below). |
restore body:
{
"target": "onsite", // required — which repo to restore from
"snapshot": "latest", // snapshot id; default "latest"
"into": "/mnt/staging", // host dir for a staging restore; omit for in-place
"stop": true, // stop the app during an in-place restore
"include": ["/data/immich-dbdump"] // limit to matching paths (sources live under /data/<name>)
}
An API-triggered run is identical to a scheduled one — it takes the same locks and records the same metrics and notifications.
Run history¶
History is durable: sabon keeps the last
SABON_MOVER_HISTORY (default 3)
exited mover containers per app/target/action, so each run's status and full
restic logs live in Docker itself and survive a sabon restart. There is no
in-memory store — they are ordinary containers, so docker ps -a and
docker logs <id> see them too. In swarm mode the retained history is mover
services instead (docker service ls / docker service logs).
| Method | Path | Description |
|---|---|---|
GET |
/runs?app=<name>&target=<name> |
Recent runs, newest first (filters optional). |
GET |
/runs/{id} |
One run (id is the mover container id). |
GET |
/runs/{id}/logs |
The run's restic output (text/plain). |
A run:
{
"id": "9f3c…",
"app": "immich",
"target": "onsite",
"action": "backup",
"status": "succeeded",
"exit_code": 0,
"started_at": "2026-07-27T21:00:00Z",
"finished_at": "2026-07-27T21:00:08Z"
}
status is running, succeeded or failed. A failure that happens before
a mover starts (e.g. a snapshot or pre-hook error) produces no run record — it is
reported via logs, notifications and metrics instead.
Example¶
export T="Authorization: Bearer $SABON_API_TOKEN"
base=http://127.0.0.1:8080/api/v1
curl -H "$T" "$base/jobs"
curl -H "$T" -X POST "$base/apps/immich/backup" # -> 202
curl -H "$T" "$base/runs?app=immich" # find the run + its id
curl -H "$T" "$base/runs/<id>/logs" # restic output