Documentation
Sign inGet started

Verify your first customer

Bird Verify confirms that someone controls an email address or phone number. You ask Bird to send a one-time passcode. The person enters it in your app, and you ask Bird whether it matched. Bird generates and delivers the code, and enforces expiry and attempt limits. Your app never receives or stores the generated code.
This quickstart verifies your own email address, which requires no setup. Bird sends email codes through its shared Authifly sender, so you need no domain or balance. After funding SMS, verifying a phone number uses the same two calls.

1. Create an API key

In the dashboard, go to Developers > API keys and create a key. Keys are scoped to a region and look like bk_us1_... or bk_eu1_...; the region in the prefix tells you which API host to call: https://us1.platform.bird.com or https://eu1.platform.bird.com.
The API Keys page in the Bird dashboard, listing keys with their masked prefix, scopes, and last-used time
The full key is shown once, at creation time. Copy it somewhere safe, then export it for the send examples:
Exemple de code
export BIRD_API_KEY="bk_us1_..."

2. Send a code

Create a verification for the address you want to confirm. The only required field is to. Use your own email address so you can read the code. Install the Bird SDK for your language by following its SDK quickstart.
In the SDK tabs, replace the example API key and user@example.com before running the code. The CLI uses your login, and the cURL tab uses BIRD_API_KEY.
import { BirdClient } from "@messagebird/sdk";

const bird = new BirdClient({ apiKey: "bk_XXXXXXXXXXXXXXXXXXXXXXXX" });

const verification = await bird.verify.verifications.create({
  to: { email: "user@example.com" },
});

console.log(verification.id, verification.status);
If your key starts with bk_eu1_, call https://eu1.platform.bird.com instead.
Bird accepts the request and starts sending the code:
Exemple de code
{
  "id": "vrf_01ky7q1fdze3695yvyz7z9nm3a",
  "status": "pending",
  "reason": null,
  "to": { "email": "user@example.com" },
  "channels": [{ "channel": "email" }],
  "last_channel": "email",
  "expires_at": "2026-07-23T14:55:58Z",
  "verified_at": null,
  "created_at": "2026-07-23T14:45:58Z",
  "updated_at": "2026-07-23T14:45:58Z"
}
No verification ID to store: the check in step 3 is keyed by the same recipient. The email arrives from Authifly OTP <otp@verify.authifly.com> with the subject "Your verification code" and a six-digit code; the message itself says when it expires. Code length, lifetime, attempt cap, and resend cooldown are workspace settings, and verification settings lists the defaults and ranges.

3. Check the code

Take the code from your inbox and submit it, keyed by the same recipient:
import { BirdClient } from "@messagebird/sdk";

const bird = new BirdClient({ apiKey: "bk_XXXXXXXXXXXXXXXXXXXXXXXX" });

const result = await bird.verify.verifications.check({
  to: { email: "user@example.com" },
  code: "123456",
});

console.log(result.success);
A correct code comes back success: true, and the embedded verification flips to verified:
Exemple de code
{
  "success": true,
  "reason": null,
  "attempts_remaining": null,
  "verification": {
    "id": "vrf_01ky7q1fdze3695yvyz7z9nm3a",
    "status": "verified",
    "reason": null,
    "to": { "email": "user@example.com" },
    "channels": [{ "channel": "email" }],
    "last_channel": "email",
    "expires_at": "2026-07-23T14:55:58Z",
    "verified_at": "2026-07-23T14:46:47Z",
    "created_at": "2026-07-23T14:45:58Z",
    "updated_at": "2026-07-23T14:46:47Z"
  }
}
Before you add this flow to a signup, account for these outcomes:
  • An unsuccessful check returns HTTP 200. The response contains success: false, a reason (incorrect_code, expired, or attempts_exhausted), and an attempts_remaining count while attempts remain. Branch on this result in your application. The verification permanently fails after it exhausts its check attempts.
  • A verification resolves once. After it reaches verified (or fails or expires), checking it again returns a 404. Treat the first definitive answer as the answer. If the user needs a new code, call the create endpoint again with the same recipient: the in-progress verification is reused, and a fresh code goes out once the resend cooldown has passed.
Every verification you create appears on the Verifications page with its status, recipient, channel, and timing. The generated code does not appear.
The Verifications page listing verifications with status, recipient, channel, and creation time columns

Verify a phone number instead

To verify over SMS, put a phone number in to in E.164 format instead of an email address:
const verification = await bird.verify.verifications.create({
  to: { phone_number: "+15551234567" },
});
console.log(verification.id, verification.status);
The check is identical: replace email with the same phone_number. Phone delivery draws on your workspace's SMS balance, and the destination country determines the route. Bird tries WhatsApp first in most countries and SMS first in some. Country configuration shows and configures the available channels and their order for each destination. Senders and branding shows what arrives on each channel.

Reach the user on both channels

You do not have to choose one channel. Include both an email and a phone_number in to, and Bird resolves a delivery plan from your country configuration, which shows the available channels and their order for each destination. Bird follows that plan until a send is accepted. If delivery later fails outright, Bird sends a fresh code through the next channel. Check the code with the same to object used to create the verification. The user enters whichever code reached them.

Next steps