Claves de idempotencia: reintentos seguros en integraciones reales
El cliente envía “crear pago”, la conexión expira y no sabe si hubo cobro. Reintentar sin identidad de operación puede duplicarlo; no reintentar puede dejar el pedido bloqueado. La clave permite al servidor reconocer que se repite la misma intención y devolver el resultado registrado.
Publicado el 28 de septiembre de 202613 min de lecturaMutaciones de API confiables
Una intención, varios intentos de entrega
La clave permanece entre reintentos; una nueva acción recibe otra
1 / IntenciónEl cliente crea clave y petición canónica.
2 / EnviarEl servidor reserva la clave por ámbito.
3 / EjecutarMutación y resultado quedan persistidos.
4 / ReintentarLa misma clave devuelve el resultado anterior.
Idempotencia no significa “ignorar HTTP duplicado”. Es un contrato: para un ámbito y plazo definidos, la misma clave con la misma entrada produce un único efecto lógico y un resultado repetible. No garantiza entrega de red exactamente una vez ni hace atómicos efectos externos.
Diseña la clave alrededor de una operación
Genera una clave aleatoria cuando usuario o job crea la intención y persístela antes de enviar. UUID aleatorio suele bastar. No uses solo hora, ID de cliente o número del intento. Limita el ámbito por cuenta y operación. No permitas que una entrada no confiable cruce fronteras entre clientes.
Guarda una huella canónica de la solicitud. Reutilizar clave con importe, moneda o destino distinto debe producir conflicto, no repetir silenciosamente el resultado ni ejecutar otra mutación. Normaliza antes de calcular el hash y excluye campos de transporte que no cambian la intención.
Persiste reserva, efecto y respuesta coherentemente
Estado
Significado
Comportamiento del retry
Nuevo
No hay operación para la clave y ámbito.
Reservar atómicamente antes de ejecutar.
En curso
Una petición ejecuta y llega otra concurrente.
Conflicto, Retry-After o espera corta documentada.
Completado
Efecto y respuesta estable guardados.
Devolver resultado anterior con la misma huella.
Fallido/incierto
Fallo definitivo o efecto remoto sin confirmar.
Clasificar y conciliar antes de otro efecto.
Usa restricción única en (ámbito, clave) para arbitrar concurrencia. Guarda estado, huella, respuesta o referencia y fechas. Si la mutación está en la misma base, registra negocio y resultado en una transacción. Para pagos o correo externos, usa también la clave del proveedor o un patrón outbox/conciliación: la transacción local no vuelve atómica una llamada HTTP remota.
Pago: timeout después del éxito remoto
El servicio crea `pedido-842:autorizar-v1` y envía. El proveedor autoriza, pero se pierde la respuesta. Repetir con la misma clave devuelve la autorización original; el servicio guarda la referencia y concluye. Un nuevo intento tras un rechazo deliberado o cambio de importe es otra intención, con clave y auditoría nuevas.
La semántica depende del proveedor. Algunos conservan la primera respuesta, incluso ciertos errores, y cada uno define una ventana de retención. Lee el contrato y conserva la clave local más que el periodo máximo de reintento y conciliación. Tras expirar la clave remota, consulta el estado antes de repetir un cobro incierto.
Webhook: entrega repetida, una transición
Guarda el ID de entrega con unicidad para deduplicar redelivery. También aplica idempotencia de dominio: actualiza una suscripción si la versión de origen es más nueva o la transición sigue siendo válida. El proveedor puede generar eventos distintos para el mismo objeto. Mantén intentos y efectos de negocio separados.
Job asíncrono: evita completar dos veces
Un mensaje puede volver tras caer el worker. Asigna ID estable al job, resérvalo atómicamente y vuelve idempotente cada acción posterior. Para varias etapas, registra estado y usa outbox/saga y compensaciones cuando corresponda. “Exactamente una vez” entre sistemas independientes suele ser una ilusión; apunta a entrega al menos una vez, efecto lógico único y conciliación.
Define la caducidad con criterio
Borrar claves pronto permite repetir una acción; guardarlas para siempre aumenta coste y obligaciones de privacidad. Define retención según retries de cliente, cola, dispositivos offline y soporte. Al borrar respuestas, conserva tombstone o unicidad de negocio cuando duplicar sea costoso. La caducidad forma parte del contrato de API.
Prueba los momentos ambiguos
Prueba misma clave/cuerpo secuencial y concurrente, cuerpo distinto, crash antes del commit, después del commit y antes de responder, timeout tras éxito remoto, caducidad y replay. Comprueba respuesta y número de efectos. Registra correlación y hash de clave, no payload sensible ni credenciales.
En resumen
La clave nombra una mutación pretendida en entregas inestables. Genérala una vez, limita su ámbito, vincúlala a una huella, resérvala con unicidad, persiste el resultado y entiende la retención del proveedor. Así el retry es recuperación, no apuesta a duplicar dinero o trabajo.