--- name: mobile-shim description: Mobile API surface (v3 only), removed legacy endpoints, and the v5-native tag format for mobile clients. --- # Mobile API (v3 Only) All legacy v1/v2/v4 mobile endpoints and the `ConvertV4TagsAction` shim have been **removed** (2026-03-01). The mobile app uses v3 endpoints exclusively. ## Key Files - `app/Http/Controllers/Uploads/UploadPhotoController.php` — v3 upload (EXIF-based) - `app/Http/Controllers/API/Tags/PhotoTagsController.php` — v3 tag CRUD (CLO format) - `app/Http/Controllers/User/Photos/UsersUploadsController.php` — v3 user photos listing - `app/Http/Controllers/Auth/AuthTokenController.php` — Mobile token login (Sanctum) - `readme/Mobile.md` — Mobile documentation ## Active Mobile Endpoints | Action | Method | Endpoint | |--------|--------|----------| | Auth (token) | `POST` | `/api/auth/token` | | Validate token | `POST` | `/api/validate-token` | | Upload photo | `POST` | `/api/v3/upload` | | Add tags | `POST` | `/api/v3/tags` | | Replace tags | `PUT` | `/api/v3/tags` | | List photos | `GET` | `/api/v3/user/photos` | | Photo stats | `GET` | `/api/v3/user/photos/stats` | | Delete photo | `POST` | `/api/profile/photos/delete` (body: `{ "photoid": 123 }`) | | Tag catalog | `GET` | `/api/tags/all` | | Profile | `GET` | `/api/user/profile/index` | | Global stats | `GET` | `/api/global/stats-data` (no auth) | | Levels | `GET` | `/api/levels` (no auth) | ## Upload: Explicit Coordinates (Mobile Mode) `POST /api/v3/upload` supports two modes: - **Web (default):** Only `photo` required. GPS + datetime from EXIF. - **Mobile:** Send `lat` + `lon` + `date` alongside `photo`. All three must be present. EXIF validation skipped. Platform set to `'mobile'`. Optional fields: `picked_up` (boolean, overrides user default), `model` (string, device name). Date accepts Unix timestamp (seconds) or ISO 8601 string. `(0, 0)` coordinates rejected. ## User Photos: Configurable Pagination `GET /api/v3/user/photos?tagged=false&per_page=100` — fetches untagged photos. `per_page` default 8, max 100. ## Invariants 1. **v3 only.** No legacy endpoints exist. Mobile must use v5 CLO tag format. 2. **Sanctum token auth.** `POST /api/auth/token` returns a Bearer token. All subsequent requests include `Authorization: Bearer `. 3. **`identifier` field for login.** `AuthTokenController` accepts `identifier`, `email`, or `username` for backward compatibility. 4. **CLO tag format.** Tags use `category_litter_object_id` + optional `litter_object_type_id`, not category/object string pairs. 5. **`picked_up` not `remaining`.** Photo responses include `picked_up` (boolean, never null) and `remaining` (deprecated, inverse). Per-tag `picked_up` in `new_tags[]` is nullable (true/false/null). The `remaining` column will be removed after the v5 migration script runs. 6. **Delete uses `photoid` (lowercase, no underscore).** `POST /api/profile/photos/delete` body: `{ "photoid": 123 }`. Response: `{ "message": "Photo deleted successfully!" }`. ## Tag Submission Format ```json { "photo_id": 123, "tags": [ { "category_litter_object_id": 42, "litter_object_type_id": 3, "quantity": 2, "picked_up": true, "materials": [{ "id": 1, "quantity": 1 }], "brands": [{ "id": 5, "quantity": 1 }] } ] } ``` ## Building Search Index `GET /api/tags/all` returns 7 flat collections. Mobile must join them: 1. Object entries: `category_objects[].id` = `cloId` 2. Type entries: `category_object_types` → `types` for display names 3. Standalone: brands and materials can be submitted alone ## Removed (2026-03-01) ### Deleted endpoints - `POST /api/photos/submit` (and all aliases) - `POST /api/add-tags` - `POST /api/v2/add-tags-to-uploaded-image` - `GET /api/v2/photos/get-untagged-uploads` - `GET /api/v2/photos/web/*` - `DELETE /api/photos/delete` - `POST /api/upload` - `GET /api/user` (closure), `GET /api/current-user` ### Deleted code - `app/Actions/Tags/ConvertV4TagsAction.php` — v4→v5 conversion shim (no longer needed) - `app/Http/Controllers/ApiPhotosController.php` — legacy mobile upload/delete - `app/Http/Controllers/API/AddTagsToUploadedImageController.php` — legacy mobile tagging - `app/Http/Controllers/API/GetUntaggedUploadController.php` — legacy untagged photos list ## React Native v7 API Changes See `MOBILE_API_CHANGES.md` at the project root for the full RN v7 change log. Key notes: - **`filename` field is a full URL** — usable directly as an image source (no base URL prefix needed) - **`new_tags` response** includes `category`, `object`, `type`, `extra_tags`, `picked_up` (bool). For loose tags (extra-tag-only), `category`, `object`, and `category_litter_object_id` may be null. - **Tag editing via `PUT /api/v3/tags`** — same CLO format. Accepts empty `tags: []` to clear all tags. - **`picked_up` cast to `(bool)`** with fallback to photo-level `picked_up` ## Common Mistakes - **Sending v4 tag format.** `{ smoking: { butts: 3 } }` is no longer accepted. Use CLO format. - **Using old upload endpoint.** `POST /api/photos/submit` no longer exists. Use `POST /api/v3/upload`. - **Using old delete endpoint.** `DELETE /api/photos/delete` no longer exists. Use `POST /api/profile/photos/delete`. - **Not building search index from `/api/tags/all`.** The 7 flat collections must be joined client-side. - **Prefixing `filename` with a base URL.** The `filename` field is already a full URL — using it with a prefix produces a broken double-URL.