Documentation menu

Poll block

A one-tap survey question: each option is both a real in-email control and a plain link, so voting works everywhere from Gmail web to the plainest text-only client, without asking a recipient to leave their inbox in most cases.

How it works

Every poll option renders with two representations at once, not one chosen at build time. Wherever a client supports in-email form posts (Gmail web/Android, Yahoo, and others), each option is a real <form method="post"> posting directly to MailInApp's public interactions endpoint — tapping it votes without leaving the inbox. That form is wrapped in a kinetic-only tier and paired with a CSS rule that hides the second representation, a plain <a href> link into /v/[token]/act that confirms and records the same vote on a hosted page. Wherever the CSS rule isn't honored — Outlook, or any client that can't post an in-email form — that hidden link becomes visible instead and does the job, because a plain link is the one universal primitive every email client can render and follow. Both paths submit through the same recording endpoint and produce an identical event, so results never fork by which client a recipient used.

A vote is recorded once you're confirmed on the live page (or immediately on an in-email submit) — not on a bare link fetch. This matters because corporate scanners and inbox security tools prefetch every link in an email as soon as it arrives; if a bare GET counted, poll results would fill up with bots before a human ever opened the message. Confirmation happens via a form post even on the link path, which is why /v/[token]/act is a two-step confirm-then-record rather than a redirect-and-record.

A recipient's own vote is deduplicated: re-clicking their own link (an eager double-tap, a retried page load) is a no-op rather than a second row, enforced by a deterministic Firestore document id keyed on the campaign, block and recipient. Anonymous votes — no recipient token, e.g. from an exported HTML file pasted into a third-party ESP — aren't deduplicated per person (there's no person to key on) but are rate-limited per IP, per campaign, per minute instead, since a campaign's id is visible in the email's own source and nothing else stops a script from posting arbitrary votes.

Configurable fields:

  • Question — the poll's prompt text, plus its own color and font size.
  • Background color, padding, border and border radius on the card.
  • Option text color and option background color — a shared style override applied to every option chip.

Each poll option is a child node with a single label field — add, remove or reorder options freely in the tree.

If the project has a close date or response cap configured (see survey lifecycle) and it's been reached, the live view swaps the interactive chips for the same options rendered inert and gray, with a "This survey is closed to new responses" note — mailed HTML itself is static at send time and never shows this state, since it's evaluated at open, not at send.

Examples

Subject

Quick one: what should we build next?

A single-question poll ("Which feature should we build next?") dropped into a product newsletter gets a direct read on interest without the overhead of a full form — see Surveys & NPS feedback for the broader pattern of collecting structured feedback straight from an inbox. An event-recap email can ask "Would you come to another one?" with Yes/No/Maybe options, feeding straight into planning the next one. A/B undecided teams can use a poll as a lightweight internal vote — "Logo A or Logo B?" — sent to stakeholders, with results visible live on the results page as they come in.

What the static fallback looks like

<div id="em-poll-1" style="margin:12px 0;background-color:transparent">
  <p style="margin:0 0 10px;font-weight:600;font-size:16px;color:#111827">Which do you prefer?</p>
  <div>
    <span class="kn-fallback">
      <a href="https://mailinapp.com/v/eyJhbGci.../act?blockId=poll-1&blockType=poll&action=vote&option=Option+A"
         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">
        Option A
      </a>
    </span>
  </div>
</div>

This is the tier-2 link path: a plain <a> into /v/[token]/act, styled to look like the same chip the in-email form renders — visible only when the kinetic form is CSS-hidden.