Tema
Richieste API
Base URL
Tutti gli endpoint vivono sotto https://<instance>/api/v1/. Prefisso v1 parte del contratto. Breaking change vanno in v2. In v1 aggiungiamo campi ma non rinominiamo né rimuoviamo mai. I campi sconosciuti in response possono essere ignorati.
Header richiesti
| Header | Valore | Note |
|---|---|---|
Authorization | Bearer <access_token> | Sempre |
Accept | application/json | Default |
Content-Type | application/json | Sulle POST/PATCH con body |
Idempotency-Key | UUID generato dal partner | Raccomandato su ogni POST (vedi Idempotenza) |
Isolamento tenant
Un partner è legato a esattamente una Organization. Non puoi vedere dati di altri partner, di LifeDome interni, o di org concorrenti. ID fuori tenant → 404 Not Found.
Rate limit
Limiti registrati sul partner (default: 100 req/min, 5 000 req/h). Gestire sempre 429 Too Many Requests con Retry-After e backoff esponenziale + jitter.
Paginazione
Due strategie, dipende dall'endpoint:
- Cursor pagination per endpoint time-series o ad alto volume:
/devices/{uid}/locations/,/devices/{uid}/telemetry/,/events/,/devices/{uid}/commands/. La response ritorna{"next": "<url-opaco>", "previous": "<url-opaco>", "results": [...]}— seguinextper la pagina successiva. Query paramcursor=<token>. Non c'ècounttotale. - Page-number pagination per liste collezionali:
/devices/,/auth/users/,/assignments/,/alert-thresholds/,/geofences/,/audit-log/. Query paramspage=N&page_size=M. Response includecount,total_pages,next,previous,results.
Date e orari
Tutti i campi timestamp nelle response (ts, device_ts, received_at, created_at, updated_at, deleted_at, connected_at, last_seen, ecc.) sono UTC in formato ISO-8601 con offset esplicito +00:00. Esempio: 2026-05-20T14:32:11+00:00.
Su Location e Telemetry ci sono tre timestamp distinti:
ts— orologio del server al momento in cui il protocol server riceve e processa il pacchetto dal device. Canonico, da usare per ordinamento, paginazione, display.device_ts— orologio del device parsato dal payload del pacchetto. Audit-only. Può esserenull(pacchetto senza data/ora) o sballato (RTC del device che deriva). Non usarlo per ordinamento.received_at— istante dell'INSERTsu database. Campo interno, quasi sempre vicinissimo ats. Ignoralo lato applicazione.
Conseguenze:
- Nessun campo è in ora locale o fuso server. Il partner converte alla timezone dell'utente finale lato proprio.
- I parametri query
ts_after/ts_before(storici location / telemetry / events) accettano ISO-8601. Passare sempre con offset esplicito (+00:00oZ); un input senza offset viene interpretato come ora locale del server e produce risultati ambigui. - L'endpoint
/organizations/self/espone il campotimezone(IANA, es.Europe/Rome) come preferenza di display della org; non altera mai la rappresentazione deitsin response.
Idempotenza
Sulle POST il partner può inviare l'header Idempotency-Key valorizzato con un UUIDv4 generato lato partner. Semantica (uguale a Stripe / GitHub / PayPal):
- Stessa key + stesso body → response cacheata (status + body), header
X-Idempotent-Replayed: true. Side-effect eseguito una volta sola. - Stessa key + body diverso →
409 Conflict, body{"detail": "Idempotency key already used with different request body."}. - Key assente → nessun dedup, la richiesta prosegue normalmente.
- TTL cache: 24 ore. Chiave di cache scoped per application del partner.
Politica consigliata: un UUID per operazione logica, riusato in caso di retry (errori di rete, timeout, 5xx). Nuovo UUID solo quando l'operazione è abbandonata.
L'header copre solo POST. Per PATCH / DELETE accettare semantica at-least-once oppure forzare manualmente l'idempotenza lato partner.
Errori
Shape
Endpoint /api/v1/* — response uniforme:
json
{
"code": "insufficient_scope",
"detail": "Token missing required scope: devices:write"
}Il campo code è opzionale (non sempre presente in errori di validazione nested). Fare matching prima sullo status HTTP, poi su code se presente.
Endpoint /o/token/ — RFC 6749 §5.2:
json
{ "error": "invalid_scope", "error_description": "..." }Quando fare retry
| Classe | Status | Retry? |
|---|---|---|
| Auth | 401 | No — ri-autenticarsi; se persiste → credenziali errate |
| Scope / permission | 403 | No — chiedere grant a LifeDome |
| Tenant boundary | 404 su risorsa attesa | No — fuori dal tuo tenant |
| Validation | 400 | No — correggere il body |
| Conflict | 409 su idempotency reuse | No — usare chiave nuova |
| Conflict | 409 su conflict di versione | Sì — ri-leggere + ri-provare |
| Rate limit | 429 | Sì — rispettare Retry-After, backoff esp. + jitter |
| Server | 5xx | Sì — 3–5 tentativi, backoff esp., stesso Idempotency-Key |
Codici da riconoscere a memoria
| Codice | Dove | Significato |
|---|---|---|
scope_not_available | auth gate | Endpoint non parte della superficie partner |
insufficient_scope | auth gate | Token valido ma scope mancante |
invalid_scope | /o/token/ | Scope fuori allow-list del partner |
partner_suspended | auth layer | Il partner non è ACTIVE |
idempotency_key_reuse (detail) | idempotency | Stessa key, body diverso |