Add a second factor in two calls.

Two-factor authentication adds a second proof on top of a password: a one-time code on a channel the user controls. With Bird Verify it's a send call at the moment of login and a check call when the user types the code back: SMS, WhatsApp, or email today, per-country channel config, and nothing to store between the two.

verify.ts
200 · pending
import { BirdClient } from "@messagebird/sdk";

const bird = new BirdClient({
  apiKey: process.env.BIRD_API_KEY!,
});

// Send the code, then check it by recipient.
await bird.verify.verifications.create({
  to: { phone_number: "+15551234567" },
}).safe();

const { data, error } = await bird.verify.verifications.check({
  to:   { phone_number: "+15551234567" },
  code: userInput,
}).safe();
if (error) throw error;
Verification flow

Your account

Confirm it’s you.

Enter the code sent to

+1 ••• ••• 4567

Example verification code

482691
Code verified
Send a code. Confirm the recipient.

2FA is the verification flow, pointed at login.

Under the hood, 2FA is the same flow the Bird Verify API runs everywhere: create a verification for the user's phone or email when they sign in, then check the code they enter. Because the check is by recipient, your login handler keeps no per-attempt state, and the workspace's routing decides which channel a given user gets. Drop the password in front of it and the same flow becomes passwordless login.

What you wire up for 2FA.

Two calls and a configuration.

  1. 01

    Send on login, check on submit.

    Create a verification when the user authenticates with their first factor; check the code when they enter it. That's the whole integration.

  2. 02

    SMS, WhatsApp, or email as the factor.

    Use the channel you already have for the user: a phone number over SMS or WhatsApp, an email over email. Voice is rolling out for more options.

  3. 03

    One configuration, workspace-wide.

    Routing, code rules, and channel plan live in a single configuration for the whole workspace, already covering login, signup, and high-value actions.

  4. 04

    Brute force is bounded.

    Attempt lockout and per-recipient send caps come built in, so a second factor doesn't become a new attack surface.

The login-time flow.

Send the code once the password checks out; verify it when the user submits. A wrong code is a result you branch on, not an exception to catch.

two-factor.ts
200
// Send the second factor once the password checks out.
await bird.verify.verifications.create({
  to: { phone_number: user.phone },
}).safe();

const { data, error } = await bird.verify.verifications.check({
  to:   { phone_number: user.phone },
  code: submitted,
}).safe();
if (error) throw error;

if (data.success) grantSession(user);

Scale without
losing control.

Organize teams in workspaces, control API access, and trace changes through audit logs.

BirdHarborOrganization
WorkspacesProductionSandbox

Delivery agent

API key · Customer operations team
Active
PermissionsAccess
EmailRead & write
SMSRead & write
ALAlex Lee AdminPermissions updated

Audit log

Production
Workspace
Production
Resource
Delivery agent
WhatsApp
Read accessRead & write
Succeeded

Start with Verify.
Build across channels with Bird.

Your next idea. Ready to connect.