Conceptos
Errores e idempotencia
Mover dinero exige que los reintentos sean aburridos y predecibles. Esta página explica cómo se ven los errores, cuáles puedes reintentar y cómo la idempotency key evita que un timeout se convierta en un pago doble.
Formato de error
Cualquier respuesta 4xx o 5xx devuelve exactamente la misma estructura: un objeto error con code (identificador estable en mayúsculas) y message (texto legible). Enruta tu lógica por code, nunca por el texto del mensaje.
{
"error": {
"code": "UNPROCESSABLE_ENTITY",
"message": "Insufficient balance for asset USDC on POLYGON"
}
}Códigos que verás
HTTP
400Bad RequestEl cuerpo no cumple el esquema: falta un campo requerido o un enum no es válido. No reintentes sin corregirlo.
401 / 403AuthAPI key ausente, inválida o sin permiso sobre el recurso o entorno solicitado.
404Not FoundEl id no existe o pertenece a otro merchant. Verifica que estés usando el mismo entorno.
409ConflictEstado incompatible: por ejemplo aprobar una transferencia que ya fue aprobada o rechazada.
422UnprocessableLa petición es válida pero no se puede ejecutar: saldo insuficiente, token deshabilitado o red no soportada para ese activo.
429Rate limitDemasiadas peticiones. Aplica backoff exponencial con jitter antes de reintentar.
5xxServerFallo temporal de nuestro lado. Reintenta con la misma idempotency_key: es seguro.
Idempotency keys
POST /v1/transfers exige idempotency_key (mínimo 8 caracteres). Es una promesa: la misma clave nunca ejecutará dos envíos, sin importar cuántas veces la reintentes.
- Genera la clave desde tu propio dominio: el id del retiro, de la factura o de la orden. Nunca uses un UUID nuevo por reintento.
- Persiste la clave antes de llamar a la API. Si tu proceso muere a mitad del request, al recuperarte reintenta con la misma clave.
- Un timeout no significa que el envío falló. Reintenta con la misma clave o consulta GET /v1/transfers antes de asumir cualquier cosa.
Reintentar una transferencia con una clave distinta porque "la primera parecía fallida" es la forma más común de enviar el doble de fondos. La clave estable es tu red de seguridad.
Estrategia de reintentos
Reintenta 429 y 5xx con backoff exponencial (por ejemplo 1s, 2s, 4s, 8s, con jitter) y un máximo de 5 intentos. No reintentes 400, 404 ni 422: requieren un cambio en tu petición o en el estado de la cuenta.
async function withRetry<T>(fn: () => Promise<T>, attempts = 5): Promise<T> {
let lastError: unknown;
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (error) {
lastError = error;
const status = (error as { status?: number }).status ?? 0;
const retryable = status === 429 || status >= 500 || status === 0;
if (!retryable) throw error;
const delay = 2 ** i * 1000 + Math.random() * 250;
await new Promise((r) => setTimeout(r, delay));
}
}
throw lastError;
}