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