Saltar al contenido
← Ideas

Idempotencia en APIs: cómo evitar duplicados sin ocultar errores

Una clave idempotente permite reintentar operaciones sensibles sin repetir sus efectos. Aprende a definir su alcance, gestionar concurrencia y responder ante errores.

Diagrama de una API que usa una clave idempotente para reconocer reintentos y evitar duplicar una operación.

Una petición puede llegar al servidor y, aun así, el cliente no recibir la respuesta. Quizá se agotó el tiempo de espera después de registrar un pago o crear un pedido. Si el cliente reintenta a ciegas, el sistema podría repetir la operación. La idempotencia en APIs ayuda a evitar ese resultado: permite reconocer que varias solicitudes corresponden al mismo intento de negocio y limitar sus efectos duplicados.

Pero añadir una cabecera con una clave no basta. Hay que decidir quién la genera, durante cuánto tiempo vale, qué respuesta se repite y cómo se coordinan las escrituras locales y los servicios externos. La meta no es ocultar errores ni prometer una ejecución exactamente una vez; es hacer que los reintentos sean seguros bajo condiciones explícitas.

Qué significa idempotencia y qué problema resuelve

Qué significa idempotencia y qué problema resuelve

Una operación es idempotente cuando repetirla con los mismos datos no cambia el resultado final después de la primera ejecución. Por ejemplo, establecer la dirección de envío de un pedido en un valor concreto puede ser idempotente: aplicar dos veces la misma actualización deja el recurso en el mismo estado que aplicarla una vez.

En cambio, una operación como “añadir una unidad al carrito” no es idempotente por sí sola: repetirla puede incrementar la cantidad dos veces. Crear un pedido o registrar un cargo también puede producir efectos adicionales si cada petición se interpreta como una nueva intención. En esas operaciones, una clave idempotente puede asociar los reintentos al mismo intento lógico.

La idempotencia no equivale a que todas las respuestas sean idénticas ni a que la petición nunca falle. Significa controlar los efectos de repetirla según un contrato. Un cliente puede recibir un error y, al reintentar, obtener el resultado ya registrado; o puede recibir de nuevo un error definitivo. Lo importante es que el servidor no ejecute inadvertidamente un segundo efecto.

Cuándo añadir idempotencia y cuándo no hace falta

El riesgo aparece cuando una operación tiene efectos relevantes y el cliente no puede saber con certeza si el servidor la completó. Una respuesta perdida, un timeout, una desconexión o un reintento automático pueden dejar el resultado ambiguo. Esto es especialmente sensible al crear pedidos, iniciar pagos, reservar inventario o tramitar solicitudes.

Antes de incorporar claves, revisa el contrato existente. Una actualización que establece un estado concreto puede ser idempotente por diseño. Una consulta de lectura normalmente no crea el problema. En cambio, un endpoint que cada vez genera un nuevo recurso o movimiento necesita una estrategia clara si sus consumidores pueden reintentar.

La idempotencia tiene coste: almacenamiento de claves y resultados, reglas de expiración, gestión de concurrencia y más casos que probar. No la añadas indiscriminadamente a cada endpoint. Priorízala donde coincidan tres señales:

  • La operación puede producir un efecto duplicado difícil o costoso de revertir.
  • El cliente o la infraestructura reintentan ante fallos transitorios.
  • Una respuesta perdida impide distinguir si la operación se completó.

Define también qué significa “mismo intento” para el negocio. Dos compras intencionales iguales no deben confundirse solo porque tienen el mismo importe y contenido. La deduplicación debe basarse en una clave de intento, no en una suposición sobre la similitud de las peticiones.

Diseñar una clave: origen, unicidad, alcance y duración

Lo habitual es que el cliente genere una clave única por operación lógica y la conserve durante todos sus reintentos. Puede enviarla en una cabecera acordada o en el cuerpo, siempre que el contrato sea explícito. Si genera una clave nueva en cada reintento, el servidor no podrá relacionarlos. Si reutiliza una clave para una nueva compra, el servidor podría bloquear una intención legítima.

La clave identifica el intento, pero no sustituye la autenticación ni la autorización. Debe estar asociada a un ámbito, como la cuenta o el comercio autenticado y el tipo de operación. Así se evita que una coincidencia accidental entre dos clientes mezcle sus resultados. El servidor debe comprobar esos límites en cada solicitud.

Guarda también una huella de la solicitud normalizada: los campos que determinan el efecto, con reglas estables para valores por defecto y representación. Si una clave ya existe y llega una petición con contenido incompatible, recházala como conflicto; no la trates como un reintento válido ni ejecutes el nuevo contenido. La huella debe excluir datos irrelevantes, pero no detalles que cambien la intención, como el importe o la moneda.

