# UpdateForge API Base URL: `https://` All API routes are under `/api/v1`. All request and response bodies are JSON. --- ## Authentication UpdateForge uses **Bearer token** authentication. Obtain a token via the login endpoint and include it in every subsequent request: ``` Authorization: Bearer ``` Tokens are long-lived JWTs signed by the server. Store them securely (e.g. Android `EncryptedSharedPreferences`). There is no built-in expiry endpoint — simply re-authenticate when you receive a `401`. --- ## Endpoints ### `POST /api/v1/auth` Authenticate with email and password. Does not require an existing token. **Request** ```json { "email": "user@example.com", "password": "secret" } ``` **Response `200`** ```json { "token": "eyJ...", "userId": "abc123", "email": "user@example.com" } ``` **Error responses** | Status | Body | Reason | | ------ | ---------------------------------------------- | ----------------------- | | `400` | `{"error": "email and password are required"}` | Missing fields | | `401` | `{"error": "invalid credentials"}` | Wrong email or password | --- ### `GET /api/v1/apps` Returns all teams the authenticated user belongs to, each with its apps. A user may belong to multiple teams. Within each app, the response includes the latest **current** and **pending** version per build type. - **current** — the highest semver version that has an artifact file attached. Always present (may be `null` if none exists yet). - **pending** — the highest semver version with no artifact file yet (work in progress). `null` when none exists. Teams and apps are ordered oldest-first by creation date. **Response `200`** ```json { "teams": [ { "id": "team_id", "name": "Acme", "apps": [ { "id": "app_id", "title": "My App", "packageName": "com.example.myapp", "icon": "/api/files/apps/app_id/icon.png", "release": { "current": { "id": "ver_id", "version": "1.2.0", "name": "January update", "public": true, "hasFile": true, "created": "2026-01-15T10:30:00Z" }, "pending": null }, "debug": { "current": { "id": "ver_id_2", "version": "1.3.0", "name": "", "public": false, "hasFile": true, "created": "2026-01-20T08:00:00Z" }, "pending": { "id": "ver_id_3", "version": "1.4.0", "name": "Next sprint", "public": false, "hasFile": false, "created": "2026-01-21T09:00:00Z" } }, "profile": { "current": null, "pending": null } } ] } ] } ``` `teams` is an empty array if the user belongs to no teams. **Version object fields** | Field | Type | Description | | --------- | ------ | ------------------------------------------------------ | | `id` | string | Version record ID — use this for downloads | | `version` | string | Semantic version string, e.g. `"1.2.0"` | | `name` | string | Optional human-readable label, empty string if not set | | `public` | bool | Whether this version is intended for all testers | | `hasFile` | bool | Whether an artifact is available for download | | `created` | string | ISO 8601 UTC timestamp | **Error responses** | Status | Body | Reason | | ------ | --------------------------- | ------------------------ | | `401` | `{"error": "unauthorized"}` | Missing or invalid token | --- ### `GET /api/v1/versions/{id}/download` Download the artifact file for a version. `{id}` is the version's `id` field from the apps response. The response is the raw file with `Content-Type: application/octet-stream` and a `Content-Disposition: attachment` header carrying the original filename. Supports HTTP range requests, so partial downloads and resume work out of the box. **Error responses** | Status | Body | Reason | | ------ | -------------------------------- | ------------------------------------------ | | `401` | `{"error": "unauthorized"}` | Missing or invalid token | | `403` | `{"error": "forbidden"}` | User is not a member of the version's team | | `404` | `{"error": "version not found"}` | ID does not exist | | `404` | `{"error": "no file attached"}` | Version exists but `hasFile` is `false` | --- ## Build types Every version belongs to exactly one build type: | Type | Intended use | | --------- | ------------------------------------------- | | `release` | Production-ready builds for general testers | | `debug` | Debug builds with logging/tooling enabled | | `profile` | Performance-profiling builds | --- ## Icons The `icon` field in the app object is a root-relative URL path. Prepend the server base URL to fetch it: ``` GET https:///api/files/apps// ``` Icon files are served publicly and do not require authentication. --- ## Typical client flow ``` 1. POST /api/v1/auth → store token 2. GET /api/v1/apps → display teams and their apps; cache version IDs 3. For each app, compare installed version string with current.version 4. If update available and current.hasFile == true: GET /api/v1/versions/{id}/download → save & install APK ``` --- ## Error format All error responses share the same shape: ```json { "error": "" } ```