# 从其他服务商迁移到 Verify

使用本指南将手机和邮箱一次性验证码（OTP）从其他验证服务商迁移到 Bird Verify。迁移工作量不大，因为接口面很小：两个调用即可替代你当前服务商的创建-校验调用对，Bird 负责验证码、消息内容和背后的投递通道。

有一个结构性差异决定了迁移的方式。Bird 没有按应用划分的 service 对象，也没有需要你跟踪的 verification ID。验证以接收方为标识，因此两个调用使用相同的 `to`，你的集成需要维护的状态缩减为零。

迁移清单：

1. [映射 create 和 check 调用](#1-映射-create-和-check-调用)
2. [设置通道、国家/地区和发送方](#2-设置通道国家地区和发送方)
3. [移植验证生命周期](#3-移植验证生命周期)
4. [切换 webhook](#4-切换-webhook)
5. [按一个验证码有效期逐步切换](#5-按一个验证码有效期逐步切换)

步骤 1 和 3 取决于你要离开的服务商。你的[服务商指南](#从特定供应商迁移)包含逐字段映射和状态转换说明。

## 1. 映射 create 和 check 调用

[`POST /v1/verify/verifications`](/docs/api/reference/create-verification) 发送验证码。最简请求只需一个接收方：

```bash
curl -X POST https://us1.platform.bird.com/v1/verify/verifications \
  -H "Authorization: Bearer $BIRD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": {"phone_number": "+15551234567"}}'
```

[`POST /v1/verify/verifications/check`](/docs/api/reference/create-verification-check) 提交用户输入的内容，以相同的接收方加验证码为键。完整载荷请参阅[发送验证](/docs/guides/verify/sending-verifications)。

迁移时需要处理四个差异：

- **接收方即为键。** 返回 verification SID 或 ID 的服务商在 check 时要求传回该值。Bird 改为按地址集匹配，且必须精确匹配：同时使用邮箱和手机号创建的验证，不能只用其中一个来查找。原来保存服务商 verification ID 的字段可以删除。
- **错误验证码返回 `200`。** 响应中包含 `success: false`，一个值为 `incorrect_code`、`expired` 或 `attempts_exhausted` 的 `reason`，以及 `attempts_remaining`。将错误处理路径留给请求失败的情况。验证进入终态后，后续 check 返回 `404` 而非 `success: false`。
- **Bird 生成验证码且从不返回它。** 没有自定义验证码参数，因此如果你的集成之前自行提供验证码或读取验证码后自行发送，这里没有对应功能。
- **两个端点都接受 `Idempotency-Key`。** 超时后的重放请求返回原始响应，不会再次发送验证码或消耗一次尝试。

每次请求可设置的选项有意保持精简：`options.code_length` 和 `options.channels`，用于为单次请求重新排列或缩减通道。其他所有配置都是工作区级别的设置，而非发送时的字段。

## 2. 设置通道、国家/地区和发送方

Bird 通过邮件、SMS、WhatsApp 和 Telegram 投递验证码。对于手机接收方，大多数国家/地区默认先尝试 WhatsApp，以 SMS 作为回退；当某次发送失败时，投递会推进到计划中的下一个通道。在 [**Countries**](https://bird.com/dashboard/w/verify/countries) 页面按国家/地区设置通道顺序或关闭某个通道；同时禁用你不服务的国家/地区，因为未使用的目的地不是覆盖面，而是 SMS 欺诈的暴露面。

在确定迁移日期之前，有两个差距值得对照你当前的流程进行检查：

- **没有语音通话通道，也没有静默网络认证。** 如果你的流程在用户无法接收 SMS 时回退到电话呼叫，在这里需要另寻方案。
- **在切换前选择发送者。** 电子邮件、SMS 和 WhatsApp 默认使用 Bird Verify，也可改用 Authifly。您还可以使用已验证的电子邮件域名、现有的 SMS Sender ID，或已连接且拥有已批准身份验证模板的 WhatsApp 号码。Telegram 使用其自有的已验证通知账号。如果您希望保留用户已熟悉的 SMS 发送者，请确认它在每个目标国家/地区受支持且已注册。[发送者与品牌设置](/docs/guides/verify/senders)涵盖了所有选项和回退行为。

如果您使用自己的 WhatsApp 号码，请在 Verify 配置中选择一个现有的已批准身份验证模板。Bird 控制电子邮件和 SMS 消息内容。您无法在单次验证请求中传入模板 ID 或自定义消息正文。

## 3. 移植验证生命周期

验证在解析之前处于 `pending` 状态：正确的验证码在有效期内到达时变为 `verified`，原因为 `attempts_exhausted` 或 `undeliverable` 时变为 `failed`，原因为 `ttl_elapsed` 时变为 `expired`。将原供应商的终态映射到这三种状态，并将 `reason` 视为开放枚举。

影响 UI 的时间参数是 [**Configure**](https://bird.com/dashboard/w/verify/configure) 页面上的工作区设置：验证码有效时长、用户可尝试校验的次数，以及重新发送的冷却时间。将它们设为与用户当前体验一致，而不是重写 UI 文案。验证码长度是唯一可以按请求单独设置的值。默认值和取值范围请参阅[验证设置](/docs/guides/verify/sending-verifications#verification-settings)。

两个行为通常可以替代你已有的代码：

- **重新发送就是再次调用 create。** 使用相同的接收方调用 create：在冷却期内会返回当前活跃的验证而不发送新消息，冷却期过后则发出一个新验证码。为同一活跃验证发出的每个验证码在验证完结前都保持有效，因此用户在收到第二个验证码后输入第一个，不会因此失败。
- **"I didn't get a code" 有自己的端点。** [`POST /v1/verify/verifications/next-channel`](/docs/api/reference/create-verification-next-channel) 推进到计划中的下一个通道并立即在该通道发送，忽略重新发送冷却时间，但保留有效期、尝试次数预算和验证本身。将它绑定到按钮上，而不是在投递失败的通道上循环重新发送。

在你的设置之上，还有你无需配置的平台防护机制：按地址的每小时发送上限和按接收方的校验上限，超限时均以 `429` 和 `Retry-After` 作为响应。如果你的当前服务商允许你提高按端点的请求速率限制且你确实提高了，请在切换前将峰值与[滥用防护](/docs/guides/verify/sending-verifications#abuse-guardrails)中的数值进行对比。

## 4. 切换 webhook

Verify 在两个维度上发出事件。会话事件 `verify.verification.created`、`verify.verification.verified` 和 `verify.verification.failed` 跟踪验证本身。尝试事件 `verify.attempt.sent`、`verify.attempt.delivered` 和 `verify.attempt.undelivered` 跟踪每次验证码发送，因此重新发送或通道故障转移会向同一会话添加尝试。使用 [`POST /v1/webhooks`](/docs/api/reference/create-webhook) 为端点订阅所需的事件类型；载荷详见 [Verify 事件](/docs/guides/verify/events)。

订阅集成所需的会话事件。`verify.verification.failed` 覆盖投递死胡同的场景：当计划耗尽且记录的失败表明没有验证码被发出时，它会以 `reason: "undeliverable"` 触发，其 `last_attempt_reason` 标识最后一个尝试通道上的失败原因。验证过期或用尽检查尝试次数时不会发出会话事件，因此这两种结果应从检查响应中获取。

这些事件用于分析、告警和支持工具。身份验证决策来自同步响应的检查调用，登录流程不应等待 webhook 才放行用户。投递至少一次且无序，按 [Standard Webhooks](https://www.standardwebhooks.com) 签名，因此请像处理其他 Bird 事件一样，根据 `webhook-id` 请求头去重。

## 5. 按一个验证码有效期逐步切换

Verify 没有模拟收件人：值得测试的是验证码能否送达，因此在上线之前，请在您启用的每个通道上，使用您控制的手机号和邮箱运行集成测试。

切换本身有一条容易遗漏的规则。**旧供应商签发的验证码无法通过 Bird 验证，反之亦然。** 因此请在 create 调用处切换，并在一个验证码有效期内，将每次 check 路由到签发该验证的供应商。实际操作如下：

1. 记录每个进行中的验证由哪个供应商创建。
2. 开始将一部分新验证通过 Bird 发送，并针对 Bird 检查这些验证。
3. 继续通过旧供应商检查较早的验证，直到最后一个过期，所需时间为一个验证码有效期加上一段余量。
4. 当第一批次的转化率正常后，提高 Bird 的比例，然后停用旧路径。

关注转化率而非仅关注投递。[**Verifications**](https://bird.com/dashboard/w/verify/verifications) 页面和 Verify 指标显示发送量、投递量以及多少验证达到了 `verified`，这个数字能告诉您通道顺序或新的发送方身份是否正在影响注册量。

## 从特定供应商迁移

- [Twilio Verify](/docs/guides/verify/migrate/twilio)：Service 变为工作区设置，`VerificationCheck` 变为基于收件人的检查，通道和状态映射
- [Prelude](/docs/guides/verify/migrate/prelude)：几乎相同的 create-and-check 结构，路由信号和静默验证是无法迁移的部分

## 后续步骤

- [发送验证](/docs/guides/verify/sending-verifications)：完整的请求与响应契约、状态和限制
- [国家/地区配置](/docs/guides/verify/countries)：按国家/地区的通道顺序和可用性
- [发送方与品牌](/docs/guides/verify/senders)：每条消息的呈现方式，以及品牌化的邮件发送方
- [Verify 事件](/docs/guides/verify/events)：会话事件和尝试事件的载荷

## 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)
