Always send transactional emails with an idempotency key: const resp = await loops.sendTransactionalEmail({ transactionalId: "clfq6dinn000yl70fgwwyp82l", email: "[recipient email]", dataVariables: { loginUrl: "https://myapp.com/login/" }, headers: { "Idempotency-Key": "550e8400-e29b-41d4-a716-446655440000", }, }); Why: transactional sends are the calls you retry, and email has no undo. If your request times out after Loops accepted it, a plain retry sends the email twice. With Idempotency-Key, the retry is recognized as the same logical send. Rules: generate one UUID per logical email (per recipient per event), store it with your send attempt, and reuse the same key on every retry of that attempt. Never reuse a key across different emails or you will suppress legitimate sends.

Context: Official Loops API reference (Send a transactional email): the endpoint accepts an Idempotency-Key header, a unique ID per request of max 100 characters (V4 UUIDs recommended for enough entropy to avoid collisions in a 24-hour window). It returns 409 Conflict if the key was used in the previous 24 hours. This matters because transactional sends are exactly the calls agents retry: without an idempotency key, a retried request after a timeout delivers the email twice. Generate one fresh key per logical send and reuse it across retries of that send; do not reuse keys across different sends or the second send 409s.