Guide

Vibe Coding to Paying Users: Where the Paywall Actually Goes

Most advice on converting vibe-coded apps stops at "find PMF" and "share it on Twitter." The harder question is structural: at which exact request does the free user become a paying user? Here is the answer mk0r ships in code, with the routes and HTTP status codes that gate the funnel.

M
Matthew Diakonov
6 min

Direct answer (verified 2026-05-12)

Don't paywall the build. Paywall the moment value crosses something the platform actually pays for: custom-domain publishing, stronger models, persistent infrastructure. In mk0r's source that is two routes returning two different status codes: POST /api/publish returns HTTP 402 with "Subscribe to mk0r Pro to publish to a custom domain" (route.ts:129), and POST /api/chat/model returns HTTP 403 "subscription_required" (route.ts:19). The build itself runs anonymously, against Haiku, with zero gates.

The mistake almost every vibe-coding tool makes

The pattern looks like this. A user lands on the page from an X thread or a Reddit comment, types a sentence, sees the value, and gets stopped by a modal that asks for an email and a credit card before the prototype finishes streaming. That modal kills the funnel. It is the wrong place for a paywall because nothing expensive has happened yet. Generating a small HTML app from a single prompt is cheap. The platform can absorb thousands of those for the price of one paying customer.

The correct frame is the inverse: subsidize the moment of activation, charge for the moment of distribution. Build is the demo. Iterate is the demo. Publish to a domain the user owns is the product. That is the only step that costs the platform real money (DNS provisioning, infrastructure attached to a name a stranger can type into a browser bar) and the only step that correlates with someone wanting their app in front of other humans.

Once you accept that frame, the rest is structural. Where in the request lifecycle does each gate fire? What status code does each gate return? What does the client render in response? Those answers are in the code below.

The two-gate funnel, stage by stage

1

Stage 1: Anonymous build (no gate)

The first prompt runs against Haiku on a Firebase anonymous UID. No email, no card. A real E2B sandbox boots, the VM agent edits files, and the live preview streams. This is the demo and the activation step rolled into one.

Code: FREE_MODEL = "haiku" at src/app/api/chat/model/route.ts:5.

2

Stage 2: Iterate for free (still no gate)

Follow-up prompts modify the running app. The current setting is six turns of unauthenticated iteration, picked because staging telemetry showed shorter gates fired during the first edit cycle and interrupted activation.

Anonymous turn count, tuned empirically. Build cost stays in the cheap-model lane.

3

Stage 3: Sign in (soft identification, still free)

Once the user has seen the product work, the next gate asks for sign-in. This is not the paywall, this is identification so the platform can attach the session to a real account and so the user can come back to their project.

Anonymous checkout is rejected here: src/app/api/billing/checkout/route.ts:24returns 403 "Sign in required to subscribe." You cannot pay until the platform knows who you are.

4

Stage 4: Publish or upgrade (hard paywall)

The user wants a real domain or a stronger model. Both routes check entitlement and return a payment-required status. The client opens the paywall modal, sends them through Stripe, and resumes the original action when checkout returns.

Publish: src/app/api/publish/route.ts:129 returns 402.

Model upgrade: src/app/api/chat/model/route.ts:19 returns 403.

The publish request, traced end to end

Here is what actually happens when a user clicks "publish to my own domain" in an anonymous session. The browser hits the publish route, gets a 402, opens the paywall modal, hits the checkout route, redirects through Stripe, comes back entitled, and retries the publish. Six round-trips, two of which are paywall round-trips, and the user only has to pay once for the entire arc.

Vibe build to first paid action

BrowserAPI: /api/publishStripe / BillingPOST /api/publish action: deploy402 Subscribe to mk0r ProPOST /api/billing/checkoutStripe checkout URLPOST /api/publish (retry, now entitled)200 ok, domain mapped

Two HTTP status codes, two different things they mean

402 (Payment Required) is the status the publish route returns. The client reads the 402 and opens the paywall modal directly, because 402 has only one meaningful remediation: take the user to checkout. The modal collects nothing else.

403 (Forbidden) is the status the model-switch route returns when a signed-in user without billing tries to call Sonnet or Opus. The remediation is the same (subscribe), but the semantic is different: the platform knows who you are, you are authenticated, you are simply not authorized for this resource. That nuance matters when the client decides what UI to render, and it shows up in the source as the difference between an inline upgrade button on the model selector versus a full paywall sheet on the publish action.

Worth noting: anonymous checkout itself is forbidden at src/app/api/billing/checkout/route.ts:24. The route returns 403 "Sign in required to subscribe." You cannot pay without an account, because the subscription has to attach to a stable identity. So the real ordering is: build anonymously, sign in to identify, then pay to publish or upgrade. Three steps, only one of which costs money.

What the user sees at the gate

