Inspiration

Cash used to carry stories. People wrote phone numbers on dollar bills, stamped them, and logged their serials on Where's George? to see where they went next. A bill passed through hundreds of hands and kept a little of each one.

Digital money carries nothing. You tap, it moves, it's gone. The most-traveled thing most of us touch has no memory at all.

We wanted a digital dollar with a passage: one that travels hand to hand, carries a story written by every hand it passes through, and can prove every step of the trip. The surrealists' exquisite corpse gave us the writing game, where each player adds a piece without controlling the whole. PayPal gave us the proof. If the platform sends every payment itself, every pass of the buck is a real payment that PayPal confirms, and we can open the books.

What it does

Four Word Pass is a $1 PayPal payment that's never spent, only passed. Here's the life of one bill.

It's born. Someone pays $2 to start a story: $1 rides with the bill and $1 keeps the lights on. They write the first four words, the Foreword. AI gives the bill a name, a voice and a few quirks. (One of ours is Wendell the Damp: "Rained on in six countries. Still not dry.") Then they name the first holder by email, or release the bill to the wild, where any stranger can claim it.

It's passed. The holder gets an email and opens the link. There's no account, no app and no PayPal login. They add exactly four words that count. Little words like the, a, to, of ride free so grammar still works, but "and then the lights dimmed" gets "That's 2 content words. The buck demands four." Stuck? AI offers three continuations, and the holder picks one or ignores them. Then they pass the buck, and the platform sends the next holder $1 through PayPal Payouts. Their words join the story only when PayPal says the dollar landed. Joining is free, and a hop takes about a minute.

It remembers. After every hop the bill writes a diary entry in its own voice about the new words and the city it just left. Its passport gets a stamp with the city, the date and an ink illustration painted for that place. A map, The Passage, traces it city to city.

It gets needy. Sit on a bill and it nags you: needy at one day, dramatic at three, theatrical at seven. Not for you? One link sends it back to whoever passed it to you, and they pick someone else.

It branches. Don't like where a story went? Fork it from any hop into a new bill. Bills keep a family tree, and anyone can follow one by email.

It ends. The creator can write the finale whenever they like. A bill that sits still for 14 days is declared lost. It writes its own eulogy and a short closing, always labeled as the bill's.

Two rules hold it together

People write the story. AI plays the bill. The AI names the bill, writes its diary, nags its holders, paints its stamps and offers suggestions a person has to accept. It never moves money, and it never writes a hop. The only AI text that can enter a story is a lost bill's closing, which is labeled and capped at about 5% of the story:

$$ \text{closing words} = \min\big(12,\ \max(1,\ \lfloor 0.05 \cdot w \rfloor)\big) $$

where \( w \) is the number of content words in the story.

The story moves only when the money does. Clicking a button never advances a bill. A payout that PayPal confirms does. The money drives the AI too: a landed payout gets a diary entry and a stamp, money that sits still gets nags, and money that stops for 14 days gets a eulogy.

Open Books

