Una aplicación envía una solicitud para cobrar un pedido, pero la conexión se interrumpe antes de recibir la respuesta. ¿Se completó el cobro o falló antes de llegar al servidor? Si el cliente vuelve a intentarlo sin ninguna protección, podría generar un segundo cargo.
La idempotencia en APIs ayuda a gestionar esta incertidumbre: permite repetir una operación sin multiplicar su efecto previsto. En este artículo verás cómo funciona, cuándo necesitas una clave de idempotencia y qué decisiones de diseño ayudan a evitar duplicados incluso ante reintentos y solicitudes concurrentes.
Qué significa que una operación sea idempotente
Una operación es idempotente cuando ejecutarla varias veces produce el mismo efecto previsto que ejecutarla una sola vez. Por ejemplo, establecer el estado de un pedido en «cancelado» puede ser idempotente: repetir la petición no debería crear nuevas cancelaciones ni modificar el resultado final.
Esto no exige que todas las respuestas sean idénticas. La primera solicitud para borrar un recurso podría devolver una confirmación y la siguiente indicar que ya no existe. Lo importante es que el efecto solicitado sobre el recurso siga siendo el mismo.
Tampoco implica que no existan efectos secundarios internos. Un servidor puede registrar cada intento en sus logs. Sin embargo, si cada reintento envía un correo al cliente o cobra de nuevo, la operación de negocio necesita una protección adicional.
Idempotencia y métodos HTTP
La semántica HTTP define GET, HEAD, PUT, DELETE y los métodos seguros como idempotentes respecto a su efecto previsto. Esto no garantiza que una implementación concreta respete esa semántica: hay que diseñar y comprobar su comportamiento.
POST no es idempotente por definición. Si crea un pago o un pedido, repetir la solicitud puede crear otro. PATCH tampoco es necesariamente idempotente: establecer un campo a un valor fijo puede serlo, mientras que incrementar un contador en cada petición no lo es.
Por qué los reintentos generan operaciones duplicadas
Los reintentos son útiles cuando hay problemas transitorios, como una desconexión, un tiempo de espera agotado o una respuesta de servidor recuperable. El problema es que el cliente no siempre puede distinguir entre estas situaciones:
- La solicitud no llegó al servidor.
- El servidor la recibió, pero no llegó a ejecutarla.
- La operación se completó y la respuesta se perdió.
- La operación sigue en curso cuando comienza el reintento.
En los dos últimos casos, enviar de nuevo un POST sin protección puede duplicar el efecto. Por eso no basta con configurar reintentos en el cliente: servidor y cliente necesitan un acuerdo para reconocer que varios intentos corresponden a una misma intención de negocio.
Cómo funciona una clave de idempotencia
Una clave de idempotencia es un identificador que el cliente genera para una operación lógica y conserva en todos sus reintentos. El servidor la asocia con la solicitud y su resultado. Si recibe de nuevo la misma clave dentro de su periodo de validez, consulta el registro en lugar de iniciar otra operación independiente.
Un intercambio simplificado podría ser:
POST /pagos HTTP/1.1
Idempotency-Key: pago-pedido-4827-intento-a
Content-Type: application/json
{"pedido_id": 4827, "importe": 2500, "moneda": "EUR"}
Si la respuesta se pierde, el cliente vuelve a enviar el mismo cuerpo con la misma clave. El servidor puede entonces recuperar el resultado guardado y devolverlo. Si el usuario inicia después una operación distinta, el cliente debe generar otra clave.
El nombre de la cabecera y las respuestas exactas dependen del contrato de la API. Lo importante es documentar cómo se envía la clave, cuánto tiempo se conserva y qué ocurre cuando se reutiliza.
Qué debe guardar el servidor
Como mínimo, el registro suele incluir:
- La clave, limitada al contexto adecuado, como cuenta o cliente y operación.
- Una huella de los parámetros relevantes para detectar cambios en la solicitud.
- El estado de procesamiento, por ejemplo, en curso o completado.
- El resultado necesario para responder al reintento, como código HTTP y cuerpo.
- La fecha de creación o expiración del registro.
La huella debe calcularse de forma consistente: dos representaciones equivalentes de un mismo JSON no deberían considerarse distintas solo por el orden de sus campos, si el contrato las trata como equivalentes. Evita almacenar datos sensibles que no sean necesarios.
Implementación paso a paso
1. Define el alcance de cada clave
Decide qué constituye una operación lógica y quién puede reutilizar su clave. Normalmente conviene asociarla con el cliente autenticado y el tipo de operación, para que dos cuentas no interfieran entre sí. Verifica siempre los permisos antes de recuperar un resultado guardado: una clave no sustituye a la autenticación ni a la autorización.
El cliente debe generar identificadores suficientemente únicos y conservarlos hasta conocer el resultado. Crear una clave nueva en cada reintento elimina la protección. También conviene evitar claves basadas únicamente en datos predecibles o sensibles.
2. Registra la solicitud de forma atómica
Dos copias de la misma petición pueden llegar casi a la vez. Si el servidor primero comprueba que la clave no existe y después la inserta sin protección, ambas podrían pasar la comprobación.
Usa una restricción de unicidad en el almacenamiento y una inserción atómica para que solo una petición obtenga el derecho de iniciar el trabajo. La otra debe consultar el registro existente. En sistemas con varias instancias, el mecanismo debe compartirse entre ellas; una caché local por proceso no ofrece esta garantía.
3. Comprueba la equivalencia de la solicitud
Si la clave ya existe, compara la huella del cuerpo y los demás parámetros relevantes. Reutilizarla para pagar otro importe no debe devolver silenciosamente el resultado anterior ni ejecutar un nuevo pago: responde con un error de conflicto o validación definido en el contrato.
Si la solicitud coincide y sigue en curso, puedes devolver una respuesta que indique ese estado y pedir al cliente que consulte o reintente más tarde. Si ya terminó, devuelve el resultado registrado según la política de la API.
4. Coordina el registro con la operación
Cuando el efecto de negocio y el registro de idempotencia están en la misma base de datos, una transacción puede confirmar ambos conjuntamente. Así se reduce el riesgo de crear un pedido sin guardar el resultado que permitirá reconocerlo después.
Las llamadas a sistemas externos son más delicadas. Una transacción local no puede garantizar por sí sola que un proveedor haya procesado un cobro exactamente una vez. Si el proveedor admite su propia clave de idempotencia, úsala de forma estable. Si no, necesitarás mecanismos como identificadores de referencia, consulta del estado y conciliación antes de repetir una acción cuyo resultado sea incierto.
Para trabajos asíncronos, un patrón de bandeja de salida transaccional puede ayudar a registrar el evento junto con el cambio de negocio. Aun así, los consumidores deben tolerar entregas repetidas: la bandeja de salida no elimina por sí sola todos los duplicados.
5. Define recuperación y expiración
Un proceso puede fallar después de marcar una petición como «en curso». Establece cómo detectar y recuperar esos registros, pero no vuelvas a ejecutar automáticamente un efecto externo solo porque haya vencido un plazo: primero determina si ya ocurrió.
El tiempo de conservación debe cubrir la ventana razonable de reintentos del cliente y las necesidades del negocio. Una vez expirada la clave, una petición tardía podría tratarse como nueva; documenta esa limitación. Para operaciones especialmente sensibles, considera además una restricción de unicidad de negocio, como un identificador único de transacción.
Cómo diseñar los reintentos del cliente
La idempotencia protege frente a duplicados, pero no convierte cualquier reintento en una buena idea. El cliente debe distinguir los errores transitorios de los permanentes. Por ejemplo, corregir una solicitud inválida suele requerir cambiar sus datos, no reenviarla sin modificaciones.
Una estrategia práctica incluye:
- Guardar la clave y el cuerpo antes del primer envío.
- Reintentar con la misma clave cuando el resultado sea incierto o la respuesta indique que procede hacerlo.
- Aplicar espera creciente con un componente aleatorio para evitar que muchos clientes reintenten a la vez.
- Respetar las indicaciones del servidor sobre cuándo volver a consultar y establecer un límite de intentos o de tiempo.
- Si se agota ese límite, mostrar un estado pendiente o consultar el resultado, en vez de asumir que la operación falló.
El servidor también debe documentar qué ocurre con los errores: cuáles se guardan como resultado definitivo, cuáles permiten reintentar y cómo se consulta una operación en curso. No conviene prometer que todo error 5xx significa que no hubo efectos.
Pruebas y errores frecuentes
Prueba el flujo completo, no solo el caso en que el servidor responde correctamente. Estas situaciones ayudan a detectar fallos de diseño:
- Envía simultáneamente varias peticiones idénticas con la misma clave y comprueba que solo se produzca el efecto previsto una vez.
- Reutiliza la clave con un importe diferente y verifica que se rechace la solicitud.
- Simula la pérdida de la respuesta después de confirmar la operación y comprueba que el reintento recupere el resultado.
- Interrumpe el procesamiento y verifica la recuperación de registros en curso y la conciliación de efectos externos.
- Prueba qué sucede al vencer la clave y al intentar acceder desde otra cuenta.
Entre los errores habituales están confiar solo en el botón deshabilitado de la interfaz, guardar claves únicamente en memoria o tratar un tiempo de espera como prueba de que no ocurrió nada. La protección debe estar en el servidor y abarcar las dependencias que realmente ejecutan la operación.
Conclusión
La idempotencia en APIs permite reintentar con más seguridad cuando no se sabe si una solicitud terminó. Para conseguirlo, combina claves estables, comprobación de parámetros, almacenamiento atómico y una política clara para resultados, errores y expiración. En operaciones externas, añade mecanismos de consulta y conciliación: una clave local no resuelve por sí sola toda la cadena.
Revisa los endpoints que crean pagos, pedidos u otros efectos importantes y prueba qué sucede si la respuesta se pierde. Ese ejercicio es un buen punto de partida para identificar dónde necesitas protección contra duplicados.

