# Migrate Verify from Prelude

This page maps Prelude's v2 verification API to Bird Verify. Follow the [main migration guide](/docs/guides/verify/migrate) in order and use these mappings for steps 1 and 3.

The shapes are close. Prelude's `POST https://api.prelude.dev/v2/verification` and `POST /v2/verification/check` are a bearer-authenticated create-and-check pair keyed by the target rather than by a verification ID, and so are [`POST /v1/verify/verifications`](/docs/api/reference/create-verification) and [`POST /v1/verify/verifications/check`](/docs/api/reference/create-verification-check). Calling create again for a live recipient retries rather than starting a new verification on both platforms. What does not port is the risk layer: Prelude's signals, routing verdicts, and silent verification have no counterpart in the Bird Verify API.

## Map the create call

| What it does       | Prelude                                                 | Bird                                                    |
| ------------------ | ------------------------------------------------------- | ------------------------------------------------------- |
| Recipient          | `target.type` + `target.value`                          | `to.phone_number` or `to.email`                         |
| Code length        | `options.code_size`                                     | `options.code_length`                                   |
| Channel preference | `options.preferred_channel`, `options.channels`         | `options.channels`, else the country's configured order |
| Correlation        | `metadata.correlation_id`                               | `metadata`                                              |
| Safe retries       | `dispatch_id`                                           | `Idempotency-Key` header                                |
| Delivery callbacks | `options.callback_url`                                  | a workspace webhook subscribed to `verify.*`            |
| Custom passcode    | `options.custom_code`                                   | no equivalent                                           |
| Localization       | `options.locale`                                        | no equivalent                                           |
| Sender identity    | `options.sender_id`                                     | no equivalent: Bird sends under Authifly                |
| Message template   | `options.template_id`, `options.variables`              | no equivalent                                           |
| Android autofill   | `options.app_realm`                                     | no equivalent                                           |
| Risk signals       | `signals` (IP, device, fingerprint)                     | not accepted                                            |
| Fallback control   | `options.max_auto_fallbacks`, `options.force_challenge` | the country's channel plan                              |

The channel sets overlap only partly. Bird delivers over email, SMS, WhatsApp, and Telegram; Prelude's RCS, Viber, Zalo, voice, and silent channels have no Bird equivalent today. A number that Prelude reached over Viber or Zalo falls back to SMS here, which is a delivery-rate question worth measuring in the pilot rather than discovering at full volume.

## Map the check call

Both check endpoints take the recipient and the code with no verification ID, so this call ports almost as it stands. The response is where they differ:

| Prelude `status`       | Bird                                            |
| ---------------------- | ----------------------------------------------- |
| `success`              | `success: true`                                 |
| `failure`              | `success: false`, `reason: incorrect_code`      |
| `expired_or_not_found` | `success: false`, `reason: expired`, or a `404` |
| (no direct value)      | `success: false`, `reason: attempts_exhausted`  |

Prelude folds "wrong code" and "out of attempts" into `failure`; Bird separates them, and returns `attempts_remaining` alongside so you can show the user how many tries are left. A verification that has already resolved returns `404` rather than a status, so store the first definitive answer instead of re-checking.

## What happens to the risk layer

Prelude's create response reports a routing verdict: a `status` of `success`, `retry`, `challenged`, `blocked`, or `shadow_blocked`, with a `reason` and `risk_factors` when it refuses, and a `method` naming the channel it chose. Bird's create response is the verification itself. There is no verdict to branch on, no signals object to send, and no equivalent of a shadow block, so an integration that gates sign-ups on Prelude's verdict needs its own decision before it calls Bird.

What Bird does carry from that space is narrower and mostly configuration: per-country enablement to shut off destinations you never serve, the platform send and check caps described in [Abuse guardrails](/docs/guides/verify/sending-verifications#abuse-guardrails), and the channel plan itself. If pumping protection was the reason you chose Prelude, size that gap before scheduling the move.

## Move the callbacks

Prelude posts delivery status to the `callback_url` you set per verification. Bird delivers to endpoints your workspace registers, each subscribed to the event types it wants, so the URL leaves the request body. Subscribe to `verify.verification.*` for the session and `verify.attempt.*` for each passcode send, and verify signatures per [Standard Webhooks](https://www.standardwebhooks.com). The payloads are in [Verify events](/docs/guides/verify/events).

## Cut over

The [cutover rule](/docs/guides/verify/migrate#5-cut-over-one-code-lifetime-at-a-time) in the main guide applies unchanged: a code Prelude issued cannot be checked by Bird, so switch at the create call and route each check to whichever provider issued that verification until the last one expires. Because both APIs key on the recipient, the branch is a single conditional around your existing call sites rather than a rewrite.

Watch conversion during the pilot alongside delivery. Prelude routes per request across a wider channel set; Bird routes on the channel order you set per country. If a market's conversion drops, reorder that country's channels before concluding anything about the port.

## Next steps

- [Sending verifications](/docs/guides/verify/sending-verifications): the full contract for both calls, statuses, and limits
- [Country configuration](/docs/guides/verify/countries): per-country channel order and availability
- [Senders and branding](/docs/guides/verify/senders): what the recipient sees on each channel
- [Verify events](/docs/guides/verify/events): the events your callback consumer moves to