Ir al contenido

Reintentos e idempotencia

Las redes fallan, sobre todo en el móvil. Todos los SDK de Frontmail reintentan automáticamente las solicitudes fallidas, y lo hacen de forma segura: un envío reintentado nunca da lugar a un correo duplicado ni a un doble cargo.

Situación ¿Se reintenta?
Error de red (sin conexión, DNS, conexión restablecida) ✓
Tiempo de espera agotado (timeoutMs, 15 s por intento por defecto) ✓
Respuestas 5xx ✓
429 rate_limited ✓: espera lo que indique Retry-After (si son 60 s o menos)
Errores 4xx de validación, autenticación, origen o CAPTCHA ✗: reintentar no serviría de nada
Bloqueos del lado del cliente (blockHeadless, blockList, limitRate) ✗

Política por defecto: 3 reintentos después del primer intento, con espera exponencial que empieza en 300 ms con jitter completo y como máximo 10 s entre intentos. Configúrala con la opción retry:

init({ publicKey: 'pk_4f2a…', retry: { retries: 5, baseDelayMs: 500, maxDelayMs: 20_000 } });
init({ publicKey: 'pk_4f2a…', retry: false }); // desactivar

Cada send() lógico recibe un UUID aleatorio como Idempotency-Key, y todos los reintentos de ese envío reutilizan la misma clave. En el servidor, Frontmail recuerda las claves durante 24 horas:

  • la misma clave con el mismo cuerpo devuelve la respuesta original (el mismo message_id) sin volver a enviar,
  • la misma clave con un cuerpo distinto falla con 409 idempotency_conflict.

Así, si el primer intento llegó realmente a Frontmail pero la respuesta se perdió, el reintento simplemente obtiene el resultado original.

Pasa una clave explícita cuando lo que desencadena el envío puede repetirse: un webhook entregado dos veces, una tarea que se reinicia, un usuario que envía un formulario dos veces en dos pestañas:

// @frontmail/node
await frontmail.send({ templateId: 'tpl_receipt', params, idempotencyKey: `receipt-${order.id}` });
// @frontmail/browser: las opciones son el último argumento
await send('svc_01J9…', 'tpl_receipt', params, { idempotencyKey: `receipt-${order.id}` });

Las claves pueden tener hasta 255 caracteres. Usa una clave nueva para cada correo distinto: si reutilizas una para otro correo, obtendrás un error en lugar de enviarlo.

Con la API REST, envía tú la cabecera:

Ventana de terminal
curl https://api.frontmail.dev/v1/send \
-H 'Idempotency-Key: receipt-A-1042' \
-H 'Authorization: Bearer sk_…' \
-H 'Content-Type: application/json' \
-d '{ "template_id": "tpl_receipt", "template_params": { "order_id": "A-1042" } }'

En los envíos por lotes, establece idempotencyKey (idempotency_key en la API REST) en cada mensaje.

Una vez que la API ha devuelto 202, los reintentos de entrega son cosa de Frontmail: el proceso de envío reintenta los errores temporales del proveedor con espera progresiva, cambia al servicio de respaldo ante errores de autenticación o permanentes y retiene los mensajes cuando un servicio no funciona. Tu código no tiene que hacer nada; si lo necesitas, comprueba el resultado con getStatus() o en el Historial.