La duración depende del comportamiento de clientes, colas y procesos de recuperación. Una ventana demasiado corta permite que un reintento tardío repita el efecto; una demasiado larga acumula registros y puede impedir reutilizaciones legítimas. Establece un periodo alineado con el máximo razonable de reintento y comunica qué ocurre después. Para operaciones financieras o de alto impacto, puede ser necesario conservar una referencia durable del resultado más allá de la ventana operativa.

Respuestas para claves repetidas y solicitudes en curso

El contrato debe distinguir varios casos. Si la clave ya terminó y la huella coincide, el servidor puede devolver el resultado almacenado de la operación original. Eso suele incluir el código y el cuerpo relevantes, aunque no necesariamente cada cabecera de transporte. La respuesta debería permitir al cliente identificar el recurso creado o el estado alcanzado.

Si la clave está en curso, evita iniciar una segunda ejecución. Puedes responder con un estado que indique que el procesamiento continúa, o con un conflicto temporal que invite a consultar o reintentar más tarde. El cliente necesita una regla clara: cuánto esperar, si debe conservar la misma clave y cómo obtener el resultado final. No devuelvas como éxito una operación que todavía no se ha confirmado.

Si la clave existe con una huella distinta, devuelve un error explícito y no alteres el registro original. Si la primera ejecución falló, define qué fallos se guardan como resultado terminal y cuáles permiten volver a procesar. Por ejemplo, un error de validación puede ser definitivo para esa petición, mientras que una interrupción antes de confirmar efectos puede admitir recuperación. No hay una política universal: debe reflejar el punto en que el sistema puede demostrar qué ocurrió.

Persistencia, concurrencia y efectos en otros sistemas

La reserva de la clave y la creación del efecto local deben coordinarse de forma atómica cuando comparten base de datos. Una restricción de unicidad sobre el ámbito y la clave ayuda a que dos solicitudes simultáneas no pasen ambas una comprobación inicial. La lógica debe gestionar la colisión y leer el estado creado por la solicitud ganadora, en vez de confiar solo en una secuencia de “buscar y luego insertar”.

Guarda estados comprensibles, por ejemplo, en curso, completado y fallido recuperable o definitivo. Añade marcas de tiempo y una política de recuperación para registros cuyo proceso quedó interrumpido. Un bloqueo que nunca expira puede dejar operaciones atascadas; uno que expira sin control puede permitir que dos trabajadores actúen a la vez. La recuperación debe verificar el estado del efecto antes de reanudarlo.

La transacción local no incluye automáticamente a un proveedor de pagos o a otro servicio remoto. Si el sistema guarda el pedido y luego falla antes de llamar al proveedor, o el proveedor procesa el pago y se pierde la respuesta, hay que reconciliar estados. Usa, cuando encaje, una bandeja de salida transaccional para publicar trabajo después de confirmar el cambio local, y propaga una referencia estable al sistema externo si admite deduplicación. Registra identificadores externos y contempla consultas o conciliaciones.

No prometas “exactamente una vez” de extremo a extremo solo porque existe una clave. Entre redes, bases de datos y proveedores pueden ocurrir fallos ambiguos. La garantía real debe describir qué efectos se deduplican, en qué ámbito, durante cuánto tiempo y qué casos requieren intervención o conciliación.

Errores frecuentes y lista de comprobación

Errores frecuentes y lista de comprobación
  • Clave nueva en cada reintento: el cliente debe persistir y reutilizar la clave del intento original.
  • Una clave global sin ámbito: asóciala al cliente autenticado y a la operación pertinente.
  • Clave con contenido distinto: compara una huella y rechaza la reutilización incompatible.
  • Expiración sin analizar reintentos tardíos: documenta la ventana y decide cómo se recuperan operaciones antiguas.
  • Respuesta ambigua ante concurrencia: especifica cómo consultar o reintentar mientras la primera solicitud sigue activa.
  • Confiar en la clave para cubrir sistemas externos: incorpora referencias, conciliación y tratamiento de fallos parciales.

Antes de publicar el endpoint, comprueba que el cliente genera una clave por intención y la conserva tras un timeout; que dos solicitudes simultáneas con la misma clave no duplican el efecto; y que una misma clave con datos diferentes no ejecuta una operación nueva. Prueba también fallos antes y después de la escritura, reinicios del proceso, expiración y respuestas perdidas.

Por último, observa métricas de claves repetidas, conflictos de huella, operaciones atascadas y discrepancias con servicios externos. Esas señales ayudan a detectar errores de integración y a ajustar la ventana de retención. Una implementación útil no elimina los fallos: hace explícito qué puede repetirse sin duplicar efectos y ofrece un camino seguro para resolver lo que queda incierto.

Fuentes y referencias

  1. Web standardsW3C
  2. OWASP Cheat Sheet SeriesOWASP Foundation
  3. Web performanceweb.dev