API de Sessões
Uma Sessão é uma inspeção. Crie uma, emita um convite de utilizador de terreno, capture provas, termine-a. Tudo o resto depende deste recurso.
O objeto 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"
} | Estado | Significado |
|---|---|
created | A linha da sessão existe; ninguém entrou ainda. |
open | O utilizador de terreno entrou; a sessão está em direto. |
recording | Gravação em curso (opcional, acionada pelo operador). |
closed | Sessão terminada. Topo da cadeia ancorado na TSA; relatórios disponíveis. |
expired | Sessão agendada em que ninguém entrou dentro do TTL. |
Criar uma sessão
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"
}' Devolve o objeto Session acabado de criar (HTTP 201).
Campos do corpo
| Campo | Tipo | Obrigatório | Notas |
|---|---|---|---|
notes | string | não | Visível para o operador. Mostrado nos emails de convite. |
scheduled_for | ISO 8601 | não | Uma data futura aciona emails de lembrete 24h + 1h antes. Omita para "começar agora". |
locale | string | não | Um dos 14 locales suportados. Determina a língua da SPA + do relatório PDF. Assume por defeito a preferência da organização. |
campaign | UUID | não | FK opcional para uma Campaign para relatórios em lote. |
Listar sessões
GET /api/v1/public/sessions — scope sessions:read
curl https://app.nexbasira.com/api/v1/public/sessions?limit=25 \
-H "Authorization: Bearer nb_sec_..." Paginado por cursor. Passe cursor da next_cursor da resposta anterior para paginar.
{
"data": [{ /* Session, Session, ... */ }],
"has_more": true,
"next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAy..."
} Obter uma sessão
GET /api/v1/public/sessions/{session_id} — scope sessions:read
Terminar uma sessão
POST /api/v1/public/sessions/{session_id}/end — scope sessions:write
Fecha a sessão, aciona a ancoragem do topo da cadeia em todas as TSAs configuradas e arranca o pós-processamento da gravação se estava a decorrer uma gravação. Idempotente — chamar numa sessão já fechada devolve o objeto Session fechado sem voltar a ancorar.
Emitir um convite de utilizador de terreno
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"
} O join_url e o otp_code são mostrados uma única vez na resposta. Os tokens são fixados por IP/UA, de uso único e limitados no tempo.
Entrada de dois fatores (OTP)
Quando fornece pelo menos um de recipient_email ou recipient_phone, a plataforma emite automaticamente um OTP numérico de 6 dígitos e envia-o no(s) canal(is) correspondente(s) numa mensagem separada do URL de entrada — defesa em profundidade para que um email ou SMS reencaminhado não deixe vazar os dois fatores de uma só vez. O código simples é também devolvido na resposta ( otp_code) para que o possa voltar a partilhar manualmente se a entrega falhar.
Do lado do terreno, a SPA apresenta o prompt de OTP na primeira redenção. Submeta o código via o cabeçalho X-Join-OTP numa retentativa de GET /v1/sessions/{id}/join/{token} — o cabeçalho (não o URL) mantém o código fora do histórico do navegador e dos logs de acesso.
Regras do OTP:
- Numérico de 6 dígitos, com hash SHA-256 + pepper em repouso.
- TTL de 10 minutos a partir da emissão.
- 5 tentativas erradas bloqueiam o convite (HTTP 423) — o operador tem de reemitir.
- Verificado uma vez na primeira redenção; a re-redenção do mesmo par IP / UA salta a barreira (o token já está fixado).
- A entrega presencial do URL (sem
recipient_email+ semrecipient_phone) salta a emissão do OTP — só o URL é o fator de autenticação. Use isto apenas para entregas presenciais.
Códigos de resposta em GET /v1/sessions/{id}/join/{token}:
| Estado | Corpo | Significado |
|---|---|---|
| 200 | {field_session_token, livekit, ...} | OTP validado (ou não obrigatório); a sessão de terreno está em direto. |
| 401 | {detail:"otp_required", channels:[...], channel_hint_email, channel_hint_phone} | A SPA deve renderizar o formulário de introdução do OTP. |
| 401 | {detail:"otp_invalid", attempts_remaining} | Código errado; mostre as tentativas restantes. |
| 401 | {detail:"otp_expired"} | A janela de 10 min decorreu; o operador tem de reemitir. |
| 423 | {detail:"otp_locked"} | 5 tentativas erradas; o convite está morto até nova emissão. |
Erros comuns
| Estado | Código | Quando |
|---|---|---|
| 402 | billing.subscription_past_due | A subscrição Stripe da organização está em atraso. |
| 402 | billing.free_plan_minutes_exhausted | O escalão Free / Pilot usou as suas 5 inspeções. |
| 403 | permission_denied | A credencial não tem sessions:write scope. |
| 409 | session.already_ended | Tentar terminar uma sessão que já está fechada (raro — `end` é normalmente idempotente). |
| 429 | rate_limited | Throttle de 60 rpm por credencial ou de 600 rpm por organização atingido. Ver X-RateLimit-Reset cabeçalho. |
Ver Erros + limites de taxa para a forma completa do envelope de erro e orientação sobre retentativas.