Gumroad's Ping sends a POST to the URL you save in your Gumroad settings (Settings, Advanced, Ping) when a sale happens. If nothing arrives, go through this list:
localhost and private IPs can't be reached from Gumroad. Use a public https:// URL, or a tunnel while testing.application/x-www-form-urlencoded, so express.json() sees an empty body. Use express.urlencoded({ extended: true }) (or request.form in Flask).200 quickly and do slow work afterwards.Pings aren't signed. Before granting access, confirm the sale yourself. For example, check the buyer's license key with Gumroad's public license verification endpoint, or look the sale up through the Gumroad API using its sale_id.
app.post('/gumroad', express.urlencoded({ extended: true }), (req, res) => {
const { sale_id, product_id, email, license_key } = req.body;
res.sendStatus(200); // acknowledge fast
// then verify license_key / sale_id before granting access
});
The guide is Stripe-only. The kit is a self-hosted Cloudflare Worker relay that adds retries, replay and a dead-letter list in front of your endpoint. Its built-in schemes cover Stripe, Lemon Squeezy, Shopify, Paddle Billing, Square, Twilio (status callbacks) and GitHub. Gumroad pings aren't signed, so they aren't supported without a small change to the kit's MIT-licensed code.
Fix Your Stripe Webhooks guide · Self-Hosted Webhook Relay Kit. Code LAUNCH40 = 40% off until Sun Oct 11, 6:40 AM MT.
Free download: Webhook debugging cheat sheet.
Fighting webhooks on more than one platform? The Fix Your Stripe Webhooks guide ($9) covers raw-body and signature problems in depth, and the Self-Hosted Webhook Relay Kit ($19) adds retries, replay and a dead-letter list in front of any endpoint.