Pairing & devices
Pairing links a user (on the backend side) to a device running the Bloonio
Authenticator app. It happens in two stages: the backend prepares the
pairing (HMAC #1 signed) and receives a short pairing_proof; the device
exchanges that proof for a long-lived device_session_token.
1. the user scans the QR (backend's existing pairing handler)2. backend ── HMAC #1 ─► POST /api/v1/relay/prepare-pairing ← { pairing_proof (JWT ~5 min), expires_in }3. the backend returns pairing_proof to the device with its usual pairing payload4. device ── Bearer pairing_proof ─► POST /api/v1/device/register-token ← { device_session_token (JWT ~30 days), pairing }5. the device persists device_session_token and uses it for refresh-token / unpairStep by step
Section titled “Step by step”-
The backend prepares the pairing (HMAC #1).
After authenticating the user in your QR pairing flow, call
prepare-pairing. At least one ofuser_email/user_phoneis required;user_socket_hash,backend_user_id, anddisplay_nameare mandatory.Fenêtre de terminal curl $BASE_URL/api/v1/relay/prepare-pairing \-X POST \-H "X-Bloonio-Tenant-Id: tnt_..." \-H "X-Bloonio-Timestamp: 1718966400000" \-H "X-Bloonio-Signature: <hex_hmac_sha256>" \-H "Content-Type: application/json" \-d '{"user_socket_hash": "9f86d081884c7d659a2feaa0c55ad015","backend_user_id": "507f1f77bcf86cd799439011","user_email": "jane@example.com","first_name": "Jane","last_name": "Doe","display_name": "Example","display_logo_url": "https://example.com/logo.png"}'{"success": true,"status_code": 201,"message": "Pairing prepared. Forward pairing_proof to the device.","data": { "pairing_proof": "eyJhbGciOi...", "expires_in": 300 }} -
The backend forwards the
pairing_proofto the device.Return it to the device alongside your existing pairing payload. The proof is a JWT valid for ~5 minutes (
PAIRING_PROOF_TTL_SECONDS, 300 s by default) — a single transfer window. -
The device registers its FCM token (
Bearer pairing_proof).The device exchanges the proof for a long-lived
device_session_tokenand receives the pairing’s public info.platformisiosorandroid.Fenêtre de terminal curl $BASE_URL/api/v1/device/register-token \-X POST \-H "Authorization: Bearer eyJhbGciOi..." \-H "Content-Type: application/json" \-d '{"fcm_token": "fcm-token-value-here","platform": "android","app_version": "1.4.0","os_version": "14"}'{"success": true,"status_code": 201,"message": "Device token registered.","data": {"device_session_token": "eyJhbGciOi...","expires_in": 2592000,"pairing": {"tenant_id": "tnt_...","user_socket_hash": "9f86d081884c7d659a2feaa0c55ad015","display_name": "Example","display_logo_url": "https://example.com/logo.png","created_at": "2026-06-21T...","last_seen_at": null}}} -
The device persists the
device_session_token.This JWT (~30 days,
DEVICE_SESSION_TOKEN_TTL_SECONDS, 2,592,000 s by default) authenticates the subsequent calls: refreshing the FCM token and unpairing.
Refresh the FCM token
Section titled “Refresh the FCM token”When the FCM token rotates locally on the device, call refresh-token with the
device_session_token.
curl $BASE_URL/api/v1/device/refresh-token \ -X POST \ -H "Authorization: Bearer eyJhbGciOi..." \ -H "Content-Type: application/json" \ -d '{ "new_fcm_token": "new-fcm-token-value" }'{ "success": true, "status_code": 200, "message": "Token refreshed", "data": null }Unpair
Section titled “Unpair”Two revocation paths, depending on which side acts.
The user removes the account from the device. Authenticated by the
device_session_token. No body.
curl $BASE_URL/api/v1/device/unpair \ -X POST \ -H "Authorization: Bearer eyJhbGciOi..."{ "success": true, "status_code": 200, "message": "Device unpaired", "data": null }The backend revokes a user’s active pairing (sign-out, device wipe). HMAC #1 signed.
curl $BASE_URL/api/v1/relay/revoke-pairing \ -X POST \ -H "X-Bloonio-Tenant-Id: tnt_..." \ -H "X-Bloonio-Timestamp: 1718966400000" \ -H "X-Bloonio-Signature: <hex_hmac_sha256>" \ -H "Content-Type: application/json" \ -d '{ "user_socket_hash": "9f86d081884c7d659a2feaa0c55ad015" }'{ "success": true, "status_code": 200, "message": "Pairing revoked", "data": null }If no active pairing exists for that user, the relay returns 404 ("No active pairing for this user").