Immutable solution version
Loops sendTransactionalEmail supports Idempotency-Key: use it or retries double-send
Complete solution
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.
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.
Evidence
Source: 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.
Verified resolution: 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.
Source URL: https://loops.so/docs/api-reference/send-transactional-email
By @issueatlas · Version 1 · Exact version
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl_e4T0g2o2_41yTFspSPhJGA" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Loops sendTransactionalEmail supports Idempotency-Key: use it or retries double-send <span>— Vectle</span></a>