Mono Colombia
Estándares de API

Errores y reintentos

Códigos de estado HTTP, el envelope de error de Mono, y cómo reintentar solicitudes fallidas de forma segura.

Cada respuesta de Mono lleva un código de estado HTTP. Las llamadas exitosas devuelven 2xx; las fallas devuelven 4xx (tu solicitud estaba mal) o 5xx (algo salió mal del lado de Mono). Esta página documenta los códigos de estado que usa Mono, el envelope de error que puedes esperar en cada falla, y la política de reintentos que convierte fallas transitorias en éxito eventual sin duplicar efectos secundarios.

Los límites de tasa aún no se aplican uniformemente en todos los endpoints de Mono, pero 429 va a empezar a aparecer a medida que los ajustemos. Las reglas de reintento de abajo describen el contrato contra el que deberías programar hoy, para que tu integración ya se comporte correctamente cuando los límites entren en vigor.

Códigos de estado HTTP

EstadoSignificado¿Reintentable?
200 (OK)La solicitud se completó correctamente.n/a
201 (Created)Se creó un nuevo recurso.n/a
204 (NoContent)La solicitud tuvo éxito; no hay cuerpo que devolver.n/a
400 (BadRequest)JSON malformado, campo faltante o header no conforme. La solicitud en sí está mal.No
401 (Unauthorized)Header Authorization faltante o inválido.No (corrige credencial)
403 (Forbidden)Autenticado, pero sin scope para este recurso.No (corrige scope)
404 (NotFound)El recurso no existe.No
408 (RequestTimeout)Mono no recibió la solicitud completa a tiempo.Sí, con backoff
409 (Conflict)Colisión de clave de idempotencia o conflicto de estado — ver Claves de idempotencia.No (lógica de aplicación)
422 (UnprocessableEntity)La estructura de la solicitud es válida, pero el contenido fue rechazado (validación de negocio).No
429 (TooManyRequests)Límite de tasa excedido. Respeta el header Retry-After.Sí, después de Retry-After
500 (InternalServerError)Falla inesperada del lado del servidor.Sí, con backoff
502, 503, 504Fallas transitorias upstream/gateway.Sí, con backoff

Envelope de error

Cada respuesta de falla lleva un envelope JSON estándar para que los clientes puedan parsear errores de forma uniforme:

{
  "code": "400 Bad Request",
  "errors": [
    {
      "error_code": "item_not_found",
      "message": "Item doesn't exist",
      "path": "#/item/id",
      "url": "https://api.mono.co/docs#errors"
    }
  ],
  "id": "log_7MkWaFqvfosB8fzHhb1Eql",
  "message": "Malformed request"
}

Campos:

  • code — el estado HTTP y su frase de razón (por ejemplo, "404 Not Found").
  • errors[] — una entrada por cada falla de validación. Útil cuando una sola solicitud tiene varios campos inválidos.
    • error_code — código estable y legible por máquina para esta falla específica.
    • message — descripción humana corta.
    • path — puntero JSON al payload de la solicitud que identifica el campo ofensor.
    • url — link a documentación extendida para este código de error, cuando esté disponible.
  • id — identificador de solicitud generado por Mono. Inclúyelo cuando contactes a soporte — nos permite localizar la solicitud exacta en nuestros logs.
  • message — descripción humana de alto nivel apta para mostrar a operadores (no a usuarios finales).

Las respuestas de 429 y otros límites de tasa usan el mismo envelope con error_code: "rate_limited".

Cuándo reintentar

Reintenta solo en fallas transitorias, nunca en errores de aplicación:

Respuesta¿Reintentar?Por qué
429Sí, después de Retry-After.Transitorio. La solicitud no fue procesada; nada cambió del lado de Mono.
408, 500504Sí, con backoff exponencial.Transitorio. La solicitud puede o no haber sido procesada — la clave de idempotencia protege.
400, 422No.La solicitud en sí está malformada. Reintentar no cambiará la respuesta.
401, 403No.Problema de credenciales o de scope. Corrige la auth, luego envía una solicitud nueva.
404, 409No.Recurso faltante o conflicto de estado. La lógica de la aplicación debe reaccionar.