When the 402 lands, the publish modal switches to its paywall step. The copy is plain: $19/month, cancel anytime, what is included. The page that renders that view is at src/app/subscription-required/page.tsxand it leans on the same three lines of value: unlimited prompts, live VM previews, persistent project history. Nothing about "14-day trial" or "cancel before you are charged." The user knows what they are paying for because they just tried to do it.

That last detail is the whole point. A free trial converts on a clock. A publish-time paywall converts on intent. The first one captures attention. The second one captures payment.

Want to see this funnel in your own stack?

I have spent the last few months tuning where the paywall fires, what status codes the client reads, and how long the anonymous window can be before activation drops. Happy to compare notes on a call.

Frequently asked questions

Why not paywall the first generation?

Because the first generation is the demo. A vibe-coded prototype that someone can build in 30 seconds, watch stream, and iterate on with a follow-up prompt is the entire reason a stranger from an X thread clicks through. Gating it behind a sign-up form turns the first impression into a chore. mk0r's source treats the build as anonymous and free: src/app/api/chat/route.ts accepts an anonymous Firebase UID, src/app/api/chat/model/route.ts:5 sets FREE_MODEL = "haiku" for everyone without billing, and src/core/service-provisioning.ts even provisions a real Postgres, a Resend key, and a private GitHub repo without an email. The first wave of value is the funnel.

Where exactly should the paywall fire?

On something the platform pays real money for, not on the moment of generation. In mk0r's code there are two of those moments. First, custom-domain publishing: src/app/api/publish/route.ts lines 104 to 137 require an active or trialing subscription and return HTTP 402 with the message "Subscribe to mk0r Pro to publish to a custom domain." Second, model upgrade: src/app/api/chat/model/route.ts lines 17 to 25 return HTTP 403 "subscription_required" when a signed-in user without billing tries to switch off Haiku. Both gates correlate with real platform cost (DNS provisioning, Anthropic API spend on Sonnet or Opus). The free path correlates with cost the platform absorbs cheaply.

Why 402 on publish and 403 on model switch?

402 (Payment Required) is the HTTP status that exists specifically for paywalls, and the publish modal in the client has a paywall step that resumes the publish flow after Stripe checkout returns. 403 (Forbidden) on the model route is the right shape for an authorization failure, because once a user is signed in we know who they are and we know they are not entitled. Different verbs, different remediation. 402 prompts payment. 403 prompts upgrade. The clients read the status code and render the right UI without reading the body.

How long should the free build window be?

Until the user has seen the product produce something they care about, not before. mk0r shipped an anonymous-turn gate at 2 turns on May 7, 2026 and raised it to 6 turns the next day after staging telemetry showed the gate was firing during the first iteration loop. Six turns is roughly: initial generation, two or three quick edits, one polish pass. The shape of the right answer is empirical: cut the gate the moment the prompt-to-sign-in step interrupts a user who is still figuring out whether the tool works. There is no universal turn count.

What if the prototype is so simple users do not need to publish?

Then they are not your paying users yet, and that is fine. The funnel converts on intent, not on usage. A user who builds a habit tracker for themselves, exports the HTML, and never comes back is not a churned customer, they are a successful demo. The paying users are the ones who built something they want other people to use, which means they want a real URL on a real domain. That sub-population is what the publish paywall captures. Everything before that step is brand and acquisition.

Does anonymous mode hurt LTV by training users to expect free?

Only if you let the anonymous experience be the whole product. mk0r's source intentionally limits anonymous sessions to the free model (Haiku at chat/model/route.ts:5) and rejects anonymous checkout requests outright (src/app/api/billing/checkout/route.ts:24 returns 403 "Sign in required to subscribe"). The anonymous user can build forever, but the moment they want a stronger model, persistent infra they own, or a real domain, they have to identify themselves. The funnel is build for free, sign in for context, subscribe for output.

How is this different from a free trial?

A free trial gates the wrong thing in two directions. It puts a credit card up front, which kills the unauthenticated demo. And it converts on a clock instead of on value, which means the user who has not yet hit the paid moment churns silently. On May 7, 2026 mk0r removed TRIAL_DAYS from src/lib/stripe-server.ts and replaced it with the publish paywall. Build is free forever. Publishing to a custom domain is the paid step. The conversion event is now tied to a real action the user wanted to take, not to a calendar.

What metrics actually predict conversion in this funnel?

Turns per session before the publish modal opens, time from first prompt to first publish attempt, and the ratio of publish attempts to successful checkout completions. Those three numbers correspond to depth of engagement (turns), intent to ship (publish attempt), and friction at the gate (checkout completion rate). Vanity metrics like signups or trial starts are noise here because the paywall is on publish, not on access. A user who has not tried to publish has not entered the funnel yet, no matter how many turns they have run.

mk0r.AI app builder
© 2026 mk0r. All rights reserved.

How did this page land for you?

React to reveal totals

Comments ()

Leave a comment to see what others are saying.

Public and anonymous. No signup.