API Docs
Bumbag's own clients are built on this same API. If you're writing a client for a device Bumbag doesn't ship for, everything you need is here.
1. The basics
Everything is JSON over HTTPS and lives under /api/v1. These are open to anyone, with no token:
GET /api— server name and API prefix.GET /api/v1/server/info— whether registration is open and how email is verified.GET /api/v1/systems— the platform catalogue, so a client can offer a picker without hard-coding one.GET /api/v1/systems/{code}returns one entry.POST /api/v1/pair,GET /api/v1/pair/{code}andPOST /api/v1/pair/{code}/claim— see Pairing a device. Holding the code is the credential there, because the device has no account.POST /api/v1/auth/register(where the operator allows it),POST /api/v1/auth/login,POST /api/v1/auth/refreshandPOST /api/v1/auth/logout— you cannot send a token to the calls that hand you one, and logout takes the refresh token instead.
Every other endpoint needs an access token. A machine-readable schema is served at /openapi.json when the operator runs the server in debug mode.
2. Getting a token
Register (or use an existing account), then log in. Send the token back as a bearer credential on every later call.
POST /api/v1/auth/register—username,email,password.POST /api/v1/auth/login— returnsaccess_token,refresh_token,token_type.POST /api/v1/auth/refresh— trade a refresh token for a new pair.POST /api/v1/auth/logoutandGET /api/v1/auth/me.
Then send Authorization: Bearer <access_token>. Access tokens are short-lived, so keep the refresh token and renew before it expires.
3. Pairing a device
Hardware with no keyboard of its own pairs in three steps. The device has no account, so possessing the code is the credential — the code is what the device holds while it waits for you to approve it in the web app.
POST /api/v1/pair— the device asks for a code, optionally reportingfirmwareandplatformso the approval screen can suggest a name. Returns an 8-character code, displayed asAB12-CD34.POST /api/v1/pair/{code}/claim— the device claims the code and receives an access token, a refresh token and adevice_id. Persist thedevice_id.POST /api/v1/pair/{code}/approve— you approve it, signed in, in the web app.
Asking for a code and claiming one are unauthenticated by design — the device is holding the code and nothing else, so the code is what authorises it. GET /api/v1/pair/{code} is open for the same reason, so a device can show "waiting for approval" before it has a token. Approving is the one step of the three that needs your token, because that is the step that attaches a device to your account. A code expires ten minutes after it is issued.
Send X-Device-Id on later calls. It's how the server tells your devices apart, so it can record where each one keeps a save and apply per-device folder choices. Claiming a code also lets you re-pair a device you've lost without losing its history.
4. What you can call
- Saves —
GET /saves,POST /saves/upload,GET /saves/{id},GET /saves/{id}/download,PATCH|DELETE /saves/{id}, plus/detect,/hash,/storage/usageand/{id}/detail. There is noPOST /saves— creating a save is the multipart upload above. - Version history —
GET /saves/{id}/versionslists the older copies of a save the server has kept, newest first;GET /saves/{id}/versions/{version}/downloadretrieves one. A copy is retained only when the save's content is replaced (a device's copy wins a conflict, or a restore from the archive) — never on an ordinary upload, and never by overwriting anything. How many are kept is the account preference below; each retained copy counts toward the same storage quota as the save itself. - Where a save lives —
PUT|DELETE /saves/{id}/devices/{device}/locationsets or clears the folder for one device.PUT /saves/{id}/devices/{device}/excludedandPUT /saves/{id}/excludedkeep a save off a device or off entirely. - Devices —
GET /devices,PATCH|DELETE /devices/{id},GET /devices/me/settings, andPOST /devices/placementsto report where this device actually put a file. - Conflicts —
GET /conflictsand, per conflict,/resolve,/escalate,/dismiss,/restore,/device-matchand the candidate/archive downloads. - Systems —
GET /systemslists every console code this server knows and the save extensions it expects for each. Use it to label a save; the server falls back toUNCLASSIFIEDwhen it can't tell. The list is the authority: any code it returns can be used to label a save, on upload or later viaPATCH /saves/{id}, so a client never has to keep its own copy of the vocabulary. - Account —
POST /auth/email,/auth/password,/auth/theme,/auth/version-history,/auth/verify/resend. The version-history preference is0–5, defaulting to3;0turns version retention off and prunes anything already kept.
5. Uploading a save
POST /api/v1/saves/upload takes multipart/form-data with a file part and an optional system and device_label. Leave system off and the server tries to work it out from the filename and extension. If it genuinely can't, the upload is rejected with 422 and the error lists the systems that file's extension could belong to — send one of those back as system and it goes through. If you would rather store it first and decide later, send system=UNCLASSIFIED explicitly and categorize it afterwards with PATCH /saves/{id}.
Uploads are keyed on the filename. Re-uploading byte-identical content under a name you already have is a harmless no-op. Different content under an existing name does not overwrite what you had: the first divergence is accepted and comes back 201 with conflict_created: true, so the upload is not lost — resolve the conflict and retry. If you re-upload that same name again while a decision is still outstanding, you get 409. Save states and emulator configs are refused: they aren't portable between emulators.
6. Limits and conventions
- Authenticated calls are limited per user and globally; uploads are limited more tightly. A
429means slow down and retry later. - Uploads are virus-scanned and size-capped. Exceeding the cap returns
413. - Errors come back as
{"detail": "..."}with a meaningful status code —400for a rejected file,401for a bad token,404for something that isn't yours,422for a malformed request. - Your files are yours: one account never sees another account's saves, and the server only tells a device about the saves belonging to the account that paired it.