Migrate Verify from Twilio
This page maps Twilio Verify v2 to Bird Verify. Follow the main migration guide in order and use these mappings for steps 1 and 3.
The Service is the piece with no counterpart. Twilio addresses POST https://verify.twilio.com/v2/Services/{ServiceSid}/Verifications, and the Service holds code length, TTL, lookup, landline handling, and rate limits. Bird addresses POST /v1/verify/verifications with no service segment: those settings belong to your workspace rather than an ID in the path. Multiple Service IDs have no equivalent within a workspace, and you cannot select a configuration per request.
Hand this to your agent
Paste this into Claude Code, Cursor, or Codex. The agent works through this page against your own repository, using whichever Bird surface it already has: the MCP server if one is connected, the CLI if it is installed and signed in.
Codebeispiel
I am moving a phone verification integration from Twilio Verify to Bird Verify. Route through it with me.
1. Check what you already have before setting anything up. If Bird's MCP server is connected, use its tools. If the Bird CLI is installed and signed in, use that. Either one is enough, and every step below is an action you take with whichever you have. Only if neither is present, follow https://bird.com/docs/ai/set-up-your-agent.md to set one up and sign me in. Every Bird docs page serves Markdown at its own URL with `.md` appended, so fetch that rather than the HTML.
2. Read https://bird.com/docs/guides/verify/migrate/twilio.md for the create, check and status mapping, and https://bird.com/docs/guides/verify/migrate.md for the order the steps go in.
3. Find and list my Twilio Verify usage in this repository before you change anything: the Verifications and VerificationCheck call sites, every Service SID they name and what each Service is configured with, and any place I read a verification status. Bird has no Service segment and no per-request configuration selection, so tell me if I use more than one Service and what differs between them.
4. Tell me early which of these I depend on, because none of them carries over and I would rather know now than at the end. Bird Verify has no voice channel and no silent or network-based authentication. It generates the code itself and never returns it, so I cannot supply my own. It takes no per-request template, locale or message body, and Bird owns the sender identity, so the message my users see will change on cutover. Twilio's Service holds code length, TTL, lookup, landline handling and rate limits; on Bird those belong to the workspace rather than to an ID in the path, so tell me which of my Service settings have no home.
5. Configure my channels and destinations following https://bird.com/docs/guides/verify/countries.md and https://bird.com/docs/guides/verify/senders.md. While you are there, disable every country I do not actually verify into. An enabled destination I never send to is not reach, it is exposure to SMS pumping, so ask me which countries I serve rather than leaving the defaults.
6. Port the create and check calls using the mapping tables on the provider page, and move my status handling to Bird's events: https://bird.com/docs/guides/verify/sending-verifications.md and https://bird.com/docs/guides/verify/events.md.
7. Cut over at the create call, not all at once, because a code issued by Twilio Verify cannot be checked by Bird and a code issued by Bird cannot be checked by Twilio Verify. From the moment I say go, send every NEW verification to Bird, and keep routing each check to whichever provider issued that verification. Keep both paths live for one full code lifetime plus margin, then retire the old one. Tell me how you will decide which provider issued a given verification before you write any of it.
8. Test before any real traffic. Bird Verify has no simulated recipients, so do not look for a sandbox: the thing worth testing is the code arriving. Run the integration against a phone number and a mailbox I control, on each channel I enabled, and show me what arrived on each one.
9. Stop and ask me wherever a step needs a decision. Do not start routing new verifications to Bird until I have seen those test results and replied with the words cut over to Bird. Retiring the Twilio Verify path is a separate step: ask me again and wait for me to reply with the words retire the Twilio Verify path, and do not retire it while any code it issued could still be checked. A reply that agrees without naming what it is authorising is not authorisation. Finish by telling me what is left that only a person can do.Map the create call
| What it does | Twilio Verify | Bird |
|---|---|---|
| Recipient | To | to.phone_number or to.email |
| Channel | Channel | options.channels, else the country's configured order |
| Code length | Service CodeLength | options.code_length, else the workspace default |
| Code lifetime | Service TTL | the workspace Duration setting |
| Attempt limit | Service max attempts | the workspace Maximum Retries setting |
| Correlation | Tags | metadata |
| Safe retries | (none) | Idempotency-Key header |
| Custom passcode | CustomCode | no equivalent |
| Localization | Locale | no equivalent |
| Message content | TemplateSid, CustomFriendlyName, ChannelConfiguration | no equivalent, apart from a branded email sender |
| Per-key throttles | RateLimits | fixed platform guardrails |
| Fraud controls | RiskCheck, Fraud Guard, DeviceIp | not exposed on the API |
| SMS autofill | AppHash | no equivalent |
| PSD2 | Amount, Payee | no equivalent |
The channels do not line up one to one either:
| Twilio Channel | Bird |
|---|---|
| sms | sms |
| call | no equivalent |
| sna, auto | no equivalent |
| rcs | no equivalent |
| (none) | telegram, available for numbers registered with Telegram |
A flow that uses call as the accessibility fallback, or sna and auto for a codeless path, needs rethinking before you commit to a date. Everything else is a channel-order change on the Countries page rather than a per-request parameter.
Map the check call
Twilio's POST /v2/Services/{ServiceSid}/VerificationCheck takes To or VerificationSid, plus Code. Bird's POST /v1/verify/verifications/check takes only the recipient and the code, so the VerificationSid path disappears along with the column you stored it in. Supply exactly the address set you created the verification with.
The result shape differs where it matters most:
- Twilio answers with a status field; Bird answers with a boolean. success: true means verified. success: false carries a reason of incorrect_code, expired, or attempts_exhausted, plus attempts_remaining, so the "how many tries left" figure you may be counting yourself comes back on the response.
- Both go to 404 once the verification is spent. Twilio deletes the verification when it is approved, expired, or out of attempts; Bird stops accepting checks in any final state. Store the first definitive answer rather than re-checking.
Translate statuses
| Twilio status | Bird status | Bird reason |
|---|---|---|
| pending | pending | none |
| approved | verified | none |
| max_attempts_reached | failed | attempts_exhausted |
| expired | expired | ttl_elapsed |
| canceled | no equivalent: a verification is not cancellable |
There is no update endpoint, so the Twilio pattern of forcing a verification to approved or canceled from your backend has no counterpart. A verification ends when the user verifies it, exhausts the attempts, or lets it expire.
Move the event stream
Twilio Verify reports activity through Event Streams: a sink plus a subscription to verification status events, configured outside the Verify API. Bird uses the same webhook mechanism as every other channel. Subscribe an endpoint to verify.verification.* for session events and verify.attempt.* for individual passcode deliveries, and verify the signature per Standard Webhooks. See Verify events.
The two axes matter when you port dashboards. Twilio's verification status events line up with Bird's session events, and Bird's attempt events add per-send delivery outcomes on the same session, including the sends that a resend or a channel failover produces.
Cut over
The cutover rule in the main guide is the one to plan around: a code issued by Twilio cannot be checked by Bird, so switch at the create call and keep routing checks to whichever provider issued the verification until the last Twilio code expires.
Also budget for the sender change. Twilio Verify delivers under your Service's friendly name and your own sender pool; Bird delivers under one of its own managed identities, Bird Verify or Authifly, on email, SMS, and WhatsApp, with your own domain available for the email channel. Users mid-signup see a different sender, and support scripts that say "look for a text from us" need updating.
Next steps
- Sending verifications: the full contract for both calls, statuses, and limits
- Country configuration: where channel order and availability now live
- Senders and branding: what the recipient sees on each channel
- Verify events: the events your Event Streams consumer moves to