API
Solicita acceso →
Empezar / Idempotencia

Idempotencia

La idempotencia es la garantía de que ejecutar la misma operación dos veces produce exactamente el mismo resultado que ejecutarla una sola vez. Cuando mueves dinero, esta garantía deja de ser un lujo y se vuelve el cimiento de todo: es lo que te permite reintentar un request sin miedo cuando la red falla, cuando tu servidor se cae a mitad de camino o cuando no sabes si tu pago llegó. En Alibanca la controlas con un solo header, idempotency-key, y en esta página te explicamos qué es, por qué importa tanto, cómo generar buenas claves y cómo se relaciona con los estados failed y unknown de un pago.

¿Qué es la idempotencia? (en simple)

Imagina el botón de un ascensor. Lo presionas una vez y el ascensor viene. Si lo presionas cinco veces más porque estás apurado, el ascensor no viene cinco veces ni sube cinco pisos: el resultado es el mismo que si lo hubieras presionado una sola vez. Eso es idempotencia. Una operación es idempotente cuando repetirla no cambia el resultado más allá de la primera vez.

En una API que mueve dinero esto es justo lo contrario de lo que pasaría con un botón "peligroso". Piensa en un cajero que dispensa efectivo cada vez que lo tocas: presionarlo cinco veces te da cinco veces el dinero. Ese comportamiento, aplicado a pagos, es una catástrofe: cada reintento sería un pago nuevo. La idempotencia convierte tu endpoint de pagos en un botón de ascensor y no en un cajero descontrolado.

i
La definición corta. Idempotente = "hacer lo mismo dos veces da el mismo resultado". Un GET es naturalmente idempotente (leer un saldo diez veces no cambia nada). Un POST que crea un pago no lo es por naturaleza — por eso Alibanca se lo agrega con la idempotency-key.

Por qué es crítica al mover dinero

El problema que resuelve la idempotencia se llama incertidumbre de red. Cuando tu servidor le manda un POST /v2/payments a Alibanca, pueden pasar tres cosas: (1) el request llega, se procesa y recibes la respuesta — todo bien; (2) el request nunca llega — nada se movió; o (3), la más traicionera, el request llega y se procesa, pero la respuesta se pierde en el camino de vuelta. En este tercer caso tu código no recibió nada: no sabe si el pago ocurrió o no.

Sin idempotencia, tu única salida ante ese silencio sería "reintentar por si acaso". Y ese reintento crearía un segundo pago por el mismo dinero. Multiplicado por miles de operaciones y timeouts inevitables, terminas dispersando el doble. Con idempotencia, reintentar es seguro: le mandas a Alibanca la misma idempotency-key del intento anterior y el sistema reconoce "esta operación lógica ya la vi" y te devuelve el resultado original en vez de mover dinero otra vez.

!
La regla mental. Cada vez que un request de pago no te devuelva una respuesta clara, la respuesta correcta nunca es "creo otro pago". Es "reintento la misma operación con la misma clave" o "consulto el estado". La idempotencia es lo que hace que ambas cosas sean seguras.

El header idempotency-key

La idempotencia en Alibanca se activa con un header en tu request:

HeaderTipoDescripción
idempotency-keyreq en pagosstringIdentificador único y determinístico de la operación lógica que estás ejecutando. "Único" = no lo reutilizas para dos operaciones distintas. "Determinístico" = tu código puede volver a calcular exactamente la misma clave para el mismo pago, aunque se reinicie. Es tu manera de decirle a Alibanca "estas dos peticiones son el mismo pago, no dos pagos".

Un detalle importante de seguridad: la idempotency-key no viaja sola. Además de ser un header, forma parte del envelope canónico que firmas con tu llave privada (RSA-SHA256). Ese envelope es la concatenación determinística de [método HTTP, ruta, api-key, idempotency-key, cuerpo crudo, nonce], en ese orden. O sea que la clave está firmada junto con el cuerpo del request: nadie puede alterarla en tránsito sin romper la firma. Si te interesa el detalle de la firma, revísalo en Autenticación y firma →; para lo de idempotencia te basta con saber que la clave es un header que además queda protegido por la firma.