The platform takes every checkout and sends every payout, so it can publish all of them. Open Books is a live public ledger of every dollar in and out, built with AG Grid and AG Charts.

  • The ledger: every $2 checkout and $1 payout, with its bill, the hop whose words rode on it, PayPal's status, a masked PayPal reference, how long it took to land, and whether a signed webhook or polling confirmed it. Sort, filter, search, export to CSV. Status cells flash when PayPal answers.
  • The charts: money in vs. out by day, payouts by PayPal status, time to land, who wrote the words (people vs. a lost bill's labeled closing), and the bill's AI writing by model. Every chart is computed from the ledger's filtered rows, so filtering to one bill re-slices the whole page. Every bill page links to its own slice.

Emails never leave the server, and PayPal references are masked.

Who it's for, and why it matters

Payment apps treat money as plumbing. Four Word Pass treats a payment as something that can carry meaning, and that opens it to people who'd never open a payments app for fun.

  • Friends and strangers who want a small, real reason to connect. A story can be a gift to a friend in another city, a game for a group chat or a book club, or a message in a bottle for a stranger. An email link is the whole onboarding.
  • PayPal. In production, every hop is a $1 payout to someone who may not have PayPal yet. That's an invitation with a dollar in it and a story attached, sent by someone who chose them. Every story is a chain of people meeting PayPal by getting paid.
  • Anyone who pays people and has to prove it. Mutual aid funds, fundraisers, creator funds and marketplaces all get asked "did the money actually go out?" Open Books is a working answer. When the platform is the sender of record, it can publish every payment with PayPal's own status, confirmed by signed webhooks, without exposing anyone.

How we built it

One Cloudflare Worker serves the React app and the API.

PayPal

  • Orders API v2 + the PayPal JavaScript SDK take the $2 when a story starts or forks. The server creates the order, PayPal's buttons collect approval, and the server captures it and checks the amount and currency before it creates anything. Money comes in first, so if the capture fails, no bill exists.
  • Payouts API moves $1 on every hop. The platform is the sender, the pass id doubles as sender_batch_id (PayPal rejects duplicates, so retries are free), and the payout note carries the serial and a link back to the bill.
  • Webhooks (PAYMENT.PAYOUTS-ITEM.*) are checked with PayPal's verify-webhook-signature API. Three paths can settle a payout: a signed webhook, fast polling right after the payout, and a once-a-minute cron. The first one wins and the others change nothing. Open Books shows which path confirmed each payout and whether a verified webhook arrived. In sandbox, a payout lands in seconds.
  • Retries that can't pay twice. If PayPal has a hiccup (a 5xx, a rate limit, an auth blip) when we create a payout, the pass stays in flight and retries with the same sender_batch_id. Only PayPal rejecting the request fails it, and anything still unsettled after two hours hands the bill back to its sender.

AI (Cloudflare Workers AI)

  • Llama 3.3 70B where someone is waiting: the bill's personality at mint, and four-word suggestions.
  • gpt-oss-120b for background writing: the diary, the nags, the eulogy and its labeled closing.
  • FLUX.1 [schnell] paints the ink illustration on each passport stamp.
  • Every prompt is versioned. Each output's shape is a Zod schema, sent to the model as JSON Schema, and every response is validated against it before it's stored. Every stored output records the model and prompt version that wrote it, and Open Books charts those records.
  • AI suggestions must pass the same four-word rule as human words, and each holder gets five.
  • The bill's writing runs after the money settles and has hand-written fallbacks, so a model outage never stalls a hop.

AG Grid and AG Charts

  • AG Grid Community runs the Open Books ledger: client-side row model, custom cell renderers for bills and PayPal statuses, column and quick filters, pagination, CSV export, and cell-change flashing for live status updates. It's themed with the Theming API to match the site's paper-and-ink look.
  • AG Charts Community draws bar and donut charts from the grid's filtered rows. The palette comes from the brand's inks, adjusted until it passed a colorblind-safety check.
  • Only the modules in use are registered, and the page loads lazily so the rest of the app doesn't pay for it.

Cloudflare and the frontend

  • D1 holds state, R2 stores stamp images, Email Sending delivers the bill's emails from Four Word Pass, Email Routing catches replies, request.cf gives city-level location, and a cron trigger runs the nag ladder, polling and deaths.
  • React 19, Vite, Tailwind CSS 4 and MapLibre GL for the map. Each banknote gets a unique spirograph rosette generated from its serial.
  • One rule, two places. The four-word counter is a single TypeScript module that the browser uses for the live counter and the server uses for validation, with unit tests.

We built it with Claude Code as a pair programmer, working from a written PRD.

Challenges we ran into

  • UNCLAIMED means success. A payout to someone without a PayPal account comes back UNCLAIMED, not SUCCESS, and in sandbox that's every real email address. Waiting for SUCCESS would have stalled every story at its first stranger. UNCLAIMED counts as delivered. FAILED, BLOCKED, DENIED, RETURNED and the rest fail the pass, and the holder keeps their words and picks someone else.
  • Sandbox PayPal never emails real inboxes, so the buzz in your pocket can't come from PayPal. The bill sends its own email with the link, and every hop shows a masked PayPal payout receipt (PayPal sandbox payout ••••7QX2 · UNCLAIMED) as proof the money moved.
  • The wild had no payout. Releasing a bill to the wild pays nobody, so under "words land when the payout lands" those words could never land. The rule became your words land when the bill leaves your hands successfully: either a delivered payout, or a release to the wild, where the platform holds the dollar until a stranger claims it.
  • Webhook verification is byte-sensitive. Re-serializing the parsed event can change it and break the signature check, so the raw body goes in verbatim.
  • Races everywhere. Duplicate webhooks, a poll and a webhook arriving together, double clicks. Every state change is a conditional UPDATE, so the second one changes nothing.
  • Publishing the books without publishing people. Open Books had to show every payment while revealing no one. The server builds a separate public projection with no emails, no internal ids and masked PayPal references.
  • Image models can't spell. Stamps are crisp SVG with the city, date, serial and hop number, and the AI illustration is tinted into the middle. The model never draws text.
  • Secret links leak. A holder link in the URL would leak through the Referer header to the map tile servers. Bill pages send Referrer-Policy: no-referrer, and the app moves the token out of the URL.
  • Real people bring real logins. Shown a PayPal button, beta testers type their real PayPal password, which can't work in the sandbox, and on phones an installed PayPal app takes over the sandbox login and can't finish. Sandbox checkout shows only PayPal's card button, with a published sandbox test card and copy buttons.
  • Nobody could say no. Someone named by a friend had to write four words to move the bill, or sit through three nags until it died. Now they can send it back, and it returns to the person who chose them.
  • Counting to four is hard. "and then the lights dimmed" has two content words, not four. We needed a stopword list, a cap on total words so nobody pads with stopwords, and the same counting in the browser and on the server.

Accomplishments that we're proud of

  • A full sandbox run: claim, payout UNCLAIMED in 18 seconds, the holder's email, four words from the emailed link, a second payout, and signed webhooks verified and matched. It's all on Open Books.
  • The story never advances on a button click. It advances when PayPal says the money moved.
  • The bill has a voice people want to screenshot, and it never writes the story.
  • It's a whole product, not a demo: it works on phones, has share cards, privacy and terms pages, and a support inbox.
  • forwordpass.com, forewordpass.com and forewardpass.com all redirect home, because "four" is a homonym.

What we learned

  • Design the money flow around what PayPal actually reports, not what you hope it reports. UNCLAIMED, RETURNED and webhook retries shaped the product as much as the UI did.
  • Being the sender of record is the trick. It's what makes every hop verifiable, and it's what lets us open the books.
  • Give AI a role with limits. "Character, not author" made every prompt easier to write and every feature easier to explain.
  • Ship a fallback for everything. Every AI call has one, and every payout has three ways to settle.

What's next

  • Production Payouts approval and a self-funding relay: each passer pays $1 in through Orders, and the next holder gets $1 out through Payouts. Open Books would then balance to the cent.
  • Read a Buck: a storybook view for finished stories.
  • Badges for people who keep bills moving, starting with Paperback, for when a bill finds its way back to someone who held it before.

Built With

  • ag-charts
  • ag-grid
  • claude-code
  • cloudflare-d1
  • cloudflare-email
  • cloudflare-r2
  • cloudflare-workers
  • flux
  • gpt-oss
  • hono
  • llama-3.3
  • maplibre
  • paypal
  • paypal-javascript-sdk
  • paypal-orders-api
  • paypal-payouts-api
  • paypal-sandbox
  • paypal-webhooks
  • react
  • tailwindcss
  • typescript
  • vite
  • vitest
  • workers-ai
  • zod
Share this project:

Updates

Submission history