DwellerGames

Dweller Games API v1

Base URL: https://dweller.games/api/v1 (local dev: http://localhost:8080/api/v1).

All bodies are JSON (Content-Type: application/json). Times are RFC 3339 UTC strings.
Errors always look like {"error": "machine_code", "message": "Human readable"} with a 4xx/5xx status.

Authentication

Send Authorization: Bearer <token>. Tokens start with dg_. Get one by:

  • POST /auth/login with a username/email and password (launcher sign-in form), or
  • the device flow (POST /auth/device, then poll POST /auth/device/token) used by the CLI and by
    the launcher's "Sign in with browser" button, or
  • creating a personal token at https://dweller.games/settings/tokens (CI, GitHub Actions).

Endpoints marked πŸ”’ need a token. Creator endpoints need the account to be an approved creator.

POST /auth/login

{"login": "alice", "password": "hunter22", "device_name": "Alice's PC (launcher)"}

β†’ 200 {"token": "dg_…", "user": User} Β· 401 {"error":"invalid_credentials"} Β· 403 {"error":"banned"} Β· 429 rate limited.

POST /auth/logout πŸ”’

Revokes the token used for the call. β†’ 204.

POST /auth/device

{"client": "launcher", "device_name": "Alice's Mac"}

β†’ 200

{"device_code": "long-secret", "user_code": "WXYZ-2345",
 "verification_uri": "https://dweller.games/activate",
 "verification_uri_complete": "https://dweller.games/activate?code=WXYZ-2345",
 "interval": 3, "expires_in": 600}

POST /auth/device/token

{"device_code": "long-secret"}

β†’ 200 {"token": "dg_…", "user": User} once the user approves in the browser.
β†’ 428 {"error":"authorization_pending"} keep polling every interval seconds.
β†’ 400 {"error":"expired_token"} or {"error":"access_denied"} stop.
(Clients should also treat {"error":"slow_down"} as "add 2 seconds to the interval".)

GET /me πŸ”’

β†’ 200 User

Objects

// User
{"id": 12, "username": "alice", "display_name": "Alice", "avatar_url": "https://…" /* or "" */,
 "role": "user" /* user | creator | admin */, "creator": true, "verified": false}

// Creator (embedded)
{"username": "phastings", "display_name": "Dweller Studio", "avatar_url": "", "verified": true}

// GameSummary
{"id": 3, "slug": "drift-kings", "title": "Drift Kings", "tagline": "Open-world night racing…",
 "creator": Creator, "cover_url": "https://cdn…/cover.jpg", "header_url": "https://cdn…/header.jpg",
 "accent": "#ff3d7f", "tags": ["racing", "open world"],
 "platforms": ["windows", "linux", "macos"], "playable_in_browser": false,
 "latest_version": "1.0.0", "updated_at": "2026-11-13T19:00:00Z", "released_at": "2026-11-01T12:00:00Z",
 "rating": {"positive": 10, "total": 12, "percent": 83}, "downloads": 1234,
 "ai_tools": ["Claude Code"], "url": "https://dweller.games/games/drift-kings"}

// Build: one downloadable file for one platform
{"id": 77, "platform": "windows" /* windows | macos | linux | android | web | gba */,
 "arch": "x64" /* x64 | arm64 | universal | any */,
 "kind": "zip" /* zip | exe | apk | rom | web | appimage | dmg | tar.gz */,
 "filename": "DriftKings-1.0.0-windows-x64.zip", "size": 109831632,
 "sha256": "hex…", "exec": "DriftKings.exe",
 "url": "https://cdn.dweller.games/builds/…",            // direct CDN file (use for downloading)
 "download_url": "https://dweller.games/dl/77",           // counted redirect (use in browsers)
 "play_url": ""                                           // set for web/gba builds
}

// Release
{"id": 9, "version": "1.0.0", "channel": "stable", "released_at": "…", "notes_md": "…", "notes_html": "…",
 "builds": [Build]}

// GameDetail = GameSummary + these fields
{"description_md": "…", "description_html": "…", "screenshots": ["https://…"],
 "controls": "…", "engine": "Godot 4.7", "website": "", "github_repo": "owner/name",
 "multiplayer": false, "latest": Release /* or null */, "releases": [Release /* without builds */],
 "in_library": true /* only when called with a token */, "in_wishlist": false}

exec rules for installers: for zip/tar.gz builds, extract the archive into the install folder and
run exec (a path relative to the install folder, forward slashes). For exe/appimage builds the
file itself is the program (exec equals filename); on Linux/macOS mark it executable. For macOS
zips, exec points at the .app bundle (Game.app) – open it with open -a. apk is for Android
phones (offer a download), web/gba builds are played at play_url.

