API de sesiones
Una Session es una inspección. Cree una, genere una invitación de usuario de campo, capture pruebas, finalícela. Todo lo demás depende de este recurso.
El 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 | La fila de la sesión existe; nadie se ha unido todavía. |
open | El usuario de campo se ha unido; la sesión está en directo. |
recording | Grabación en curso (opcional, iniciada por el operador). |
closed | Sesión finalizada. Cabeza de la cadena anclada en la TSA; informes disponibles. |
expired | Sesión programada a la que nadie se unió dentro del TTL. |
Crear una sesión
POST /api/v1/public/sessions — alcance 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"
}' Devuelve el objeto Session recién creado (HTTP 201).
Campos del cuerpo
| Campo | Tipo | Obligatorio | Notas |
|---|---|---|---|
notes | string | no | Visible para el operador. Aparece en los correos de invitación. |
scheduled_for | ISO 8601 | no | Una fecha futura activa correos de recordatorio 24 h y 1 h antes. Omítalo para "empezar ahora". |
locale | string | no | Uno de los 14 idiomas admitidos. Determina el idioma de la SPA y del informe PDF. Por defecto usa la preferencia de la organización. |
campaign | UUID | no | FK opcional a una Campaign para informes por lotes. |
Listar sesiones
GET /api/v1/public/sessions — alcance sessions:read
curl https://app.nexbasira.com/api/v1/public/sessions?limit=25 \
-H "Authorization: Bearer nb_sec_..." Paginado por cursor. Pase cursor del next_cursor de la respuesta anterior para paginar.
{
"data": [{ /* Session, Session, ... */ }],
"has_more": true,
"next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAy..."
} Recuperar una sesión
GET /api/v1/public/sessions/{session_id} — alcance sessions:read
Finalizar una sesión
POST /api/v1/public/sessions/{session_id}/end — alcance sessions:write
Cierra la sesión, activa el anclaje de la cabeza de la cadena en todas las TSA configuradas e inicia el posprocesamiento de la grabación si había una grabación en curso. Idempotente — llamarlo en una sesión ya cerrada devuelve el objeto Session cerrado sin volver a anclar.
Generar una invitación de usuario de campo
POST /api/v1/public/sessions/{session_id}/participants — alcance 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"
} El join_url y el otp_code se muestran exactamente una vez en la respuesta. Los tokens están fijados a IP/UA, son de un solo uso y tienen límite de tiempo.
Unión de dos factores (OTP)
Cuando proporciona al menos uno de recipient_email o recipient_phone, la plataforma genera automáticamente un OTP numérico de 6 dígitos y lo envía por el/los canal(es) correspondiente(s) en un mensaje separado de la URL de acceso — defensa en profundidad para que un correo o SMS reenviado no filtre ambos factores a la vez. El código en texto plano también se devuelve en la respuesta ( otp_code) para que pueda volver a compartirlo manualmente si la entrega falla.
En el lado de campo, la SPA muestra el aviso de OTP en el primer canje. Envíe el código mediante la X-Join-OTP cabecera en un reintento de GET /v1/sessions/{id}/join/{token} — la cabecera (no la URL) mantiene el código fuera del historial del navegador y de los registros de acceso.
Reglas del OTP:
- Numérico de 6 dígitos, con hash SHA-256 + pepper en reposo.
- TTL de 10 minutos desde la emisión.
- 5 intentos incorrectos bloquean la invitación (HTTP 423) — el operador debe reemitirla.
- Verificado una vez en el primer canje; volver a canjear desde el mismo par IP / UA omite la comprobación (el token ya está fijado).
- Entrega de la URL en persona (sin
recipient_email+ sinrecipient_phone) omite la generación del OTP — la URL por sí sola es el factor de autenticación. Use esto solo para entregas en persona.
Códigos de respuesta en GET /v1/sessions/{id}/join/{token}:
| Estado | Cuerpo | Significado |
|---|---|---|
| 200 | {field_session_token, livekit, ...} | OTP superado (o no requerido); la sesión de campo está en directo. |
| 401 | {detail:"otp_required", channels:[...], channel_hint_email, channel_hint_phone} | La SPA debería mostrar el formulario de entrada del OTP. |
| 401 | {detail:"otp_invalid", attempts_remaining} | Código incorrecto; muestra los intentos restantes. |
| 401 | {detail:"otp_expired"} | Ventana de 10 min transcurrida; el operador debe reemitir. |
| 423 | {detail:"otp_locked"} | 5 intentos incorrectos; la invitación queda inactiva hasta la reemisión. |
Errores comunes
| Estado | Código | Cuándo |
|---|---|---|
| 402 | billing.subscription_past_due | La suscripción de Stripe de la organización está vencida. |
| 402 | billing.free_plan_minutes_exhausted | El nivel Free / Piloto ha agotado sus 5 inspecciones. |
| 403 | permission_denied | La credencial no tiene sessions:write alcance. |
| 409 | session.already_ended | Intentar finalizar una sesión que ya está cerrada (poco común — `end` normalmente es idempotente). |
| 429 | rate_limited | Se alcanzó el límite de 60 rpm por credencial o 600 rpm por organización. Consulte la cabecera X-RateLimit-Reset . |
Consulte Errores y límites de tasa para la forma completa del sobre de error y la guía de reintentos.