API Sessioni
Una Session è una singola ispezione. Creane una, genera un invito per l'utente sul campo, cattura le prove, terminala. Tutto il resto dipende da questa risorsa.
L'oggetto Session
{
"id": "0c8f4d2e-1a3b-4c5d-9e7f-1234567890ab",
"status": "open",
"operator_email": "ops@yourco.com",
"scheduled_for": "2026-05-23T10:00:00Z",
"started_at": "2026-05-23T10:00:14Z",
"ended_at": null,
"notes": "Vehicle damage — claim CL-2026-0042",
"locale": "fr",
"consent_state": { "camera": "granted", "gps": "granted" },
"campaign": null,
"created_at": "2026-05-21T14:21:00Z",
"updated_at": "2026-05-23T10:00:14Z"
} | Stato | Significato |
|---|---|
created | La riga Session esiste; nessuno si è ancora unito. |
open | L'utente sul campo si è unito; la sessione è live. |
recording | Registrazione in corso (opzionale, attivata dall'operatore). |
closed | Sessione terminata. Testa della catena ancorata alla TSA; report disponibili. |
expired | Sessione programmata a cui nessuno si è unito entro il TTL. |
Crea una sessione
POST /api/v1/public/sessions — scope sessions:write
curl -X POST https://app.nexbasira.com/api/v1/public/sessions \
-H "Authorization: Bearer nb_sec_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"notes": "Vehicle damage — claim CL-2026-0042",
"scheduled_for": "2026-05-23T10:00:00Z",
"locale": "fr"
}' Restituisce l'oggetto Session appena creato (HTTP 201).
Campi del body
| Campo | Tipo | Obbligatorio | Note |
|---|---|---|---|
notes | string | no | Visibile all'operatore. Mostrato nelle email di invito. |
scheduled_for | ISO 8601 | no | Una data futura attiva le email di promemoria 24h + 1h prima. Ometti per "inizia ora". |
locale | string | no | Una delle 14 lingue supportate. Determina la lingua della SPA + del report PDF. Il default è la preferenza dell'org. |
campaign | UUID | no | FK opzionale a una Campaign per il reporting in batch. |
Elenca le sessioni
GET /api/v1/public/sessions — scope sessions:read
curl https://app.nexbasira.com/api/v1/public/sessions?limit=25 \
-H "Authorization: Bearer nb_sec_..." Paginata a cursore. Passa cursor dalla risposta precedente next_cursor per paginare.
{
"data": [{ /* Session, Session, ... */ }],
"has_more": true,
"next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAy..."
} Recupera una sessione
GET /api/v1/public/sessions/{session_id} — scope sessions:read
Termina una sessione
POST /api/v1/public/sessions/{session_id}/end — scope sessions:write
Chiude la sessione, attiva l'ancoraggio della testa della catena su tutte le TSA configurate e avvia il post-processing della registrazione se ne era in corso una. Idempotente — chiamarla su una sessione già chiusa restituisce l'oggetto Session chiuso senza ri-ancorare.
Genera un invito per l'utente sul campo
POST /api/v1/public/sessions/{session_id}/participants — scope participants:write
curl -X POST https://app.nexbasira.com/api/v1/public/sessions/0c8f.../participants \
-H "Authorization: Bearer nb_sec_..." \
-H "Content-Type: application/json" \
-d '{
"recipient_first_name": "Alex",
"recipient_last_name": "Garcia",
"recipient_email": "alex@policyholder.com",
"send_email": true,
"ttl_minutes": 1440
}' {
"id": "i-1",
"session": "0c8f...",
"role": "field",
"expires_at": "2026-05-24T10:00:00Z",
"recipient_first_name": "Alex",
"recipient_last_name": "Garcia",
"recipient_email": "alex@policyholder.com",
"recipient_phone": "",
"join_url": "https://app.nexbasira.com/join/0c8f.../?t=tok_PLAINTEXT_ONCE",
"otp_code": "487192",
"otp_required": true,
"otp_expires_at": "2026-05-23T10:10:00Z",
"created_at": "2026-05-23T10:00:00Z"
} Il join_url e otp_code sono mostrati esattamente una volta nella risposta. I token sono vincolati a IP/UA, monouso e a tempo limitato.
Join a due fattori (OTP)
Quando fornisci almeno uno tra recipient_email o recipient_phone, la piattaforma genera automaticamente un OTP numerico a 6 cifre e lo invia sui canali corrispondenti in un messaggio separato dalla URL di join — difesa in profondità così un'email o un SMS inoltrato non fa trapelare entrambi i fattori in una volta. Il codice in chiaro è restituito anche nella risposta ( otp_code) così puoi ricondividerlo manualmente se il recapito fallisce.
Lato campo, la SPA mostra il prompt dell'OTP alla prima redenzione. Invia il codice tramite l'header X-Join-OTP in un retry di GET /v1/sessions/{id}/join/{token} — l'header (non la URL) tiene il codice fuori dalla cronologia del browser e dai log di accesso.
Regole OTP:
- Numerico a 6 cifre, hashato con SHA-256 + pepper a riposo.
- TTL di 10 minuti dall'emissione.
- 5 tentativi errati bloccano l'invito (HTTP 423) — l'operatore deve riemetterlo.
- Verificato una sola volta alla prima redenzione; la ri-redenzione dalla stessa coppia IP / UA salta il gate (il token è già vincolato).
- Il passaggio della URL faccia a faccia (nessun
recipient_email+ nessunrecipient_phone) salta la generazione dell'OTP — la URL da sola è il fattore di autenticazione. Usalo solo per i passaggi di persona.
Codici di risposta su GET /v1/sessions/{id}/join/{token}:
| Stato | Body | Significato |
|---|---|---|
| 200 | {field_session_token, livekit, ...} | OTP superato (o non richiesto); la sessione sul campo è live. |
| 401 | {detail:"otp_required", channels:[...], channel_hint_email, channel_hint_phone} | La SPA dovrebbe mostrare il form di inserimento dell'OTP. |
| 401 | {detail:"otp_invalid", attempts_remaining} | Codice errato; mostra i tentativi rimanenti. |
| 401 | {detail:"otp_expired"} | Finestra di 10 min trascorsa; l'operatore deve riemettere. |
| 423 | {detail:"otp_locked"} | 5 tentativi errati; l'invito è morto fino alla riemissione. |
Errori comuni
| Stato | Codice | Quando |
|---|---|---|
| 402 | billing.subscription_past_due | L'abbonamento Stripe dell'org è scaduto. |
| 402 | billing.free_plan_minutes_exhausted | Il piano Free / Pilot ha esaurito le sue 5 ispezioni. |
| 403 | permission_denied | La credenziale non ha sessions:write scope. |
| 409 | session.already_ended | Tentativo di terminare una sessione già chiusa (raro — `end` è normalmente idempotente). |
| 429 | rate_limited | Throttle di 60-rpm per-credenziale o 600-rpm per-org raggiunto. Vedi l'header X-RateLimit-Reset header. |
Vedi Errori + rate limit per la forma completa dell'envelope di errore e le indicazioni sui retry.