Typestate
Sign in

Launch waitlist with referrals

People join a waiting list from your page, and each friend they bring moves them up.

For a maker who collects email addresses before a launch.

Resend logo Resend
Start it with your agentStarting from a blueprint does not count against the action changes of your plan.

Start the launch waitlist with referrals blueprint on Typestate

Asked you for your website's address and the address your emails come from.

Started Launch waitlist with referrals: 1 record types, 4 web endpoints.

4 actions are compiling.

Deployed to Sandbox. Try it at your Sandbox address.

What your agent does when you start this blueprint

What runs

Each step has 3 parts: who starts it, how it reaches your backend, and the action that answers.

  1. A visitorjoins from your page and gets their own link
    Web endpointPOST /waitlist
    Join the WaitlistPuts the person on the list and sends them their link.saves a Signupsends 1 email
  2. A friendjoins through that link, and the first person moves 5 places up
    Web endpointPOST /waitlist
    Join the WaitlistMoves the friend who sent them 5 places up.saves a Signupchanges a Signupsends 1 email
  3. A visitorchecks their place
    Web endpointGET /waitlist/place
    Show My PlaceAnswers their place in the queue.reads only
  4. Youinvite the next 50 when you are ready
    Web endpoint, your keyPOST /waitlist/invite
    Invite the Next PeopleInvites the people at the front of the queue.changes Signupssends emails
  • Web endpoint: your website or app calls it

The rule you will want to change first

Each person who joins through your link moves you 5 places up the list.

It lives in the action Join the Waitlist. Tell your agent how you want it instead: one sentence changes, and you read the change before it goes Live.

What it is made of

The map shows the records it keeps, the actions it runs and the services it calls.

What it keeps

Signupemail, name, referral code, referred by, friends, number, score, state

What it does

Join the Waitlistweb endpointThe rule to change first
Show My Placeweb endpoint
Invite the Next Peopleweb endpoint, your key
Read the Listweb endpoint, your key

What it reaches

ResendSend email from your own address: sign-in links, receipts, reminders.
Your website4 web endpoints

What it keeps

Signup

8 fields

One person on the list, with their place and the friends they brought.

email
required, an email address, never twice
name
optional, at most 120 characters
referral code
required, never twice, at most 8 characters
referred by
optional
friends
required, a whole number
number
required, a whole number
score
required, a whole number
state
required, one of waiting, invited

Anyone can read and write it.

What it does

Open an action to read its steps: each sentence, with the calls that do it under it.

Join the WaitlistA person joins from your page, and gets an email with their own link.

Who starts it

From the webPOST /waitlist

What it is given

EmailEmail, needed

Nametext, optional

Referral codetext, optional

What it does

  • input.emailwhat the caller gives
  • same_emaila value it works out
  • Signupa record type
  • Signupan attribute of a record or an answer
  • "waiting"a fixed text or number
  • toa part of an email or a payment
  • Refusewhere it says no

Find the Signup whose email is the given email address.When one matches, refuse with "This address is already on the list".

same_email is

Count Signups where

email is input.email

When same_email is more than 0

Refuse "This address is already on the list"

Make a new referral code of 8 random letters and digits.

code is a random code of 8 letters and digits

The person's number is the count of Signups plus one.

signups is

Count all Signups

place is signups + 1

When a referral code was given, find the Signup whose referral_code is that code.When one matches, add 1 to that Signup's friends and take 5 off its score.A code that matches nobody is ignored, and the person still joins.

A friend's code moves its owner up, because a person who brings friends is worth more at launch.

When input.referral_code is given

referrer is

Find the first Signup where

referral_code is input.referral_code

when none carry on without one

When referrer holds a value

Change the Signup where

id is referrer.id

set friends to referrer.friends + 1

set score to referrer.score - 5

Create a Signup with email and name set to the given email address and name, referral_code set to the new code, referred_by set to the given referral code, friends set to 0, number and score set to the person's number, and state set to waiting.

Add a Signup

set email to input.email

set friends to 0

set name to input.name

set number to place

set referral_code to code

set referred_by to input.referral_code

set score to place

set state to "waiting"

Email the given address with the subject "You are on the list", saying their place and their own link, which is the site address with ?ref= and their referral code on the end.

Send an email

to input.email

template "Join the Waitlist Email"

place place as a number

referral_code signup.referral_code

value the setting SITE_ADDRESS

Answer with the person's number as the place, and the new referral code.

Answer

place is place

referral_code is code

Show My PlaceA person sees their place, from the code in their link.

Who starts it

From the webGET /waitlist/place

What it is given

Referral codetext, needed

What it does

  • input.referral_codewhat the caller gives
  • signupa value it works out
  • Signupa record type
  • Signupan attribute of a record or an answer
  • "invited"a fixed text or number
  • refusewhere it says no

Find the Signup whose referral_code is the given referral code.When none matches, refuse with "No place matches this link".

signup is

Find the first Signup where

referral_code is input.referral_code

when none refuse "No place matches this link"

When that Signup's state is invited, answer that they are invited.

When signup.state is "invited"

Answer

invited is true

Otherwise their place is the count of Signups whose state is waiting and whose score is lower than that Signup's score, plus one.When two scores are the same, the Signup with the lower number, who joined first, is ahead.

lower_scores is

Count Signups where

score is less than signup.score

state is "waiting"

earlier_same_score is

Count Signups where

number is less than signup.number

score is signup.score

state is "waiting"

place is (lower_scores + earlier_same_score) + 1

Answer with the place, that Signup's friends, and the count of Signups whose state is waiting.

waiting is

Count Signups where

state is "waiting"

Answer

friends is signup.friends

