Sessions-API
Eine Session ist eine Inspektion. Erstellen Sie eine, prägen Sie eine Feldbenutzer-Einladung, erfassen Sie Beweise, beenden Sie sie. Alles andere hängt an dieser Ressource.
Das Session-Objekt
{
"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 | Bedeutung |
|---|---|
created | Session-Zeile existiert; noch niemand ist beigetreten. |
open | Feldbenutzer ist beigetreten; die Sitzung ist live. |
recording | Aufzeichnung läuft (optional, operator-ausgelöst). |
closed | Sitzung beendet. Kettenkopf an der TSA verankert; Berichte verfügbar. |
expired | Geplante Sitzung, der niemand innerhalb der TTL beigetreten ist. |
Eine Session erstellen
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"
}' Gibt das frisch erstellte Session-Objekt zurück (HTTP 201).
Body-Felder
| Feld | Typ | Erforderlich | Anmerkungen |
|---|---|---|---|
notes | string | nein | Für den Operator sichtbar. In Einladungs-E-Mails angezeigt. |
scheduled_for | ISO 8601 | nein | Ein zukünftiges Datum löst Erinnerungs-E-Mails 24 h + 1 h vorher aus. Für „jetzt starten“ weglassen. |
locale | string | nein | Eine der 14 unterstützten Locales. Steuert die Sprache von SPA + PDF-Bericht. Standardmäßig die Präferenz der Org. |
campaign | UUID | nein | Optionaler FK auf eine Campaign für gebündeltes Reporting. |
Sessions auflisten
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-paginiert. Übergeben Sie cursor aus dem next_cursor der vorherigen Response, um zu paginieren.
{
"data": [{ /* Session, Session, ... */ }],
"has_more": true,
"next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAy..."
} Eine Session abrufen
GET /api/v1/public/sessions/{session_id} — Scope sessions:read
Eine Session beenden
POST /api/v1/public/sessions/{session_id}/end — Scope sessions:write
Schließt die Sitzung, löst den Kettenkopf-Anker an allen konfigurierten TSAs aus und stößt die Aufzeichnungs-Nachbearbeitung an, falls eine Aufzeichnung lief. Idempotent — ein Aufruf auf einer bereits geschlossenen Sitzung gibt das geschlossene Session-Objekt zurück, ohne neu zu verankern.
Eine Feldbenutzer-Einladung prägen
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"
} Die join_url und otp_code werden in der Response genau einmal angezeigt. Tokens sind IP/UA-gebunden, einmalig nutzbar und zeitlich begrenzt.
Zwei-Faktor-Beitritt (OTP)
Wenn Sie mindestens eines von recipient_email oder recipient_phoneangeben, prägt die Plattform automatisch ein 6-stelliges numerisches OTP und sendet es auf dem/den passenden Kanal/Kanälen in einer separaten Nachricht getrennt von der Beitritts-URL — Defense-in-Depth, damit eine weitergeleitete E-Mail oder SMS nicht beide Faktoren auf einmal preisgibt. Der Klartext-Code wird außerdem in der Response zurückgegeben ( otp_code), sodass Sie ihn manuell erneut teilen können, falls die Zustellung fehlschlägt.
Feldseitig blendet die SPA bei der ersten Einlösung die OTP-Abfrage ein. Übermitteln Sie den Code über den X-Join-OTP -Header bei einem Retry von GET /v1/sessions/{id}/join/{token} — der Header (nicht die URL) hält den Code aus dem Browser-Verlauf und den Zugriffslogs heraus.
OTP-Regeln:
- 6-stellig numerisch, im Ruhezustand mit SHA-256 + Pepper gehasht.
- 10-Minuten-TTL ab Ausstellung.
- 5 falsche Versuche sperren die Einladung (HTTP 423) — der Operator muss sie neu ausstellen.
- Einmal bei der ersten Einlösung verifiziert; eine erneute Einlösung vom selben IP- / UA-Paar überspringt das Gate (das Token ist bereits gebunden).
- Ein Face-to-Face-URL-Handoff (kein
recipient_email+ keinrecipient_phone) überspringt das OTP-Prägen — die URL allein ist der Auth-Faktor. Nutzen Sie dies nur für persönliche Übergaben.
Response-Codes bei GET /v1/sessions/{id}/join/{token}:
| Status | Body | Bedeutung |
|---|---|---|
| 200 | {field_session_token, livekit, ...} | OTP bestanden (oder nicht erforderlich); die Feldsitzung ist live. |
| 401 | {detail:"otp_required", channels:[...], channel_hint_email, channel_hint_phone} | Die SPA sollte das OTP-Eingabeformular rendern. |
| 401 | {detail:"otp_invalid", attempts_remaining} | Falscher Code; verbleibende Versuche anzeigen. |
| 401 | {detail:"otp_expired"} | 10-Minuten-Fenster verstrichen; der Operator muss neu ausstellen. |
| 423 | {detail:"otp_locked"} | 5 falsche Versuche; die Einladung ist tot bis zur Neuausstellung. |
Häufige Fehler
| Status | Code | Wann |
|---|---|---|
| 402 | billing.subscription_past_due | Das Stripe-Abonnement der Org ist überfällig. |
| 402 | billing.free_plan_minutes_exhausted | Die Free- / Pilot-Stufe hat ihre 5 Inspektionen aufgebraucht. |
| 403 | permission_denied | Dem Credential fehlt sessions:write Scope. |
| 409 | session.already_ended | Versuch, eine bereits geschlossene Sitzung zu beenden (selten — `end` ist normalerweise idempotent). |
| 429 | rate_limited | 60-rpm-Per-Credential- oder 600-rpm-Per-Org-Throttle erreicht. Siehe X-RateLimit-Reset -Header. |
Siehe Fehler + Rate-Limits für die vollständige Fehler-Envelope-Form und Retry-Hinweise.