Saltar al contenido

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ónComportamiento al reintentar
POST /v1/payment_linksRequiere Idempotency-Key. La respuesta exitosa se conserva 24 horas y se devuelve byte a byte.
POST /v1/payment_links/{id}/disableIdempotency-Key opcional. Repetir la llamada deja el enlace desactivado.
POST /v1/payment_links/{id}/emailRequiere Idempotency-Key. Evita enviar el mismo correo dos veces.
POST /v1/payments/{id}/syncNaturalmente 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
}