invited is false

place is place

waiting is waiting

Invite the Next PeopleYou invite the next people in the queue, and each gets an email.

Who starts it

From the webPOST /waitlist/invite

What it is given

Numbera whole number, needed

What it does

  • input.numberwhat the caller gives
  • waiting_peoplea value it works out
  • Signupa record type
  • Signupan attribute of a record or an answer
  • 50a fixed text or number
  • toa part of an email or a payment
  • Refusewhere it says no

When the given number is less than 1 or more than 50, refuse with "Invite between 1 and 50 people at a time".

When input.number is less than 1

or input.number is more than 50

Refuse "Invite between 1 and 50 people at a time"

List that many Signups whose state is waiting, lowest score first.When two scores are the same, the Signup with the lower number, who joined first, goes first.

waiting_people is

Find Signups where

state is "waiting"

sorted by number, lowest first

by_score is waiting_people

sorted by score, lowest first

queue is the first input.number items of by_score

For each of those Signups, change its state to invited, and email its email with the subject "You are in", saying that their access is ready.

For each person in queue, at most 50

Change the Signup where

id is person.id

set state to "invited"

Send an email

to person.email

template "Invite the Next People Email"

Answer with the count of Signups invited now, and the count of Signups whose state is still waiting.

still_waiting is

Count Signups where

state is "waiting"

Answer

invited is the number of the value named queue

waiting is still_waiting

Read the ListYou read the list, newest first, with each person's place and state.

Who starts it

From the webGET /waitlist

What it is given

Stateone of waiting, invited, optional

What it does

  • input.statewhat the caller gives
  • all_signupsa value it works out
  • Signupa record type
  • Signupan attribute of a record or an answer
  • "waiting"a fixed text or number

List the Signups, newest first.When a state is given, list only the Signups in that state.

all_signups is

Find all Signups

sorted by created_at, highest first

signups is

each signup of all_signups where

input.state is not given

or signup.state is input.state

For each Signup, answer its email, name, state, friends and score.

rows is

each signup of signups as

email is signup.email

friends is signup.friends

name is signup.name

score is signup.score

state is signup.state

Also answer the count of Signups whose state is waiting, and the count whose state is invited.

waiting is

Count Signups where

state is "waiting"

invited is

Count Signups where

state is "invited"

Answer

invited is invited

signups is rows

waiting is waiting

Who signs in and what they may read

People sign up and sign in with an email address and a password, or with a provider you add, such as Google.

  • Signup: Anyone can read and write it.

Web endpoints

What it doesWho may callAddress
Your page posts the form here.anyonePOST /waitlist
Your page shows a person their place.anyoneGET /waitlist/place
Only you, with your key, invite people.you, with your keyPOST /waitlist/invite
Only you, with your key, read the list.you, with your keyGET /waitlist

Example pages on this backend

3 frontends, each in a different stack, for the same backend. Under each page are the web endpoints it calls and the action that answers each one.

Astro

Launch page

For visitors

The product's own page. A visitor joins the list and sees their place and their own link.

  1. / Join

    The product, and the form that puts a visitor on the list. A friend's link fills in their code.

    • POST /waitlistJoin the Waitlist
  2. /place Your place

    The visitor's place in the queue, how many friends joined through their link, and the link to share.

    • GET /waitlist/placeShow My Place

HTML and JavaScript

Form for another page

For visitors of a page you did not build

One script tag puts the form on a page somebody else built, here a baking blog. After the join it shows the place in the same box.

  1. / Recipe with the form

    A recipe post with the form at the end. The form joins, then shows the place and the link to share.

    • POST /waitlistJoin the Waitlist
    • GET /waitlist/placeShow My Place

React with Vite

Owner's console

For you, with your API key

The owner reads the list and invites the next people. The API key stays in the browser tab and is never in the build.

  1. / The list

    Everybody on the list, newest first, with the friends each one brought. Filter by waiting or invited.

    • GET /waitlistRead the List
  2. /invite Invite people

    Who is next in the queue, and the button that invites them.

    • GET /waitlistRead the List
    • POST /waitlist/inviteInvite the Next People

What it cannot do yet

  • It does not check that an address is real before a person takes a place. Add a proof by email with set_email_verification if spam becomes a problem.
  • It does not build the page. Your site posts the form to it.

Build the pages with your tool

Typestate is the backend. Paste this brief into Lovable, v0, Claude Code or the tool that builds your pages.

Show the brief
Build the pages for "Launch waitlist with referrals". The backend runs on Typestate and answers these web endpoints: POST /waitlist: Your page posts the form here. Who may call: anyone. GET /waitlist/place: Your page shows a person their place. Who may call: anyone. POST /waitlist/invite: Only you, with your key, invite people. Who may call: you, with your key. GET /waitlist: Only you, with your key, read the list. Who may call: you, with your key. People sign up at POST /auth/signup and sign in at POST /auth/login, which answers a token. Send it as Authorization: Bearer <token>. A refused call answers a sentence that can be shown to the person as it is.

Start it

  1. Tell your agent the sentence on the right.
  2. It asks you for the https address of the page people join from, such as https://launch.example.com. Each person's own link starts with it.
  3. It asks you for the address emails come from, on a domain you verified at Resend, such as "Riverside Physio <hello@riverside.example>".
  4. It tells you what only you can do, such as pasting your Resend key in the cockpit. A key never goes through the agent.
  5. Open your Sandbox address and try it yourself.

Starting from this blueprint does not count against the action changes of your plan. The Free plan runs it on Sandbox. Live needs a paid plan.

Start the launch waitlist with referrals blueprint on TypestateChoose a plan

Related blueprints