Double Opt-in
Double opt-in is the practice of asking new sign-ups to confirm their email address by clicking a link in a confirmation email before they actually become a subscriber. It protects you against:
- People mistyping their email address (“johnn@example.com” instead of “john@โฆ”)
- Bots filling forms with random addresses
- Someone signing up someone else’s address as a prank
GDPR / PECR also strongly prefer it as evidence that consent was actually given by the person who owns the address.
Super Speedy Emails supports per-list double opt-in: you configure whether each list requires confirmation. Lists default to requiring confirmation.
Table of Contents
Enabling double opt-in on a list
- Go to SSE Emails > Lists.
- Edit the list.
- Check Require email confirmation (double opt-in).
- Save.
After this, any new signup to that list lands in pending status. They do not appear in audience queries (campaign sends, newsletter digests, automations) until they confirm.
๐ก The default is on for new lists, because that’s the legally safer default. Turn it off only for operational lists where the signup itself is an explicit informed action (e.g. an internal staff distribution list).
What the user experience looks like
From the visitor’s perspective
- Visitor fills out a signup form on your site.
- They see a confirmation page: “Almost there! Please check your email for a confirmation link.”
- They open the confirmation email titled “Confirm your subscription to “.
- They click the link. They land on a thank-you page: “Thanks โ your subscription is confirmed!”
- They’re now an active subscriber. They will receive sends to this list.
Behind the scenes
- Form submit โ
SSE_Subscribers::create()withstatus = pending, list membership inserted withstatus = pending. - A confirmation token is generated and stored in
sse_confirmation_tokenswith a 7-day TTL. sse_send(category='transactional')sends the confirmation email โ never blocked by suppressions (transactional always sends, even to globally suppressed addresses, because we’re confirming the address itself works).- Click on the link hits
/wp-json/sse/v1/confirm?token=.... - Token consumed โ subscriber
status = active,confirmed_at = now. List membershipstatus = subscribed. - Optional redirect to a thank-you URL configured on the list.
Token TTL & expired confirmations
Tokens are valid for 7 days. After that, clicking the link shows an error: “This confirmation link has expired. Please subscribe again.”
The 7-day window is a compromise between:
- Long enough that someone who saw the email on Monday morning can confirm Tuesday after the weekend (etc.)
- Short enough that an old leaked link can’t be used to spuriously add an address as confirmed long after the original signup
Expired tokens are not automatically cleaned up โ they’re left in the table with a consumed_at status, since rows are tiny and the history is occasionally useful for support.
What happens if they don’t confirm
After 7 days the token expires. The subscriber row stays in pending status indefinitely. You can see them in SSE Emails > Subscribers by filtering for status = pending.
By default we do NOT send a reminder email. Sending an unsolicited reminder to an address that hasn’t yet confirmed they want emails is the kind of thing that creates spam complaints. If you want a “we still haven’t heard from you” reminder, build it as an automation (manual, opt-in per list).
๐ก If you migrated from MailPoet/MailerLite and saw some “unconfirmed” subscribers, they were imported as
pendingand no re-confirmation email was sent automatically. See Email Migration Guide for the reasoning.
Re-subscribing after declining
If a subscriber has previously unsubscribed, then re-signs up through a form:
- The form submit recognises the existing subscriber row.
- If the list requires confirmation, a new confirmation token is issued and emailed. Until they click, the list membership remains
unsubscribed. - Once they click, the list membership flips to
subscribed. Theirmarketing_opt_inis also flipped back on if they’re re-subscribing to a marketing list.
This treats re-subscribe as “fresh consent” โ the previous unsubscribe is on file, the new consent is on file, and you have a clean audit trail.
What appears in the confirmation email
The email is a plain-text message with:
- Subject: “Confirm your subscription”
- Body: a one-line message + the confirmation link
- Sender: configured “From” for the
transactionalcategory (see MailGun Setup)
The body is currently fixed; per-list custom confirmation copy is on the roadmap.
The confirmation page (where the link lands) is a minimal HTML page built into SSE. To customise it, override via the existing templates/ directory โ see Developer Reference.
Disabling on a list
You can turn off double opt-in for a specific list โ useful for operational lists where the signup itself is enough (e.g. an internal alerts list, or a list users join from inside their authenticated account):
- Edit the list.
- Uncheck Require email confirmation.
- Save.
Future signups land directly as active / subscribed. Existing pending subscribers are not retroactively confirmed โ they stay pending unless they click their existing confirmation link or the admin manually changes their status.
Audit / compliance
Every confirmation event is recorded:
sse_subscribers.confirmed_atโ timestamp of confirmationsse_subscribers.sourceโ where the signup originated (form/migration_mailpoet/ etc.)sse_confirmation_tokensโ the token history withcreated_at/consumed_atsse_subscriber_lists.subscribed_atโ when this specific list membership became active
Together that’s a complete record of “they consented to be on this list at this time via this source.” If a regulator ever asks, the evidence is on file.
- Lists โ creating and configuring lists
- Email Categories & Unsubscribes โ what
transactionalmeans and why it bypasses suppressions - Email Migration Guide โ how MailPoet/MailerLite unconfirmed sign-ups were imported