Part 2 of 7 · Event RSVP manager series ~5 min read

How an RSVP gets confirmed

A guest goes from “I’d like to come” to “I have a seat” in one step, and that one step has to be airtight. There are three ways a guest gets onto the list: they fill the web form, they reply to your invite email, or the host imports a batch of names. All three end at the same place — a request for a seat. And that request is granted by a single, careful database write that makes overselling impossible, even when two people reach for the last seat in the same second.

Key takeaways

  • Three sign-up lanes feed one list: the web form, an email reply, and a host import.
  • A new sign-up is checked for duplicates so nobody double-books a seat.
  • A seat is claimed by a single conditional write — it only succeeds if the event isn’t full.
  • If the event is full, the person is placed on the waitlist and told their position.
  • The capacity cap is defended by the database, not by hopeful counting in code.

Three lanes onto one list

Three sign-up lanes funnel into one guest list A diagram with three vertical lane columns at the top and a single unified row at the bottom. Lane one, Web form: a guest enters their name and email on the register page; the form posts to a Function URL Lambda that checks for a duplicate, then asks the guest-list keeper for a seat. Lane two, Email reply: a guest replies "yes" to the invite email; SES inbound writes the raw message to S3, a parser Lambda reads the sender's name and email and runs the same seat request. Lane three, Host import: the host pastes a list of names and emails; an import Lambda walks the list and runs a seat request for each one, marking them pre-confirmed if the host chooses. All three lanes converge on the same guest-list keeper, which holds every guest's state and the live confirmed count. The keeper claims a seat with a single conditional write that only succeeds if the confirmed count is below the capacity cap; if it succeeds the guest is confirmed and a confirmation email goes out, and if it fails because the event is full the guest is placed on the waitlist and told their position. A note at the bottom: the keeper is the source of truth — the three lanes are just different doors into the same seat request. Lane 1 · web form Register page • Guest enters name and email • Posts to a Function URL • Duplicate check by email • Asks keeper for a seat Lane 2 · SES inbound Email reply • Guest replies “yes” to invite • SES writes mail to S3 • Parser reads name and email • Same seat request runs Lane 3 · bulk import Host import • Host pastes a list of guests • Import Lambda walks the list • Same seat request per person • Can pre-confirm if host chooses Guest-list keeper (source of truth) name · email · state · seat or waitlist · live confirmed count vs cap one conditional write claims a seat — only if confirmed count is below the cap to messenger, confirm email The keeper is the source of truth — the three lanes are just different doors into the same seat request.
Fig 2. Three lanes converge on one guest-list keeper. The web form, the email reply, and the host import all end at the same seat request. The keeper grants a seat with a single conditional write that only succeeds when the event isn’t full.

Lane 1: the web form

The simplest lane. The register page asks for a name and email and posts to a Function URL — a plain web address that runs a small Lambda, with no API Gateway in front of it. The Lambda first checks whether this email is already on the list: if it is, the guest just gets their existing status back (“you’re already confirmed” or “you’re number 4 on the waitlist”) instead of a second row. If it’s a new email, the Lambda asks the guest-list keeper for a seat. Most guests come in this way.

The form itself is static — it can sit on any page you already have. There’s no login, no account to create. The whole interaction is: type two fields, click, get an email. That low friction is the point; the easier it is to RSVP, the more accurate your headcount.

Lane 2: the email reply

Plenty of people will just reply “yes, count me in” to your invite rather than click a form. Lane 2 catches them. Set up a dedicated inbound address through Amazon SES — something like rsvp@your-event.com. When a reply lands, SES writes the raw message to S3, which triggers a small parser Lambda. The parser reads the sender’s name and email from the message headers and runs the same seat request the form would. A short confirmation (or waitlist notice) goes back to the same address.

The parser keeps it deliberately simple: it only needs the name and email, both of which are in the message envelope. It does not try to interpret the body for clever intent — a reply to the RSVP address is the intent. If someone replies to cancel instead, the body is scanned for a clear “cancel” or “can’t make it,” and on a match the guest is routed to the cancel flow from Part 4 rather than a sign-up.

Lane 3: host import

Sometimes the host already has the list — a team roster, last year’s attendees, a sheet of names collected at a previous event. Forcing all of them to fill a form would be silly. Lane 3 lets the host paste a list of names and emails. An import Lambda walks the list and runs the same seat request for each person. The host chooses whether the imported guests are placed as pending (they still get an invite and have to confirm) or pre-confirmed (they’re straight onto the seat, useful for a guaranteed VIP list).

Even on a bulk import, every single person still goes through the same conditional write. If the import would push past the cap, the people beyond the cap land on the waitlist in list order — the import can’t sneak past the capacity limit any more than a single sign-up can.

The one write that defends the cap

Here is the heart of the whole system. Granting a seat is not “read the count, check it’s under the cap, then add one.” That two-step approach has a gap: two sign-ups can both read “39 of 40” at the same instant, both decide there’s room, and both write — and now you have 41 confirmed for a 40-seat room. Instead, the keeper claims a seat with a single conditional write: one database update that says “increase the confirmed count by one, but only if it’s currently below 40.” The database runs that check and the increase as one indivisible step. If two writes race, exactly one succeeds and the other is refused. The loser isn’t an error — they’re simply placed on the waitlist instead.

This is why the cap can be trusted absolutely. There is no moment where the count is read separately from being changed. The number of confirmed guests is never wrong, never even briefly over the cap, no matter how many people click at once.

What the guest sees

From the guest’s side, none of this is visible. They sign up and within a second or two they get one of two emails. Confirmed: “You’re in — here are the details, and a link to cancel if your plans change.” Waitlisted: “The event is full, but you’re number 3 in line. If a seat frees up we’ll email you a link to claim it.” Both are clear, both set the right expectation, and both contain the links the rest of the system needs.

Next post: how the system schedules each confirmed guest’s reminders — a week out, a day out, the morning of — and how quiet hours keep any of them from landing in the middle of the night.

All posts