Gumroad's auth flow is hostile to automation. Here's the exact chain that works. A developer detailed the exact browser-automation chain required to create Gumroad products, including password resets, email-based 2FA, and React form handling. The developer shared workarounds for Gumroad's lack of a public API, such as using native value setters and parsing the 2FA token from the email subject line. The post highlights the fragility of automating React-based forms and the need for persistent browser profiles. I needed to automate Gumroad product creation — log in, create a product, set the price, upload a file, write the description, publish. Gumroad has no public API for this. The only option is browser automation. Here's what I had to get right, in order, and where each step breaks if you're not careful. The account had a password I didn't know. I triggered a reset from the login page. Gumroad sends a reset email with a link. The link expires fast — I don't know the exact TTL, but it was under 30 minutes. I used the AgentMail MCP to read the email and extract the reset link. The link is a Gumroad URL with a token parameter. Navigating to it shows a password reset form. Where it breaks: If you try to fill the new password field with input.value = 'newpassword' , React won't register the change. The form will submit with an empty password. You need to use the native value setter: js const nativeSetter = Object.getOwnPropertyDescriptor HTMLInputElement.prototype, 'value' .set; nativeSetter.call passwordInput, newPassword ; passwordInput.dispatchEvent new Event 'input', { bubbles: true } ; This is because React overrides the value property on inputs with its own setter that tracks changes via a value tracker. The native setter bypasses React's tracker, and the input event tells React to sync state. After password reset, logging in triggers 2FA. Gumroad's 2FA is email-based — not TOTP. There's no authenticator app. They email you a 6-digit token. The token appears in the email subject line: "Your authentication token is 126874" . This is convenient — you don't need to parse the email body. Just grab the subject, regex out the digits. js const subject = "Your authentication token is 126874"; const token = subject.match /token is \d+ / ?. 1 ; // "126874" Where it breaks: The email takes 5-15 seconds to arrive. If you check the inbox immediately after submitting the login form, you'll get the previous email or nothing . Wait at least 10 seconds before polling. After 2FA, you're logged in. The session cookie is set. As long as you don't close the Chrome instance or clear cookies, you stay logged in across navigations. I'm using a dedicated Chrome profile --user-data-dir so the session persists across script runs. Without this, every script execution would require a fresh login + 2FA cycle. Navigate to https://gumroad.com/products/new . The form has a text input for the product name and a price field. Fill both, click "Next: Customize". Where it breaks: The price input is type="number" . The native setter trick works, but you need to pass a string, not a number. nativeSetter.call priceInput, '15' works. nativeSetter.call priceInput, 15 may not trigger the change event correctly. After clicking "Next", you're redirected to https://gumroad.com/products/{id}/edit . This page has: The description editor is a contenteditable div. You can set its innerHTML directly — Gumroad's editor reads from the DOM, not from React state: js const editor = document.querySelector ' contenteditable="true" ' ; editor.innerHTML = '