BitelioBitelio

Double opt-in

Make new signups click a confirmation link before any marketing email reaches them

With double opt-in, a signup doesn't become a marketing recipient until they've clicked a confirmation link. That extra click is your defence against mistyped addresses, shared role inboxes, and submissions from people who never meant to opt in.

Everything hinges on {{subscribeUrl}} — a link Bitelio generates per contact and injects into any email you send. When the recipient clicks it, their subscribed flag becomes true and Bitelio emits a contact.subscribed event.

Setup

Create two templates

  • A Transactional template holding the confirmation message, with {{subscribeUrl}} inside:

    <p>Hi {{firstName}}, please confirm your email to start receiving updates:</p>
    <p><a href="{{subscribeUrl}}">Confirm my email</a></p>
  • A Marketing template for the welcome message that only goes out once they've confirmed.

The confirmation must be transactional

If a marketing template is aimed at a contact who isn't subscribed, the send is silently skipped. That's exactly why the confirmation email needs to be transactional: transactional sends ignore subscription state.

Trigger the signup from your backend

From your server, make two requests with your secret key (sk_*): first register the contact as unsubscribed, then track the event that kicks off the confirmation workflow.

curl https://api.bitelio.com/contacts \
  -H "Authorization: Bearer sk_your_secret_key" \
  -d '{ "email": "ada@example.com", "subscribed": false, "data": { "firstName": "Ada" } }'

curl https://api.bitelio.com/v1/track \
  -H "Authorization: Bearer sk_your_secret_key" \
  -d '{ "event": "signup.pending", "email": "ada@example.com", "subscribed": false }'

Note that subscribed: false appears in both requests. Calling /v1/track on its own would still create the missing contact — but as a subscribed one, which undermines the whole double opt-in scheme. Hence the explicit contact creation first.

Workflow A: send the confirmation

Under Workflows → New workflow:

  • Trigger: EVENT on signup.pending
  • SEND_EMAIL step → transactional confirmation template

Turn it on.

Workflow B: welcome them after confirmation

Under Workflows → New workflow:

  • Trigger: EVENT on contact.subscribed
  • SEND_EMAIL step → marketing welcome template

Turn it on. Because contact.subscribed is emitted on every opt-in — a {{subscribeUrl}} click, the preferences page, or an API update — this single workflow covers both fresh confirmations and contacts who resubscribe later.

Reminder if they don't confirm

Add a WAIT_FOR_EVENT step to Workflow A, right after the send:

  • Event: contact.subscribed
  • Timeout: 86400 (24 hours)

If the timeout fires, send one reminder — again from a transactional template. Resist adding more: to mailbox providers (and to the recipient), a stream of "please confirm" emails reads as spam.

What's next