# Mobile Integration Spec (Stage 1) ## Base URL - Processor endpoints are mounted under `/api/v1/mobile`. ## Headers - Optional: `x-flow-id` for end-to-end trace correlation. - Required for ping/sync endpoints: `x-device-id` with a previously registered `externalDeviceId`. ## Endpoints ### Register Device - Method: `POST` - Path: `/api/v1/mobile/register` - Auth: none (trusted private network assumption) Payload: ```json { "externalDeviceId": "android-1234", "name": "Pixel 8 Pro", "manufacturer": "Google", "model": "Pixel 8 Pro", "androidVersion": "15" } ``` ### Ping Device - Method: `PUT` - Path: `/api/v1/mobile/ping` - Required header: `x-device-id` Payload: ```json { "pingAt": "2026-03-01T10:15:00.000Z" } ``` ### Sync SMS - Method: `PUT` - Path: `/api/v1/mobile/sms/sync` - Required header: `x-device-id` Payload: ```json { "messages": [ { "externalMessageId": "msg-1", "sender": "+358401111111", "recipient": "+358402222222", "body": "Hello from device", "sentAt": "2026-03-01T10:10:00.000Z", "receivedAt": "2026-03-01T10:10:01.000Z", "rawPayload": { "threadId": "7" } } ] } ``` ### Sync Media Assets - Method: `PUT` - Path: `/api/v1/mobile/media/sync` - Required header: `x-device-id` - Content-Type: `multipart/form-data` Upload contract: - One request uploads one raw media file. - Multipart field `file` is required (binary). - Optional multipart metadata fields: - `externalMediaId` - `mimeType` - `filename` - `capturedAt` (ISO date string) - `sizeBytes` (number) - `hash` - `metadata` (JSON object string) - Optional metadata headers (alternative to multipart fields): - `x-media-external-id` - `x-media-mime-type` - `x-media-filename` - `x-media-captured-at` - `x-media-size-bytes` - `x-media-hash` - `x-media-metadata` (JSON object string) ## Response Contract Success: ```json { "data": {}, "error": null } ``` Failure: ```json { "data": null, "error": { "flowId": "uuid", "code": "VALIDATION_ERROR", "message": "Human message", "description": "Actionable description", "detail": "Technical detail" } } ``` ## Admin Query Contract - Pagination: - `page`: 1-based integer - `pageSize`: integer - Sorting: - `sortBy`: operation-specific - `sortOrder`: `asc` or `desc` - Paginated response payload: - `data`: rows - `total`: full row count - `page`, `pageSize`, `totalPages` ## Dedup Rules - Device: - Upsert on unique `externalDeviceId`. - SMS: - Dedup key #1: `(deviceId, externalMessageId)` when provided. - Dedup key #2 fallback: `(deviceId, dedupHash)` where dedup hash is SHA-256 of `(deviceId + sentAt + sender + body)`. - Media: - Raw file is uploaded first and persisted in `file`. - Then one `mobile_media_asset` row is created referencing uploaded `fileId`. - Dedup key: `(deviceId, externalMediaId)` when provided. ## Operator Checklist 1. Register a device. 2. Send ping with `x-device-id` and verify dashboard `lastPingAt` updates. 3. Sync SMS and verify device detail `SMS` tab updates (polling every 5s). 4. Sync media and verify device detail `Media Assets` tab displays rows.