Especificación de la API Técnica - Carburo v1
Esta documentación detalla los endpoints, la estructura de datos, el flujo de seguridad y la gestión de errores de la API REST v1. Todas las respuestas exitosas de la API devuelven una estructura estándar con código HTTP 200 OK.
1. Arquitectura de Seguridad
La API se divide en dos entornos de seguridad diferenciados por la ruta de acceso (requestMatchers):
Endpoints Públicos (/api/v1/public/**)
- Mecanismo de Seguridad: Protegido globalmente por API Key.
- Cabecera Requerida:
X-API-KEY: <clave_aplicacion>
- Filtro Aplicado:
ApiKeyFilter. Si falta la clave o es incorrecta, intercepta la petición antes de llegar al controlador y retorna inmediatamente un estado HTTP 401 Unauthorized.
- Autenticación: No requiere credenciales ni tokens de usuario.
{
"success": false,
"error": {
"error": "UNAUTHORIZED",
"message": "Invalid API key"
},
"timestamp": "2026-05-29T10:40:08.403471762Z"
}
Endpoints Privados/Protegidos (/api/v1/** excluyendo /public/**)
- Mecanismo de Seguridad: Protegido por JWT (JSON Web Token) de Supabase.
- Cabecera Requerida:
Authorization: Bearer <token_jwt>
- Filtro Aplicado:
SupabaseJwtFilter. Descarga las claves públicas (JWKS) desde la URL de Supabase para validar la firma, vigencia del token (TokenExpiredException) e identidad del emisor.
- Validación Cruzada de Propiedad (Ownership): En los endpoints que involucran un parámetro UUID en la ruta, se ejecuta el método
validateOwnership(UUID requestUuid). Este extrae el JwtUser del contexto de seguridad de Spring (SecurityContextHolder) y comprueba de forma estricta que coincida con el de la petición. Si no coinciden, se lanza una excepción UnauthorizedException con el mensaje "UUID mismatch".
{
"success": false,
"error": {
"error": "UNAUTHORIZED",
"message": "Invalid token"
},
"timestamp": "2026-05-29T10:40:58.459608195Z"
}
2. Estructura Estándar de Respuestas
Todas las peticiones devuelven un objeto JSON con la misma estructura base parametrizada (ApiResponse):
Respuesta Exitosa
{
"success": true,
"data": { ... },
"timestamp": "2026-05-29T10:32:42Z"
}
Respuesta de Error
{
"success": false,
"error": {
"error": "CODIGO_DE_ERROR",
"message": "Descripción detallada del error o causa de la excepción"
},
"timestamp": "2026-05-29T10:32:45Z"
}
3. Matriz de Gestión de Errores y Estados HTTP
La lógica interna intercepta excepciones globales mediante GlobalExceptionHandler mapeándolas a respuestas JSON estandarizadas:
| Excepción Java |
Código HTTP |
Código Interno (error) |
Contexto de Activación |
ResourceNotFoundException / NotFoundException |
404 Not Found |
ERR_NOT_FOUND |
El recurso solicitado (Usuario, Vehículo, Combustible) no existe en BBDD. |
UnauthorizedException / TokenExpiredException |
401 Unauthorized |
ERR_UNAUTHORIZED |
Token JWT expirado, firma inválida, API key errónea o discrepancia de UUID (UUID mismatch). |
MethodArgumentTypeMismatchException |
404 Bad Request |
ERR_BAD_REQUEST |
Tipos de datos inválidos en el Path o Query Parameters (ej. un String en un ID numérico). |
HttpMessageNotReadableException |
400 Bad Request |
ERR_BAD_REQUEST |
El cuerpo de la petición (@RequestBody) contiene un JSON mal formado sintácticamente. |
MethodArgumentNotValidException |
400 Bad Request |
ERR_BAD_REQUEST |
Fallo en las restricciones de validación del DTO. Retorna el primer campo que incumpla. |
InvalidUsuarioDataException / InvalidVehiculoDataException / InvalidRepostajeDataException |
400 Bad Request |
ERR_BAD_REQUEST |
Datos del modelo semánticamente incorrectos o que violan reglas de negocio. |
UsuarioAlreadyExistsException |
409 Conflict |
ERR_USER_ALREADY_EXISTS |
Intento de registrar un UUID de usuario que ya se encuentra dado de alta en el sistema. |
4. Modelos de Datos (DTOs)
GrupoCombustibleDto
id (short)
codigo (String)
combustibles (List<CombustibleDto>?)
CombustibleDto
id (short)
denominacion (String)
codigo (String)
id_grupo_combustible (Short?)
ComunidadAutonomaDto
id (short)
denominacion (String)
provincias (List<ProvinciaDto>)
ProvinciaDto
id (short)
denominacion (String)
id_comunidad_autonoma (short)
municipios (List<MunicipioDto>)
MunicipioDto
id (short)
denominacion (String)
id_provincia (short)
EstacionDeServicioDto
id (int)
rotulo (String)
horario (String)
direccion (String)
localidad (String)
codigo_postal (int)
id_municipio (short)
id_provincia (short)
latitud (double)
longitud (double)
margen (String)
remision (String)
venta (String)
x100BioEtanol (double)
x100EsterMetilico (double)
abierto (boolean)
distancia_metros (Long, opcional)
id_combustibles_disponibles (Set<Short>)
precios_de_combustibles (List<PrecioCombustibleDto>)
PrecioCombustibleDto
id_estacion_de_servicio (int)
id_combustible (short)
fecha (LocalDate - yyyy-MM-dd)
precio (double)
UsuarioDto
uuid (UUID)
id_provincia_favorita (short)
ids_combustibles_favoritos (Set<Short>)
ids_estaciones_de_servicio_favoritas (Set<Integer>)
VehiculoDto
id (int)
uuid_usuario_solicitante (UUID)
is_usuario_solicitante_propietario (boolean)
matricula (String?)
marca (String)
modelo (String)
odometro_actual (double)
capacidad_deposito (double)
notas (String?)
id_grupo_combustible (short)
RepostajeDto
id (int)
id_vehiculo (int)
id_combustible (short)
id_estacion_de_servicio (int)
uuid_usuario_creador (UUID)
fecha_repostaje (OffsetDateTime)
fecha_registro (OffsetDateTime)
cantidad (double)
coste_unitario (double)
odometro_inicial (Double?)
odometro_final (double)
deposito_lleno (boolean)
nota (String?)
5. Catálogo de Endpoints
5.1 Módulo Público (Seguridad: X-API-KEY)
Combustibles
- GET
/api/v1/public/combustibles
- Descripción: Obtiene el listado maestro de combustibles del sistema.
- Respuesta data: List<CombustibleDto>
- GET
/api/v1/public/grupos-de-combustibles
- Descripción: Obtiene el listado maestro de grupos de combustibles del sistema.
- Respuesta data: List<GrupoCombustibleDto>
- GET
/api/v1/public/grupos-de-combustibles/combustibles
- Descripción: Obtiene el listado maestro de combustibles agrupados por los grupos de combustibles del sistema.
- Respuesta data: List<GrupoCombustibleDto>
Comunidades Autónomas, Provincias y Municipios
- GET
/api/v1/public/comunidades-autonomas
- Descripción: Obtiene las comunidades autónomas sin anidamiento profundo.
- Respuesta data: List<ComunidadAutonomaDto>
- GET
/api/v1/public/comunidades-autonomas/provincias/municipios
- Descripción: Devuelve todo el árbol geográfico completo (Comunidades -> Provincias -> Municipios anidados).
- Respuesta data: List<ComunidadAutonomaDto>
- GET
/api/v1/public/provincias
- Descripción: Obtiene el listado completo de provincias disponibles.
- Respuesta data: List<ProvinciaDto>
- GET
/api/v1/public/municipios
- Descripción: Obtiene el listado de todos los municipios del territorio nacional.
- Respuesta data: List<MunicipioDto>
- GET
/api/v1/public/municipios/provincia/{id}
- Descripción: Filtra municipios pertenecientes a una provincia específica.
- Respuesta data: List<MunicipioDto>
- GET
/api/v1/public/municipios/provincia/{id}/con-estaciones-de-servicio
- Descripción: Filtra municipios de una provincia que cuenten con al menos una estación de servicio activa.
- Respuesta data: List<MunicipioDto>
Estaciones de Servicio
- GET
/api/v1/public/estaciones-de-servicio
- Descripción: Listado global de estaciones de servicio.
- Respuesta data: List<EstacionDeServicioDto>
- GET
/api/v1/public/estaciones-de-servicio/count
- Descripción: Retorna el volumen totalizado de estaciones de servicio a nivel nacional.
- Respuesta data: Long
- GET
/api/v1/public/estaciones-de-servicio/{id}
- Descripción: Detalle de una estación. Permite calcular distancias de forma dinámica si se envían coordenadas opcionales.
- Query Parameters:
latitud (Double, opcional)
longitud (Double, opcional)
- Respuesta data: EstacionDeServicioDto (incluye distancia_metros si se calcularon coordenadas válidas).
- GET
/api/v1/public/estaciones-de-servicio/cercanas
- Descripción: Geolocalización de estaciones cercanas a un punto geométrico.
- Query Parameters (Obligatorios):
latitud (double): Rango válido de -90 a 90.
longitud (double): Rango válido de -180 a 180.
limite (int, opcional, por defecto 1): Capado internamente a un máximo de 10 si se excede o es inválido.
- Respuesta data: List<EstacionDeServicioDto>
- GET
/api/v1/public/estaciones-de-servicio/municipio/{id}
- Descripción: Estaciones ubicadas en el municipio indicado.
- Respuesta data: List<EstacionDeServicioDto>
- GET
/api/v1/public/estaciones-de-servicio/provincia/{id}
- Descripción: Estaciones ubicadas en la provincia indicada.
- Respuesta data: List<EstacionDeServicioDto>
- GET
/api/v1/public/estaciones-de-servicio/comunidad-autonoma/{id}
- Descripción: Estaciones ubicadas en la comunidad autónoma indicada.
- Respuesta data: List<EstacionDeServicioDto>
Precios de Combustibles Históricos
- GET
/api/v1/public/estaciones-de-servicio/{id}/precios-combustibles
- Descripción: Histórico de precios de los últimos X días para una estación específica.
- Query Parameters:
dias (int, opcional, por defecto 5).
- Respuesta data: List<PrecioCombustibleDto>
- GET
/api/v1/public/estaciones-de-servicio/{id}/precios-combustibles/{fecha}
- Descripción: Precios exactos registrados en una fecha determinada.
- Path Variables:
fecha (LocalDate formateado como yyyy-MM-dd).
- Respuesta data: List<PrecioCombustibleDto>
5.2 Módulo de Usuarios (Seguridad: JWT + Ownership de UUID)
Base Path: /api/v1/usuarios
- GET
/api/v1/usuarios/{uuid}
- Descripción: Obtiene los datos del perfil del usuario.
- Respuesta data: UsuarioDto
- HEAD
/api/v1/usuarios/{uuid}
- Descripción: Verificación rápida de la existencia del usuario. Retorna código HTTP 200 OK si el usuario existe o 404 Not Found en caso contrario (cuerpo vacío).
- POST
/api/v1/usuarios
- Descripción: Registra un nuevo usuario en la base de datos centralizada. El UUID dentro del JSON del body debe validar contra el del JWT.
- Body: UsuarioDto
- Respuesta data: null
- GET
/api/v1/usuarios/{uuid}/provincia-favorita
- Descripción: Obtiene la provincia de preferencia configurada por el usuario.
- Respuesta data: Short
- PATCH
/api/v1/usuarios/{uuid}/provincia-favorita/{provinciaId}
- Descripción: Modifica la provincia favorita.
- Respuesta data: null
- GET
/api/v1/usuarios/{uuid}/combustibles-favoritos
- Descripción: Conjunto de tipos de combustibles de uso común para el usuario.
- Respuesta data: Set<Short>
- PATCH
/api/v1/usuarios/{uuid}/combustibles-favoritos
- Descripción: Sobrescribe/reemplaza completamente la colección de combustibles preferidos.
- Body: Set<Short>
- Respuesta data: null
- GET
/api/v1/usuarios/{uuid}/estaciones-de-servicio-favoritas
- Descripción: Obtiene el detalle de las estaciones guardadas como favoritas.
- Respuesta data: List<EstacionDeServicioDto>
- PATCH
/api/v1/usuarios/{uuid}/estaciones-de-servicio-favoritas/{estacionId}
- Descripción: Agrega una estación de servicio a su lista de favoritos.
- Respuesta data: null
- DELETE
/api/v1/usuarios/{uuid}/estaciones-de-servicio-favoritas/{estacionId}
- Descripción: Remueve la estación de la lista de favoritos.
- Respuesta data: null
5.3 Módulo de Vehículos y Repostajes (Seguridad: JWT + Ownership de UUID)
Base Path: /api/v1/vehiculos
Regla de Negocio del Servicio Interno: Cualquier operación de escritura, actualización o borrado en este módulo invoca el método validateVehiculoExistsAndOwnership(uuid, idVehiculo, mustBeOwner). Éste no solo comprueba que el vehículo exista, sino que verifica en la tabla relacional (VehiculoUsuario) que el usuario esté vinculado al recurso y, si la operación lo requiere (mustBeOwner = true), exige que su rol sea estrictamente propietario para evitar manipulaciones de datos cruzados entre usuarios de la plataforma.
Gestión de Vehículos
- GET
/api/v1/vehiculos/{uuid}
- Descripción: Recupera la flota de vehículos vinculados al usuario autenticado. No incluye la colección interna de repostajes.
- Respuesta data: List<VehiculoDto>
- GET
/api/v1/vehiculos/{uuid}/{idVehiculo}
- Descripción: Obtiene el detalle completo de un vehículo específico mapeando sus repostajes internos.
- Respuesta data: VehiculoDto
- POST
/api/v1/vehiculos/{uuid}
- Descripción: Da de alta un nuevo vehículo asignándolo al usuario.
- Body: VehiculoDto
- Respuesta data: Integer (ID autogenerado del vehículo creado).
- PATCH
/api/v1/vehiculos/{uuid}/{idVehiculo}
- Descripción: Actualiza de forma parcial las propiedades permitidas de un vehículo.
- Campos Modificables: matricula, marca, modelo, odometro_actual, capacidad_deposito, notas, ids_combustibles_utilizados.
- Body: VehiculoDto
- Respuesta data: null
- DELETE
/api/v1/vehiculos/{uuid}/{idVehiculo}
- Descripción: Operación transaccional (@transactional) que elimina de forma definitiva el vehículo y purga en cascada todos los repostajes asociados en la base de datos tras verificar la propiedad legítima.
- Respuesta data: null
Gestión de Repostajes
- GET
/api/v1/vehiculos/{uuid}/repostajes
- Descripción: Historial consolidado de todos los repostajes recientes efectuados por el usuario, independientemente del vehículo implicado.
- Respuesta data: List<RepostajeDto>
- GET
/api/v1/vehiculos/{uuid}/{idVehiculo}/repostajes
- Descripción: Obtiene cronológicamente todos los repostajes asociados en exclusiva a un vehículo.
- Respuesta data: List<RepostajeDto>
- POST
/api/v1/vehiculos/{uuid}/{idVehiculo}/repostajes
- Descripción: Registra un nuevo repostaje. Valida previamente que el vehículo pertenezca al usuario, y la existencia tanto de la estación de servicio como del combustible seleccionado.
- Body: RepostajeDto
- Respuesta data: Integer (ID asignado al repostaje).
- PATCH
/api/v1/vehiculos/{uuid}/{idVehiculo}/repostajes/{idRepostaje}
- Descripción: Actualiza los parámetros de un repostaje existente. Solamente se permite actualizar los datos de cantidad, coste unitario, odómetro inicial, odómetro final, si el depósito se llenó o no, y la nota.
- Body: RepostajeDto
- Respuesta data: null
- DELETE
/api/v1/vehiculos/{uuid}/{idVehiculo}/repostajes/{idRepostaje}
- Descripción: Elimina un repostaje del sistema comprobando previamente la integridad de la jerarquía relacional de datos.
- Respuesta data: null
Especificación de la API Técnica - Carburo v1
Esta documentación detalla los endpoints, la estructura de datos, el flujo de seguridad y la gestión de errores de la API REST v1. Todas las respuestas exitosas de la API devuelven una estructura estándar con código HTTP 200 OK.
1. Arquitectura de Seguridad
La API se divide en dos entornos de seguridad diferenciados por la ruta de acceso (requestMatchers):
Endpoints Públicos (/api/v1/public/**)
X-API-KEY: <clave_aplicacion>ApiKeyFilter. Si falta la clave o es incorrecta, intercepta la petición antes de llegar al controlador y retorna inmediatamente un estado HTTP 401 Unauthorized.{ "success": false, "error": { "error": "UNAUTHORIZED", "message": "Invalid API key" }, "timestamp": "2026-05-29T10:40:08.403471762Z" }Endpoints Privados/Protegidos (/api/v1/** excluyendo /public/**)
Authorization: Bearer <token_jwt>SupabaseJwtFilter. Descarga las claves públicas (JWKS) desde la URL de Supabase para validar la firma, vigencia del token (TokenExpiredException) e identidad del emisor.validateOwnership(UUID requestUuid). Este extrae elJwtUserdel contexto de seguridad de Spring (SecurityContextHolder) y comprueba de forma estricta que coincida con el de la petición. Si no coinciden, se lanza una excepciónUnauthorizedExceptioncon el mensaje "UUID mismatch".{ "success": false, "error": { "error": "UNAUTHORIZED", "message": "Invalid token" }, "timestamp": "2026-05-29T10:40:58.459608195Z" }2. Estructura Estándar de Respuestas
Todas las peticiones devuelven un objeto JSON con la misma estructura base parametrizada (ApiResponse):
Respuesta Exitosa
{ "success": true, "data": { ... }, "timestamp": "2026-05-29T10:32:42Z" }Respuesta de Error
{ "success": false, "error": { "error": "CODIGO_DE_ERROR", "message": "Descripción detallada del error o causa de la excepción" }, "timestamp": "2026-05-29T10:32:45Z" }3. Matriz de Gestión de Errores y Estados HTTP
La lógica interna intercepta excepciones globales mediante
GlobalExceptionHandlermapeándolas a respuestas JSON estandarizadas:ResourceNotFoundException/NotFoundException404 Not FoundERR_NOT_FOUNDUnauthorizedException/TokenExpiredException401 UnauthorizedERR_UNAUTHORIZEDMethodArgumentTypeMismatchException404 Bad RequestERR_BAD_REQUESTHttpMessageNotReadableException400 Bad RequestERR_BAD_REQUESTMethodArgumentNotValidException400 Bad RequestERR_BAD_REQUESTInvalidUsuarioDataException/InvalidVehiculoDataException/InvalidRepostajeDataException400 Bad RequestERR_BAD_REQUESTUsuarioAlreadyExistsException409 ConflictERR_USER_ALREADY_EXISTS4. Modelos de Datos (DTOs)
GrupoCombustibleDto
id(short)codigo(String)combustibles(List<CombustibleDto>?)CombustibleDto
id(short)denominacion(String)codigo(String)id_grupo_combustible(Short?)ComunidadAutonomaDto
id(short)denominacion(String)provincias(List<ProvinciaDto>)ProvinciaDto
id(short)denominacion(String)id_comunidad_autonoma(short)municipios(List<MunicipioDto>)MunicipioDto
id(short)denominacion(String)id_provincia(short)EstacionDeServicioDto
id(int)rotulo(String)horario(String)direccion(String)localidad(String)codigo_postal(int)id_municipio(short)id_provincia(short)latitud(double)longitud(double)margen(String)remision(String)venta(String)x100BioEtanol(double)x100EsterMetilico(double)abierto(boolean)distancia_metros(Long, opcional)id_combustibles_disponibles(Set<Short>)precios_de_combustibles(List<PrecioCombustibleDto>)PrecioCombustibleDto
id_estacion_de_servicio(int)id_combustible(short)fecha(LocalDate - yyyy-MM-dd)precio(double)UsuarioDto
uuid(UUID)id_provincia_favorita(short)ids_combustibles_favoritos(Set<Short>)ids_estaciones_de_servicio_favoritas(Set<Integer>)VehiculoDto
id(int)uuid_usuario_solicitante(UUID)is_usuario_solicitante_propietario(boolean)matricula(String?)marca(String)modelo(String)odometro_actual(double)capacidad_deposito(double)notas(String?)id_grupo_combustible(short)RepostajeDto
id(int)id_vehiculo(int)id_combustible(short)id_estacion_de_servicio(int)uuid_usuario_creador(UUID)fecha_repostaje(OffsetDateTime)fecha_registro(OffsetDateTime)cantidad(double)coste_unitario(double)odometro_inicial(Double?)odometro_final(double)deposito_lleno(boolean)nota(String?)5. Catálogo de Endpoints
5.1 Módulo Público (Seguridad: X-API-KEY)
Combustibles
/api/v1/public/combustibles/api/v1/public/grupos-de-combustibles/api/v1/public/grupos-de-combustibles/combustiblesComunidades Autónomas, Provincias y Municipios
/api/v1/public/comunidades-autonomas/api/v1/public/comunidades-autonomas/provincias/municipios/api/v1/public/provincias/api/v1/public/municipios/api/v1/public/municipios/provincia/{id}/api/v1/public/municipios/provincia/{id}/con-estaciones-de-servicioEstaciones de Servicio
/api/v1/public/estaciones-de-servicio/api/v1/public/estaciones-de-servicio/count/api/v1/public/estaciones-de-servicio/{id}latitud(Double, opcional)longitud(Double, opcional)/api/v1/public/estaciones-de-servicio/cercanaslatitud(double): Rango válido de -90 a 90.longitud(double): Rango válido de -180 a 180.limite(int, opcional, por defecto 1): Capado internamente a un máximo de 10 si se excede o es inválido./api/v1/public/estaciones-de-servicio/municipio/{id}/api/v1/public/estaciones-de-servicio/provincia/{id}/api/v1/public/estaciones-de-servicio/comunidad-autonoma/{id}Precios de Combustibles Históricos
/api/v1/public/estaciones-de-servicio/{id}/precios-combustiblesdias(int, opcional, por defecto 5)./api/v1/public/estaciones-de-servicio/{id}/precios-combustibles/{fecha}fecha(LocalDate formateado como yyyy-MM-dd).5.2 Módulo de Usuarios (Seguridad: JWT + Ownership de UUID)
Base Path:
/api/v1/usuarios/api/v1/usuarios/{uuid}/api/v1/usuarios/{uuid}/api/v1/usuarios/api/v1/usuarios/{uuid}/provincia-favorita/api/v1/usuarios/{uuid}/provincia-favorita/{provinciaId}/api/v1/usuarios/{uuid}/combustibles-favoritos/api/v1/usuarios/{uuid}/combustibles-favoritos/api/v1/usuarios/{uuid}/estaciones-de-servicio-favoritas/api/v1/usuarios/{uuid}/estaciones-de-servicio-favoritas/{estacionId}/api/v1/usuarios/{uuid}/estaciones-de-servicio-favoritas/{estacionId}5.3 Módulo de Vehículos y Repostajes (Seguridad: JWT + Ownership de UUID)
Base Path:
/api/v1/vehiculosGestión de Vehículos
/api/v1/vehiculos/{uuid}/api/v1/vehiculos/{uuid}/{idVehiculo}/api/v1/vehiculos/{uuid}/api/v1/vehiculos/{uuid}/{idVehiculo}/api/v1/vehiculos/{uuid}/{idVehiculo}Gestión de Repostajes
/api/v1/vehiculos/{uuid}/repostajes/api/v1/vehiculos/{uuid}/{idVehiculo}/repostajes/api/v1/vehiculos/{uuid}/{idVehiculo}/repostajes/api/v1/vehiculos/{uuid}/{idVehiculo}/repostajes/{idRepostaje}/api/v1/vehiculos/{uuid}/{idVehiculo}/repostajes/{idRepostaje}