twilio-migrate-messaging-to-verify
This skill guides migrating an existing one-time-passcode system from Twilio's Programmable Messaging API to the Verify API, which handles code generation…
Identity proven by GitHub's verified organisation badge (twilio)
- Agents
- Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode, claude.ai and API
- License
- MIT (permissive)
- Pin
- 8aba46fpinned Oct 9, 2026
- Context cost
- about 3,200 tokenswhen the skill loads
- GitHub stars
- 37on twilio/ai
- Scripts
- None found
What it does
“Migrate an existing OTP / one-time-passcode flow from the Twilio Programmable Messaging API (self-built: generate code → send SMS → validate) to the Twilio Verify API (managed code generation, delivery, expiry, rate limiting, and Fraud Guard). Covers what Verify replaces, creating a Verify Service, replacing send + check logic, E.164 formatting with Lookup, bring-your-own custom codes, error-code translation, and a safe incremental cutover. Use when a developer is moving OTP/2FA delivery off Programmable Messaging onto Verify, or asks "should I use Verify instead of sending my own SMS codes?".”
Generated summary from the skill's own text
This skill guides migrating an existing one-time-passcode system from Twilio's Programmable Messaging API to the Verify API, which handles code generation, delivery, expiry, and fraud protection automatically. It covers creating a Verify Service, replacing custom send and validation logic with two API calls, normalizing phone numbers to E.164 format, and executing a safe incremental cutover. The skill maps error codes, explains rate limits, and documents what features are unavailable post-migration.
When to use it
- Moving OTP/2FA delivery from self-built Messaging to Verify
- Replacing custom code generation and database persistence logic
- Adding fraud protection without maintaining SMS-pumping defenses
- Supporting multiple channels (SMS, voice, WhatsApp, email) with one API
When not to
- Need full control of message body beyond template customization
- Sending non-OTP transactional messages via that number
- Cannot normalize existing phone numbers to E.164 format
Install
These commands install from the publisher's repository and need no account.
skills CLI
npx skills add twilio/ai --skill twilio-migrate-messaging-to-verifyInstalls from the repository's default branch, which may have changed since the commit we checked.
GitHub CLI 2.90 or later
gh skill install twilio/ai twilio-migrate-messaging-to-verify@8aba46fb65dc8d9a20f4b301a68352064b4159a5Installs the commit we checked (8aba46f).
Copy by hand
Download the folder at 8aba46f and put it in .claude/skills/twilio-migrate-messaging-to-verify for Claude Code and in .agents/skills/twilio-migrate-messaging-to-verify for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.
Claude Code plugin
/plugin install twilio-developer-kit@claude-plugins-officialThe publisher's plugin in Claude's official marketplace. It may hold more than this skill, and we have not pinned it.
Copy for your agent
A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.
Prompt
Install the agent skill "twilio-migrate-messaging-to-verify" by Twilio. Source, pinned to commit 8aba46fb65dc8d9a20f4b301a68352064b4159a5: https://github.com/twilio/ai/tree/8aba46fb65dc8d9a20f4b301a68352064b4159a5/skills/twilio/twilio-migrate-messaging-to-verify 1. Download that folder at exactly this commit. Do not use the default branch. 2. Copy every file unchanged, keeping the folder structure, into .claude/skills/twilio-migrate-messaging-to-verify/ for Claude Code and .agents/skills/twilio-migrate-messaging-to-verify/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist. 3. Do not edit the files, and do not run any script in the folder unless I ask you to. 4. When you are done, list the files you copied and where you put them. Listing and checks: https://www.agentnative.dev/skills/twilio/twilio-migrate-messaging-to-verify
SKILL.md at the pin
---
name: twilio-migrate-messaging-to-verify
description: >
Migrate an existing OTP / one-time-passcode flow from the Twilio Programmable
Messaging API (self-built: generate code → send SMS → validate)
to the Twilio Verify API (managed code generation, delivery, expiry, rate
limiting, and Fraud Guard). Covers what Verify replaces, creating a
Verify Service, replacing send + check logic, E.164 formatting with Lookup,
bring-your-own custom codes, error-code translation, and
a safe incremental cutover. Use when a developer is moving OTP/2FA delivery off
Programmable Messaging onto Verify, or asks "should I use Verify instead of
sending my own SMS codes?".
---
## Overview
If you built OTP on the **Programmable Messaging API**, you are maintaining code
that Twilio Verify manages for you: random code generation, a database of codes
with expiry, resend/rate-limit logic, and fraud filtering. **Verify** replaces
that entire stack with two API calls — send a verification, check a code — and
adds one-click Fraud Guard (SMS pumping protection), managed sender pools, and
automatic A2P/compliance handling.
**What you delete when you migrate:**
| You built (Programmable Messaging) | Verify replaces it with |
|---|---|
| Buy/manage a sending number or Messaging Service | Managed sender pool (short/long code, toll-free, alpha ID) chosen per-country |
| `random.randint(...)` code generation | Built-in generation (4–10 digits, default 6) |
| DB table storing `code`, `phone`, `expires_at` | Stateless — Verify tracks the code and 10-min expiry |
| Custom resend throttling / attempt counting | Built-in rate limits (5 sends & 5 checks per number / 10 min) |
| SMS pumping / fraud filtering | Fraud Guard (geo-permissions, rate anomaly) — one click |
| A2P 10DLC campaign registration for the OTP number | Exempt — Verify traffic needs no 10DLC campaign |
| Per-channel integration (SMS, voice, WhatsApp…) | One API; change the `channel` param |
| Locale-specific message copy | Pre-screened templates in dozens of languages, auto-resolved by country |
> This skill focuses on **the migration**. For full Verify API reference (all
> channels, service config, status values), see `twilio-verify-send-otp`. To
> decide *whether* Verify fits before writing code, see
> `twilio-identity-verification-advisor`.
**When to stay on Programmable Messaging:** you need full control of the message
body/branding beyond what templates allow, custom delivery routing, or you are
sending non-OTP transactional traffic. For standard OTP/2FA, migrate to Verify.
---
## Prerequisites
- An existing Programmable Messaging OTP implementation (the thing you're replacing)
- Twilio account — New to Twilio? See `twilio-account-setup`
- Environment variables:
- `TWILIO_ACCOUNT_SID`
- `TWILIO_AUTH_TOKEN`
- `VERIFY_SERVICE_SID` (created in Step 1 below)
— See `twilio-iam-auth-setup` for credential setup
- SDK: `pip install twilio` / `npm install twilio` (same SDK you already use)
- Verify needs **no separate product activation** — just create a Service
---
## Migration steps
The self-built pattern you're replacing does three things by hand: **generate** a
random code, **store** it with an expiry, **send** it over a number you own — then
later **look up + compare + check expiry** yourself. Verify collapses all of that
into the two calls below. (The "What you delete" table above maps each piece to its
Verify replacement.)
### The Verify pattern
**Step 1 — Create a Verify Service (one-time)**
Do this once and reuse the SID. Create it in the [Console](https://1console.twilio.com/go?to=/account/__account__/us1/verify/services) or via API:
**Python**
```python
import os
from twilio.rest import Client
client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"])
service = client.verify.v2.services.create(friendly_name="Acme") # your business name — shown in the SMS
print(service.sid) # VAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx — save in environment variable as VERIFY_SERVICE_SID
```
**Node.js**
```node
const twilio = require("twilio");
const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
const service = await client.verify.v2.services.create({ friendlyName: "Acme" }); // your business name — shown in the SMS
console.log(service.sid); // VAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
> `friendlyName` appears in the default verification SMS (e.g. "Your Acme verification code is 123456"), so set it to your business name — not an internal label. Avoid embedding 5 or more digits in the name — it can trigger error 60200.
**Step 2 — Send the verification (replaces your existing messaging logic)**
This single `verifications.create` call replaces steps you did by hand:
Verify generates the code, tracks it with a 10-min expiry, and delivers it over a
managed sender — so whatever you used to produce the code, persist it, and send
the SMS can all go. A `status` of `pending` means the code is on its way.
**Python**
```python
verification = client.verify.v2 \
.services(os.environ["VERIFY_SERVICE_SID"]) \
.verifications \
.create(to="+15558675310", channel="sms")
print(verification.status) # pending
```
**Node.js**
```node
const verification = await client.verify.v2
.services(process.env.VERIFY_SERVICE_SID)
.verifications.create({ to: "+15558675310", channel: "sms" });
console.log(verification.status); // pending
```
No code generation, no DB write, no owned number. Switch channel by changing
`channel` to `"call"`, `"whatsapp"`, or `"email"` (also `"sna"` and `"auto"`).
RCS isn't a `channel` value — Verify upgrades an `sms` verification to RCS where
the device supports it and falls back to SMS automatically.
**Step 3 — Check the code (replaces your existing validation logic)**
**Python**
```python
check = client.verify.v2 \
.services(os.environ["VERIFY_SERVICE_SID"]) \
.verification_checks \
.create(to="+15558675310", code="123456")
if check.status == "approved":
grant_access()
else:
reject() # "pending" = wrong/not-yet-submitted, "expired" = past TTL
```
**Node.js**
```node
const check = await client.verify.v2
.services(process.env.VERIFY_SERVICE_SID)
.verificationChecks.create({ to: "+15558675310", code: "123456" });
if (check.status === "approved") grantAccess();
else reject();
```
> A wrong code does **not** throw — it returns `status: "pending"`. You must
> test `status === "approved"` explicitly. After approval the verification is
> single-use; re-checking it returns 404. Delete your OTP database table once
> traffic is fully on Verify.
---
## Key patterns
### Format numbers as E.164 (Verify is strict)
Programmable Messaging tolerated loose formats; **Verify requires E.164**
(`+15558675310`). If your DB stores national-format numbers, normalize them
before sending or you'll get error 60200. Use the **Lookup API** to convert user
input to E.164 and capture the ISO country code — useful for fraud allow/block
lists. See `twilio-lookup-phone-intelligence`.
### Keep your own codes (bring-your-own OTP)
If you can't rip out your existing generation logic on day one, pass your code
via `custom_code` so Verify still handles delivery, templates, and localization.
**Python**
```python
verification = client.verify.v2 \
.services(os.environ["VERIFY_SERVICE_SID"]) \
.verifications \
.create(to="+15558675310", channel="sms", custom_code="123456")
```
Caveats — read before relying on this:
- **Enable it per-Service first** — it is not on by default. Set `CustomCodeEnabled=true` when creating/updating the Service via the API (`services.update(custom_code_enabled=True)`), or toggle "Enable Custom Verification Code" in the Console (**Verify → Services → your Service → General**).
- **Do not pass a custom message body.** Send only the code via `custom_code`; Verify maps it into an approved template. Custom message text returns [error 60243](https://www.twilio.com/docs/api/errors/60243).
- You are billed per **attempted** send (not per completed verification), and custom-code flows tend to generate more sends than checks (abandoned/re-requested codes) — expect higher cost than standard Verify.
- **Report the outcome so Twilio can keep deliverability high.** Verify's route-intelligence engine uses whether a code gets validated as a signal — it automatically retries different carriers/routes when codes go unvalidated or delivery underperforms. Tell Verify the result by updating the verification's status to `approved` so Twilio can keep optimizing routing:
**Python**
```python
client.verify.v2 \
.services(os.environ["VERIFY_SERVICE_SID"]) \
.verifications(verification.sid) \
.update(status="approved") # after your own code check passes
```
Treat custom codes as a transition aid, not the end state — migrating to Verify-generated codes removes all these caveats.
### Translate your error handling
Verify uses different error codes than Messaging. Map them during migration:
| Situation | Verify error | Handle by |
|---|---|---|
| Malformed / non-E.164 number, bad param | 60200 | Fix `to` format (use Lookup) |
| Too many sends to a number | 60203 | Back off; surface "try again later" |
| Too many check attempts | 60202 | Issue a new verification |
| Service SID wrong | 60212 | Check `VERIFY_SERVICE_SID` |
| Country not enabled | 60410 | Enable geo-permission in Console |
| Custom message passed | 60243 | Use `custom_code` only, not a body |
Full Verify error handling and built-in protections: `twilio-verify-send-otp`.
### Rate limits & fraud — delete your custom throttling
Verify enforces, per phone per service, out of the box: **5 sends / 10 min** and
**5 check attempts** per verification. Codes live **10 minutes** and are
immutable within that window (a re-request inside 10 min returns the *same*
code). This replaces hand-rolled resend counters. Need different limits? Define
custom [Rate Limits / Buckets](https://www.twilio.com/docs/verify/api/service-rate-limits)
on the Service. Enable **Fraud Guard** in the Console to retire your SMS-pumping
defenses. See `twilio-security-hardening` for the pumping-attack background.
### Safe incremental cutover
Don't flip 100% of traffic at once:
1. **Stand up Verify in parallel.** Keep the Messaging path live; route a small % of new OTP requests (or one region / internal users) through Verify.
2. **Dual-run the check.** If a user was issued a legacy code, validate against your DB; if issued via Verify, use `verification_checks`. Track which system issued each code so checks hit the right validator.
3. **Drain, then delete.** Once no legacy codes remain within their 10-min TTL, stop issuing them, ramp Verify to 100%, then decommission the OTP number/Messaging Service and drop the OTP DB table.
4. **Reuse one Verify Service** across the app — do not create a Service per request.
For testing without tripping rate limits during the cutover, see Twilio's
[Verify testing docs](https://www.twilio.com/docs/verify/quickstarts/test).
---
## CANNOT
- **Cannot retrieve the code Verify generated** — never returned by any API (by design for security). If your old flow logged or displayed the code, that stops working.
- **Cannot reuse your owned number as the Verify sender by default** — Verify picks from managed sender pools per country. (You *can* bring your own number/Messaging Service for specific channels/WhatsApp, but that's not the default.)
- **Cannot pass a raw custom message body** — the `customMessage` param is deprecated; raw text → error 60243. To customize the copy, use an approved template (`TemplateSid`) instead.
- **`custom_code` has strings attached** — must be enabled per-Service (Console → Verify → Services → General), bills per attempt (higher cost than standard Verify), and mandates updating Verification Status manually.
- **Cannot change the 10-minute token TTL yourself** — to change the default (adjustable between 2 minutes and 24 hours), you must contact Twilio Support to set it on your Service.
- **Cannot re-check an approved verification** — single-use; a second check returns 404.
- **Cannot send to non-E.164 numbers** — normalize first (Lookup).
- **No push webhook on completion** — poll `verification_checks` (rate-limited: 60/min, 180/hr, 250/day) or use the Verify Events API.
- **Cannot skip WhatsApp sender setup** — as of March 1, 2024 there is no shared Verify WhatsApp sender; bring your own. See `twilio-whatsapp-manage-senders`.
- **Verify traffic is A2P-exempt, but your remaining non-OTP Messaging is not** — keep 10DLC registration for any other traffic on that number.
---
## Next Steps
- **Full Verify API reference (all channels, service config):** `twilio-verify-send-otp`
- **Decide product fit / architecture first:** `twilio-identity-verification-advisor`
- **Normalize numbers to E.164 + fraud signals:** `twilio-lookup-phone-intelligence`
- **Bring a WhatsApp sender for WhatsApp OTP:** `twilio-whatsapp-manage-senders`
- **Retire SMS-pumping defenses safely:** `twilio-security-hardening`
- **Credential setup:** `twilio-iam-auth-setup`
- **Source blog post:** [Migrate from Programmable Messaging to Verify](https://www.twilio.com/en-us/blog/migrate-programmable-messaging-to-verify)
From twilio/ai at 8aba46f, shown as plain text under its license: MIT (permissive). The license text is in LICENSE.
View SKILL.md on GitHub at 8aba46fFiles
- SKILL.md13 KB · ca7c95d93d84
1 file, 13 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.
Skill folder on GitHub at 8aba46fMore from Twilio
twilio-account-setup
Sets up a Twilio account from scratch, including free trial signup, credential retrieval, phone number purchase, and enabling specific products like AI…
Claude Code, Codex, Cursor +4 · about 2.5K tokens · pinned 6 days ago
twilio-agent-augmentation-architect
This skill helps developers design AI-augmented contact center systems that enhance human agents with real-time intelligence.
Claude Code, Codex, Cursor +3 · about 3.6K tokens · pinned 6 days ago
twilio-agent-connect
Twilio Agent Connect is middleware that integrates LLM agents with Twilio communication channels (voice, SMS, RCS, WhatsApp, chat).
Claude Code, Codex, Cursor +4 · about 4.1K tokens · pinned 6 days ago
twilio-ai-agent-architect
Planning skill that helps developers design AI-powered conversational agents by assessing their use case across outcome sophistication, channels, and customer…
Claude Code, Codex, Cursor +3 · about 4.9K tokens · pinned 6 days ago
twilio-call-recordings
This skill teaches agents to record Twilio voice calls correctly using the appropriate method for the use case.
Claude Code, Codex, Cursor +4 · about 2.6K tokens · pinned 6 days ago
twilio-cli-reference
The Twilio CLI reference enables agents to manage Twilio resources directly from the terminal, including phone number provisioning, sending SMS and email…
Claude Code, Codex, Cursor +4 · about 1.8K tokens · pinned 6 days ago
Related in Backend and APIs
twilio-conference-calls
This skill enables multi-party call handling using Twilio Conference, covering warm and cold transfers, hold versus mute, supervisor coaching with whisper…
by TwilioOfficial publisher
Claude Code, Codex, Cursor +4 · about 2.8K tokens · pinned 6 days ago
twilio-content-template-builder
This skill enables agents to create, manage, and send message templates through Twilio's Content API across WhatsApp, SMS, RCS, and MMS channels.
by TwilioOfficial publisher
Claude Code, Codex, Cursor +4 · about 1.3K tokens · pinned 6 days ago
twilio-conversation-orchestrator
This skill configures Twilio Conversation Orchestrator to automatically capture and route SMS, voice, WhatsApp, RCS, and web chat into unified conversations.
by TwilioOfficial publisher
Claude Code, Codex, Cursor +4 · about 8.3K tokens · pinned 6 days ago
twilio-conversations-classic-api
Build multi-channel messaging conversations using Twilio's classic API, combining SMS, WhatsApp, and web chat participants in persistent threads with message…
by TwilioOfficial publisher
Claude Code, Codex, Cursor +4 · about 4.2K tokens · pinned 6 days ago
twilio-iam-auth-setup
This skill guides agents through setting up Twilio authentication methods including Auth Tokens, API Keys (Standard, Main, Restricted), Access Tokens for…
by TwilioOfficial publisher
Claude Code, Codex, Cursor +4 · about 2.3K tokens · pinned 6 days ago
twilio-isv-sms-best-practices
This skill guides ISVs building multi-tenant SMS platforms on Twilio through subaccount architecture, customer onboarding for A2P 10DLC and toll-free…
by TwilioOfficial publisher
Claude Code, Codex, Cursor +4 · about 2.7K tokens · pinned 6 days ago