API Overview
Base URL, transport, auth model summary, and the error envelope every endpoint shares.
Mosaic exposes a JSON REST surface plus a single WebSocket fan-out at /api/ws. Every endpoint the desktop SPA calls is reachable from the API; the SPA itself is a client of this surface, with a transport shim that swaps Wails IPC for fetch + WS when running in browser mode.
This page covers the cross-cutting concerns. For the per-endpoint reference, see REST Endpoints and WebSocket.
Base URL
http://127.0.0.1:<port>/api # loopback-only mode (default)
https://<host>:<port>/api # bind-all mode (LAN/external)
<port> defaults to 8080 and is configurable via WebConfigDTO.port. When the remote interface is enabled in loopback-only mode the listener binds 127.0.0.1 and speaks plain HTTP — no certificate is generated or used. Flipping “Bind to all” on makes the listener bind 0.0.0.0 (or [::]:<port>) over HTTPS with a self-signed cert generated at <DataDir>/web-tls/.
The self-signed cert covers localhost, 127.0.0.1, and ::1 only. Expect a browser warning on first connect, and pass -k (or pin the cert) for curl-driven scripts.
Transport
| Surface | Transport |
|---|---|
| REST | application/json over HTTP (loopback) or HTTPS (bind-all) |
| WebSocket | ws:///wss://<host>:<port>/api/ws matching the listener |
| File upload | multipart/form-data (one route — POST /api/torrents/file) |
Request bodies are JSON unless noted. Response bodies are JSON; errors share an envelope (see below).
Authentication
Two methods, accepted on every gated route:
- Session cookie — set on
POST /api/login, sent automatically by the browser. Name:mosaic_session. HttpOnly, SameSite=Strict, Secure when behind HTTPS. - Bearer API key —
Authorization: Bearer <key>. (The legacy?key=<key>URL form has been removed for security; browser WebSocket clients rely on the session cookie instead.)
State-changing cookie-authed requests must satisfy the Origin/Referer CSRF guard: at least one of Origin or Referer must be present, and the host portion of whichever is present must equal Host. Bearer-keyed requests skip this check. See Authentication for the full flow.
Error envelope
Every error response carries the same shape — JSON regardless of route, no exceptions:
{ "error": "<message>" }
| Status | Meaning |
|---|---|
| 200 | Success. |
| 400 | Bad request — missing/invalid body, invalid params, validation failure. |
| 401 | Unauthorized — no valid session cookie and no valid bearer token. |
| 403 | Forbidden — Origin/Referer didn’t match Host on a cookie-authed state-changing request. |
| 429 | Too Many Requests — login rate limiter tripped. Retry-After: 12 header is set. |
| 500 | Internal error. |
| 502 | Upstream failure — e.g. blocklist URL didn’t return. |
A success response is either an explicit shape (DTO or list of DTOs), or a generic ack: {"ok": true} for fire-and-forget commands, {"id": "<value>"} for create endpoints.
Versioning
Mosaic does not currently version its API surface in the URL. The DTOs are documented per release; backwards-incompatible field renames will bump the minor version and call out the change in release notes. The build’s version string is reachable at GET /api/version ({"version": "vX.Y.Z"}).
Quick check
# Replace with your own
export MOSAIC="https://localhost:8080"
export MOSAIC_API_KEY="<paste-from-Settings-WebInterface>"
curl -k "$MOSAIC/api/version" -H "Authorization: Bearer $MOSAIC_API_KEY"
# → {"version":"v1.5.0"}
curl -k "$MOSAIC/api/torrents" -H "Authorization: Bearer $MOSAIC_API_KEY"
# → [...torrent rows...]
If you get {"error":"unauthorized"}, double-check the API key and that the bearer header isn’t being stripped by an intermediate proxy.