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 are workspace configuration. If you ran several Services to separate brands, apps, or environments, that separation becomes separate workspaces, each with its own API key and settings, rather than an ID in the path.
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 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