Engineering

Why Zapier Zaps Run Twice
(And How to Stop It)

6 min read
idempotency idempotency key deduplication at-least-once delivery webhook retries Zapier Webhooks duplicate requests exactly-once execution

You checked Stripe. The customer was charged twice. No error logs. No failed Zaps. Just… duplicate money gone.

Your automation "worked." And still caused damage. This isn't a Zapier bug. This is how distributed systems behave.

If you're seeing similar issues with Make.com scenarios or Stripe webhooks, you're experiencing the same fundamental problem in distributed automation: retries + non-idempotent actions.

🚨 The Real Problem: No Exactly-Once Guarantee

Most people assume: "If my Zap runs, it runs once."
That is not how automation platforms work.

Zapier (like Make, n8n, and most workflow tools) uses retries to handle failures. If a step times out, returns a 5xx error, or Zapier loses connection — it retries. This is called at-least-once delivery.

  • From Zapier's perspective: The step might have failed. The safe move is to try again.
  • From your system's perspective: The first request may have actually succeeded. The retry becomes a duplicate action.
Zapier guarantees at-least-once delivery, not exactly-once. That difference causes nearly all duplicate automation bugs.

🔎 Related concepts you’ll keep running into

  • Idempotency: repeating the same request produces the same outcome (no extra side effects).
  • Idempotency key: a stable request identifier used for deduplication.
  • Webhook retries: upstream systems (Stripe, Shopify, etc.) re-deliver the same event when they can’t confirm receipt.
  • Deduplication store: cache/database where you store “I already processed this key” + the result.
  • Exactly-once execution: the *effect* happens once, even if messages arrive multiple times.

💥 Why This Becomes Dangerous Fast

When actions aren't idempotent (safe to repeat), automatic retries turn into real-world damage:

Action What happens on retry
Charge a customer Double charge 💸
Send email Duplicate email 📧
Create DB record Duplicate rows 🗄
Trigger AI step Infinite loops 🤖
Post to Slack Message spam 📢

🧠 "But My Zap Looks Fine…"

That's because duplicates don't come from logic errors — they come from infrastructure reality.

Network latency, temporary API slowdowns, webhook delivery delays, or rate limiting can all trigger a retry. In distributed systems, retries are not an edge case — they are expected behavior. If your automation runs long enough in production, retries are not "if" — they're "when."

❌ Common "Fixes" That Don't Work

People try to patch this inside Zapier. It usually fails.

  • Add a delay: Retries still happen later.
  • Use a Filter: Race conditions often bypass filters.
  • Check DB first: If two requests hit at the same millisecond, both read "not found" before the first one writes.

✅ The Correct Architecture

Reliable automation needs an idempotency layer. That means the same action request can be safely repeated without creating side effects.

Instead of Zapier → Your API, you do:

Zapier → Idempotency Layer → Your API

The layer accepts an idempotency key, stores the result of the first execution, and blocks duplicates. Even if Zapier retries 5 times, your action runs once.

🔧 Example: Making a Zap Idempotent

In Zapier, add a first step that calls OnceOnly /v1/check-lock. Only continue if the response is status=locked.

POST https://api.onceonly.tech/v1/check-lock
Authorization: Bearer once_live_***
Content-Type: application/json

{
  "key": "zapier:{{zap_meta_timestamp}}:invoice:{{user_id}}",
  "ttl": 86400,
  "metadata": {
    "source": "zapier",
    "user_id": "{{user_id}}"
  }
}

If Zapier retries with the same key, OnceOnly returns status=duplicate so you can skip the side-effect step (or fetch the prior result from your system).

Response example:

{
  "success": true,
  "status": "locked",
  "key": "zapier:1706101800:invoice:u_123",
  "ttl": 86400,
  "first_seen_at": null
}

On retry, the response becomes {"success":false,"status":"duplicate",...} so you can stop the Zap from creating a second invoice.

🤖 Why AI Automations Make This Worse

AI agents don't behave like normal software. They retry when uncertain, repeat actions after partial failures, and loop when prompts are ambiguous.

Imagine this flow: AI agent → "Send follow-up email" → timeout → retry.

Without idempotency, 3 emails go out. The customer marks you as spam. AI systems increase retries, which means the risk of duplicate side-effects explodes.

🚀 The 5-Minute Fix

Zapier handles triggers. Your API handles logic. But no one is handling execution safety. That's the gap.

Instead of rebuilding your backend, add an idempotency layer built for automation tools. It enforces at-most-once execution per key so your Zaps stay predictable — even when the internet isn't.

Get an API Key for Free

Takes 2 minutes to integrate with Zapier Webhooks.

❓ Frequently Asked Questions

Why does Zapier run twice?

Zapier is built for at-least-once delivery. If a step times out, returns a 5xx, hits a transient network issue, or Zapier can’t confirm the result, it retries. If your endpoint already succeeded, that retry becomes a duplicate request unless you add idempotency (deduplication using an idempotency key).

Why does Zapier retry actions?

Retries are how distributed systems stay reliable. When Zapier sees timeouts, intermittent failures, or ambiguous outcomes, it retries to avoid data loss. You can’t treat retries as rare — design your actions to be idempotent.

How can I tell if my Zap is creating duplicates?

Look for: duplicate database records with identical timestamps, customers reporting double charges or emails, Zaps that show one successful run but create multiple side effects, or tasks that appear to execute twice in your logs.

What's an idempotency key and how do I generate one?

An idempotency key is a stable identifier for an action request. On retries (or webhook redelivery), the server uses that key for deduplication and returns the original result. In Zapier, combine fields like {{zap_meta_timestamp}}-{{user_id}}, or use a stable event ID from the trigger payload (best for webhook retries).

Does this happen with other automation platforms?

Yes. Make.com, n8n, and most automation platforms use at-least-once delivery. The same solution applies across all platforms: idempotency + deduplication at the action boundary.

Can Zapier Filters prevent duplicate runs?

Not reliably. Filters decide whether a run should proceed based on data conditions, but they don’t stop retries. If a step fails after passing the filter, Zapier can retry the same step, and you still get duplicates unless the action is idempotent.

What about webhook-triggered Zaps?

Webhook-triggered Zaps face double exposure: the sender (like Stripe) may do webhook retries, and Zapier may also retry processing. This is why you want the idempotency key to be based on a stable event ID from the webhook payload.