Los cinco SDK traen la misma política de reintentos, pensada para que reintentar nunca duplique una operación.

La política por defecto #

ParámetroValor por defectoQué hace
Intentos máximos3 (el primero + 2 reintentos)1 desactiva los reintentos.
Base del backoff0,5 sEspera del primer reintento, duplicándose en cada uno.
Tope del backoff30 sNinguna espera supera este valor.
JittercompletoCada espera es aleatoria entre 0 y el valor calculado, para no sincronizar a todos los clientes.
Retry-Afterse respetaSi el gateway lo envía, el SDK espera exactamente eso.
Estados que se reintentan429, 502, 503, 504Fallos transitorios. Nunca 4xx de negocio ni 500.
Métodossolo GETLas escrituras se reintentan solo con retry_writes activado.
Errores de red sin respuestano se reintentanLos reenvías tú con la misma Idempotency-Key.
flowchart TD
  A[Petición] --> B{¿Respuesta?}
  B -- no: timeout / red --> X[Excepción de transporte<br/>reenvía tú con la misma llave]
  B -- sí --> C{¿429/502/503/504?}
  C -- no --> D[Éxito o error tipado]
  C -- sí --> E{¿GET o retry_writes?}
  E -- no --> D
  E -- sí --> F{¿Quedan intentos?}
  F -- no --> D
  F -- sí --> G[Espera Retry-After<br/>o backoff con jitter] --> A

¿Por qué las escrituras no se reintentan solas? #

El gateway sí deduplica: cada escritura con Idempotency-Key repetida devuelve la respuesta guardada (idempotent_replay: true) en lugar de ejecutarse otra vez, en todas las rutas con llave. La razón es otra: el SDK no puede saber qué versión del gateway tiene enfrente, y reintentar una escritura contra un despliegue antiguo sin esa garantía la ejecutaría dos veces. Por eso el valor seguro por defecto es reintentar solo GET.

Cuando sepas que tu gateway deduplica (todos los entornos de Compense lo hacen desde la versión actual del contrato), activa los reintentos de escrituras. El SDK reutiliza la misma Idempotency-Key en todos los intentos de una misma llamada, así que el servidor convierte el reintento en una repetición:

from compense_gateway import CompenseClient, RetryConfig

client = CompenseClient(
    api_key=os.environ["COMPENSE_API_KEY"],
    environment="sandbox",
    retry=RetryConfig(max_attempts=5, backoff_base=0.5, backoff_max=30.0, jitter=True,
                      retry_writes=True),
)

En .NET con AddCompense, la misma política se configura en appsettings.json ("Compense": { "Retry": { "MaxAttempts": 5, "RetryWrites": true } }); en Go, un campo en cero de RetryConfig toma el valor por defecto.

Timeouts #

El timeout es por intento: Python timeout= (segundos), JS timeout (milisegundos), PHP timeout: (segundos, cuando el SDK construye Guzzle), .NET Timeout (TimeSpan), Go WithTimeout (o el context.Context de cada llamada, que también corta las esperas entre reintentos). Un timeout es un error sin respuesta: no sabes si la escritura se aplicó, así que reenvíala con la misma llave (Idempotencia).

Qué hacer con cada fallo #

FalloEl SDKTú
429Reintenta (GET, o escrituras con retry_writes) respetando Retry-After.Si se agotan los intentos, encola y baja el ritmo (Límites).
502/504Reintenta igual que 429.Si persiste, reintenta más tarde con la misma llave y cita el trace_id.
503Reintenta.Igual que 502.
500No reintenta.Reporta el trace_id a soporte.
409 en cursoNo reintenta.Espera y repite con la misma llave.
4xx de negocioNo reintenta.Corrige la petición o el caso: reintentar no lo arregla.
Red / timeoutNo reintenta.Reenvía con la misma Idempotency-Key.