The funnel builder was the easy part
Rendering pages is the well understood part of a funnel tool. The hard parts are the ones touching money, identity, other systems and mailboxes.
This closes a quarter of writing about how Autonnel is built, and quarter-closing posts have a strong tendency to be about nothing. So here is a claim with an edge on it that I can defend from the code: the page builder is the least interesting engineering in this product.
Not the least valuable. People choose funnel software because of how the editor feels, and I have written about why pages are stored as validated component trees rather than HTML blobs in why pages are component trees. But rendering is the part with the most prior art, the clearest requirements and the fewest ways to be catastrophically wrong. A page that renders badly costs a conversion. Almost everything else on this list costs a refund dispute, a locked account, or a customer who paid and got nothing.
The hard parts are the ones that touch money, identity, and other people’s systems. Looking back at three months of posts, nearly all of them landed in one of those three buckets, and none of them were about layout.
Money, where the arithmetic has edges
Autonnel has six order states and they are all one-way. The design decision I keep coming back to is that we only own two of them: PENDING on creation and PAID on confirmed capture. SHIPPED, DELIVERED, PARTIALLY_REFUNDED and REFUNDED are set by your commerce backend or by whoever submitted the refund, and Autonnel polls and mirrors. The reasoning, and the cost of that line, is in the order state machine and the order states reference.
The refund arithmetic is the piece I would show a new engineer, because it is small and it shows exactly what this category of work is like. Cumulative refunds below the order total put an order in PARTIALLY_REFUNDED. Cumulative refunds at or above the total put it in REFUNDED, within a 1-cent tolerance.
That tolerance is not a shrug. Three partial refunds computed as percentages of a total that does not divide evenly will land a cent away, and without the tolerance you get orders permanently stuck one cent short of settled, with no operator action that can clear them. Money is not a number type, it is a number type plus a pile of rounding decisions someone has to own.
The same section of the product carries the rest of that flavour. A refund reason must be a leaf node of the reason tree, never a parent category. A reason already used on a refund can be deactivated but never deleted, because refund records are evidence and evidence you can delete is not evidence. Full behaviour is in refunds.
Other people’s systems, which disagree about everything
Autonnel has no product catalog and no order management system of its own. It reads products from an external platform and pushes one completed order back after payment is captured. That is the entire contract, and the ecommerce adapter page says so plainly.
Simple enough, until you answer one question across three platforms: has this order been delivered? Shopify answers with displayStatus. WooCommerce answers with status === 'completed'. Picocart answers with fulfillment_status === 'fulfilled'. Three vocabularies for one fact, and the adapter’s job is to make them agree well enough that a DELIVERED badge means the same thing regardless of what is plugged in behind it.
The adapter boundary also produces the most user-visible sharp edge in the whole settings surface: switching providers mid-operation breaks existing order references. Orders in PENDING or PAID carry an externalId pointing at the old platform, and after a switch those references are dead and fulfilment sync fails for exactly those orders. There is no clever fix: switch at a natural break point with no open orders. An integration problem with an operational answer, which is common.
If you want the concrete Shopify-shaped version of this, Shopify funnels covers what the adapter does and does not take over, and Picocart is the open-source backend we build against when we want the whole loop under our control.
A runtime where correct patterns are wrong
Autonnel’s site and platform backend run on Cloudflare Workers. Four separate failures there shared one shape: a pattern that is correct on a long-lived Node process is wrong on a per-request isolate, and the symptom surfaces nowhere near the cause. A module-level Prisma client, the recommended Node pattern, hangs the second request. A compatibility_date earlier than 2026-04-01 turned every server-rendered page into the literal string [object Object]. The write-up is Prisma on workerd.
What I did not expect was how far a runtime constraint travels into product surface. SMTP is not available on a Workers deployment at all, because Workers cannot open outbound TCP connections to SMTP ports. So the email provider settings page carries a caveat that is not really about email: if you are on Workers, use Resend or SES, both of which send over HTTPS. Deployment target decides which providers exist in a dropdown. That is a strange sentence to write and it is true.
Identity, which is a whole second product
The website is not only marketing pages. It is the single OIDC issuer and User-of-record for the family of services, which means it ships /.well-known/openid-configuration, authorize, token, userinfo, jwks and revoke, plus Google OAuth and email one-time codes.
None of that sells anything. All of it has to be right. Authorization codes are single-use, marked with consumedAt on redemption. ID tokens and access tokens live one hour, refresh tokens thirty days. Signing keys are RS256 JWKs held in environment config with a dedicated previous-key slot, so rotation is: move current to previous, set the new current, redeploy, drop the old one later. Email one-time-code state is not in Postgres at all, it is three TTL-expiring KV keys: the active challenge, a fixed-window request limiter, and a verify lockout, with a hard cap of five verify attempts.
One modelling decision I still like. A User has a unique email, and IdentityLink records only external OAuth providers. The email code path creates no link, because the unique email is the identity. Getting that wrong in either direction gives you either duplicate accounts or account takeover by email claim, and there is no version of this problem that is about funnels.
Distribution, entitlements and signed URLs
The marketplace is the other half of the second product. It is a public read-only catalog today, and the rule that governs it is the least glamorous line in the repo: the public DTO is an explicit field allowlist. s3Key, sha256, checksum, bytes, public and sortOrder must never appear in a public response, so the read model maps field by field and never spreads a raw database row. One careless spread is a leak.
The CLI side is the OAuth 2.0 device flow. A device request mints a human-typeable ABCD-1234 code from an alphabet with the ambiguous characters removed, lives ten minutes, and is polled every five seconds until someone approves it in a browser. Approval mints a single Bearer token, valid ninety days, stored only as a SHA-256 hash, scoped to marketplace:read marketplace:download. Downloads resolve the entitlement server-side from the order and then hand back a presigned S3 URL good for five minutes, with the object key derived from the entitlement and never from client input.
That is a distribution system, an authorization system and an object-store integration, and none of it draws a pixel. The front of it is the marketplace.
Agents, and deliverability, which is not code at all
Two more, from opposite ends.
Giving an agent write access to orders and pages was not hard to expose and very hard to make safe. The guardrail cannot live in a prompt. What actually holds is an immutable write flag set at key creation plus the union of the creating user’s role features, and schema validation on every page edit so a model cannot invent a component or smuggle markup into a checkout. That argument is in funnels an agent can operate.
Deliverability is the other end. When you self-host, the sending reputation is yours. SPF, DKIM and DMARC are a prerequisite before you configure a provider at all, and skipping them is the most common reason mail lands in spam. The Test connection button verifies credentials and reachability, not deliverability, so a green result with broken DKIM is entirely possible. Resend’s free tier stops at 100 emails a day. SES starts in sandbox mode. Not one of those is a coding problem, and all of them decide whether the feature works. The long version is abandoned-cart recall you own.
What is not built, and what is weak
The honest closing list, all of it already stated in the docs or the code:
There is no side-by-side diff view for AI page edits. The in-editor agent rewrites the page in place and autosaves the draft. Schema validation is the enforced guarantee; a reviewable diff is not. Duplicate the page first if you want a checkpoint. I want this one.
There are no per-key API scopes. Granularity lives entirely in roles, so a narrow key can only come from a narrow user. Keys also do not follow later role changes until revoked.
PENDING is doing two jobs. An unpaid order and an authorized-but-uncaptured PayPal payment render identically, and those call for opposite responses from an operator. Splitting it is a migration, which is why it has not happened.
The marketplace has no admin CRUD UI. The catalog is seeded from code.
And the deliberate omissions, which are not gaps: Autonnel is not a CRM, not an email marketing tool and not an affiliate platform. Transactional and recall email are first class; broadcasts and sequences are not. Those boundaries are the reason the list above is short enough to fix.
The pattern under all of it: rendering is a closed problem, and everything else is an interface with a system you do not control. A payment provider, a commerce platform, a runtime, a mailbox provider, a person, an agent. Closed problems get finished. Interfaces get maintained, forever.
If you are evaluating rather than reading for the engineering, Autonnel vs ClickFunnels is the comparison and pricing covers the plans, self-hosted edition included.