Sessions API
En Session er én inspektion. Opret en, præg en feltbruger-invitation, opsaml beviser, afslut den. Alt andet hænger på denne ressource.
Session-objektet
{
"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"
} | Status | Betydning |
|---|---|
created | Sessionsrækken findes; ingen har tilsluttet sig endnu. |
open | Feltbrugeren har tilsluttet sig; sessionen er live. |
recording | Optagelse i gang (valgfri, operatør-udløst). |
closed | Sessionen er afsluttet. Kædens hoved forankret hos TSA; rapporter tilgængelige. |
expired | Planlagt session, som ingen tilsluttede sig inden for TTL'en. |
Opret en session
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"
}' Returnerer det netop oprettede Session-objekt (HTTP 201).
Body-felter
| Felt | Type | Påkrævet | Noter |
|---|---|---|---|
notes | string | nej | Synlig for operatøren. Vises i invitations-e-mails. |
scheduled_for | ISO 8601 | nej | En fremtidig dato udløser påmindelses-e-mails 24 t + 1 t før. Udelad for "start nu". |
locale | string | nej | En af de 14 understøttede locales. Styrer sproget for SPA'en + PDF-rapporten. Falder tilbage til organisationens præference. |
campaign | UUID | nej | Valgfri FK til en Campaign for samlet rapportering. |
List sessioner
GET /api/v1/public/sessions — scope sessions:read
curl https://app.nexbasira.com/api/v1/public/sessions?limit=25 \
-H "Authorization: Bearer nb_sec_..." Cursor-pagineret. Send cursor fra det forrige svars next_cursor for at paginere.
{
"data": [{ /* Session, Session, ... */ }],
"has_more": true,
"next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAy..."
} Hent en session
GET /api/v1/public/sessions/{session_id} — scope sessions:read
Afslut en session
POST /api/v1/public/sessions/{session_id}/end — scope sessions:write
Lukker sessionen, udløser forankring af kædens hoved hos alle konfigurerede TSA'er og starter efterbehandling af optagelsen, hvis en optagelse kørte. Idempotent — et kald på en allerede lukket session returnerer det lukkede Session-objekt uden at forankre igen.
Præg en feltbruger-invitation
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"
} Både join_url og otp_code vises præcis én gang i svaret. Tokens er IP/UA-bundne, engangsbrug og tidsbegrænsede.
To-faktor-tilslutning (OTP)
Når du angiver mindst én af recipient_email eller recipient_phone, præger platformen automatisk en 6-cifret numerisk OTP og sender den på de(n) matchende kanal(er) i en separat besked adskilt fra tilslutnings-URL'en — defence-in-depth, så en videresendt e-mail eller SMS ikke lækker begge faktorer på én gang. Den rå kode returneres også i svaret ( otp_code) så du kan dele den manuelt igen, hvis leveringen fejler.
På feltsiden viser SPA'en OTP-prompten ved første indløsning. Indsend koden via X-Join-OTP -headeren ved et gentaget forsøg på GET /v1/sessions/{id}/join/{token} — headeren (ikke URL'en) holder koden ude af browserhistorik og adgangslogs.
OTP-regler:
- 6-cifret numerisk, hashet med SHA-256 + pepper i hvile.
- 10-minutters TTL fra udstedelse.
- 5 forkerte forsøg låser invitationen (HTTP 423) — operatøren skal genudstede.
- Verificeres én gang ved første indløsning; genindløsning fra samme IP/UA-par springer kontrollen over (tokenet er allerede bundet).
- Ansigt-til-ansigt-URL-overdragelse (ingen
recipient_email+ ingenrecipient_phone) springer OTP-prægning over — URL'en alene er autentifikationsfaktoren. Brug kun dette til personlige overdragelser.
Svarkoder på GET /v1/sessions/{id}/join/{token}:
| Status | Body | Betydning |
|---|---|---|
| 200 | {field_session_token, livekit, ...} | OTP bestået (eller ikke påkrævet); feltsessionen er live. |
| 401 | {detail:"otp_required", channels:[...], channel_hint_email, channel_hint_phone} | SPA'en skal vise OTP-indtastningsformularen. |
| 401 | {detail:"otp_invalid", attempts_remaining} | Forkert kode; vis resterende forsøg. |
| 401 | {detail:"otp_expired"} | 10-minutters-vinduet er udløbet; operatøren skal genudstede. |
| 423 | {detail:"otp_locked"} | 5 forkerte forsøg; invitationen er død, indtil den genudstedes. |
Almindelige fejl
| Status | Kode | Hvornår |
|---|---|---|
| 402 | billing.subscription_past_due | Organisationens Stripe-abonnement er forfaldent. |
| 402 | billing.free_plan_minutes_exhausted | Gratis-/Pilot-niveauet har brugt sine 5 inspektioner. |
| 403 | permission_denied | Legitimationen mangler sessions:write scope. |
| 409 | session.already_ended | Forsøg på at afslutte en session, der allerede er lukket (sjældent — `end` er normalt idempotent). |
| 429 | rate_limited | Grænsen på 60 rpm pr. legitimation eller 600 rpm pr. organisation er nået. Se X-RateLimit-Reset -headeren. |
Se Fejl + rate limits for den fulde fejlkonvolut-struktur og vejledning om genforsøg.