n8n logon8n Automation Hub
Build breakdown

When One Gumroad Webhook Fires for Every Product on the Account

Build breakdown · n8n workflow · August 26, 2026

A real customer bought a product that had nothing to do with this workflow, and a few seconds later a lead-gen automation sent them a welcome email built for a completely different business, plus a Telegram alert that read "New subscriber: ... for unknown in unknown." Nothing had crashed. The workflow just didn't know it wasn't supposed to run at all.

Workflow at a glance
  1. A Gumroad sale Ping webhook is account-wide by design — every registered URL fires for every sale, not just its own product
  2. A lead-gen workflow had this webhook wired straight into "append subscriber, send welcome email, notify Telegram" with no product check
  3. A real purchase of an unrelated product produced a wrong welcome email and a garbled Telegram message
  4. Added an "Is Known Lead-Gen Product?" IF node right after the format step
  5. Unknown products now dead-end at an immediate success response — known products run the full chain unchanged
Screenshot of the actual n8n workflow canvas: the B2B lead-gen feed's subscribe webhook chain, with the Is Known Lead-Gen Product? gate right after Format Subscriber

The actual workflow canvas, straight from my n8n instance.

What a Gumroad sale webhook actually is

Gumroad's `resource_name=sale` Ping is registered once per seller account, not once per product. Every URL you've ever pointed at that Ping gets called for every sale across every product in the shop — a $9.99/month lead-gen subscription and a one-time PDF planner purchase both hit the exact same webhook. The workflow already had a `PRODUCT_MAP` lookup with a graceful `{niche:'unknown', location:'unknown'}` fallback for permalinks it didn't recognize, which looked like handling for this — but the fallback only filled in blanks. Nothing actually stopped the run when the permalink wasn't a real match.

Where the wrong data actually went

An unrecognized sale still walked the full chain: format the subscriber row, append it to the leads sheet, send a "welcome, here's your lead feed" email, post a Telegram notification. For a real product this account sells, that's fine — that's the point of the workflow. For anything else, it meant a wrong-context welcome email landing in a real customer's inbox and a Telegram message that named neither the product nor the buyer's actual purchase, because the fields it depended on had all fallen through to the unknown default. The bug had been flagged as a known gap during an earlier build and left alone as out of scope — it turned into an actual complaint the next time someone bought something unrelated.

The fix: fail closed, not gracefully

The format step now also returns a plain boolean — whether the permalink matched a real, known product — instead of just quietly substituting placeholder text. Right after it, an IF node checks that boolean directly. A known product continues into the append/email/Telegram chain exactly as before. An unknown one routes straight to the webhook's success response and stops — Gumroad still sees `{success:true}` either way, so nothing on Gumroad's side needs to know or care that the workflow silently declined to act.

Why the fallback data wasn't the actual problem

The instinct when a webhook receives data it doesn't recognize is to make the unknown case degrade gracefully — show "unknown" instead of crashing. That's the right instinct for data that's going to be displayed. It's the wrong instinct for a workflow deciding whether to act at all. A placeholder value that flows all the way through to a sent email or a fired notification isn't graceful degradation — it's still doing the thing, just with worse inputs. The fix wasn't better fallback text, it was a gate that decides membership before anything with a side effect runs.

Testing both directions before trusting it

Verifying this meant firing the webhook with a known permalink and confirming the full chain still ran unchanged, then firing it again with an unrelated one and confirming the run stopped at the IF node with nothing downstream executing. Only checking the fix on the case you're trying to prevent isn't enough — a gate that blocks the bad case but also silently blocks half the good ones is a different bug wearing the same shape.

Got a webhook that fires for more than you think it does?

I audit n8n workflows for exactly this kind of scope leak — webhooks, triggers, and integrations that quietly process more than the one thing they were built for.