---
title: "Set up Bird as your Auth0 phone provider"
description: "Configure Auth0 to send verification codes through Bird Verify and account notifications through Bird SMS, then test delivery and sign-in."
canonical: "https://bird.com/docs/knowledge-base/integrations/auth0"
---

# Set up Bird as your Auth0 phone provider

Use Bird as the **Custom Phone Provider** for Auth0 MFA, passwordless SMS, and account notifications. Bird Verify delivers the codes Auth0 generates; Auth0 checks the codes users enter. Bird SMS delivers other text notifications, including blocked-account, password-change, and password-breach messages, using Auth0's rendered text.

This guide supports SMS notifications and SMS or voice verification codes. Other voice notifications and email delivery need separate implementations. Older Auth0 tenants can use the separate **Send Phone Message** MFA flow.

## Before you start

You need an Auth0 test tenant, a Bird workspace with balance, and a phone number you control. Start in a development tenant before changing your production login flow.

## Prepare your Bird workspace

1. Open **Verify** > [**Configure**](https://bird.com/dashboard/w/verify/configure) > **General**.
2. Set **Duration** to your Auth0 code lifetime. These settings apply across the workspace; use a separate workspace if other applications need different settings.
3. Open [**Destinations**](https://bird.com/dashboard/w/verify/destinations), select the recipient's country, and enable SMS. Enable voice only if you intend to use voice challenges.
4. Check your [Verify senders](/docs/guides/verify/senders). Verify uses its own templates and branding for codes; Auth0's OTP message text is not used.
5. For account notifications, open [**SMS** > **Senders**](https://bird.com/dashboard/w/sms/senders) and configure a sender permitted for your destinations. Copy its phone number, alphanumeric sender ID, or short code for the `BIRD_SMS_FROM` setting in Auth0. See [SMS senders](/docs/guides/sms/senders).
6. For account notifications, also enable the recipient countries under [**SMS** > **Destinations**](https://bird.com/dashboard/w/sms/destinations). These settings are separate from Verify destinations.

![Bird Verify settings with code duration, attempt limits, and autofill options](/images/docs/dashboard-verify-configure-general.png)

## Create the Bird API key

1. Open **Platform tools** > [**API keys**](https://bird.com/dashboard/w/api-keys).
2. Select **Create API key** and enter a name such as **Auth0 phone provider**.
3. Grant **Verify** > **Write** for verification codes. If your tenant also sends account notifications, add **SMS** > **Write**. Create the key.
4. Copy the secret to your secret manager. You will paste it into Auth0 in the next step. Replace the key in Auth0 before it expires.

![Bird API Keys page with the Create API key button and a masked example key](/images/docs/dashboard-api-keys.png)

The key's prefix selects the EU or US API region. You do not need an OAuth client or a region setting. `BIRD_SMS_FROM` applies only to account notifications; Verify uses its own sender settings. See [API key management](/docs/knowledge-base/account-security/api-keys) for key rotation and access restrictions.

## Configure the Auth0 phone provider

The provider sends OTP notifications through Verify and other text notifications through SMS. SMS notifications keep Auth0's rendered text and use the `transactional` category. They require `BIRD_SMS_FROM`; the snippet uses this configured sender instead of Auth0's optional `from` value.

1. In the [Auth0 Dashboard](https://manage.auth0.com/), select your test tenant.
2. Open **Branding** > **Phone Provider** and enable the **Phone Message Provider** toggle.
3. Select **Custom** under **Choose a provider**.
4. Select **Text** under **Delivery Methods**. Select **Voice** only if it is available in your Auth0 plan and configured in Bird.
5. Under **Provider Configuration**, select the **Secrets** key icon, then **Add Secret**.
6. Enter `BIRD_API_KEY` as the name, paste your Bird key as the value, and select **Create**.
7. If your tenant sends account notifications, add another secret named `BIRD_SMS_FROM` with the sender you configured in Bird. For verification codes, this secret is not required. Close the Secrets panel.
8. Paste the code below into the provider editor. It uses Node.js 22 and needs no npm dependencies.
9. Select **Save** and confirm the provider change.

<!-- external-screenshot: Auth0 → Branding → Phone Provider → Custom; tenant and trial banners excluded from capture -->

![Auth0 Custom phone provider with Text selected and the Provider Configuration editor](/images/docs/auth0-verify-phone-provider.png)

Use a real enrollment or login to test OTP delivery. A test notification without a code cannot test Verify. Non-OTP text notifications use the SMS route, including provider test messages when Auth0 supplies them in that form.

### Provider code

<!-- auth0:custom-phone-provider -->

```javascript
async function sendNotification(apiKey, notification, smsFrom) {
  const region = typeof apiKey === "string" && /^bk_([a-z]{2}\d+)_[A-Za-z0-9_-]+$/.exec(apiKey);
  if (!region) {
    return { error: "Set BIRD_API_KEY to a current Bird workspace API key.", retryable: false };
  }
  if (
    typeof notification.recipient !== "string" ||
    !/^\+[1-9]\d{1,14}$/.test(notification.recipient)
  ) {
    return { error: "Auth0 must supply the recipient in E.164 format.", retryable: false };
  }
  if (typeof notification.message_type !== "string" || !notification.message_type) {
    return { error: "Auth0 must supply a notification message type.", retryable: false };
  }

  const isOtp = notification.message_type.startsWith("otp");
  let path;
  let body;
  if (isOtp) {
    const channel = { text: "sms", voice: "voice" }[notification.delivery_method];
    if (channel !== "sms" && channel !== "voice") {
      return { error: "Select Text or Voice for Auth0 verification codes.", retryable: false };
    }
    if (typeof notification.code !== "string" || !/^[0-9]{4,8}$/.test(notification.code)) {
      return {
        error: "Auth0 must supply a verification code containing 4 to 8 digits.",
        retryable: false,
      };
    }
    path = "/v1/verify/verifications";
    body = {
      to: { phone_number: notification.recipient },
      options: { custom_code: notification.code, channels: [channel] },
    };
  } else {
    if (notification.delivery_method !== "text") {
      return {
        error: "This provider sends account notifications by SMS. Select Text for these messages.",
        retryable: false,
      };
    }
    if (typeof smsFrom !== "string" || !smsFrom.trim()) {
      return {
        error: "Set BIRD_SMS_FROM to a sender configured in your Bird workspace.",
        retryable: false,
      };
    }
    if (typeof notification.as_text !== "string" || !notification.as_text.trim()) {
      return { error: "Auth0 must supply the rendered notification text.", retryable: false };
    }
    path = "/v1/sms/messages";
    body = {
      to: notification.recipient,
      from: smsFrom.trim(),
      text: notification.as_text,
      category: "transactional",
    };
  }

  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 8000);
  try {
    const response = await globalThis.fetch(`https://${region[1]}.platform.bird.com${path}`, {
      method: "POST",
      redirect: "error",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
      signal: controller.signal,
    });
    if (!isOtp && response.status === 202) {
      await response.body?.cancel();
      return {};
    }
    if (isOtp && response.status === 200) {
      const verification = await response.json();
      if (verification.status === "pending") return {};
      return {
        error: "Bird did not accept the verification for delivery. Check the Verify log.",
        retryable: false,
      };
    }
    // Provider error bodies can echo input. Keep codes, messages, and recipients out of Auth0 logs.
    await response.body?.cancel();
    const advice = {
      401: "Replace the expired or revoked Bird API key.",
      402: "Check the Bird workspace balance.",
      403: "Check the key's Verify or SMS write permission and access restrictions.",
      422: "Check the message, enabled destinations, and sender settings for the relevant Bird product.",
      429: "Wait for the Bird resend cooldown or rate limit before retrying.",
    };
    return {
      error: `Bird returned HTTP ${response.status}. ${advice[response.status] || "Check the Bird request log."}`,
      retryable: response.status === 429 || response.status >= 500,
    };
  } catch {
    return { error: "The Bird request timed out or could not be completed.", retryable: true };
  } finally {
    clearTimeout(timeout);
  }
}

exports.onExecuteCustomPhoneProvider = async (event, api) => {
  const result = await sendNotification(
    event.secrets?.BIRD_API_KEY,
    event.notification || {},
    event.secrets?.BIRD_SMS_FROM,
  );
  if (result.error) {
    if (result.retryable) api.notification.retry(result.error);
    else api.notification.drop(result.error);
  }
};
```

<!-- /auth0:custom-phone-provider -->

## Enable phone verification for MFA

1. Open **Security** > **Multi-factor Auth**.
2. Select **Phone Message** in the **Factors** list.
3. Enable the phone factor and select **SMS** as the delivery method. Set **Code Length** to four to eight digits and make **Code Lifetime** match your Bird Verify duration. If **Use Tenant-Level Messaging Provider** appears, enable it. New tenants already use that provider.
4. Save the factor settings and return to **Multi-factor Auth**.
5. Choose when your application requires MFA. For a development tenant, **Always** requires MFA at login. Leave the option to remember the browser unchecked during testing. This policy applies to the tenant's applications; review [Auth0's MFA setup](https://auth0.com/docs/secure/multi-factor-authentication/enable-mfa) before changing a production tenant.

<!-- external-screenshot: Auth0 → Security → Multi-factor Auth → Phone Message; tenant banner hidden for capture -->

![Auth0 Phone Message factor settings with SMS enabled](/images/docs/auth0-verify-mfa.png)

For verification codes, **Text** sends SMS and **Voice** makes a voice call. Account notifications support **Text** only; a voice notification fails with an explanation in the Auth0 log. The provider does not select another delivery method as a fallback.

## Enable passwordless SMS

Complete this section only if you want users to sign in with a phone number and code instead of a password.

1. Open **Authentication** > **Authentication Profile**.
2. Select **Identifier First** and save.
3. Open **Authentication** > **Passwordless** and select **SMS**. Some tenants label this connection **SMS (Twilio)** even when the custom tenant-level provider handles delivery.
4. On **Settings**, use the tenant-level provider if that option appears. Set **OTP Length** to four to eight digits. If you also use MFA, use the same code length for both flows: Bird rejects a different length while a verification for the recipient is still pending. Make the expiration match the Bird Verify duration, then save the settings.
5. On **Applications**, enable the test application.
6. Select **Try**, enter a phone number you control, and enter the received code.

<!-- external-screenshot: Auth0 → Authentication → Passwordless → SMS → Applications; title shortened to SMS; no credentials or tenant identifiers included -->

![Auth0 SMS passwordless Applications tab with the test application enabled](/images/docs/auth0-verify-passwordless.png)

If your policy also requires MFA, the user must complete another configured factor after the passwordless code. A delivered SMS alone does not prove the full login succeeds. See [Auth0's passwordless SMS documentation](https://auth0.com/docs/authenticate/passwordless/authentication-methods/sms-otp) for application prerequisites.

## Use the Send Phone Message Action

The separate MFA Action targets Auth0's **Send Phone Message** flow for MFA enrollment and challenges. It is separate from the tenant-level provider and does not replace the passwordless setup.

This flow requires a tenant that still offers the custom MFA delivery-provider selector. New tenants can require the tenant-level provider instead; use the setup above when the selector is absent.

Ask Bird for the **Send Phone Message** preview bundle before configuring this flow. The bundle includes `integration.action.js` and its installation guide. Create the Action with that file, select Node.js 22, and add the `BIRD_API_KEY` secret. Deploy the Action and add it to the flow. Select the custom MFA provider under **Security → Multi-factor Auth → Phone Message**. A tenant-level provider or an older Send Phone Message Hook can take precedence; configure one delivery path for the test.

The Marketplace installation guide will become available with the reviewed listing. The integration has not been published in Auth0 Marketplace yet.

## Test delivery and sign-in

Start an MFA enrollment with a test user and enter the received code in Auth0. Sign in again to test the challenge. Confirm that a wrong code fails, an expired code fails, and a resend delivers a usable code. Test passwordless SMS separately if you use that flow. Test voice before enabling it for customers.

For account notifications, trigger a password-change, blocked-account, or password-breach notification in your Auth0 test tenant. Confirm that the SMS contains the text from your Auth0 template and comes from the configured Bird sender. Check its delivery in the [**SMS log**](https://bird.com/dashboard/w/sms/messages). Bird's `202 Accepted` response means delivery is queued; check the message status and the received SMS.

Inspect the [**Verify log**](https://bird.com/dashboard/w/verify/verifications) for delivery attempts and Auth0 tenant logs for authentication results. An accepted API request means Bird started delivery; it does not prove that the phone received the code.

Auth0 checks the code itself. Bird does not receive that result yet, so a successful Auth0 login can leave a Bird verification that later expires. Bird's verified events and conversion metrics do not reflect these successful logins. Do not configure an Auth0 log-stream webhook to Bird for this integration yet.

Do not call Bird's Check endpoint with Auth0's generated code inside the delivery Action. That would mark the verification successful before the user entered the code.

## Troubleshoot delivery

| Symptom                  | What to check                                                                                                                                                                                                                         |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| HTTP 401 or 403          | Replace an expired key, confirm the workspace and the relevant Verify or SMS write permission, and check key access restrictions.                                                                                                     |
| HTTP 402                 | Check the Bird workspace balance.                                                                                                                                                                                                     |
| HTTP 422                 | For OTPs, check code length and Verify settings. For account notifications, check SMS destinations and sender registration.                                                                                                           |
| HTTP 429                 | For Verify, check the [resend cooldown and sending limits](/docs/guides/verify/sending-verifications#abuse-guardrails). For SMS, check your organization's [`sms_send` request limit](/docs/guides/rate-limits#when-you-hit-a-limit). |
| Timeout or HTTP 5xx      | Inspect the Verify or SMS log. Auth0 retries transient Custom Phone Provider failures; a lost response can result in another send.                                                                                                    |
| No Action execution      | Check the selected provider, the MFA tenant-level setting, and any older Send Phone Message Hook.                                                                                                                                     |
| Account alert is dropped | Set `BIRD_SMS_FROM`, supply a nonempty Auth0 message, and use Text delivery. Voice account notifications are not supported.                                                                                                           |

The Action reports safe diagnostics without logging codes, phone numbers, message text, API keys, or provider response bodies.

### Recover from a lost response

Bird can accept a request and start delivery before the response is lost. This snippet cannot confirm that earlier delivery automatically. For Verify, a retry during Bird's cooldown returns HTTP 429. After the cooldown, a retry can send another message and consume another send attempt. Retrying an SMS notification can also send a duplicate; check the SMS log before retrying manually.

[Auth0's event reference](https://auth0.com/docs/actions/reference/custom-phone-provider/custom-phone-provider-event-object) does not document a stable notification ID for retry deduplication. Do not use only the phone number and code as an idempotency key: that can suppress an intentional resend of the same code.

Check the Bird Verify log before requesting another code. If the code arrived and Auth0 still shows the code-entry screen, enter it there. If delivery failed or Auth0 closed the challenge, wait for the cooldown and request a new code through Auth0. A delivery log entry does not replace Auth0's code check.

## Next steps

- [Sending SMS](/docs/guides/sms/sending-sms): check account-notification senders, destinations, and delivery.
- [Verify settings](/docs/guides/verify/settings): configure code lifetime and resend behavior.
- [Verify senders](/docs/guides/verify/senders): configure delivery and branding.
- [Auth0 Custom Phone Provider reference](https://auth0.com/docs/actions/reference/custom-phone-provider/custom-phone-provider-event-object): inspect the notification event.

## Related resources

- [Verify phone numbers at signup](/learn/series/verify-phone-numbers-at-signup) (video)
- [What does OTP mean? One-time passwords explained](/explained/verify/what-does-otp-mean) (answer)
- [Customer verification](/verify-api) (product)
- [Build your first integration](/learn/paths/integration) (course)

[Get an implementation brief](/learn/workspace?topic=verify)
