Documentación
Idempotencia y reintentos
Las redes fallan y las respuestas se pierden. Con Idempotency-Key puedes reintentar una creación sin duplicar cobros.
Contrato
| Operación | Comportamiento al reintentar |
|---|---|
POST /v1/payment_links | Requiere Idempotency-Key. La respuesta exitosa se conserva 24 horas y se devuelve byte a byte. |
POST /v1/payment_links/{id}/disable | Idempotency-Key opcional. Repetir la llamada deja el enlace desactivado. |
POST /v1/payment_links/{id}/email | Requiere Idempotency-Key. Evita enviar el mismo correo dos veces. |
POST /v1/payments/{id}/sync | Naturalmente idempotente: verifica el estado actual. |
La clave debe tener de 1 a 255 caracteres ASCII visibles, sin espacios. Usa un UUID nuevo por cada operación que quieras realizar y reutilízalo exactamente igual en los reintentos.
Reglas
- Las claves se limitan a tu cuenta y modo, y se comparten entre REST, dashboard y MCP.
- La huella incluye la operación y el cuerpo validado y normalizado. El orden de las propiedades no importa; el orden de los conceptos sí.
- Un replay responde el estado y el cuerpo originales con
Idempotency-Replayed: true. Es el resultado de la operación original, no el estado actual del recurso. - Misma clave con otro cuerpo u otra operación:
422 idempotency_key_reuse, sin ejecutar nada. - Trabajo concurrente con la misma clave:
409 request_in_flight. Espera y reintenta con la misma clave; no generes otra. - Solo las mutaciones que se confirmaron en la base de datos se guardan. Un error de validación no deja nada cacheado y puede corregirse y reintentarse.
- Tras 24 horas la clave puede crear un recurso nuevo. Antes de reintentar una petición vieja, consulta tus enlaces.
Patrón recomendado
const key = crypto.randomUUID(); // guárdala junto a tu pedido
for (let attempt = 0; attempt < 5; attempt++) {
const res = await fetch(`${API}/v1/payment_links`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PAGOS_API_KEY}`,
"Idempotency-Key": key, // la misma en cada intento
"Content-Type": "application/json",
},
body: JSON.stringify(payload), // el mismo cuerpo en cada intento
});
if (res.ok) return res.json();
if (res.status === 409) { await sleep(500 * 2 ** attempt); continue; }
throw new Error(await res.text()); // 4xx distinto: corrige antes de reintentar
}