Skip to main content
Todas las respuestas de error siguen el mismo formato JSON:
{
  "error": "CODIGO_MAYUSCULAS",
  "message": "Detalle legible",
  "details": { /* opcional, contexto extra */ }
}

Códigos

HTTPerrorCausaAcción del cliente
400BAD_REQUESTBody inválido / falta campo requeridoCorregir payload
400INFERENCE_FAILEDEl gateway no pudo deducir emisor/receptor en un endpoint _autoReintentar con la versión sin _auto y emisor explícito
401NO_API_KEYFalta el header x-api-keyAgregarlo
401INVALID_API_KEYKey revocada, mal formada o secreto incorrectoGenerar una nueva
401UNAUTHORIZEDSesión Supabase ausente/expirada (rutas de gestión)Volver a iniciar sesión
402QUOTA_EXCEEDEDCuota mensual del usuario agotadaUpgrade de plan; no reintentar
404NOT_FOUNDBanxico no devolvió un CEP con esos parámetrosRevisar fecha, monto, clave_rastreo
429RATE_LIMITEDExcediste per_minute de la keyBackoff con jitter y reintentar
500INTERNAL_ERRORFalla interna no clasificadaReintentar 1 vez; reportar a soporte si persiste
502UPSTREAM_ERROREl backend de Banxico devolvió un error transitorioEl gateway ya hace un reintento; si persiste, esperar

Patrón de manejo recomendado

async function downloadCep(req: CepReq) {
  const res = await fetch("/api/v1/cep/xml_auto", {
    method: "POST",
    headers: { "x-api-key": process.env.VERIPAY_KEY!, "content-type": "application/json" },
    body: JSON.stringify(req),
  });

  if (res.ok) return Buffer.from(await res.arrayBuffer());

  const err = await res.json();
  switch (err.error) {
    case "RATE_LIMITED":
      await sleep(300 + Math.random() * 500);
      return downloadCep(req);
    case "QUOTA_EXCEEDED":
      throw new UpgradeRequiredError();
    case "INFERENCE_FAILED":
      return downloadCepExplicit(req);
    case "NOT_FOUND":
      return null;
    default:
      throw new Error(`${err.error}: ${err.message}`);
  }
}
Nunca reintentes un 402 ni un 400 — el resultado no cambiará y consumirás cuota de error logging.