Documentation menu

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, confirmed on the hosted live view (or answered directly in 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: because a plain GET link can't offer a choice the way a real form field can, the guest count becomes a live-view-only kinetic <form><select> control (mirroring the way the Assignment block's text/link answers need the live view for anything a fixed link can't carry), with "Yes (choose guests)" rendering 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, e.g. from an exported HTML file pasted into a third-party ESP) skips the dedup check entirely — there's no recipient to key on — but still runs through the same capacity transaction, 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 — 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.