# API access with an API key Shelfmark's web interface is driven entirely by a JSON API under `/api/`. Set the `SHELFMARK_API_KEY` environment variable and scripts, dashboards and assistants can call the same API without a browser session. Browser logins keep working exactly as before: it is cookie **or** key. ## Set the key ```yaml environment: SHELFMARK_API_KEY: "a-long-random-secret" ``` Generate something long and random (for example `openssl rand -base64 32`). A request carrying the key acts as an **admin**: the first admin user in Shelfmark's user database. Create an admin before relying on the key in any install that has none yet (for example an OIDC-only install). Without an admin user, the key still authenticates as an admin identity with no user row, and routes that need one (requests, activity) answer 403. To rotate, change the variable and restart. Unset it and the feature is off. When the instance runs with no authentication configured (`AUTH_METHOD=none`), the key is simply unnecessary. ## Send the key Either header works, and both are checked, so the key can be sent in `X-Api-Key` behind a reverse proxy that sets its own `Authorization` header. ```bash curl -s -H "Authorization: Bearer $SHELFMARK_API_KEY" https://shelfmark.example.com/api/downloads/active curl -s -H "X-Api-Key: $SHELFMARK_API_KEY" https://shelfmark.example.com/api/downloads/active ``` A request that carries the key is authenticated by the key alone. Session cookies are ignored and none are set. A bearer value that is not the configured key is ignored and the request continues with normal session authentication, so reverse proxies that forward their own tokens are unaffected; without a valid session such a request gets the usual `401 {"error": "Unauthorized"}`. A database error while resolving the admin returns `500 {"error": "Authentication error"}` — never anonymous access. `/api/auth/check` reflects the browser session only and ignores the key, so use `/api/status` to verify a key. ## Examples Search, then look up releases, then queue one (the same calls the web UI makes): ```bash curl -s -H "Authorization: Bearer $SHELFMARK_API_KEY" \ "https://shelfmark.example.com/api/metadata/search?query=dune%20frank%20herbert" # -> {"books":[{"provider":"hardcover","provider_id":"427363", ...}]} curl -s -H "Authorization: Bearer $SHELFMARK_API_KEY" \ "https://shelfmark.example.com/api/releases?provider=hardcover&book_id=427363&content_type=ebook" # -> {"releases":[{"source":"direct_download","source_id":"...", ...}], ...} curl -s -X POST -H "Authorization: Bearer $SHELFMARK_API_KEY" -H "Content-Type: application/json" \ -d @release.json https://shelfmark.example.com/api/releases/download # release.json = one object from "releases" (source and source_id are required) curl -s -H "Authorization: Bearer $SHELFMARK_API_KEY" https://shelfmark.example.com/api/status ``` ## Security notes - The key is compared in constant time and is never logged. - Keyed requests never set cookies and ignore any cookie sent with them. - WebSocket (live activity) connections do not accept the key; poll `/api/status` instead. - The key is a root-equivalent credential: an admin can configure a custom post-download script that the server executes, so treat it like a root password and send it only over HTTPS.