# How to integrate a desktop client

Talk to the live API the way `POST /api/v1/register` → login → upload actually work **today**. Ignore HMAC signatures unless you change the route flags.

## Prerequisites

- An `API_KEY_*` in the server `.env` (generate with `php scripts/generate_api_key.php desktop`)
- `FS_BASE_URL` reachable from the client
- PHP `json` + the upload MIME list on the server

## Steps

1. Persist a device id (UUID). Reuse it on every launch.

2. Register the device once (or when the session token expires):

   ```http
   POST /api/v1/register
   Content-Type: application/json
   X-API-Key: <API_KEY_DESKTOP>

   {
     "device_id": "550e8400-e29b-41d4-a716-446655440000",
     "device_info": {
       "platform": "desktop",
       "version": "1.0.0",
       "os": "Windows 11"
     }
   }
   ```

   Save `session_token`. Send it as `X-Session-Token` after this.

3. If the user should own the shots in the web gallery, log in:

   ```http
   POST /api/v1/login
   Content-Type: application/json
   X-API-Key: <API_KEY_DESKTOP>
   X-Session-Token: <session_token>

   { "username": "alice", "password": "..." }
   ```

   Save `token` (JWT). Send `Authorization: Bearer <token>` on later calls if you want `user_id` bound. Without JWT, upload still requires a device session; the row may be device-scoped depending on middleware context.

4. Upload a capture as a data URI:

   ```http
   POST /api/v1/upload
   Content-Type: application/json
   X-API-Key: <API_KEY_DESKTOP>
   X-Session-Token: <session_token>
   Authorization: Bearer <jwt>

   {
     "image": "data:image/png;base64,iVBOR...",
     "filename": "capture.png",
     "title": "optional"
   }
   ```

   Put `data.share_url` on the clipboard. That is `FS_BASE_URL` + `/` + `short_code`.

5. Do **not** send `X-Request-Signature` unless the server route is changed to `requires_signature => true`. The verifier exists; the current register/login/upload routes do not enable it.

## Verification

```bash
curl -s https://prn.tf/api/v1/test
# then register, login, upload against your host
```

Open `data.share_url` in a browser. Log into `/gallery` as that user and confirm the new row.

Rate limits (defaults): register 10/min/IP, login 20/min/IP, upload 100/min (no user) or 200/min (authenticated) per device identifier.

## Troubleshooting

| Response | Cause |
|---|---|
| `INVALID_API_KEY` | Key not in env as `API_KEY_*`, or typo |
| `INVALID_SESSION` | Token expired or device revoked |
| `INVALID_CREDENTIALS` | Bad password; lockout after 5 web failures is a separate path |
| `RATE_LIMIT_EXCEEDED` | Back off until `X-RateLimit-Reset` |
| CORS error from a browser extension | Origin not in `CORS_ALLOWED_ORIGINS` |

Anonymous demo (no device): `POST /api/v1/upload-anon` with only `{ "image": "..." }`. No gallery management.

## Related

- Full API: [reference-api.md](reference-api.md)
- Why two tokens: [explanation-security.md](explanation-security.md)
