API para integraciones
API de FlotaDoc
Conecta tu ERP, tu programa de nóminas, tu telemática o tu propia app con FlotaDoc. Consulta y registra viajes y gastos, gestiona vehículos y conductores, y descarga los informes del mes sin pasar por la web.
El acceso se activa por empresa y bajo petición: lo pide el titular de la cuenta y el equipo de FlotaDoc lo aprueba con los permisos que necesitáis.
- URL base
https://app.flotadoc.com/api- Autenticación
Bearer fd_live_…- Llamadas
- POST con JSON
- Especificación
- OpenAPI 3.1.0 · versión 1.0.0
En esta página
Referencia por área
Cada operación con su permiso, sus campos, un ejemplo con curl y sus errores. Todas son POST a la URL base más la ruta.
Viajes
Viajes de trabajo de los conductores (libro de viajes).
6 operaciones
viajes/list· Lista los viajes (libro o sincronización incremental)viajes/get· Ficha de un viajeviajes/history· Historial de versiones de un viajeviajes/reasons-list· Lista las categorías de motivoviajes/save· Registra un viaje para un conductor o corrige unoviajes/delete· Anula un viaje
Gastos
Combustible, recarga y otros gastos, con su ticket.
9 operaciones
gastos/list· Lista los gastos o sincroniza los cambiosgastos/get· Consulta un gastogastos/history· Historial de versiones de un gastogastos/types-list· Tipos de gasto disponiblesgastos/attachment-download-url· URL de descarga del ticketgastos/save· Registra o corrige un gastogastos/delete· Anula un gastogastos/attachment-upload-url· URL de subida del ticketgastos/attachment-attach· Adjunta o sustituye el ticket de un gasto
Flota
Vehículos, modelos de la empresa y asignación de conductores.
9 operaciones
flota/list· Lista los vehículosflota/get· Obtiene un vehículoflota/models-search· Busca marca y modelo con su consumoflota/company-models-list· Lista los modelos de la empresaflota/save· Da de alta o edita un vehículoflota/delete· Da de baja un vehículoflota/assign· Fija los conductores de un vehículo fijoflota/company-models-save· Guarda o corrige un modelo de la empresaflota/company-models-delete· Retira un modelo de la empresa
Conductores
Usuarios de la empresa. Con clave API solo se dan de alta y de baja conductores.
6 operaciones
usuarios/list· Lista las personas de la empresa y sus plazasusuarios/get· Obtiene una persona de la empresausuarios/invite· Da de alta un conductor y le envía la invitaciónusuarios/resend-invite· Reenvía la invitación a un conductorusuarios/deactivate· Da de baja (desactiva) a un conductorusuarios/reactivate· Devuelve el acceso a un conductor desactivado
Cierres
Cierres mensuales por vehículo y conductor.
3 operaciones
cierres/list· Mes de la flota o historial de un vehículocierres/get· Un mes de un vehículocierres/pending· Pendientes de cierre de la flota
Informes
Informe mensual y descargas asíncronas (Excel, ZIP de tickets, PDF).
4 operaciones
informes/monthly-report· Informe mensual de un vehículo o de la flotainformes/export-create· Pedir una descarga del mes (asíncrona)informes/export-status· Estado de una descargainformes/export-download-url· Enlace de descarga de un fichero listo
Integridad
Verificación de la huella de los registros inalterables.
1 operación
integridad/verify· Verificar la cadena de huellas
Para quien conecta un ERP, un programa de nóminas, una telemática o una app propia con FlotaDoc. La referencia exacta de cada operación (campos, tipos, límites y errores) está en openapi.yaml; esta guía explica cómo usarla, con ejemplos completos.
1. Cómo conseguir el acceso
La API se activa por empresa. Hay dos vías:
- Lo activa el equipo de FlotaDoc cuando lo habéis pactado con nosotros.
- Lo pide el titular de la empresa (owner) en la web, en Mi cuenta › Integraciones › Solicitar acceso, con un formulario guiado: qué sistema queréis conectar, qué permisos necesitáis, cada cuánto se conectará, quién lo desarrolla, el contacto técnico, la fecha prevista y qué queréis conseguir. El equipo puede pedir más información, aprobarla (con los permisos pedidos o menos) o rechazarla con un motivo. Los avisos llegan por email.
El acceso queda limitado a los permisos aprobados. Para ampliarlos, el owner pide más permisos desde la misma página.
Con el acceso activo, el owner crea las claves en Mi cuenta › Integraciones › Claves: nombre, permisos y caducidad opcional (máximo 10 claves activas por empresa). En la misma página ve el uso (peticiones, errores, tiempos) y el historial de peticiones de los últimos 90 días.
2. Autenticación
Authorization: Bearer fd_live_7Abcdefghijklmnopqrstuvwxyz0123456789ABCDE
- La clave es
fd_live_seguida de 43 caracteres (letras y cifras). Se muestra una sola vez, al crearla: guárdala en el gestor de secretos de tu sistema. Nunca en el código, en una URL, en un fichero compartido ni en los logs. - La clave actúa con los derechos del owner que la creó y solo en su empresa. Si esa persona deja de ser owner o se da de baja, la clave deja de funcionar (
api_key_inactive). - Para rotar una clave: crea otra, cambia tu sistema y revoca la antigua. La revocación es inmediata.
| HTTP | error.code |
Qué pasa | Qué hacer |
|---|---|---|---|
| 401 | api_key_invalid |
La clave está mal formada o no existe | Revisa la cabecera y la clave |
| 401 | api_key_revoked |
Se revocó, o la empresa ya no tiene el acceso API activo | Pide al owner una clave nueva |
| 401 | api_key_expired |
Caducó | Pide al owner una clave nueva |
| 401 | api_key_inactive |
Quien la creó ya no es owner activo | Pide a un owner activo una clave nueva |
| 403 | account_closed |
La cuenta de la empresa está cerrada | — |
No reintentes un 401: no se arregla solo.
3. Llamadas y respuestas
-
URL base:
https://app.flotadoc.com/api. Todas las operaciones sonPOST {URL base}/{módulo}/{submódulo}/{acción}con un cuerpo JSON (Content-Type: application/json). Son los mismos endpoints que usa la web de FlotaDoc. -
Los identificadores van en el cuerpo, nunca en la ruta:
{"tripId": 812}. -
Respuesta correcta:
{ "data": … }. -
Error:
{ "error": { "code": "…", "message": "…" } }. Interpreta siempreerror.code;messagees un texto técnico que puede cambiar. Un422 invalid_inputtrae ademáserror.fieldscon el error de cada campo, todos a la vez:{ "error": { "code": "invalid_input", "message": "invalid fields", "fields": { "date": "invalid", "km": "out_of_range" } } } -
X-Request-Id: toda respuesta a una clave conocida lleva esta cabecera. Es el id de la petición en el historial que ve el owner. Guárdalo en tus logs junto al error: es lo que te pedirá soporte. -
Se aplican las mismas reglas que en la web: una cuenta en solo lectura (prueba vencida, pago pendiente…) consulta y exporta pero no registra; un mes cerrado no admite cambios; con los registros inalterables activados, corregir crea una versión con motivo y borrar es anular con motivo; los límites del plan (vehículos y usuarios) son los de la empresa.
4. Permisos y catálogo
Cada clave tiene uno o más permisos. Una clave solo usa los permisos que su empresa sigue teniendo permitidos: si el equipo de FlotaDoc retira un permiso a la empresa, las claves dejan de poder usarlo en el acto (403 api_scope_missing, con scope en el cuerpo).
| Permiso | Para qué | Operaciones |
|---|---|---|
lectura |
Consultar viajes, gastos, vehículos, conductores y cierres, y las descargas del gestor | movimientos/viajes: list, get, history, reasons-list · movimientos/gastos: list, get, history, types-list, attachment-download-url · movimientos/flota: list, get, models-search, company-models-list · movimientos/cierres: list, get, pending · movimientos/informes: monthly-report, export-create, export-status, export-download-url · movimientos/integridad: verify · auth/usuarios: list, get |
registros |
Crear, corregir y anular viajes y gastos (con ticket) en nombre de un conductor | movimientos/viajes: save, delete · movimientos/gastos: save, delete, attachment-upload-url, attachment-attach |
flota |
Vehículos, asignaciones y conductores (solo con rol conductor) | movimientos/flota: save, delete, assign, company-models-save, company-models-delete · auth/usuarios: invite, resend-invite, deactivate, reactivate |
Cualquier otra ruta responde 404 not_found a una clave, aunque exista para la web (claves, uso, facturación, equipo, ajustes de la empresa…). En el OpenAPI, cada operación indica su permiso en x-flotadoc-scope.
5. Límites (429)
- Por clave: cada clave admite un número de peticiones por minuto natural (por defecto 120; lo fija el equipo de FlotaDoc por empresa, entre 30 y 600). Al pasarlo recibes
429 rate_limitedcon la cabeceraRetry-After(segundos hasta el minuto siguiente) y, en el cuerpo,retryAfterSecondsylimitPerMinute. Cuentan todas las peticiones de la clave, también las que fallan y las repetidas por idempotencia. - Global: la API entera tiene un techo de 50 peticiones por segundo (ráfaga de 100) para todos los clientes juntos. Ese 429 llega sin cuerpo JSON, sin
X-Request-Idy sinRetry-After, y no aparece en el historial. - En los dos casos: espera y reintenta. Usa
Retry-Aftersi viene; si no, una espera exponencial con algo de azar (1 s, 2 s, 4 s… hasta 60 s). Reparte las cargas grandes (por ejemplo, la sincronización nocturna) en vez de lanzarlas en ráfaga.
async function callFlotaDoc(path, body, { idempotencyKey } = {}) {
for (let attempt = 0; attempt < 6; attempt++) {
const res = await fetch(`https://app.flotadoc.com/api${path}`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.FLOTADOC_API_KEY}`,
'Content-Type': 'application/json',
...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),
},
body: JSON.stringify(body),
});
if (res.status !== 429 && res.status !== 503) return res;
const retryAfter = Number(res.headers.get('retry-after'));
const waitMs = retryAfter > 0 ? retryAfter * 1000 : Math.min(60000, 1000 * 2 ** attempt) + Math.random() * 500;
await new Promise((r) => setTimeout(r, waitMs));
}
throw new Error('FlotaDoc no responde: reintenta más tarde');
}
6. Idempotencia (Idempotency-Key)
Las escrituras (registros y flota) admiten la cabecera Idempotency-Key. Úsala siempre: si se corta la conexión, puedes repetir la petición sin crear un viaje o un gasto dos veces. La excepción es movimientos/gastos/attachment-upload-url, que solo firma una URL (ver al final de esta sección).
Idempotency-Key: erp-viaje-2026-10-000123
- De 1 a 100 caracteres
A-Z a-z 0-9 _ -. Usa un id estable de tu sistema (el del registro que estás volcando), no uno aleatorio por intento. - Vale 24 horas por clave API.
| Caso | Respuesta |
|---|---|
| Primera vez | Se ejecuta y se guarda la respuesta, también si es un error 4xx (un 422 o un 409 se repite igual) |
Misma Idempotency-Key, misma ruta y mismo cuerpo exacto |
La respuesta guardada, con Idempotent-Replayed: true (no se ejecuta otra vez) |
Misma Idempotency-Key con otro cuerpo u otra ruta |
409 idempotency_conflict |
| La primera petición aún está en curso | 409 idempotency_in_progress: reintenta en unos segundos |
| La primera respondió 5xx | No se guarda: el reintento se ejecuta de nuevo |
| La primera se perdió (sin respuesta tras 2 minutos) | El reintento se ejecuta de nuevo |
| Cabecera mal formada | 422 invalid_idempotency_key |
«Mismo cuerpo exacto» quiere decir los mismos bytes: serializa el cuerpo una vez y reutilízalo en los reintentos. Tras un 5xx o un corte, reintenta con la misma Idempotency-Key; tras corregir un 4xx (datos, mes cerrado…), manda la petición corregida con otra. En las lecturas la cabecera se ignora (por eso informes/export-create, que es de lectura, crea un trabajo nuevo en cada llamada).
attachment-upload-url no guarda respuesta. Solo firma una URL de subida que caduca a los 5 minutos: repetir la respuesta guardada te daría una URL caducada. Por eso ahí la cabecera se ignora, como en las lecturas: cada llamada devuelve una url y una key nuevas, sin Idempotent-Replayed. Si se corta la conexión, vuelve a pedirla; la subida que no uses se borra sola a las 24 horas. La idempotencia que importa está en el paso siguiente: gastos/save o attachment-attach con su Idempotency-Key.
7. Listados, paginación y sincronización
- Listados completos, sin paginar:
movimientos/flota/list,auth/usuarios/list,movimientos/viajes/reasons-list,movimientos/gastos/types-listy los de cierres devuelven todo de una vez. movimientos/viajes/listsinupdatedSince(el libro): los viajes activos del filtro (from,to,userId,vehicleId,status), conorder(asc|desc) ylimitopcional (1-500; sin él, todos).movimientos/gastos/listsinupdatedSince: los gastos activos del filtro con sus totales, hasta 1000 (truncated: truesi hay más: acota el filtro, por ejemplo por mes).
Sincronización incremental con updatedSince (viajes y gastos)
Para mantener una copia en tu sistema, pide solo lo que ha cambiado desde la última vez:
- Primera página:
{ "updatedSince": "<instante>", "limit": 200, …filtros }.updatedSincees una fecha y hora ISO 8601 con zona (2026-10-13T08:00:00Z) y es exclusiva. La primera vez, usa1970-01-01T00:00:00Z. - La respuesta trae
{ "trips" | "expenses", "nextCursor", "syncedUntil" }. - Páginas siguientes: el mismo cuerpo con
"cursor": "<nextCursor>". Repite los filtros: el cursor no los guarda. - Cuando
nextCursoresnull, guardasyncedUntily úsalo comoupdatedSinceen la próxima sincronización.
Detalles que importan:
- Llegan también los anulados (
active: false): bórralos o márcalos en tu copia. En viajes llegan además los que están en curso y los descartados. syncedUntillleva 60 segundos de margen sobre la hora del servidor: no se pierde un cambio que se estuviera guardando mientras sincronizabas.- Los filtros se aplican al estado actual del registro: si un gasto se corrige y sale del filtro (otro vehículo, otra fecha), deja de llegar sin aviso. Para una copia fiel, sincroniza sin filtros.
- En viajes, el coste calculado y los nombres pueden cambiar sin que cambie
updatedAt(por ejemplo, al llegar los tickets del mes). Para las cifras del mes, vuelve a pedir el libro o el informe mensual. - En sincronización,
limitva de 1 a 200 (200 por defecto) y en viajes no se admiteorder.
API=https://app.flotadoc.com/api
curl -sS -X POST "$API/movimientos/gastos/list" \
-H "Authorization: Bearer $FLOTADOC_API_KEY" -H "Content-Type: application/json" \
-d '{"updatedSince":"2026-10-12T22:00:00Z","limit":200}'
{ "data": { "expenses": [ { "expenseId": 81, "active": true, "updatedAt": "2026-10-13T09:30:12.345Z", "…": "…" } ],
"nextCursor": null, "syncedUntil": "2026-10-13T10:14:02.123456Z" } }
8. Flujos completos
En los ejemplos, API=https://app.flotadoc.com/api y la clave está en la variable FLOTADOC_API_KEY.
8.1 Registrar un viaje para un conductor
Permisos: lectura (para buscar conductor y vehículo) y registros.
- Conductor:
POST /auth/usuarios/listdevuelve los usuarios de la empresa. El conductor es un usuario con roluser(o un owner, que también conduce) que no esté desactivado; uno invitado que aún no ha entrado también vale. Necesitas suid. - Vehículo:
POST /movimientos/flota/list. Un conductoruserpuede usar los vehículos variables y los fijos en los que está asignado (drivers). - Categoría del motivo (opcional):
POST /movimientos/viajes/reasons-list. - Guardar:
POST /movimientos/viajes/savesintripIdy condriverUserId, que es obligatorio en un alta con clave.
curl -sS -X POST "$API/movimientos/viajes/save" \
-H "Authorization: Bearer $FLOTADOC_API_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: erp-viaje-2026-10-000123" \
-d '{
"driverUserId": 15,
"vehicleId": 9,
"tripDate": "2026-10-09",
"origin": "Calle Mayor 1, Logroño",
"destination": "Polígono Cantabria, Logroño",
"distanceKm": 7.4,
"reason": "Revisión de la instalación del cliente",
"reasonCategoryId": 3
}'
La respuesta (200) trae el viaje en data.trip, a nombre del conductor, con status: "recorded" y la clave que lo registró (apiKeyId).
- Obligatorios:
driverUserId,vehicleId,tripDate(YYYY-MM-DD, no posterior a hoy en Madrid),origin,destination(1-200 caracteres),distanceKm(mayor que 0, hasta 2 decimales) yreason(el motivo, 1-500). Opcionales: coordenadas (por parejas),odometerKm,routeKm,routeDurationMin,consumptionL100kmyreasonCategoryId. - El importe lo calcula FlotaDoc: en un vehículo propio (
km_rate) copia la tarifa por km de la empresa (kmRate) y calculaamount; en uno de empresa (fuel) el coste sale de los tickets de energía del mes. No se manda. - Errores propios:
422confields.driverUserId=required(falta),not_found(no es conductor de la empresa) ovehicle_not_allowed(no puede usar ese vehículo);404 vehicle_not_found;409 period_closed(el mes de ese vehículo está cerrado, conerror.periods: lo reabre el owner en la web);423 account_read_only(la cuenta no registra: consulta y exporta). - Corregir: la misma llamada con
tripId. Es un guardado completo: lee antes el viaje (viajes/get) y manda todos los campos. El conductor no se puede cambiar; si cambias el vehículo, el conductor tiene que poder usarlo, como en el alta (422confields.vehicleId=vehicle_not_allowed). MandachangeReason(motivo de la corrección): es obligatorio si la empresa tiene activados los registros inalterables (422 reason_required). - Anular:
POST /movimientos/viajes/deletecon{ "tripId": 412, "changeReason": "Duplicado" }(changeReason, obligatorio con registros inalterables).
8.2 Gasto con ticket (URL firmada y PUT)
Permiso: registros. El ticket se sube directamente al almacenamiento de FlotaDoc con una URL firmada; después se liga al gasto.
1. Pedir la URL de subida:
curl -sS -X POST "$API/movimientos/gastos/attachment-upload-url" \
-H "Authorization: Bearer $FLOTADOC_API_KEY" -H "Content-Type: application/json" \
-d '{"filename":"ticket-repsol.jpg","contentType":"image/jpeg","sizeBytes":184320}'
{ "data": { "url": "https://…amazonaws.com/…/ticket-repsol.jpg?X-Amz-…", "key": "42/expenses/u10/9f1c…/ticket-repsol.jpg",
"filename": "ticket-repsol.jpg", "contentType": "image/jpeg", "sizeBytes": 184320, "expiresIn": 300 } }
contentType:application/pdf,image/jpeg,image/pngoimage/webp.sizeBytes: el tamaño exacto del fichero, hasta 20 MB (más:413 payload_too_large).- Sin
Idempotency-Key(§6): cada llamada da una URL nueva. Si la URL caduca antes delPUT, pide otra.
2. Subir el fichero con un PUT a url, en menos de 5 minutos y sin la cabecera Authorization. Content-Type tiene que ser exactamente el declarado y el tamaño exactamente sizeBytes (van firmados: si no coinciden, el almacenamiento responde 403).
curl -sS -X PUT "$UPLOAD_URL" -H "Content-Type: image/jpeg" --upload-file ticket-repsol.jpg
3. Guardar el gasto con el ticket (antes de 24 horas desde el paso 1), con su key en attachmentKey. El gasto y el ticket se guardan juntos o ninguno:
curl -sS -X POST "$API/movimientos/gastos/save" \
-H "Authorization: Bearer $FLOTADOC_API_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: erp-gasto-2026-10-000456" \
-d '{
"driverUserId": 11,
"vehicleId": 7,
"type": "fuel",
"expenseDate": "2026-10-01",
"totalAmount": 50.00,
"baseAmount": 41.32,
"vatRate": 21,
"vatAmount": 8.68,
"supplierName": "Repsol Getafe",
"supplierTaxId": "A12345678",
"liters": 30.12,
"odometerKm": 120800,
"attachmentKey": "42/expenses/u10/9f1c2a1b0d4e5f60718293a4b5c6d7e8/ticket-repsol.jpg"
}'
- Obligatorios:
vehicleId,type,expenseDateytotalAmount(ydriverUserIden el alta). Los tipos disponibles para tu empresa salen dePOST /movimientos/gastos/types-list(available): los de peajes y aparcamiento, mantenimiento e ITV, y seguro, impuestos y renting dependen de los complementos que tenga contratados. fuelexigelitersy un vehículo de combustión o híbrido;ev_chargeexigekwhy uno eléctrico o híbrido. Si mandasbaseAmountyvatAmount, tienen que sumartotalAmount(±0,02 €).- Ligar el ticket después: guarda el gasto sin
attachmentKeyy llama aPOST /movimientos/gastos/attachment-attachcon{ "expenseId": 81, "key": "<key>" }. - Errores del ticket:
422 attachment_not_uploaded(no se subió, pasaron más de 24 horas o ya está en otro gasto) y422 attachment_invalid(el fichero no es una imagen o un PDF válidos). La subida es del owner que creó la clave: solo sus claves pueden ligarla. - Descargar un ticket:
POST /movimientos/gastos/attachment-download-urlcon{ "expenseId": 81 }devuelve una URL de descarga temporal. - Corregir y anular: como en viajes,
gastos/saveconexpenseId(guardado completo) yPOST /movimientos/gastos/deletecon{ "expenseId": 81, "reason": "Duplicado" }(aquí el motivo se llamareason; obligatorio con registros inalterables).
8.3 Descarga asíncrona (Excel, ZIP de tickets, PDF)
Permiso: lectura. Son las descargas del gestor; se generan en el servidor.
type |
Fichero |
|---|---|
monthly_pdf |
Informe mensual en PDF |
trips_xlsx |
Libro de viajes en Excel |
expenses_xlsx |
Gastos en Excel, con resumen por tipo |
tickets_zip |
Tickets del mes en un ZIP con su índice en Excel |
(trips_csv y expenses_csv existen solo por compatibilidad: no los uses.)
1. Pedirla: month (YYYY-MM) y, opcional, vehicleId (sin él, toda la flota). Responde 202 con el trabajo en pending.
curl -sS -X POST "$API/movimientos/informes/export-create" \
-H "Authorization: Bearer $FLOTADOC_API_KEY" -H "Content-Type: application/json" \
-d '{"type":"trips_xlsx","month":"2026-09","vehicleId":7}'
{ "data": { "export": { "exportId": 77, "type": "trips_xlsx", "month": "2026-09", "vehicleId": 7, "status": "pending", "…": "…" } } }
2. Consultar el estado cada 2-5 segundos, durante 5 minutos como mucho (cada consulta cuenta para el límite de la clave):
curl -sS -X POST "$API/movimientos/informes/export-status" \
-H "Authorization: Bearer $FLOTADOC_API_KEY" -H "Content-Type: application/json" \
-d '{"exportId":77}'
status |
Qué hacer |
|---|---|
pending, running |
Esperar y volver a consultar |
done |
Pedir el enlace. Disponible hasta expiresAt (7 días) |
error |
Con errorCode: export_too_large (ZIP de más de 400 MB), pídelo por vehículo; si no, vuelve a pedirlo |
expired |
Volver a pedirlo |
3. Descargar: export-download-url devuelve una URL temporal (expiresIn: 300, 5 minutos). Descárgala con un GET tal cual, sin la cabecera Authorization (el almacenamiento rechaza una petición con dos autenticaciones):
curl -sS -X POST "$API/movimientos/informes/export-download-url" \
-H "Authorization: Bearer $FLOTADOC_API_KEY" -H "Content-Type: application/json" \
-d '{"exportId":77}'
# → { "data": { "url": "https://…", "filename": "libro-viajes-1111AAA-2026-09.xlsx", "contentType": "…", "expiresIn": 300 } }
curl -sS -o libro-viajes-1111AAA-2026-09.xlsx "$DOWNLOAD_URL"
- Si el enlace caduca, pide otro con
export-download-url(el fichero sigue disponible hastaexpiresAt). - Errores:
409 export_not_ready(aún se está generando),409 export_failed,410 export_expired,404 export_not_foundy503 export_unavailable(fallo pasajero: reintenta). export-createno admiteIdempotency-Key: cada llamada crea un trabajo. Guarda elexportIdy reutilízalo en vez de volver a pedirlo.- Toda descarga queda registrada en la empresa.
8.4 Alta y baja de un conductor
Permiso: flota (y lectura para listar). Con una clave solo se gestionan conductores (rol user): invitar o tocar a un owner o a un gestor responde 403 api_role_not_allowed.
1. Invitar:
curl -sS -X POST "$API/auth/usuarios/invite" \
-H "Authorization: Bearer $FLOTADOC_API_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: rrhh-alta-000789" \
-d '{"email":"luis.perez@transportesgarcia.es","fullName":"Luis Pérez","role":"user"}'
{ "data": { "user": { "id": 42, "email": "luis.perez@transportesgarcia.es", "fullName": "Luis Pérez", "role": "user",
"status": "invited", "inviteExpiresAt": "2026-10-20T09:30:00.000Z", "…": "…" }, "emailSent": true } }
- El conductor recibe un email con un enlace (válido 7 días, de un solo uso) para crear su contraseña. Desde entonces entra en la app o en la web con su email.
- Mientras está invitado ya se le pueden asignar vehículos y registrar viajes y gastos a su nombre.
- Plazas:
auth/usuarios/listdevuelve las plazas ocupadas y el límite (data.seats). Sin plaza:409 seat_limit(el owner amplía el plan o habla con FlotaDoc). - Email ya usado en la empresa:
409 email_in_useconuserIdystatus. Si estáinvited, reenvía; si estáinactive, reactívalo.
2. Reenviar la invitación (si emailSent fue false o el enlace caducó): POST /auth/usuarios/resend-invite con { "userId": 42 }. El enlace anterior deja de valer. Hay que esperar 60 segundos entre envíos (429 resend_too_soon, con la cabecera Retry-After y retryAfterSeconds en el cuerpo, como rate_limited; reintenta con otra Idempotency-Key).
3. Asignarle vehículos fijos (opcional): POST /movimientos/flota/assign con { "vehicleId": 7, "userIds": [42, 15] }. userIds es la lista completa de conductores del vehículo (sustituye a la anterior).
4. Darle de baja: POST /auth/usuarios/deactivate con { "userId": 42 }.
- Pasa a
inactive, pierde el acceso (sus sesiones caducan en unos minutos) y libera su plaza. - Sus viajes, gastos, tickets y firmas se conservan y siguen en los informes.
- Sigue en la lista de conductores de sus vehículos fijos: quítalo con
flota/assign(si lo mantienes, unassignposterior responde422coninvalidUserIds). - La clave no puede darse de baja a sí misma (el owner que la creó):
409 cannot_modify_self.
5. Reactivarlo: POST /auth/usuarios/reactivate con { "userId": 42 }, si la cuenta registra y hay plaza. Vuelve a active (o a invited si nunca llegó a entrar; no se envía otro email). Si fue la propia persona quien pidió su baja: 409 deletion_pending (lo resuelve el owner en la web).
9. Errores comunes
| HTTP | error.code |
Qué significa | Qué hacer |
|---|---|---|---|
| 401 | api_key_* |
La clave no sirve (ver §2) | No reintentes; pide otra clave |
| 403 | api_scope_missing |
La clave (o la empresa) no tiene el permiso de la operación | Pide al owner una clave con ese permiso o que la empresa pida más permisos |
| 403 | account_closed |
La cuenta de la empresa está cerrada | — |
| 404 | not_found |
La ruta no está en el catálogo | Revisa la ruta en el OpenAPI |
| 404 | *_not_found |
El registro no existe o no es de esta empresa | Revisa el id |
| 409 | idempotency_conflict / idempotency_in_progress |
Ver §6 | — |
| 409 | otros | Choca con el estado (mes cerrado, límite del plan, duplicado…) | Ver la operación en el OpenAPI |
| 422 | invalid_input |
Campos no válidos (error.fields) |
Corrige los campos |
| 423 | account_read_only y otros |
Cuenta en solo lectura o periodo bloqueado | Consulta y exporta; el owner resuelve el estado de la cuenta en la web |
| 429 | rate_limited (o sin cuerpo) |
Límite por clave o global | Espera y reintenta (§5) |
| 503 | service_unavailable |
No se ha podido atender | Reintenta con espera (en escrituras, con la misma Idempotency-Key) |
Para soporte, envía siempre el X-Request-Id de la respuesta, la fecha y hora, y la ruta. Nunca la clave.
10. Uso e historial
El owner ve en Mi cuenta › Integraciones:
- Uso: peticiones, porcentaje de éxito, errores 4xx y 5xx, limitadas (429) y tiempo medio; gráfico por día y tablas por endpoint y por clave, de hoy a 13 meses.
- Historial: las peticiones de los últimos 90 días con su resultado, duración, tamaño, IP de origen, user agent y
X-Request-Id. No se guardan los cuerpos de las peticiones ni la clave.
Pon a tu sistema un User-Agent que lo identifique (por ejemplo erp-sync/2.3): así el owner distingue sus integraciones en el historial.
Respuestas comunes
Pueden llegar en cualquier operación; cada operación enlaza las que le aplican.
403Forbidden
Sin permiso. api_scope_missing: la clave no tiene el permiso de la operación o la empresa ya no
lo tiene permitido (scope en el cuerpo). account_closed: la cuenta de la empresa está cerrada.
Otros códigos propios de la operación se indican en su descripción.
Cabeceras
X-Request-Idstring (uuid)Id de la petición en el historial de la empresa (para soporte).
Ejemplo de respuesta
{
"error": {
"code": "api_scope_missing",
"message": "La clave API no tiene permiso para esta accion",
"scope": "registros"
}
}
404NotFound
No existe o no es de tu empresa (*_not_found), o la ruta no está en este documento (not_found).
Cabeceras
X-Request-Idstring (uuid)Id de la petición en el historial de la empresa (para soporte).
Ejemplo de respuesta
{
"error": {
"code": "not_found",
"message": "Recurso no encontrado"
}
}
409Conflict
Choca con el estado actual (los códigos de cada operación se indican en su descripción), o con la
idempotencia: idempotency_conflict (la Idempotency-Key ya se usó con otra petición) o
idempotency_in_progress (la primera aún está en curso: reintenta en unos segundos).
Cabeceras
X-Request-Idstring (uuid)Id de la petición en el historial de la empresa (para soporte).
Ejemplo de respuesta
{
"error": {
"code": "idempotency_conflict",
"message": "Idempotency-Key ya usada con otra peticion"
}
}
422ValidationError
Datos no válidos. invalid_input trae en fields el código del error de cada campo, todos a la
vez. invalid_idempotency_key: la cabecera Idempotency-Key está mal formada.
Cabeceras
X-Request-Idstring (uuid)Id de la petición en el historial de la empresa (para soporte).
Ejemplo de respuesta
{
"error": {
"code": "invalid_input",
"message": "invalid fields",
"fields": {
"date": "invalid"
}
}
}
423Locked
Bloqueado por el estado de la cuenta o del periodo (el código concreto se indica en la operación).
Cabeceras
X-Request-Idstring (uuid)Id de la petición en el historial de la empresa (para soporte).
429RateLimited
Demasiadas peticiones. Por clave (rate_limited): has pasado el límite por minuto de tu clave;
lleva Retry-After y, en el cuerpo, retryAfterSeconds y limitPerMinute. Global: la API
entera tiene un techo de 50 peticiones por segundo (ráfaga de 100) para todos los clientes juntos;
ese 429 llega sin cuerpo JSON, sin X-Request-Id y sin Retry-After. En los dos casos, reintenta
con espera (por ejemplo, exponencial con un poco de azar).
Cabeceras
Retry-AfterintegerSegundos que faltan para el minuto siguiente.
Mínimo
1X-Request-Idstring (uuid)Id de la petición en el historial de la empresa (para soporte).
Ejemplo de respuesta
{
"error": {
"code": "rate_limited",
"message": "Demasiadas peticiones con esta clave API: espera antes de reintentar",
"retryAfterSeconds": 23,
"limitPerMinute": 120
}
}
Formato de los errores
Todas las respuestas de error llevan este cuerpo. Decide siempre por el código del error, nunca por el texto del mensaje.
Errorobject
errorobjectSegún el error, puede traer más datos (por ejemplo
scope,retryAfterSecondsostatus).codestringCódigo estable del error (
snake_case). Es lo que debe interpretar tu programa.Ejemplo:
api_key_revokedmessagestringTexto técnico para depurar. Puede cambiar; no lo interpretes.
fieldsobjectopcionalSolo en
422 invalid_input: código del error de cada campo (required,invalid,too_long…).Admite cualquier clave, con valores
string
¿Quieres conectar tu sistema?
Cuéntanos qué quieres integrar y lo vemos en una llamada: qué datos necesitas, con qué permisos y cada cuánto.
Si tu empresa ya usa FlotaDoc, el titular puede pedir el acceso desde la web, en Mi cuenta › Integraciones.