RSVP block
A free "Will you attend?" question with capacity and waitlist support — the no-charge counterpart to the Product block's paid-ticket inventory cap, for events that just need a headcount rather than a payment.
How it works
A guest-less RSVP behaves exactly like a poll's single-choice vote: "Yes" and "No" render as the same link-first chip pair buildChipGroup produces for a poll option. Confirmation happens on the hosted live view (or directly through an in-email form post, where the client supports it) before anything is recorded, so a corporate scanner prefetching the link can't inflate the count.
Turning on Allow bringing guests changes only the "Yes" response: a plain GET link can't offer a choice the way a real form field can, so the guest count becomes a live-view-only kinetic <form><select> control. That mirrors the way the Assignment block's text/link answers need the live view for anything a fixed link can't carry. "Yes (choose guests)" renders as a plain link into the live view itself on the tier-2/tier-3 fallback, rather than straight into /act. A guest-less "Yes" (0 guests picked, or guests disabled entirely) still reaches /act as a normal one-tap link either way — only the guest-count step specifically needs the live view.
Capacity accounting happens inside a single Firestore transaction that also performs the per-recipient dedup check. Unlike the Product block's checkout, which just re-checks a .count() query and refunds on a rare oversold race, an RSVP has no refund-style backstop for an over-capacity response. So the decision and the write happen atomically together, rather than as two separate steps a race could split apart. Set Capacity above 0 and once confirmed RSVPs plus their guests would exceed it, the response is recorded as waitlisted instead of confirmed, and the recipient sees your own Waitlist message rather than a generic thank-you.
A "No" response is always recorded as declined, never checked against capacity. Capacity 0 means unlimited, the same "0 = unset" convention the Product block's inventory field uses. An anonymous response (no recipient token — for example, one from an exported HTML file pasted into a third-party ESP) skips the dedup check entirely, since there's no recipient to key on. It still runs through the same capacity transaction, though, so an anonymous flood still can't oversell the waitlist boundary; only per-person duplicate detection is unavailable on that path.
If the project has a close date or response cap configured (see survey lifecycle), a closed RSVP swaps both chips for inert gray versions plus a "This survey is closed" note on the live view.
Configurable fields:
- Question — defaults to "Will you attend?"
- Capacity (0 = unlimited).
- Allow bringing guests — adds a guest-count picker to "Yes."
- Max guests per RSVP (if guests allowed) — caps the picker's range (1–20).
- Waitlist message — shown instead of a normal thank-you once capacity is reached.
- Background color, padding, border and border radius on the card.
- Question color/size, and Yes/No button color/background overrides.
Every RSVP rolls up into confirmed/declined/waitlisted counts and a guest total, written directly into the project's rsvpBlocks aggregate bucket rather than through the generic response-increment path every other block uses. That's a deliberate exception, since the confirmed-vs-waitlisted decision has to happen inside the same transaction as the count itself.
Examples
Subject
Will you join us?
The "Event" project purpose starts a new project with an RSVP block already in place alongside Meeting link, Venue, and Add to calendar. Set a capacity for a limited-seating venue and the block handles the waitlist without any extra setup — see Events & webinars. A club uses the same block for a tryout or a limited number of away-trip seats, with capacity keeping the headcount honest without anyone needing to track it by hand — see Sports teams & clubs. A free community meetup with no seating limit at all just leaves Capacity at 0, using RSVP purely for a reliable headcount rather than any capacity enforcement.
What the static fallback looks like
<div id="em-rsvp-1" style="margin:12px 0;background-color:transparent">
<p style="margin:0 0 10px;font-weight:600;font-size:16px;color:#111827">Will you attend?</p>
<div>
<span class="kn-fallback">
<a href="https://mailinapp.com/v/eyJhbGci.../act?blockId=rsvp-1&blockType=rsvp&action=rsvp&response=yes"
style="display:inline-block;margin:0 6px 6px 0;padding:8px 14px;border:1px solid #d1d5db;
border-radius:6px;background-color:#ffffff;color:#111827;font-size:14px;text-decoration:none">
Yes
</a>
<a href="https://mailinapp.com/v/eyJhbGci.../act?blockId=rsvp-1&blockType=rsvp&action=rsvp&response=no"
style="display:inline-block;margin:0 6px 6px 0;padding:8px 14px;border:1px solid #d1d5db;
border-radius:6px;background-color:#ffffff;color:#111827;font-size:14px;text-decoration:none">
No
</a>
</span>
</div>
</div>
This is the guest-less tier-2 fallback: plain links into /v/[token]/act, identical in shape to a poll's option chips, visible wherever the in-email form isn't rendered.