i
No confundas idempotency-key con el nonce. Son cosas distintas. El nonce es un número siempre creciente (high-water) que evita replays — impide que alguien reenvíe un request viejo capturado. La idempotency-key hace lo opuesto: existe precisamente para que puedas repetir la misma operación a salvo. Uno bloquea reenvíos maliciosos; el otro habilita reintentos tuyos.

Cómo generar una clave: única y determinística

La palabra clave es determinística. Mucha gente resuelve la unicidad con un UUID aleatorio nuevo en cada llamada — y eso es justo lo que no quieres, porque si tu servidor se cae y reintenta, generaría un UUID distinto y crearía un pago nuevo. La clave debe poder recalcularse igual a partir de datos estables de tu operación.

1

Ánclala a la operación de negocio, no al request

Piensa en cuál es la "operación lógica" que jamás debe ocurrir dos veces. Por ejemplo: "el payout #123 de la orden de retiro del usuario X". Esa unidad de negocio tiene un identificador estable en tu base de datos. Úsalo como semilla de la clave.

2

Hazla reproducible tras un reinicio

Si tu proceso muere y se levanta de nuevo, tu código tiene que llegar a la misma clave leyendo el mismo registro. Por eso conviene persistir la clave en tu DB junto al payout antes de mandar el request, o derivarla de campos inmutables (id del payout, no la hora actual).

3

Hazla única entre operaciones distintas

Dos payouts diferentes nunca deben compartir clave. Un prefijo con el tipo de operación más el id interno funciona muy bien: payout-2026-000123. Si dudas, agrégale un componente que garantice unicidad de negocio (id de la orden + secuencia).

4

Mantenla acotada y estable

Un string corto, ASCII, estable en el tiempo. No metas dentro de la clave datos que puedan cambiar entre intentos (timestamps, contadores de reintento, hostname). Si cambian, dejaría de ser la misma clave y perderías la protección.

i
Patrón recomendado. {tipo}-{id-de-negocio}, por ejemplo payout-{orderId} o disbursement-{ledgerEntryId}. Si necesitas más entropía, usa un hash determinístico de esos campos (no un aleatorio). La prueba de fuego: "si mi servidor se reinicia justo ahora, ¿mi código genera exactamente esta misma clave?" Si la respuesta es no, la clave está mal.

Qué pasa cuando reintentas

Aquí está el corazón del mecanismo, y depende por completo de qué clave uses al reintentar.

Reintento con la MISMA idempotency-key

Alibanca reconoce que esa operación lógica ya la procesó. No crea un pago nuevo. Te devuelve el mismo resultado que la primera vez — el mismo id de pago, el mismo state, el mismo monto. Da igual si reintentas una vez o veinte: siempre obtienes el pago original. Esto es lo que hace seguro reintentar tras un timeout: en el peor caso, "molestaste" a la API para que te repita algo que ya existía.

Un pago genuinamente nuevo necesita una clave NUEVA

La otra cara: si de verdad quieres emitir otro pago (por ejemplo, un segundo retiro real del mismo usuario), tienes que usar una idempotency-key diferente. Si reutilizas por error la clave de un pago anterior para una operación distinta, Alibanca te devolverá el pago viejo y tu nuevo pago no ocurrirá. Por eso "único entre operaciones distintas" es tan importante como "determinístico dentro de la misma operación".

!
Los dos errores espejo. Clave aleatoria en un reintento → doble-pago (creaste dos). Clave reciclada en una operación nueva → pago fantasma (el segundo nunca se hizo). La clave correcta es determinística por operación: la misma dentro del mismo payout, distinta entre payouts.

Idempotencia + estados del pago: la regla de oro