Store

GET /home

Everything the launcher's store front page needs in one call.
β†’ 200 {"featured": [GameSummary], "new": [GameSummary], "updated": [GameSummary], "popular": [GameSummary], "browser": [GameSummary /* playable in the browser */], "tags": [{"tag": "rpg", "count": 4}], "announcement": ""}

GET /catalog?q=&tag=&platform=&sort=&page=

sort = popular (default) | new | updated | rating | title. 30 per page, page starts at 1.
β†’ 200 {"games": [GameSummary], "page": 1, "pages": 3, "total": 61}

GET /games/{slug}

β†’ 200 GameDetail Β· 404

GET /games/{slug}/latest?platform=windows&arch=x64

Best build for that platform (arch falls back to universal, then any).
β†’ 200 {"version": "1.0.0", "release_id": 9, "build": Build} Β· 404 {"error":"no_build"}

POST /updates/check

{"platform": "windows", "arch": "x64",
 "installed": [{"slug": "drift-kings", "version": "0.9.0"}, {"slug": "terald", "version": "1.2.0"}]}

β†’ 200 {"updates": [{"slug": "drift-kings", "from": "0.9.0", "to": "1.0.0", "build": Build, "notes_md": "…"}]}
Only games with a newer release that has a build for the platform are listed.

Library πŸ”’ (synced between the website and the launcher)

GET /library

{"games": [{"game": GameSummary, "added_at": "…", "last_played_at": "…" /* or null */,
            "playtime_seconds": 3600, "latest": Release /* with builds, or null */}]}

PUT /library/{slug} β†’ 204 (add) Β· DELETE /library/{slug} β†’ 204 (remove)

PUT /wishlist/{slug} β†’ 204 Β· DELETE /wishlist/{slug} β†’ 204 Β· GET /wishlist β†’ {"games":[GameSummary]}

POST /library/{slug}/played

{"seconds": 1200} β†’ 204. Adds playtime and sets last_played_at (the launcher calls it when a game exits).

Creator API πŸ”’

GET /creator/games

β†’ {"games": [GameSummary + {"status": "draft|published|unlisted|removed"}]}

POST /creator/games

{"title": "My Game", "slug": "my-game" /*optional*/, "tagline": "", "description_md": "", "tags": ["arcade"], "ai_tools": ["Claude Code"], "engine": "Godot 4.7"} β†’ 201 GameDetail

PATCH /creator/games/{slug}

Any GameDetail editable field, plus "status": "published". β†’ 200 GameDetail

POST /creator/games/{slug}/uploads

Ask for a direct upload URL for one build file.

{"filename": "MyGame-windows.zip", "size": 123456, "sha256": "hex", "platform": "windows", "arch": "x64"}

β†’ 200 {"upload_id": "u_…", "put_url": "https://…", "headers": {"Content-Type": "application/zip", "x-amz-acl": "public-read"}, "expires_in": 3600}
PUT the raw file bytes to put_url with exactly those headers.

POST /creator/games/{slug}/releases

{"version": "1.2.0", "notes_md": "What's new…", "channel": "stable", "publish": true,
 "builds": [{"upload_id": "u_…", "platform": "windows", "arch": "x64", "exec": "MyGame.exe"}]}

β†’ 201 Release. kind is inferred from the file name; exec defaults to the only executable in the archive
or to the file itself. Web builds (platform: "web", a zip with index.html) are unpacked for browser play.

POST /creator/games/{slug}/media (multipart form: kind = cover|header|screenshot, file)

Images are resized server-side: header is cropped to 1280Γ—600 (wide, used for cards, the hero and page backgrounds),
cover to 600Γ—900 (portrait library art), screenshots fit within 1920Γ—1080 with a 480Γ—270 thumbnail.
β†’ 201 {"url": "…"}

Launcher self-update

GET /launcher/update/{target}/{arch}/{current_version}

Tauri v2 dynamic update server. target = windows|darwin|linux, arch = x86_64|aarch64.
β†’ 204 when up to date, otherwise
200 {"version": "0.2.0", "notes": "…", "pub_date": "…", "url": "https://cdn…", "signature": "…"}

GET /launcher/latest

β†’ 200 {"version": "0.2.0", "pub_date": "…", "notes": "…", "downloads": {"windows-x64": {"url": "…", "size": 1}, "macos-universal": {…}, "linux-x64": {…}, "linux-x64-deb": {…}}}

GitHub

POST /hooks/github/{link_id}: GitHub webhook receiver (release events, HMAC-SHA256 signed). Set up from
the game's GitHub tab on the creator dashboard.

View as Markdown (handy for feeding to an AI)