Saltar al contenido principal

Códigos de error

La API usa los códigos HTTP estándar. El cuerpo de respuesta siempre es JSON con la clave detail.


Códigos de éxito

CódigoCuándo
200 OKPetición correcta, devuelve datos.
201 CreatedRecurso creado correctamente.
204 No ContentOperación correcta sin cuerpo de respuesta.

Códigos de error del cliente (4xx)

400 Bad Request

Los parámetros enviados son válidos en formato pero incoherentes con el estado actual del sistema.

{ "detail": "Ya existe un cliente con ese CIF." }

Qué hacer: mostrar el mensaje al usuario y permitir corregir.


401 Unauthorized

Falta autenticación o las credenciales son inválidas.

Causas posibles:

CausaCómo se ve en detail
Sin cabecera de auth"Falta autenticación. Envía Authorization: Bearer ‹jwt› o X-API-Key."
JWT expirado"Token has expired" o "Could not validate credentials"
JWT inválido o malformado"Could not validate credentials"
API Key inexistente, pausada o revocada"API Key inválida o revocada."
Empresa suspendida (impago)"API Key inválida o revocada."

Qué hacer:

  • Si usas JWT: intenta refresh con el refresh_token. Si falla, vuelve a pedir login.
  • Si usas API Key: la clave necesita atención del cliente. No reintentes — notifícale.

403 Forbidden

Estás autenticado, pero no tienes permiso para este recurso o acción.

Causas posibles:

CausaCuándo
API Key sin scope necesario"La API Key no tiene permiso 'X:Y'..."
Usuario JWT sin rol suficiente"El usuario no tiene permisos para esta acción."
Recurso de otra empresa"Sin empresa asociada."

Qué hacer:

  • Scope faltante: avisar al cliente con instrucciones claras para que añada el permiso. Ver Scopes.
  • Rol insuficiente: el usuario tiene que pedir a su admin que le suba de rol.
  • No reintentes — repetir la misma petición dará el mismo error.

404 Not Found

El recurso solicitado no existe o no pertenece a tu empresa.

{ "detail": "Cliente no encontrado." }

Qué hacer:

  • Verificar que el token UUID es correcto.
  • Comprobar que el recurso no ha sido borrado.
  • No reintentes con el mismo identificador.

409 Conflict

La operación entra en conflicto con el estado actual.

{ "detail": "No se puede eliminar: el cliente tiene facturas asociadas." }

Qué hacer: mostrar el mensaje al usuario; la operación no es posible en ese momento.


422 Unprocessable Entity

Los datos enviados no cumplen el schema esperado.

{
"detail": [
{
"loc": ["body", "email"],
"msg": "value is not a valid email address",
"type": "value_error.email"
},
{
"loc": ["body", "telefono"],
"msg": "ensure this value has at most 20 characters",
"type": "value_error.any_str.max_length"
}
]
}

Qué hacer:

  • Mostrar los errores campo a campo.
  • Permitir corregir y reenviar.

429 Too Many Requests

Reservado para rate limiting — no está aplicado actualmente, pero tu código debe manejarlo de cara al futuro.

Qué hacer: esperar el tiempo indicado en la cabecera Retry-After (en segundos) antes de reintentar.


Códigos de error del servidor (5xx)

500 Internal Server Error

Error inesperado en el servidor. No es culpa de tu petición.

Qué hacer:

  1. Reintentar con backoff exponencial (1s, 2s, 4s, 8s…).
  2. Si persiste tras 3-5 intentos, registrar y notificar al equipo de TPVReady (soporte@extremanet.com).

502 Bad Gateway / 503 Service Unavailable / 504 Gateway Timeout

La API está temporalmente no disponible (despliegue, mantenimiento, sobrecarga).

Qué hacer: reintentar con backoff. Es probable que se resuelva en minutos.


Patrón de manejo robusto

import time
import requests

class APIError(Exception): pass
class APIKeyInvalida(APIError): pass
class ScopeInsuficiente(APIError): pass
class RecursoNoEncontrado(APIError): pass

def call_api(method, path, max_reintentos=3, **kwargs):
for intento in range(max_reintentos):
try:
resp = requests.request(
method,
f"https://api.tpvready.es/api{path}",
headers={"X-API-Key": API_KEY},
timeout=20,
**kwargs,
)

# Errores del cliente: no reintentar
if resp.status_code == 401:
raise APIKeyInvalida(resp.json().get("detail"))
if resp.status_code == 403:
raise ScopeInsuficiente(resp.json().get("detail"))
if resp.status_code == 404:
raise RecursoNoEncontrado(resp.json().get("detail"))
if 400 <= resp.status_code < 500:
raise APIError(f"{resp.status_code}: {resp.json().get('detail')}")

# Errores del servidor: reintentar con backoff
if resp.status_code >= 500:
if intento < max_reintentos - 1:
time.sleep(2 ** intento)
continue
raise APIError(f"Servidor no disponible: {resp.status_code}")

return resp.json()

except requests.Timeout:
if intento < max_reintentos - 1:
time.sleep(2 ** intento)
continue
raise APIError("Timeout repetido")

Resumen visual

✅ 2xx              → procesa la respuesta
🟡 401 → renueva JWT o avisa de API Key inválida
🟡 403 → avisa de falta de scope/rol
🟡 404 → recurso no existe
❌ 4xx restantes → error del cliente; corregir y reenviar
🔄 5xx → backoff exponencial y reintentar