An AI tool that writes personalized wedding, best man, maid of honor, and retirement speeches. Visitors get a free preview and pay a one-time fee to unlock and download the full speech. No accounts, no database — the whole thing is a static site plus two serverless functions.
- Visitor fills out a short form (occasion, names, relationship, a few memories, tone, length).
api/generate.jscalls the Anthropic API to write the speech, returns a free preview, and sends the full text back to the browser encrypted (nothing is stored server-side).- Visitor clicks "Unlock," pays via a Lemon Squeezy checkout overlay.
api/unlock.jsverifies the payment with the Lemon Squeezy API, then decrypts and returns the full speech for the browser to display, copy, or download as PDF/txt.
You need three things before this can go live: an Anthropic API key, a Lemon Squeezy account with one product, and a Vercel deployment. None of these can be created on your behalf — they involve identity/payment verification only you can complete.
- Create an account at console.anthropic.com.
- Add billing and set a spend limit (Settings → Limits) — this is your real cost control, since the free preview has no other rate limiting. $20–30 is plenty to start.
- Create an API key (Settings → API Keys). You'll paste this into Vercel as
ANTHROPIC_API_KEY.
- Create a store at lemonsqueezy.com and complete payout/identity verification (needed before you can receive money).
- Create one product, e.g. "Speech Unlock," priced around $8.99, type "Single Payment."
- On that product's variant, enable "License Keys" and set the
activation limit to 1. This is not optional —
api/unlock.jsrelies on Lemon Squeezy's license-key activation (one-time use per order) to stop a single paid order from unlocking unlimited speeches. Without this enabled, unlock requests will fail withlicense_not_ready. - Open the product's variant and copy its Buy Link — paste it into
config.jsasSPEECH_WRITER_CHECKOUT_URL. - Under Settings → API, create an API key — this becomes
LEMONSQUEEZY_API_KEYin Vercel. - Note your Store ID and the product variant's Variant ID (visible
in the dashboard URLs or via the API) — these become
LEMONSQUEEZY_STORE_IDandLEMONSQUEEZY_VARIANT_IDin Vercel. They're optional but recommended: without them,api/unlock.jsonly checks that an order was paid, not that it was paid for this product.
-
Push this repo to GitHub (or connect it directly) and import it into Vercel — no build step needed, it's static +
/apifunctions. -
In the Vercel project's Settings → Environment Variables, add:
Variable Value ANTHROPIC_API_KEYfrom step 1 ANTHROPIC_MODELoptional, defaults to claude-haiku-4-5-20251001UNLOCK_SECRETany long random string (e.g. openssl rand -hex 32) — used to encrypt speeches in transitLEMONSQUEEZY_API_KEYfrom step 2 LEMONSQUEEZY_STORE_IDfrom step 2 (optional but recommended) LEMONSQUEEZY_VARIANT_IDfrom step 2 (optional but recommended) -
Edit
config.jswith your realSPEECH_WRITER_CHECKOUT_URLand price, commit, and deploy. -
Test before going live: switch the Lemon Squeezy store to Test Mode, run through the full generate → checkout → unlock flow with a test card, and confirm the full speech unlocks. Then switch the store to Live Mode.
You're launching on Vercel's free *.vercel.app subdomain — no domain
purchase needed to start. Only buy a custom domain later if the product
shows real traction.
npm i -g vercel
vercel dev
This serves the static pages and /api functions locally, reading the same
environment variables from a .env.local file (see vercel env pull).
A paid order alone doesn't prove which speech it should unlock, and nothing
stops a client from generating unlimited free preview/blob pairs from
api/generate.js. So api/unlock.js doesn't just check "is this order
paid" — it also requires activating that order's Lemon Squeezy license
key (see setup step 3 above), which Lemon Squeezy allows exactly once per
order when the activation limit is set to 1. The first unlock request for a
given order succeeds; any replay of that same order ID fails with
order_already_redeemed. This pushes single-use tracking onto Lemon
Squeezy's own infrastructure instead of needing a database here.
index.html,best-man-speech.html,maid-of-honor-speech.html,wedding-toast.html,retirement-speech.html— landing pages sharing one widget, each targeting a different search intent.app.js/style.css— the shared form, checkout, and unlock UI.config.js— the one file you edit with your Lemon Squeezy checkout URL.api/generate.js— calls Anthropic, returns a preview + encrypted full text.api/unlock.js— verifies payment with Lemon Squeezy, decrypts full text.lib/crypto.js,lib/prompt.js— shared helpers (not deployed as routes).