✅ base implementation done, now onto mobile app
This commit is contained in:
155
spec.mobile.md
Normal file
155
spec.mobile.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# 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`
|
||||
|
||||
Payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"assets": [
|
||||
{
|
||||
"externalMediaId": "media-1",
|
||||
"fileId": "01JNE3Q1S3KQX9Y7G2J8G7R0A8",
|
||||
"mimeType": "image/jpeg",
|
||||
"filename": "IMG_1234.jpg",
|
||||
"capturedAt": "2026-03-01T10:05:00.000Z",
|
||||
"sizeBytes": 1350021,
|
||||
"hash": "sha256-...",
|
||||
"metadata": {
|
||||
"width": 3024,
|
||||
"height": 4032
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 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:
|
||||
- Dedup key: `(deviceId, externalMediaId)` when provided.
|
||||
- `fileId` in `mobile_media_asset` is unique.
|
||||
|
||||
## 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.
|
||||
Reference in New Issue
Block a user