Double opt-in email verification
Require participants to confirm their email address before a sign-up counts, using a Function you control.
Turn on double opt-in to hold a sign-up until the participant confirms their email address, so your contact list stays clean and you have a record of consent. OmniLab does not send the confirmation message itself — you write a small Function that decides how confirmation happens, then bind it to the organisation.
What the participant goes through
Worth reading before you configure anything: double opt-in adds a round trip through the participant's inbox, and where they end up depends on whether they open the email on the same device they signed up on.
The participant fills in the sign-up form
Your Function sends the confirmation email
Through your own email provider.
A waiting screen holds the sign-up
Asks them to confirm, with Resend email and a recheck button.
They open the email and click the link
A brief confirmation page appears
It confirms the address, then forwards them on by itself.
They land back on the page they signed up from
Recognised, with anything they did before confirming still attached to them.
They continue the experience
Same device: they land back in the experience already recognised. Different device: they return to the waiting screen and sign in with a code instead.
The participant fills in the sign-up form
Your Function sends the confirmation email
Through your own email provider.
A waiting screen holds the sign-up
Asks them to confirm, with Resend email and a recheck button.
They open the email and click the link
A brief confirmation page appears
It confirms the address, then forwards them on by itself.
They land back on the page they signed up from
Recognised, with anything they did before confirming still attached to them.
They continue the experience
The sign-up is held at step 3 and only completes when the link is opened. Nothing is counted before that.
Three things about that journey are worth knowing before you turn it on.
The confirmation page is not a dead end. It confirms the address and forwards the participant back to the campaign on its own. They see it for a moment; there is nothing for them to click.
The link works once. A second click reports that it has already been used and points the participant back to the campaign, where they can sign in with a code sent to their email instead. This matters because mail security scanners and link previews open links before people do — the page is built so those scanners cannot burn the link before the real click.
Opening the email on another device is normal. They signed up on a laptop and opened the email on their phone, so the phone has no trace of the sign-up. On the laptop, the waiting screen's recheck button moves them on; on the phone, they are asked for a code instead. Neither is an error, and both end in the same place.
Before you begin
- You need Admin access. Turning on the setting and binding the Function both require it.
- A Function bound to the contact.verify hook point must exist before you turn double opt-in on. Without one, every affected sign-up fails.
- Decide which email service provider (ESP) — the service that actually sends the confirmation message, such as Brevo — your Function will call, and have its API credentials ready.
Bind the Function first
Once double opt-in is enabled for an organisation, OmniLab refuses every passwordless sign-up until a Function is bound to contact.verify. Set up the Function before you flip the switch, not after.
Publishing is blocked while the binding is missing, so this surfaces as a validation error on any campaign in that organisation — including campaigns built by colleagues who never touched the setting.
Steps
Bind a Function to the verification hook
In the left sidebar, switch to the Global Organization, then open General Settings. On the Enterprise Settings page, select the Functions tab, and create or choose a Function bound to the contact.verify hook point. This is the code that actually delivers the confirmation message — OmniLab hands it the single-use link and waits for an answer.
Your Function has two valid responses: send the link through your ESP, or tell OmniLab the contact is already verified when your own records already prove it.
Two details on the binding decide whether OmniLab accepts it. It has to be Active — a binding left inactive counts as no binding at all. And it has to cover the organisation the campaign belongs to, either by being global or by naming that organisation among its targets. A binding that targets only one organisation leaves every other one blocked.

Writing the Function itself
Building and testing the Function is covered in Hook functions. Share that page with your developer.
Store your provider credentials as a secret
Still on the Functions tab, add your ESP's API key as a secret bundle and bind that bundle to your Function, so the credentials are available at runtime without being written into the code. See Configure runtime and secrets for the exact fields.
Turn on double opt-in for the organisation
Open General Settings > Authentication to set the tenant-wide default, or Organization Settings > Authentication to set it for one organisation only. Set Double opt-in for passwordless sign-up to Enabled.

A per-organisation choice always wins over the tenant default, so use the organisation setting for any group that needs different behaviour.
Confirm a sign-up is actually held
Sign up with a test contact and confirm the participant is not counted until they act on the confirmation message your Function sent, or until your own system reports them already verified. Check your Function's execution log to see which of the two paths ran.
Adjust the waiting screen copy if you need to
While a sign-up is held, the participant sees a screen asking them to confirm their address, with buttons to resend the email and to recheck once they have clicked the link. It ships translated in every language OmniLab supports, so this step is optional — do it only if you want different wording.
Edit it in Build > Settings > Campaign Translation > Acquisition Form; the exact labels are listed in the acquisition form reference.
The verification link is generated in the Campaign's default language, so a participant lands on a confirmation page in the language they signed up in.
If something's blocked
- A campaign won't publish, with a double opt-in error under Auth. Double opt-in is on for this organisation but no active contact.verify binding covers it. Bind one as described above, or set double opt-in back to Disabled for that organisation. Both need Admin access, so a campaign builder who hits this has to escalate rather than work around it. See Validation & publishing.
- The validation error says the binding couldn't be checked. OmniLab could not reach the function service to confirm the binding, which is not the same as finding it missing. Run the validation again; if it keeps happening, contact support.
- Sign-ups fail immediately. Double opt-in is on for this organisation but no Function is bound to contact.verify. Bind one, or turn the setting off until you're ready.
- A participant says the confirmation message never arrived. Open the Function's execution log for that sign-up — a successful run only means your Function's call to the ESP was accepted, not that the message was delivered. Check the ESP's own delivery logs next.
- Sign-ups succeed instantly for everyone, with no message sent. Your Function is returning already verified on every run instead of only when your own records support it. Review its logic.