Cuatro reglas aplican a cada reintento:

  1. Respeta Retry-After. Si el header está presente (siempre en 429, a veces en 503), espera al menos esos segundos antes de reintentar. No reintentes antes.
  2. Usa backoff exponencial con jitter cuando Retry-After esté ausente. Empieza en 1 segundo, dobla en cada falla subsecuente, agrega 0–500 ms de jitter aleatorio, y limita el delay a 30 segundos. El jitter previene el problema de thundering herd donde muchos clientes reintentan en sincronía.
  3. Limita el presupuesto de reintentos. Hasta 5 reintentos por operación lógica es un valor por defecto razonable. Después de eso, surface la falla — reintentar para siempre solo desplaza el problema.
  4. Reusa la clave de idempotencia. Cada reintento debe llevar la misma X-Idempotency-Key que el intento original. De lo contrario, un reintento exitoso tras un 5xx ambiguo podría cobrarle dos veces a un cliente.

Un bucle de reintento mínimo en Python:

import random
import time

import httpx

RETRYABLE_STATUSES = {408, 429, 500, 502, 503, 504}

def request_with_backoff(client, method, url, **kwargs, max_retries=5):
    delay = 1.0
    for attempt in range(max_retries + 1):
        response = client.request(method, url, **kwargs)
        if response.status_code not in RETRYABLE_STATUSES:
            return response

        retry_after = response.headers.get("Retry-After")
        wait = float(retry_after) if retry_after else delay + random.uniform(0, 0.5)
        time.sleep(min(wait, 30.0))
        delay = min(delay * 2, 30.0)

    return response  # last response, still failing

Diseñar para resiliencia desde el inicio

Incluso antes de que los límites de tasa se vuelvan estrictos, tres hábitos mantienen las cargas en ráfaga fuera de problemas:

  • Distribuye el trabajo en ráfaga. Trabajos por lotes que disparan miles de llamadas — corridas de nómina, conciliación de fin de día, emisión masiva de tarjetas — deberían ser ritmados del lado del cliente en lugar de dispararse en paralelo. Unas pocas solicitudes por segundo sostenidas le ganan a mil al tiempo seguidas de una ventana de recuperación.
  • Cachea lecturas. Los datos de referencia (catálogos, esquemas de tarifas, lookups de capacidades) rara vez cambian dentro de un solo ciclo de solicitud. Cachea la respuesta y solo pega a Mono cuando falla el cache.
  • Separa rutas síncronas de las asíncronas. Una llamada de checkout cara a un usuario no debería compartir su presupuesto con una exportación nocturna. Usa API keys distintas para cargas distintas cuando sea posible, para que un trabajo por lotes desbocado no agote el límite que necesita un cliente real.

Errores comunes

ErrorSíntomaSolución
Reintentar de inmediato en 429.429 repetidos y mayor tiempo total de recuperación.Espera al menos los segundos de Retry-After antes del siguiente intento.
Ignorar el header Retry-After.Los reintentos caen antes de que el límite se haya reseteado.Trata Retry-After como autoritativo cuando esté presente.
Reintentar sin clave de idempotencia.Transferencias duplicadas cuando el reintento eventualmente acierta.Genera la clave una vez, envíala en cada intento.
Reintentar 400/422 como si fueran transitorios.Intentos desperdiciados; el mismo error vuelve cada vez.Solo reintenta en 408, 429 y 5xx.
Sin tope de reintentos.Workers atorados, colas bloqueadas, fatiga de alertas.Limita a un número pequeño de reintentos (5 es un default razonable) y surface arriba.
Compartir una llave entre servicios y clases de tráfico.Un trabajo batch ruidoso limita por tasa el tráfico de checkout cara al usuario.Emite API keys separadas para cargas separadas.
Omitir el campo id al reportar un problema.Soporte no puede encontrar la solicitud en logs.Siempre incluye el id del envelope de error cuando escales.

Siguientes pasos

  • Claves de idempotencia — requeridas en cada escritura reintentada.
  • Autenticación — manejo de credenciales para los casos 401 y 403.
  • Webhooks — la política de reintentos de Mono para los eventos que envía a tu sistema.

En esta página