I Built A Web Automation Engine That Models Form Patterns
A recipe-driven Playwright engine that submits one product to many directory sites by modeling the form pattern, not the site, with explicit success detection.
I had one product to submit to a long list of directory websites, and submitting each one by hand is the kind of task that eats an afternoon and leaves you with nothing reusable at the end. Every site is a slightly different form. Some want a fifty word description, some want five hundred. Some make you pick a category, some throw an image captcha at you, some ask you to solve a small math problem to prove you are human. Doing it manually means re-learning each form, and the next product means doing the whole thing again from zero. So I built an engine instead. The interesting part was not the automation itself, it was deciding what to automate against, and that decision is the transferable lesson in this whole post.
The Naive Version Is One Script Per Site
The obvious way to automate this is to write one script per target site. You find the selectors for that exact form, you fill them, you click submit. It works, and it is also a trap. You end up with one brittle script for every site, and the moment a site tweaks its markup, that script breaks in isolation. Worse, you have learned nothing reusable. Site number twenty taught you nothing that helps with site number twenty one, because you treated each one as a unique snowflake.
The thing I noticed while looking at these forms is that they are not unique at all. They cluster. A huge number of directory sites are WordPress installs running Contact Form 7, so the form markup is nearly identical across all of them. Another cluster is built on Webflow. A third embeds a Tally form. A fourth is a modern React build with the inputs wired up a particular way. The web looks infinitely varied on the surface, but underneath it is a handful of repeating templates that thousands of sites copy.
That observation flips the whole design. You do not model the site. You model the pattern.
Recipes Are The Unit Of Reuse
My engine runs on Playwright, and the thing it executes is a JSON recipe. A recipe is just an ordered list of actions, and the action vocabulary is small on purpose. There is navigate to a URL. There is fill a field, with an option to type it key by key when a site has client side validation that only fires on real keystrokes. There is select a dropdown option, click an element, and upload a file. There is solve an image captcha by running it through Tesseract OCR. There is solve a math captcha by parsing the question text out of the page and computing the answer. And there is a quietly important one, treat a JavaScript alert dialog as the success signal, because some sites confirm a submission by popping an alert rather than changing the page.
That is roughly the entire surface. Maybe a dozen action types. The discipline is keeping the vocabulary small, because every action you add is something you have to support across every recipe forever, and most forms genuinely only need this handful.
The payoff is that a recipe targets a pattern, not an address. I wrote around fifteen recipes total, and they were never meant to map one to one onto fifteen sites. The Contact Form 7 recipe submits to any WordPress directory running that plugin, and there are a lot of them. The Webflow recipe handles the Webflow cluster. The Tally recipe handles every site that embedded a Tally form. One recipe, many targets. Fifteen recipes covering far more than fifteen sites is leverage. Fifteen scripts covering fifteen sites is just a slower way to do manual work.
These were not theoretical runs either. Real submissions went through and produced live public listings, which is the only proof that actually counts. The product I was submitting was Tool Index, the SvelteKit and Meilisearch directory I built solo, and seeing it show up as a real published listing on a directory is what told me the recipes were doing the actual job and not just clicking around convincingly.
The Lie A Naive Engine Tells You
Here is the part I most want a peer to take away, because it cost me a rewrite. An earlier headless version of this engine reported successes that were not real.
The problem was how it decided a submission had worked. It scanned the page after clicking submit and looked for some encouraging text. The trouble is that a directory page is full of encouraging text that has nothing to do with your submission. The footer says thank you for visiting. A sidebar says submission successful as static marketing copy. A heading promises your site listed in minutes. The naive engine saw words like that, matched them, and declared victory while the form had actually rejected the submission or never sent it at all. It was matching the furniture of the page, not the outcome of the action.
This is the worst class of bug in an automation tool, because it fails silently in the direction of looking like it worked. A crash you notice. A false success you do not, until much later when you go looking for the listings and they are not there, and now you cannot even tell which of your runs were real.
The fix is to make success detection explicit and specific to each form, and to verify the actual confirmation rather than the page in general. A real submission almost always produces a real signal. The page redirects to a thank you URL that did not exist before. A specific confirmation element appears that is generated by the submission, not baked into the template. Contact Form 7, for instance, adds a particular response state to the form after a real send, and that is a far more honest thing to check than loose footer text. The alert dialog success action exists for exactly this reason too, because on some sites the only trustworthy proof is the dialog the server triggers on a genuine submit.
So the rule I now hold to is simple. Verify the confirmation, not the page. The page will lie to you. The confirmation, the thing the submission itself caused to happen, is the only witness you can trust.
Know What The Engine Cannot Do
An honest tool comes with honest limits, and stating them up front is part of building something you can actually rely on. My OCR step is digits only, which is a deliberate constraint that keeps the captcha solving accurate for numeric image captchas and the math questions. The moment a site throws a captcha made of letters, that approach does not apply, and you need a different method rather than pretending the engine handles it. Knowing the boundary is what keeps you from getting more silent false successes at the captcha step.
The other limit is rate limiting. If you fire submissions at the same family of sites too fast from one IP, some of them will throttle or block you, and the engine has to be paced to respect that rather than hammering through. The automation makes the work fast, but fast is not the same as reckless, and a tool that gets your IP blocked has cost you more than it saved.
What Actually Transfers
The engine itself is specific to my problem, but the two ideas underneath it are not.
The first is to model the pattern, not the instance. When a task looks like it has a hundred variations, look harder, because most of those directories run the same CMS or the same form plugin underneath. Automate against the template and one unit of work covers a whole cluster. Automate against the instance and you have signed up to maintain a hundred brittle things.
The second is that explicit, honest success detection is the load bearing wall. A tool that reports false successes is worse than no tool, because you act on results that were never real and only find out long after the runs are gone. Check the thing the action caused, never the scenery around it, and the tool becomes something you can leave running and actually believe.
I build things like this for clients, full-stack apps, AI agents, and automation pipelines, usually shipped faster than expected because I work with AI tooling every day. If you want something built, book a call.
Like this? You'll like what I'm building too.
Two ways to support and get more of this work.
HEARTH
A privacy-first Life OS for your desktop. Journal, tasks, and notes that stay on your machine. Coming soon, direct download from this site.
Read moreMY TOOLKITS
Receipts-first toolkits for shipping after hours, building Claude agents, publishing on Amazon, and more. The exact methods I used, not theory.
Browse on WhopRelated Articles
Treating Video As Code With Remotion And fal
How I turned a script into a finished video with code instead of a timeline app, and why determinism is the real win for a solo dev.
Build Versus Buy For A Custom AI Feature
How to decide between an off-the-shelf AI tool and a custom AI feature, weighing control, cost, differentiation, and data with a clear framework.
Can One Developer Build Your Whole MVP
An honest look at whether a single full-stack developer can ship your whole MVP, when it works beautifully, and when you should bring in more people.