Why the webhook is the easy part
Gumroad's sale webhook is reliable and fires fast — that was never the problem. The actual engineering is what happens between "webhook received" and "file delivered." For a static product, that gap is nothing. For anything personalized, it's the whole workflow: read the order's custom fields, run whatever generation step produces the actual file, confirm it built correctly, then attach and send it. I'd rather have that confirmation step than assume generation always succeeds — a silently-failed build with a "you're all set!" email is worse than a slightly slower delivery.
The API gotcha nobody warns you about until you hit it
Building a new product's files is straightforward — upload, get file ids, attach them. Updating the files on a product that's already live and already has sales is a different problem entirely. Gumroad's product-update endpoint treats `files[]` as a full-replacement array, and if any of your existing files are still referenced by a variant's rich content, the API just refuses the update outright rather than telling you how to fix it cleanly. The actual fix is a specific three-step sequence: strip the rich-content file references from every variant first, then replace the file list, then re-attach the new files to each variant's rich content. Skip step one and the whole update bounces with an error that doesn't obviously point at the fix.
The other one: a live product can go missing from your own API
I also hit a case where a product that was very much live and sellable simply didn't show up when I listed products through the API — not a permissions issue, just absent from the response. The product still existed, still took payments, still had a working public page; it just wasn't enumerable the normal way. The workaround was pulling it by its public permalink URL directly instead of relying on the list endpoint to surface everything. Worth knowing before you assume "not in the list" means "doesn't exist."
What actually mattered for reliability
Two things made this dependable in production: never trusting the webhook payload's fields blindly (validate before generating anything — a bad or missing field should fail loudly, not produce a broken file), and keeping my own order log outside of Gumroad's dashboard, so I have a record I can search and re-drive from if a delivery ever needs to be resent manually.
n8n Automation Hub