Skip to content

Signup forms: require a sender for double opt-in, error instead of a fake "Check your inbox" - #103

Merged
clbotdev merged 1 commit into
mainfrom
issue-102-forms-sender
Oct 21, 2026
Merged

clbotdev merged 1 commit into
mainfrom
issue-102-forms-sender

Conversation

@clbotdev

Copy link
Copy Markdown
Contributor

Part of #79. Closes #102.

A double opt-in signup form on a team with no sending mailbox silently lost every signup: the visitor saw "Check your inbox", and the failure was only a warning in the server log.

Block at setup

  • createForm / updateForm check the team's sender with resolveDeliverySource(teamId, undefined, "transactional"), the same choice the confirmation email makes. Creating a double opt-in form, or switching a form to double opt-in, fails with 422 confirmation_sender_unavailable and a message saying what to fix: no mailbox, more than one mailbox and no default, or sending paused. This applies to REST, MCP (create_signup_form, update_signup_form) and the dashboard.
  • Editing an already double opt-in form (rename, tags) isn't blocked. The sender can disappear after setup, so forms now report sendingIssue: the forms page shows a warning banner with a link to email settings and a "Can't send confirmations" badge on each affected form.

On submission

  • For double opt-in forms, the public POST /f/:formId checks the sender before looking at the address or the honeypot. A team that can't send answers every submission the same way, so the form still can't reveal who is suppressed or listed:
    • JSON gets 503 { error: "form_unavailable", message }.
    • HTML posts get the form back with "This form can't accept signups right now. Please try again later."
  • It's reported through captureError (PostHog) with the form and team ID. A confirmation send that still fails after the check returns unavailable too and is reported as forms.confirmation_email.

Docs: signup-forms.mdx (new "Double opt-in needs a sending mailbox" section and the 503 response), plus the OpenAPI descriptions and MCP tool descriptions.

Tests: 4 new cases in forms.test.ts. The API suite (735), web (80), contract, check:drift, test:conformance, typecheck, lint and format all pass.

Browser E2E (fresh account, over the Tailscale stack): creating a double opt-in form without a mailbox is refused with the reason and a settings link → single opt-in works → switching it to double is refused → add and activate a Mailpit SMTP mailbox → double opt-in saves, the hosted form says "Check your inbox" and the confirmation arrives in Mailpit → suspend the mailbox → the forms page shows the banner and badge, the hosted form shows the error, and JSON gets 503 form_unavailable.

…nfirmations were sent

Creating a double opt-in form, or switching a form to it, now fails with
422 confirmation_sender_unavailable when the team can't send email (REST,
MCP, dashboard). Forms report a sendingIssue, shown as a warning on the
forms page. A form that can't send confirmations answers every submission
with 503 form_unavailable (an error page for HTML posts) instead of
"Check your inbox", and reports it through captureError (PostHog).

Closes #102
@clbotdev
clbotdev merged commit 27349e0 into main Oct 21, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Signup forms: block double opt-in without a sender, and show an error when the confirmation can't be sent

2 participants