La idempotencia no vive sola: se combina con los estados de un pago para decirte cuándo reintentar y con qué clave. Un pago en Alibanca termina en uno de tres estados:

completed unknown failed

Cada uno cambia tu jugada de idempotencia:

EstadoQué significaQué haces
completedéxitoEl dinero se movió. No reintentas nada. Si por un timeout no viste esta respuesta, un reintento con la misma clave te la devolverá.
failedfallo garantizadoGarantía dura: NO hubo débito. El dinero no se movió, punto. Es seguro emitir el pago de nuevo — pero como es un intento genuinamente nuevo, usa una idempotency-key NUEVA.
unknownindeterminadoEl banco no respondió (timeout/caída). "No sabemos todavía." NUNCA creas un pago nuevo sobre un unknown. Se resuelve solo por conciliación de saldo. Reintentar aquí es exactamente lo que produce el doble-pago.
!
La Regla de Oro, memorízala. failed = seguro reintentar, con clave nueva (garantiza que no hubo débito). unknown = no reintentar (se reconcilia). El estado unknown es de primera clase en Alibanca precisamente porque el mundo real de la banca tiene timeouts, y tratar "no sé" como "falló" es lo que rompe la integridad del dinero.

Fíjate en la sutileza: la idempotency-key te protege del doble-pago cuando reintentas la misma operación (misma clave). Pero si un pago quedó en unknown y tú decides "voy a hacer otro con clave nueva", la idempotencia ya no te cubre — porque le estás diciendo explícitamente a Alibanca que es un pago distinto. Por eso la disciplina de estados manda: sobre un unknown, esperas la conciliación; no fabricas una operación nueva.

Ejemplo completo: un pago y su reintento

Veamos el flujo real. Primero emites un pago con su idempotency-key determinística derivada de tu payout interno #123:

cURLNodeCopiar
curl -X POST https://alibanca-api-production.up.railway.app/v2/payments \
  -H "x-api-key: sk_test_TU_LLAVE" \
  -H "x-signature: <firma RSA-SHA256 del envelope>" \
  -H "idempotency-key: payout-2026-000123" \
  -H "Content-Type: application/json" \
  -d '{"amountMinor":"150000","currency":"VES","beneficiaryId":"ben_a1b2c3"}'

El mismo request desde Node, generando la clave a partir de un id de negocio estable (no un aleatorio):

cURLNodeCopiar
// La clave se deriva del payout interno: reproducible tras un reinicio
const idempotencyKey = `payout-2026-${payout.id}`;

const res = await fetch("https://alibanca-api-production.up.railway.app/v2/payments", {
  method: "POST",
  headers: {
    "x-api-key": process.env.ALIBANCA_KEY,
    "x-signature": sign(envelope),
    "idempotency-key": idempotencyKey,
    "content-type": "application/json"
  },
  body: JSON.stringify({
    amountMinor: "150000",
    currency: "VES",
    beneficiaryId: "ben_a1b2c3"
  })
});

La respuesta trae el pago creado, con su id y su state. Envuelta en el sobre estándar de Alibanca:

RESPUESTA 201 application/json
{
  "status": "success",
  "traceId": "trc_9f2c7a10",
  "payload": {
    "id": "pay_7h3k9m",
    "state": "completed",
    "subStatus": "settled",
    "amountMinor": "150000",
    "currency": "VES"
  }
}

Ahora supón que tu servidor se cayó justo después de enviar el request y nunca vio esta respuesta. Al levantarse, recalcula la misma clave (payout-2026-000123) porque la derivó del mismo payout, y reenvía exactamente el mismo request. Alibanca reconoce la operación y te devuelve el pago original — el mismo id, sin mover dinero de nuevo:

RESPUESTA 200 application/json — reintento idempotente
{
  "status": "success",
  "traceId": "trc_5b81ce44",
  "payload": {
    "id": "pay_7h3k9m",       // mismo id: NO es un pago nuevo
    "state": "completed",
    "subStatus": "settled",
    "amountMinor": "150000",
    "currency": "VES"
  }
}
Pruébalo. En sandbox (proveedor simulado, no mueve un centavo real) emite un pago con una idempotency-key tuya y luego reenvía el mismo request con la misma clave. Confirma que el id del payload es idéntico en ambas respuestas: esa es tu prueba de que el reintento fue idempotente y no creó un segundo pago. Los montos mágicos → te dejan además ensayar el caso unknown del doble-pago sin arriesgar dinero.

Buenas prácticas

Persiste la clave antes de enviar

Guarda la idempotency-key en tu base de datos, junto al payout, antes de disparar el request. Así, si algo falla en medio, tu proceso de recuperación lee esa misma clave y reintenta sin ambigüedad. La clave debe existir en tu lado con la misma vida útil que la operación de negocio.

Deriva, no inventes

Prefiere derivar la clave de identificadores estables (id del payout, id de la orden) en vez de generarla al azar. Si usas un aleatorio, al menos guárdalo persistido de inmediato para poder reusarlo. La palabra que nunca falla es determinística.

Un reintento no cambia el cuerpo

Cuando reintentas con la misma clave, manda el mismo cuerpo (mismo amountMinor, mismo destino). La idempotency-key identifica una operación concreta; cambiarle el monto a mitad de camino es una contradicción lógica. Recuerda además que el cuerpo crudo va firmado en el envelope, así que un reintento consistente también es un reintento verificable.

Combina reintento con consulta de estado

Ante un timeout tienes dos herramientas complementarias: reintentar el POST con la misma clave, o consultar GET /v2/payments/:id → para leer el state actual. Ambas son seguras porque ninguna crea un pago. Usa la que encaje mejor en tu flujo — muchas integraciones reintentan un par de veces y, si siguen sin certeza, pasan a consultar estado.

Respeta la Regla de Oro por estado

Automatiza la decisión: si el pago quedó failed, tu sistema puede reemitir con clave nueva; si quedó unknown, tu sistema marca y espera la conciliación, sin tocar nada. No dejes esa decisión al azar de un reintento ciego.

Errores comunes que debes evitar

!
UUID nuevo en cada intento. Es el error #1. Genera una clave distinta cada vez, así que el reintento crea un pago nuevo y produce el doble-pago exacto que la idempotencia debía evitar. Si vas a usar UUID, genéralo una vez, persístelo, y reúsalo en los reintentos.
!
Reintentar sobre un unknown. "El pago quedó en unknown, lo mando otra vez con clave nueva por si acaso." Ese "por si acaso" es la trampa: si el dinero se había movido, acabas de pagar doble. Sobre unknown nunca se crea un pago nuevo — se reconcilia.
!
Reciclar una clave vieja para un pago genuinamente nuevo. El segundo retiro real reutiliza la clave del primero → Alibanca te devuelve el pago viejo y el nuevo nunca ocurre. Un pago nuevo siempre lleva clave nueva.
!
Meter datos volátiles en la clave. Incluir la hora, un contador de reintentos o el nombre del servidor hace que la clave cambie entre intentos y pierdas la protección. La clave debe salir de campos inmutables de la operación.

En resumen

La idempotencia convierte tus reintentos en una operación segura: repetir un pago con la misma idempotency-key devuelve el pago original en vez de mover dinero otra vez, y emitir un pago genuinamente nuevo exige una clave nueva. Genera claves únicas y determinísticas ancladas a tu operación de negocio, persístelas antes de enviar, y combina el mecanismo con la Regla de Oro de los estados: failed se reintenta con clave nueva porque garantiza que no hubo débito, y unknown nunca se reintenta porque se resuelve por conciliación. Con esa disciplina, mover dinero se vuelve una operación repetible sin riesgo — que es exactamente lo que la infraestructura de pagos de Alibanca está diseñada para darte. Sigue con Estados de un pago → y Montos mágicos → para ensayarlo todo en sandbox.