API Reference & WebSocket Protocol¶
Interactive API Explorer¶
The server ships with a built-in interactive API explorer and a downloadable OpenAPI specification:
- Scalar UI:
http://<server>/api/docs— a modern API explorer powered by Scalar - OpenAPI spec:
http://<server>/api/openapi.json— download for use with Postman, Insomnia, code generators, etc.
The Scalar UI lets you browse every endpoint, inspect request/response schemas, and execute requests directly from the browser. The OpenAPI document is generated with utoipa and always reflects the routes compiled into the running server.
Note
This page serves the built MkDocs documentation site (/docs), which is a different route from the Scalar API explorer (/api/docs).
Authentication¶
All API endpoints (except /api/health and /api/auth/login) require authentication.
Bearer Token¶
Include an API token in the Authorization header:
Authorization: Bearer <token>
Tokens are created and managed via the /api/tokens endpoints or the web UI.
Session Cookie¶
Browser-based access uses a session cookie set after a successful login. The web UI handles this automatically. Cookies are HttpOnly; SameSite=Lax and last 24 hours (7 days when remember me is selected). Set ASSIMILATE_SECURE_COOKIES=true behind TLS to add the Secure flag.
Login¶
POST /api/auth/login
Content-Type: application/json
{"username": "admin", "password": "secret", "remember_me": false}
A successful response sets a session cookie and returns the authenticated user object.
REST API Overview¶
| Property | Value |
|---|---|
| Base URL | /api/ |
| Versioning | None (single version) |
| Request format | application/json |
| Response format | application/json |
| Error format | {"error": "human-readable message"} |
All timestamps are ISO 8601 strings in UTC. Numeric IDs are integers; agents are addressed by hostname, not by a numeric ID.
API Endpoints Summary¶
For full request/response schemas, use the interactive explorer. Path parameters below use {name} placeholders matching the OpenAPI document.
Auth¶
| Method | Path | Description |
|---|---|---|
POST |
/api/auth/login |
Authenticate and start a session |
POST |
/api/auth/logout |
End the current session |
POST |
/api/auth/refresh |
Refresh the current session cookie |
GET |
/api/auth/me |
Return the currently authenticated user |
POST |
/api/auth/change-password |
Change the current user's password |
GET / PUT |
/api/auth/preferences |
Get or replace the current user's UI preferences ({"theme": "light" \| "dark" \| "auto"}) |
Users¶
| Method | Path | Description |
|---|---|---|
GET |
/api/users |
List all users |
POST |
/api/users |
Create a user |
PUT |
/api/users/{id}/role |
Change a user's built-in role |
PUT |
/api/users/{id}/password |
Reset a user's password |
DELETE |
/api/users/{id} |
Delete a user |
API Tokens¶
| Method | Path | Description |
|---|---|---|
GET |
/api/tokens |
List all API tokens |
POST |
/api/tokens |
Create a new API token (plaintext returned once) |
DELETE |
/api/tokens/{id} |
Revoke a token |
Agents¶
Agents are keyed by hostname. Hostname alone is not always unique — two hosts on different networks can share an OS hostname — so every hostname-keyed endpoint below also accepts an optional ?domain= query parameter. It is required only when the hostname is ambiguous (more than one agent shares it); the server returns 409 Conflict naming the candidate domains if it's needed and omitted. See Agent Management — Duplicate Hostnames.
| Method | Path | Description |
|---|---|---|
GET |
/api/agents |
List all registered agents |
POST |
/api/agents |
Register a new agent |
GET / PUT / DELETE |
/api/agents/{hostname} |
Get, update, or delete an agent |
POST |
/api/agents/{hostname}/regenerate-token |
Issue a new agent token |
POST |
/api/agents/{hostname}/restart |
Send a restart command to the agent |
POST |
/api/agents/{hostname}/deploy |
Push the agent binary and install a systemd unit over SSH |
POST |
/api/agents/{hostname}/service-unit |
Generate the systemd unit file for the agent |
GET / POST |
/api/agents/{hostname}/hostname-patterns |
List or add hostname alias patterns |
DELETE |
/api/agents/{hostname}/hostname-patterns/{pattern_id} |
Delete a hostname alias pattern |
POST |
/api/agents/{hostname}/merge-from/{source_id} |
Merge an imported placeholder agent into this one |
PUT |
/api/agents/{hostname}/hide / /unhide |
Hide or reveal an imported placeholder agent |
POST |
/api/agents/{hostname}/delete-archives |
Delete selected archives owned by the agent |
GET |
/api/agents/{hostname}/repos |
List repositories assigned to the agent |
POST |
/api/agents/{hostname}/repos/{repo_id}/cancel-backup |
Cancel the backup currently in progress for a repository on the agent |
GET |
/api/agents/{hostname}/reports |
List backup reports for the agent |
GET |
/api/agents/{hostname}/tunnel |
Get the agent's SSH tunnel configuration |
GET / PUT |
/api/agents/{hostname}/tags |
Get or set the agent's tags |
GET / PUT |
/api/agents/{hostname}/availability |
Get or set whether the agent's host is not always online, and list the schedules waiting for it to reconnect |
See Agent Management for setup and configuration details.
Repositories¶
| Method | Path | Description |
|---|---|---|
GET |
/api/repos |
List all repositories |
POST |
/api/repos |
Create a repository record |
GET |
/api/repos/stats |
List repositories with storage statistics |
POST |
/api/repos/init |
Initialize a new borg repository on the agent |
GET / PUT / DELETE |
/api/repos/{repo_id} |
Get, update, or delete a repository record |
POST |
/api/repos/{repo_id}/destroy |
Run borg delete to destroy the remote repository |
GET |
/api/repos/{repo_id}/availability |
Read whether the repository's host is not always online, and list the schedules waiting on this repository. Set on the repository host |
POST |
/api/repos/{repo_id}/sync |
Sync archive metadata from the repository |
POST |
/api/repos/{repo_id}/rescan |
Rebuild the searchable archive file index |
POST |
/api/repos/{repo_id}/reset-and-sync |
Clear cached metadata and re-sync |
POST |
/api/repos/{repo_id}/reset-import |
Reset an imported repository to unmatched state |
POST |
/api/repos/{repo_id}/break-lock |
Break a stale repository lock, clearing any stale local cache lock too |
POST |
/api/repos/{repo_id}/confirm-relocation |
Acknowledge a repository relocation warning |
POST |
/api/repos/{repo_id}/exec |
Execute an allow-listed borg maintenance command |
POST |
/api/repos/{repo_id}/dry-run |
Preview which files a schedule would back up |
GET |
/api/repos/{repo_id}/passphrase |
Retrieve the stored passphrase (admin only) |
POST |
/api/repos/{repo_id}/key/export |
Export the borg repository key |
POST |
/api/repos/{repo_id}/key/import |
Import a borg repository key |
POST |
/api/repos/{repo_id}/key/change-passphrase |
Change the repository passphrase |
POST |
/api/repos/{repo_id}/migrate-encryption |
Migrate the repository to a new encryption mode |
GET |
/api/repo-hosts |
List every repository host with its repositories (admin only) |
GET / PUT / DELETE |
/api/repo-hosts/{repo_host_id} |
Get a repository host, change its hostname and SSH port, or remove it once no repository uses it (admin only) |
PUT |
/api/repo-hosts/{repo_host_id}/power |
Set the host's Wake-on-LAN and shutdown settings (admin only) |
GET / PUT |
/api/repo-hosts/{repo_host_id}/availability |
Get or set whether the host is not always online, and list the schedules waiting on it (admin only) |
POST |
/api/repo-hosts/{repo_host_id}/availability/check |
Ask the host whether it is back now, and catch up every schedule waiting on any of its repositories if it is (admin only) |
POST |
/api/repo-hosts/{repo_host_id}/ssh-host-key/scan |
Scan the key the host presents (admin only) |
POST |
/api/repo-hosts/{repo_host_id}/ssh-host-key |
Pin a key for every repository on the host (admin only) |
GET |
/api/repos/{repo_id}/schedules |
List schedules for the repository |
GET / PUT |
/api/repos/{repo_id}/tags |
Get or set repository tags |
GET / PUT |
/api/repos/{id}/quota |
Get or set the repository storage quota |
GET |
/api/repos/{repo_id}/permissions |
List per-repo user permissions |
PUT |
/api/repos/{repo_id}/permissions/{user_id} |
Grant or update a user's repo permission |
See Repositories for full details.
Archives¶
| Method | Path | Description |
|---|---|---|
GET |
/api/repos/{repo_id}/archives |
List archives in a repository |
GET |
/api/repos/{repo_id}/archives/diff |
Diff two archives |
GET |
/api/repos/{repo_id}/archives/{archive_name} |
Get details for a specific archive |
DELETE |
/api/repos/{repo_id}/archives/{archive_name} |
Delete an archive |
GET |
/api/repos/{repo_id}/archives/{archive_name}/contents |
Browse the archive file tree |
GET |
/api/repos/{repo_id}/archives/{archive_name}/index-status |
Check the file-index build status |
GET |
/api/repos/{repo_id}/archives/{archive_name}/extract |
Stream a single file from the archive |
GET |
/api/repos/{repo_id}/archives/{archive_name}/export |
Export the whole archive as a tarball |
POST |
/api/repos/{repo_id}/archives/{archive_name}/download |
Download selected paths as an archive |
POST |
/api/repos/{repo_id}/archives/{archive_name}/restore |
Restore selected paths to a target on the agent |
GET |
/api/repos/{repo_id}/archives/{archive_name}/search |
Search files within a single archive |
GET |
/api/repos/{repo_id}/search |
Search files across all archives in a repo |
GET / POST |
/api/repos/{repo_id}/archives/{archive_name}/tags |
List or add archive tags |
DELETE |
/api/repos/{repo_id}/archives/{archive_name}/tags/{tag} |
Remove an archive tag |
See Archives and Restoring Files for browsing and restore workflows.
Schedules¶
| Method | Path | Description |
|---|---|---|
GET |
/api/schedules |
List all schedules |
POST |
/api/schedules |
Create a schedule |
GET / PUT / DELETE |
/api/schedules/{id} |
Get, update, or delete a schedule |
POST |
/api/schedules/{id}/run |
Trigger an immediate run for this schedule |
POST |
/api/schedules/{id}/cancel |
Cancel a running backup for this schedule |
GET |
/api/schedules/{id}/reports |
List reports produced by this schedule |
GET |
/api/schedules/{id}/sources |
List the schedule's backup sources |
GET |
/api/schedules/{id}/catch-up |
List the schedule's agents and repository hosts that are marked as not always online |
GET |
/api/schedules/{id}/targets |
List the schedule's target repositories |
See Scheduling for cron expression syntax and examples.
Global Excludes¶
| Method | Path | Description |
|---|---|---|
GET |
/api/excludes |
List all global exclude patterns |
PUT |
/api/excludes |
Replace the full set of global exclude patterns |
See Global Excludes for pattern syntax and usage.
Tags¶
| Method | Path | Description |
|---|---|---|
GET |
/api/tags |
List all tags |
POST |
/api/tags |
Create a tag |
DELETE |
/api/tags/{id} |
Delete a tag |
GET |
/api/agent-tags |
List agent→tag associations |
GET |
/api/repo-tags |
List repo→tag associations |
Storage Quotas¶
| Method | Path | Description |
|---|---|---|
GET / PUT |
/api/repos/{id}/quota |
Get or set a repository's quota thresholds |
See Storage Quotas for details.
SSH Tunnels¶
| Method | Path | Description |
|---|---|---|
GET |
/api/tunnels |
List all configured SSH tunnels |
POST |
/api/tunnels |
Create a new SSH tunnel |
GET / PUT / DELETE |
/api/tunnels/{id} |
Get, update, or delete a tunnel |
POST |
/api/tunnels/{id}/enable / /disable |
Enable or disable a tunnel |
POST |
/api/tunnels/{id}/reconnect |
Force a tunnel to reconnect |
See SSH Tunnels for configuration details.
Notifications¶
| Method | Path | Description |
|---|---|---|
GET / POST |
/api/notifications/channels |
List or create notification channels |
PUT / DELETE |
/api/notifications/channels/{id} |
Update or delete a channel |
POST |
/api/notifications/channels/{id}/test |
Send a test notification |
GET / POST |
/api/notifications/rules |
List or create notification rules |
DELETE |
/api/notifications/rules/{id} |
Delete a rule |
GET |
/api/notifications/deliveries |
List recent notification deliveries |
POST |
/api/notifications/validate-smtp |
Validate SMTP settings (with channel_id and a blank smtp_password, logs in with that channel's saved password) |
GET / PUT |
/api/notifications/push/vapid-key |
Get or set the Web Push VAPID keys |
POST |
/api/notifications/push/subscribe / /unsubscribe |
Manage this browser's Web Push subscription |
GET |
/api/notifications/push/subscriptions |
List Web Push subscriptions |
A channel carries its transport as channel_type (email, webhook or web_push) next to that
transport's config. To change a channel's configuration, send both fields together; the transport
itself is fixed when the channel is created. A web push channel always pushes to the devices of the
admin who created it, so its config only takes the content templates.
An email channel's config.smtp_password is write-only. The server stores it encrypted, leaves it out of every response, and reports only a boolean has_password on the channel. On PUT, a missing or empty smtp_password keeps the saved password. Changing smtp_host, smtp_port or security without supplying the password again returns 400, and so does validate-smtp with a channel_id and a blank password for any other host, port or security mode than the channel's.
A webhook channel's config.headers values are write-only too. headers is an object of header name to value. The server stores every value encrypted, leaves headers out of config in every response, and lists the saved headers as webhook_headers: [{ "name": "Authorization", "has_value": true }] on the channel. On PUT, headers is the complete new header set: a header left out is removed, and an empty or null value keeps the value saved under that name (matched case-insensitively). Leaving headers out of config keeps every saved header. Changing the url to another scheme, host or port without supplying every saved value again returns 400. Invalid header names or values, and header names that differ only in case, return 400.
See Notifications for channel and rule configuration.
Access Control (RBAC)¶
| Method | Path | Description |
|---|---|---|
GET / POST |
/api/roles |
List or create roles |
PUT / DELETE |
/api/roles/{id} |
Update or delete a custom role |
GET / POST |
/api/groups |
List or create groups |
PUT / DELETE |
/api/groups/{id} |
Update or delete a group |
GET / PUT |
/api/groups/{id}/members |
List or set group members |
GET / PUT |
/api/users/{id}/roles |
List or set a user's roles |
GET |
/api/users/{id}/groups |
List a user's groups |
GET |
/api/users/{id}/permissions |
List a user's directly assigned repo permissions |
GET |
/api/users/{id}/effective-permissions |
List a user's effective (resolved) permissions |
See Access Control for roles, groups, and permissions management.
Activity & Statistics¶
| Method | Path | Description |
|---|---|---|
GET |
/api/stats/summary |
Top-level dashboard summary |
GET |
/api/stats/dashboard-overview |
Dashboard overview (coverage, success rates, findings) |
GET |
/api/stats/health |
Aggregate backup health (?schedule_id= narrows it to one schedule) |
GET |
/api/stats/activity |
Backup activity entries (?acknowledged=all\|unacknowledged\|acknowledged) |
POST / DELETE |
/api/stats/activity/{id}/acknowledge |
Acknowledge or clear a warning/failed run |
POST |
/api/stats/activity/acknowledge-all |
Acknowledge every outstanding warning and failure the caller may act on (?repo_id=&days= narrows it to one repository and window, and leaves system events alone) |
GET |
/api/stats/activity/outstanding |
Counts still awaiting acknowledgement, scoped to the caller's permissions (same optional ?repo_id=&days=) |
GET |
/api/stats/system-events |
System event log (same ?acknowledged= filter) |
POST / DELETE |
/api/stats/system-events/{id}/acknowledge |
Acknowledge or clear a problem system event (admin only) |
GET |
/api/stats/trends |
Backup success/failure trends |
GET |
/api/stats/storage |
Storage usage per repository |
GET |
/api/stats/storage-breakdown |
Storage breakdown by category |
GET |
/api/stats/storage-trends |
Storage growth over time |
GET |
/api/stats/storage-trends/by-repo |
Storage growth per repository |
GET |
/api/stats/calendar |
Calendar heatmap data |
GET |
/api/stats/schedule-counts |
Counts of schedules by type/state |
POST / DELETE |
/api/stats/findings/{finding_id}/dismiss |
Dismiss or undismiss a health finding |
See Activity Log for the activity timeline UI.
Configuration Import / Export¶
| Method | Path | Description |
|---|---|---|
GET |
/api/config/export |
Export server configuration as JSON |
POST |
/api/config/import |
Import server configuration from JSON |
Server Logs¶
| Method | Path | Description |
|---|---|---|
GET |
/api/logs |
Retrieve buffered server log entries (filterable by level) |
SSH Helpers¶
| Method | Path | Description |
|---|---|---|
POST |
/api/ssh/test-connection |
Test SSH connectivity to a repository host |
POST |
/api/ssh/deploy-key |
Deploy the server public key to a host |
POST |
/api/ssh/list-dir |
List a directory on a remote host over SSH |
POST |
/api/ssh/mkdir |
Create a directory on a remote host over SSH |
Audit Log¶
| Method | Path | Description |
|---|---|---|
GET |
/api/audit-log |
Retrieve audit log entries |
See Audit Log for details.
System¶
| Method | Path | Description |
|---|---|---|
GET |
/api/system/version |
Server version and build info |
GET / PUT |
/api/system/settings |
Get or update system settings |
GET |
/api/system/ssh-public-key |
Public SSH key used for borg repository access |
POST |
/api/system/ssh-regenerate-key |
Regenerate the server's SSH key pair |
GET |
/api/system/database-storage |
PostgreSQL storage usage per table |
POST |
/api/system/reset |
Reset the system (destructive; admin only) |
Health¶
| Method | Path | Description |
|---|---|---|
GET |
/api/health |
Liveness check — returns 200 OK when the server is up |
No authentication required for /api/health. The response also includes background_ops_in_flight, a boolean reporting whether any repo sync or notification delivery is currently running — used by CI to wait for background work to finish before tearing down test infrastructure.
WebSocket Protocol¶
Agents connect to the server over a persistent WebSocket connection. All messages are JSON-encoded using a tagged envelope:
{"type": "<VariantName>", "payload": { ... }}
The type is the message variant name (PascalCase). Messages with no data (for example Ping, Pong, RestartAgent, ShuttingDown) omit the payload field.
Endpoint¶
ws://<server>/ws/agent
Authentication is performed via the Hello message immediately after connection — the hostname and token are not part of the URL. The agent sends Hello { hostname, token, agent_version, ... }; the server validates the token against the bcrypt hash stored for that hostname.
Message Types¶
Agent → Server (AgentToServer)¶
| Type | Description |
|---|---|
Hello |
Sent immediately after connect; carries hostname, token, and agent version/capabilities |
Pong |
Response to a server Ping |
BackupStarted / BackupCompleted / BackupRejected / BackupCancelled |
Backup lifecycle events |
BackupLog |
Streams a log line from an in-progress backup |
CheckCompleted / VerifyCompleted / CanaryVerified |
Integrity operation results |
InitRepoCompleted |
Result of a repository initialization |
StatusUpdate |
Per-repo status change |
SearchResult / RestoreCompleted / DryRunResult / ExportReady |
Results of on-demand operations |
KeyExportResult / KeyImportResult / PassphraseChanged / MigrateEncryptionCompleted |
Key-management results |
DeleteArchivesResult |
Result of an archive deletion request |
OperationProgress / OperationFailed |
Progress and failure reporting for long operations |
RestartFailed |
Sent when the agent cannot honor a restart request |
Server → Agent (ServerToAgent)¶
| Type | Description |
|---|---|
Ping |
Heartbeat; agent responds with Pong |
ConfigUpdate |
Full agent configuration (repos, schedules, excludes) |
RunBackupNow / RunCheckNow / RunVerifyNow |
Trigger a backup, check, or verify for a repo |
CancelBackup |
Cancel a running backup |
InitRepo |
Initialize a new borg repository |
SearchArchive / RestoreFiles / DryRun / ExportArchive |
On-demand operations |
KeyExport / KeyImport / ChangePassphrase |
Key-management operations |
DeleteArchives |
Delete named archives |
RestartAgent |
Instruct the agent process to restart |
ShuttingDown |
Sent when the server is shutting down |
Connection Lifecycle¶
- Agent opens the WebSocket to
/ws/agentand immediately sends aHellomessage with its hostname and token. - Server validates the token, sends a
ConfigUpdatewith the agent's full configuration, and begins sending periodicPingmessages. - Agent responds to each
PingwithPong. - Server sends
RunBackupNow(orRunCheckNow/RunVerifyNow) when a scheduled or manual operation is due. - Agent streams
BackupLogmessages during the run, then sendsBackupCompleted. - Either side may close the connection; the agent reconnects automatically.
UI WebSocket¶
Browsers open a separate, server-push-only WebSocket at /ws/ui to receive live events (AgentConnected, AgentDisconnected, BackupStarted, BackupCompleted, CheckCompleted, VerifyCompleted, ConfigUpdated, RunEvent, and more — the ServerToUi enum). It carries no client→server commands. RunEvent carries one step of a run's power-management timeline as it happens; the same history is available after the fact via GET /api/runs/{run_id}/events.
SSH Agent WebSocket¶
The server relays the SSH agent protocol over a dedicated WebSocket endpoint, giving agents transparent access to the server's SSH keys without distributing private key material.
Endpoint¶
ws://<server>/ws/ssh-agent/{hostname}
The token is not part of the URL. The agent sends its token as the first WebSocket message; the server verifies it against the bcrypt hash stored for that hostname before opening the relay.
Protocol¶
The relay is a binary byte-stream bridge between the agent's local Unix domain socket and the server's SSH_AUTH_SOCK. After the initial token message, raw SSH agent protocol bytes flow in both directions with no additional framing or JSON.
For setup instructions and Docker configuration, see SSH Agent Forwarding.
Rate Limiting & Security¶
The server applies two brute-force protections to the login endpoint:
- IP rate limiting — the
/api/auth/loginroute is limited to 10 requests per 60 seconds per client; excess requests receive429 Too Many Requests. - Account lockout — after 5 failed login attempts for a username within a 15-minute rolling window, further attempts are rejected until the window elapses.
Other API routes are not rate-limited by the server. For production deployments:
- Place the server behind a reverse proxy (nginx, Caddy, Traefik) and configure additional rate limiting there.
- Restrict access to the admin API by IP allowlist at the proxy or firewall level.
- CORS is not enabled by default; the API is intended to be served from the same origin as the frontend.
- All agent tokens are cryptographically random (32+ bytes). Rotate them via
/api/agents/{hostname}/regenerate-tokenif compromised.
See Security for hardening recommendations.
Error Codes¶
| HTTP Status | Meaning |
|---|---|
200 OK |
Request succeeded |
201 Created |
Resource created successfully |
204 No Content |
Request succeeded; no body returned (e.g., DELETE) |
400 Bad Request |
Invalid input; body contains {"error": "..."} |
401 Unauthorized |
Missing or invalid credentials |
403 Forbidden |
Authenticated but not permitted to perform this action |
404 Not Found |
Resource does not exist |
409 Conflict |
Resource already exists or state conflict |
429 Too Many Requests |
Login rate limit or account lockout triggered |
500 Internal Server Error |
Unexpected server error; check server logs |
All error responses use the same envelope:
{"error": "human-readable description of the problem"}