[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-twilio-twilio-migrate-messaging-to-verify":3,"mdc-lusrwb-key":36,"related-org-twilio-twilio-migrate-messaging-to-verify":1878,"related-repo-twilio-twilio-migrate-messaging-to-verify":2058},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":26,"repoUrl":27,"updatedAt":28,"license":29,"forks":30,"topics":31,"repo":32,"sourceUrl":34,"mdContent":35},"twilio-migrate-messaging-to-verify","migrate Twilio messaging flows to Verify API","Migrate an existing OTP \u002F 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\u002F2FA delivery off Programmable Messaging onto Verify, or asks \"should I use Verify instead of sending my own SMS codes?\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"twilio","Twilio","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Ftwilio.png",[12,16,19,22,25],{"name":13,"slug":14,"type":15},"Auth","auth","tag",{"name":17,"slug":18,"type":15},"SMS","sms",{"name":20,"slug":21,"type":15},"Migration","migration",{"name":23,"slug":24,"type":15},"Messaging","messaging",{"name":9,"slug":8,"type":15},25,"https:\u002F\u002Fgithub.com\u002Ftwilio\u002Fai","2026-08-01T06:06:06.110251",null,7,[],{"repoUrl":27,"stars":26,"forks":30,"topics":33,"description":29},[],"https:\u002F\u002Fgithub.com\u002Ftwilio\u002Fai\u002Ftree\u002FHEAD\u002Fskills\u002Ftwilio\u002Ftwilio-migrate-messaging-to-verify","---\nname: twilio-migrate-messaging-to-verify\ndescription: >\n  Migrate an existing OTP \u002F one-time-passcode flow from the Twilio Programmable\n  Messaging API (self-built: generate code → send SMS → validate)\n  to the Twilio Verify API (managed code generation, delivery, expiry, rate\n  limiting, and Fraud Guard). Covers what Verify replaces, creating a\n  Verify Service, replacing send + check logic, E.164 formatting with Lookup,\n  bring-your-own custom codes, error-code translation, and\n  a safe incremental cutover. Use when a developer is moving OTP\u002F2FA delivery off\n  Programmable Messaging onto Verify, or asks \"should I use Verify instead of\n  sending my own SMS codes?\".\n---\n\n## Overview\n\nIf you built OTP on the **Programmable Messaging API**, you are maintaining code\nthat Twilio Verify manages for you: random code generation, a database of codes\nwith expiry, resend\u002Frate-limit logic, and fraud filtering. **Verify** replaces\nthat entire stack with two API calls — send a verification, check a code — and\nadds one-click Fraud Guard (SMS pumping protection), managed sender pools, and\nautomatic A2P\u002Fcompliance handling.\n\n**What you delete when you migrate:**\n\n| You built (Programmable Messaging) | Verify replaces it with |\n|---|---|\n| Buy\u002Fmanage a sending number or Messaging Service | Managed sender pool (short\u002Flong code, toll-free, alpha ID) chosen per-country |\n| `random.randint(...)` code generation | Built-in generation (4–10 digits, default 6) |\n| DB table storing `code`, `phone`, `expires_at` | Stateless — Verify tracks the code and 10-min expiry |\n| Custom resend throttling \u002F attempt counting | Built-in rate limits (5 sends & 5 checks per number \u002F 10 min) |\n| SMS pumping \u002F fraud filtering | Fraud Guard (geo-permissions, rate anomaly) — one click |\n| A2P 10DLC campaign registration for the OTP number | Exempt — Verify traffic needs no 10DLC campaign |\n| Per-channel integration (SMS, voice, WhatsApp…) | One API; change the `channel` param |\n| Locale-specific message copy | Pre-screened templates in dozens of languages, auto-resolved by country |\n\n> This skill focuses on **the migration**. For full Verify API reference (all\n> channels, service config, status values), see `twilio-verify-send-otp`. To\n> decide *whether* Verify fits before writing code, see\n> `twilio-identity-verification-advisor`.\n\n**When to stay on Programmable Messaging:** you need full control of the message\nbody\u002Fbranding beyond what templates allow, custom delivery routing, or you are\nsending non-OTP transactional traffic. For standard OTP\u002F2FA, migrate to Verify.\n\n---\n\n## Prerequisites\n\n- An existing Programmable Messaging OTP implementation (the thing you're replacing)\n- Twilio account — New to Twilio? See `twilio-account-setup`\n- Environment variables:\n  - `TWILIO_ACCOUNT_SID`\n  - `TWILIO_AUTH_TOKEN`\n  - `VERIFY_SERVICE_SID` (created in Step 1 below)\n  — See `twilio-iam-auth-setup` for credential setup\n- SDK: `pip install twilio` \u002F `npm install twilio` (same SDK you already use)\n- Verify needs **no separate product activation** — just create a Service\n\n---\n\n## Migration steps\n\nThe self-built pattern you're replacing does three things by hand: **generate** a\nrandom code, **store** it with an expiry, **send** it over a number you own — then\nlater **look up + compare + check expiry** yourself. Verify collapses all of that\ninto the two calls below. (The \"What you delete\" table above maps each piece to its\nVerify replacement.)\n\n### The Verify pattern\n\n**Step 1 — Create a Verify Service (one-time)**\n\nDo this once and reuse the SID. Create it in the [Console](https:\u002F\u002F1console.twilio.com\u002Fgo?to=\u002Faccount\u002F__account__\u002Fus1\u002Fverify\u002Fservices) or via API:\n\n**Python**\n```python\nimport os\nfrom twilio.rest import Client\n\nclient = Client(os.environ[\"TWILIO_ACCOUNT_SID\"], os.environ[\"TWILIO_AUTH_TOKEN\"])\n\nservice = client.verify.v2.services.create(friendly_name=\"Acme\")  # your business name — shown in the SMS\nprint(service.sid)  # VAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx — save in environment variable as VERIFY_SERVICE_SID\n```\n\n**Node.js**\n```node\nconst twilio = require(\"twilio\");\nconst client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);\n\nconst service = await client.verify.v2.services.create({ friendlyName: \"Acme\" }); \u002F\u002F your business name — shown in the SMS\nconsole.log(service.sid); \u002F\u002F VAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n```\n\n> `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.\n\n**Step 2 — Send the verification (replaces your existing messaging logic)**\n\nThis single `verifications.create` call replaces steps you did by hand:\nVerify generates the code, tracks it with a 10-min expiry, and delivers it over a\nmanaged sender — so whatever you used to produce the code, persist it, and send\nthe SMS can all go. A `status` of `pending` means the code is on its way.\n\n**Python**\n```python\nverification = client.verify.v2 \\\n    .services(os.environ[\"VERIFY_SERVICE_SID\"]) \\\n    .verifications \\\n    .create(to=\"+15558675310\", channel=\"sms\")\n\nprint(verification.status)  # pending\n```\n\n**Node.js**\n```node\nconst verification = await client.verify.v2\n    .services(process.env.VERIFY_SERVICE_SID)\n    .verifications.create({ to: \"+15558675310\", channel: \"sms\" });\n\nconsole.log(verification.status); \u002F\u002F pending\n```\n\nNo code generation, no DB write, no owned number. Switch channel by changing\n`channel` to `\"call\"`, `\"whatsapp\"`, or `\"email\"` (also `\"sna\"` and `\"auto\"`).\nRCS isn't a `channel` value — Verify upgrades an `sms` verification to RCS where\nthe device supports it and falls back to SMS automatically.\n\n**Step 3 — Check the code (replaces your existing validation logic)**\n\n**Python**\n```python\ncheck = client.verify.v2 \\\n    .services(os.environ[\"VERIFY_SERVICE_SID\"]) \\\n    .verification_checks \\\n    .create(to=\"+15558675310\", code=\"123456\")\n\nif check.status == \"approved\":\n    grant_access()\nelse:\n    reject()  # \"pending\" = wrong\u002Fnot-yet-submitted, \"expired\" = past TTL\n```\n\n**Node.js**\n```node\nconst check = await client.verify.v2\n    .services(process.env.VERIFY_SERVICE_SID)\n    .verificationChecks.create({ to: \"+15558675310\", code: \"123456\" });\n\nif (check.status === \"approved\") grantAccess();\nelse reject();\n```\n\n> A wrong code does **not** throw — it returns `status: \"pending\"`. You must\n> test `status === \"approved\"` explicitly. After approval the verification is\n> single-use; re-checking it returns 404. Delete your OTP database table once\n> traffic is fully on Verify.\n\n---\n\n## Key patterns\n\n### Format numbers as E.164 (Verify is strict)\n\nProgrammable Messaging tolerated loose formats; **Verify requires E.164**\n(`+15558675310`). If your DB stores national-format numbers, normalize them\nbefore sending or you'll get error 60200. Use the **Lookup API** to convert user\ninput to E.164 and capture the ISO country code — useful for fraud allow\u002Fblock\nlists. See `twilio-lookup-phone-intelligence`.\n\n### Keep your own codes (bring-your-own OTP)\n\nIf you can't rip out your existing generation logic on day one, pass your code\nvia `custom_code` so Verify still handles delivery, templates, and localization.\n\n**Python**\n```python\nverification = client.verify.v2 \\\n    .services(os.environ[\"VERIFY_SERVICE_SID\"]) \\\n    .verifications \\\n    .create(to=\"+15558675310\", channel=\"sms\", custom_code=\"123456\")\n```\n\nCaveats — read before relying on this:\n- **Enable it per-Service first** — it is not on by default. Set `CustomCodeEnabled=true` when creating\u002Fupdating 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**).\n- **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:\u002F\u002Fwww.twilio.com\u002Fdocs\u002Fapi\u002Ferrors\u002F60243).\n- You are billed per **attempted** send (not per completed verification), and custom-code flows tend to generate more sends than checks (abandoned\u002Fre-requested codes) — expect higher cost than standard Verify.\n- **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\u002Froutes 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:\n\n**Python**\n```python\nclient.verify.v2 \\\n    .services(os.environ[\"VERIFY_SERVICE_SID\"]) \\\n    .verifications(verification.sid) \\\n    .update(status=\"approved\")  # after your own code check passes\n```\n\nTreat custom codes as a transition aid, not the end state — migrating to Verify-generated codes removes all these caveats.\n\n### Translate your error handling\n\nVerify uses different error codes than Messaging. Map them during migration:\n\n| Situation | Verify error | Handle by |\n|---|---|---|\n| Malformed \u002F non-E.164 number, bad param | 60200 | Fix `to` format (use Lookup) |\n| Too many sends to a number | 60203 | Back off; surface \"try again later\" |\n| Too many check attempts | 60202 | Issue a new verification |\n| Service SID wrong | 60212 | Check `VERIFY_SERVICE_SID` |\n| Country not enabled | 60410 | Enable geo-permission in Console |\n| Custom message passed | 60243 | Use `custom_code` only, not a body |\n\nFull Verify error handling and built-in protections: `twilio-verify-send-otp`.\n\n### Rate limits & fraud — delete your custom throttling\n\nVerify enforces, per phone per service, out of the box: **5 sends \u002F 10 min** and\n**5 check attempts** per verification. Codes live **10 minutes** and are\nimmutable within that window (a re-request inside 10 min returns the *same*\ncode). This replaces hand-rolled resend counters. Need different limits? Define\ncustom [Rate Limits \u002F Buckets](https:\u002F\u002Fwww.twilio.com\u002Fdocs\u002Fverify\u002Fapi\u002Fservice-rate-limits)\non the Service. Enable **Fraud Guard** in the Console to retire your SMS-pumping\ndefenses. See `twilio-security-hardening` for the pumping-attack background.\n\n### Safe incremental cutover\n\nDon't flip 100% of traffic at once:\n\n1. **Stand up Verify in parallel.** Keep the Messaging path live; route a small % of new OTP requests (or one region \u002F internal users) through Verify.\n2. **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.\n3. **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\u002FMessaging Service and drop the OTP DB table.\n4. **Reuse one Verify Service** across the app — do not create a Service per request.\n\nFor testing without tripping rate limits during the cutover, see Twilio's\n[Verify testing docs](https:\u002F\u002Fwww.twilio.com\u002Fdocs\u002Fverify\u002Fquickstarts\u002Ftest).\n\n---\n\n## CANNOT\n\n- **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.\n- **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\u002FMessaging Service for specific channels\u002FWhatsApp, but that's not the default.)\n- **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.\n- **`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.\n- **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.\n- **Cannot re-check an approved verification** — single-use; a second check returns 404.\n- **Cannot send to non-E.164 numbers** — normalize first (Lookup).\n- **No push webhook on completion** — poll `verification_checks` (rate-limited: 60\u002Fmin, 180\u002Fhr, 250\u002Fday) or use the Verify Events API.\n- **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`.\n- **Verify traffic is A2P-exempt, but your remaining non-OTP Messaging is not** — keep 10DLC registration for any other traffic on that number.\n\n---\n\n## Next Steps\n\n- **Full Verify API reference (all channels, service config):** `twilio-verify-send-otp`\n- **Decide product fit \u002F architecture first:** `twilio-identity-verification-advisor`\n- **Normalize numbers to E.164 + fraud signals:** `twilio-lookup-phone-intelligence`\n- **Bring a WhatsApp sender for WhatsApp OTP:** `twilio-whatsapp-manage-senders`\n- **Retire SMS-pumping defenses safely:** `twilio-security-hardening`\n- **Credential setup:** `twilio-iam-auth-setup`\n- **Source blog post:** [Migrate from Programmable Messaging to Verify](https:\u002F\u002Fwww.twilio.com\u002Fen-us\u002Fblog\u002Fmigrate-programmable-messaging-to-verify)\n",{"data":37,"body":38},{"name":4,"description":6},{"type":39,"children":40},"root",[41,50,71,79,246,286,296,300,306,405,408,414,447,454,462,478,486,561,569,617,631,639,668,675,729,736,782,847,855,862,941,948,1001,1032,1035,1041,1047,1081,1087,1100,1107,1143,1148,1239,1246,1284,1289,1295,1300,1455,1466,1472,1529,1535,1540,1592,1605,1608,1614,1759,1762,1768,1872],{"type":42,"tag":43,"props":44,"children":46},"element","h2",{"id":45},"overview",[47],{"type":48,"value":49},"text","Overview",{"type":42,"tag":51,"props":52,"children":53},"p",{},[54,56,62,64,69],{"type":48,"value":55},"If you built OTP on the ",{"type":42,"tag":57,"props":58,"children":59},"strong",{},[60],{"type":48,"value":61},"Programmable Messaging API",{"type":48,"value":63},", you are maintaining code\nthat Twilio Verify manages for you: random code generation, a database of codes\nwith expiry, resend\u002Frate-limit logic, and fraud filtering. ",{"type":42,"tag":57,"props":65,"children":66},{},[67],{"type":48,"value":68},"Verify",{"type":48,"value":70}," replaces\nthat entire stack with two API calls — send a verification, check a code — and\nadds one-click Fraud Guard (SMS pumping protection), managed sender pools, and\nautomatic A2P\u002Fcompliance handling.",{"type":42,"tag":51,"props":72,"children":73},{},[74],{"type":42,"tag":57,"props":75,"children":76},{},[77],{"type":48,"value":78},"What you delete when you migrate:",{"type":42,"tag":80,"props":81,"children":82},"table",{},[83,102],{"type":42,"tag":84,"props":85,"children":86},"thead",{},[87],{"type":42,"tag":88,"props":89,"children":90},"tr",{},[91,97],{"type":42,"tag":92,"props":93,"children":94},"th",{},[95],{"type":48,"value":96},"You built (Programmable Messaging)",{"type":42,"tag":92,"props":98,"children":99},{},[100],{"type":48,"value":101},"Verify replaces it with",{"type":42,"tag":103,"props":104,"children":105},"tbody",{},[106,120,140,173,186,199,212,233],{"type":42,"tag":88,"props":107,"children":108},{},[109,115],{"type":42,"tag":110,"props":111,"children":112},"td",{},[113],{"type":48,"value":114},"Buy\u002Fmanage a sending number or Messaging Service",{"type":42,"tag":110,"props":116,"children":117},{},[118],{"type":48,"value":119},"Managed sender pool (short\u002Flong code, toll-free, alpha ID) chosen per-country",{"type":42,"tag":88,"props":121,"children":122},{},[123,135],{"type":42,"tag":110,"props":124,"children":125},{},[126,133],{"type":42,"tag":127,"props":128,"children":130},"code",{"className":129},[],[131],{"type":48,"value":132},"random.randint(...)",{"type":48,"value":134}," code generation",{"type":42,"tag":110,"props":136,"children":137},{},[138],{"type":48,"value":139},"Built-in generation (4–10 digits, default 6)",{"type":42,"tag":88,"props":141,"children":142},{},[143,168],{"type":42,"tag":110,"props":144,"children":145},{},[146,148,153,155,161,162],{"type":48,"value":147},"DB table storing ",{"type":42,"tag":127,"props":149,"children":151},{"className":150},[],[152],{"type":48,"value":127},{"type":48,"value":154},", ",{"type":42,"tag":127,"props":156,"children":158},{"className":157},[],[159],{"type":48,"value":160},"phone",{"type":48,"value":154},{"type":42,"tag":127,"props":163,"children":165},{"className":164},[],[166],{"type":48,"value":167},"expires_at",{"type":42,"tag":110,"props":169,"children":170},{},[171],{"type":48,"value":172},"Stateless — Verify tracks the code and 10-min expiry",{"type":42,"tag":88,"props":174,"children":175},{},[176,181],{"type":42,"tag":110,"props":177,"children":178},{},[179],{"type":48,"value":180},"Custom resend throttling \u002F attempt counting",{"type":42,"tag":110,"props":182,"children":183},{},[184],{"type":48,"value":185},"Built-in rate limits (5 sends & 5 checks per number \u002F 10 min)",{"type":42,"tag":88,"props":187,"children":188},{},[189,194],{"type":42,"tag":110,"props":190,"children":191},{},[192],{"type":48,"value":193},"SMS pumping \u002F fraud filtering",{"type":42,"tag":110,"props":195,"children":196},{},[197],{"type":48,"value":198},"Fraud Guard (geo-permissions, rate anomaly) — one click",{"type":42,"tag":88,"props":200,"children":201},{},[202,207],{"type":42,"tag":110,"props":203,"children":204},{},[205],{"type":48,"value":206},"A2P 10DLC campaign registration for the OTP number",{"type":42,"tag":110,"props":208,"children":209},{},[210],{"type":48,"value":211},"Exempt — Verify traffic needs no 10DLC campaign",{"type":42,"tag":88,"props":213,"children":214},{},[215,220],{"type":42,"tag":110,"props":216,"children":217},{},[218],{"type":48,"value":219},"Per-channel integration (SMS, voice, WhatsApp…)",{"type":42,"tag":110,"props":221,"children":222},{},[223,225,231],{"type":48,"value":224},"One API; change the ",{"type":42,"tag":127,"props":226,"children":228},{"className":227},[],[229],{"type":48,"value":230},"channel",{"type":48,"value":232}," param",{"type":42,"tag":88,"props":234,"children":235},{},[236,241],{"type":42,"tag":110,"props":237,"children":238},{},[239],{"type":48,"value":240},"Locale-specific message copy",{"type":42,"tag":110,"props":242,"children":243},{},[244],{"type":48,"value":245},"Pre-screened templates in dozens of languages, auto-resolved by country",{"type":42,"tag":247,"props":248,"children":249},"blockquote",{},[250],{"type":42,"tag":51,"props":251,"children":252},{},[253,255,260,262,268,270,276,278,284],{"type":48,"value":254},"This skill focuses on ",{"type":42,"tag":57,"props":256,"children":257},{},[258],{"type":48,"value":259},"the migration",{"type":48,"value":261},". For full Verify API reference (all\nchannels, service config, status values), see ",{"type":42,"tag":127,"props":263,"children":265},{"className":264},[],[266],{"type":48,"value":267},"twilio-verify-send-otp",{"type":48,"value":269},". To\ndecide ",{"type":42,"tag":271,"props":272,"children":273},"em",{},[274],{"type":48,"value":275},"whether",{"type":48,"value":277}," Verify fits before writing code, see\n",{"type":42,"tag":127,"props":279,"children":281},{"className":280},[],[282],{"type":48,"value":283},"twilio-identity-verification-advisor",{"type":48,"value":285},".",{"type":42,"tag":51,"props":287,"children":288},{},[289,294],{"type":42,"tag":57,"props":290,"children":291},{},[292],{"type":48,"value":293},"When to stay on Programmable Messaging:",{"type":48,"value":295}," you need full control of the message\nbody\u002Fbranding beyond what templates allow, custom delivery routing, or you are\nsending non-OTP transactional traffic. For standard OTP\u002F2FA, migrate to Verify.",{"type":42,"tag":297,"props":298,"children":299},"hr",{},[],{"type":42,"tag":43,"props":301,"children":303},{"id":302},"prerequisites",[304],{"type":48,"value":305},"Prerequisites",{"type":42,"tag":307,"props":308,"children":309},"ul",{},[310,316,327,372,393],{"type":42,"tag":311,"props":312,"children":313},"li",{},[314],{"type":48,"value":315},"An existing Programmable Messaging OTP implementation (the thing you're replacing)",{"type":42,"tag":311,"props":317,"children":318},{},[319,321],{"type":48,"value":320},"Twilio account — New to Twilio? See ",{"type":42,"tag":127,"props":322,"children":324},{"className":323},[],[325],{"type":48,"value":326},"twilio-account-setup",{"type":42,"tag":311,"props":328,"children":329},{},[330,332],{"type":48,"value":331},"Environment variables:\n",{"type":42,"tag":307,"props":333,"children":334},{},[335,344,353],{"type":42,"tag":311,"props":336,"children":337},{},[338],{"type":42,"tag":127,"props":339,"children":341},{"className":340},[],[342],{"type":48,"value":343},"TWILIO_ACCOUNT_SID",{"type":42,"tag":311,"props":345,"children":346},{},[347],{"type":42,"tag":127,"props":348,"children":350},{"className":349},[],[351],{"type":48,"value":352},"TWILIO_AUTH_TOKEN",{"type":42,"tag":311,"props":354,"children":355},{},[356,362,364,370],{"type":42,"tag":127,"props":357,"children":359},{"className":358},[],[360],{"type":48,"value":361},"VERIFY_SERVICE_SID",{"type":48,"value":363}," (created in Step 1 below)\n— See ",{"type":42,"tag":127,"props":365,"children":367},{"className":366},[],[368],{"type":48,"value":369},"twilio-iam-auth-setup",{"type":48,"value":371}," for credential setup",{"type":42,"tag":311,"props":373,"children":374},{},[375,377,383,385,391],{"type":48,"value":376},"SDK: ",{"type":42,"tag":127,"props":378,"children":380},{"className":379},[],[381],{"type":48,"value":382},"pip install twilio",{"type":48,"value":384}," \u002F ",{"type":42,"tag":127,"props":386,"children":388},{"className":387},[],[389],{"type":48,"value":390},"npm install twilio",{"type":48,"value":392}," (same SDK you already use)",{"type":42,"tag":311,"props":394,"children":395},{},[396,398,403],{"type":48,"value":397},"Verify needs ",{"type":42,"tag":57,"props":399,"children":400},{},[401],{"type":48,"value":402},"no separate product activation",{"type":48,"value":404}," — just create a Service",{"type":42,"tag":297,"props":406,"children":407},{},[],{"type":42,"tag":43,"props":409,"children":411},{"id":410},"migration-steps",[412],{"type":48,"value":413},"Migration steps",{"type":42,"tag":51,"props":415,"children":416},{},[417,419,424,426,431,433,438,440,445],{"type":48,"value":418},"The self-built pattern you're replacing does three things by hand: ",{"type":42,"tag":57,"props":420,"children":421},{},[422],{"type":48,"value":423},"generate",{"type":48,"value":425}," a\nrandom code, ",{"type":42,"tag":57,"props":427,"children":428},{},[429],{"type":48,"value":430},"store",{"type":48,"value":432}," it with an expiry, ",{"type":42,"tag":57,"props":434,"children":435},{},[436],{"type":48,"value":437},"send",{"type":48,"value":439}," it over a number you own — then\nlater ",{"type":42,"tag":57,"props":441,"children":442},{},[443],{"type":48,"value":444},"look up + compare + check expiry",{"type":48,"value":446}," yourself. Verify collapses all of that\ninto the two calls below. (The \"What you delete\" table above maps each piece to its\nVerify replacement.)",{"type":42,"tag":448,"props":449,"children":451},"h3",{"id":450},"the-verify-pattern",[452],{"type":48,"value":453},"The Verify pattern",{"type":42,"tag":51,"props":455,"children":456},{},[457],{"type":42,"tag":57,"props":458,"children":459},{},[460],{"type":48,"value":461},"Step 1 — Create a Verify Service (one-time)",{"type":42,"tag":51,"props":463,"children":464},{},[465,467,476],{"type":48,"value":466},"Do this once and reuse the SID. Create it in the ",{"type":42,"tag":468,"props":469,"children":473},"a",{"href":470,"rel":471},"https:\u002F\u002F1console.twilio.com\u002Fgo?to=\u002Faccount\u002F__account__\u002Fus1\u002Fverify\u002Fservices",[472],"nofollow",[474],{"type":48,"value":475},"Console",{"type":48,"value":477}," or via API:",{"type":42,"tag":51,"props":479,"children":480},{},[481],{"type":42,"tag":57,"props":482,"children":483},{},[484],{"type":48,"value":485},"Python",{"type":42,"tag":487,"props":488,"children":493},"pre",{"className":489,"code":490,"language":491,"meta":492,"style":492},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import os\nfrom twilio.rest import Client\n\nclient = Client(os.environ[\"TWILIO_ACCOUNT_SID\"], os.environ[\"TWILIO_AUTH_TOKEN\"])\n\nservice = client.verify.v2.services.create(friendly_name=\"Acme\")  # your business name — shown in the SMS\nprint(service.sid)  # VAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx — save in environment variable as VERIFY_SERVICE_SID\n","python","",[494],{"type":42,"tag":127,"props":495,"children":496},{"__ignoreMap":492},[497,508,517,527,536,544,553],{"type":42,"tag":498,"props":499,"children":502},"span",{"class":500,"line":501},"line",1,[503],{"type":42,"tag":498,"props":504,"children":505},{},[506],{"type":48,"value":507},"import os\n",{"type":42,"tag":498,"props":509,"children":511},{"class":500,"line":510},2,[512],{"type":42,"tag":498,"props":513,"children":514},{},[515],{"type":48,"value":516},"from twilio.rest import Client\n",{"type":42,"tag":498,"props":518,"children":520},{"class":500,"line":519},3,[521],{"type":42,"tag":498,"props":522,"children":524},{"emptyLinePlaceholder":523},true,[525],{"type":48,"value":526},"\n",{"type":42,"tag":498,"props":528,"children":530},{"class":500,"line":529},4,[531],{"type":42,"tag":498,"props":532,"children":533},{},[534],{"type":48,"value":535},"client = Client(os.environ[\"TWILIO_ACCOUNT_SID\"], os.environ[\"TWILIO_AUTH_TOKEN\"])\n",{"type":42,"tag":498,"props":537,"children":539},{"class":500,"line":538},5,[540],{"type":42,"tag":498,"props":541,"children":542},{"emptyLinePlaceholder":523},[543],{"type":48,"value":526},{"type":42,"tag":498,"props":545,"children":547},{"class":500,"line":546},6,[548],{"type":42,"tag":498,"props":549,"children":550},{},[551],{"type":48,"value":552},"service = client.verify.v2.services.create(friendly_name=\"Acme\")  # your business name — shown in the SMS\n",{"type":42,"tag":498,"props":554,"children":555},{"class":500,"line":30},[556],{"type":42,"tag":498,"props":557,"children":558},{},[559],{"type":48,"value":560},"print(service.sid)  # VAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx — save in environment variable as VERIFY_SERVICE_SID\n",{"type":42,"tag":51,"props":562,"children":563},{},[564],{"type":42,"tag":57,"props":565,"children":566},{},[567],{"type":48,"value":568},"Node.js",{"type":42,"tag":487,"props":570,"children":574},{"className":571,"code":572,"language":573,"meta":492,"style":492},"language-node shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const twilio = require(\"twilio\");\nconst client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);\n\nconst service = await client.verify.v2.services.create({ friendlyName: \"Acme\" }); \u002F\u002F your business name — shown in the SMS\nconsole.log(service.sid); \u002F\u002F VAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n","node",[575],{"type":42,"tag":127,"props":576,"children":577},{"__ignoreMap":492},[578,586,594,601,609],{"type":42,"tag":498,"props":579,"children":580},{"class":500,"line":501},[581],{"type":42,"tag":498,"props":582,"children":583},{},[584],{"type":48,"value":585},"const twilio = require(\"twilio\");\n",{"type":42,"tag":498,"props":587,"children":588},{"class":500,"line":510},[589],{"type":42,"tag":498,"props":590,"children":591},{},[592],{"type":48,"value":593},"const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);\n",{"type":42,"tag":498,"props":595,"children":596},{"class":500,"line":519},[597],{"type":42,"tag":498,"props":598,"children":599},{"emptyLinePlaceholder":523},[600],{"type":48,"value":526},{"type":42,"tag":498,"props":602,"children":603},{"class":500,"line":529},[604],{"type":42,"tag":498,"props":605,"children":606},{},[607],{"type":48,"value":608},"const service = await client.verify.v2.services.create({ friendlyName: \"Acme\" }); \u002F\u002F your business name — shown in the SMS\n",{"type":42,"tag":498,"props":610,"children":611},{"class":500,"line":538},[612],{"type":42,"tag":498,"props":613,"children":614},{},[615],{"type":48,"value":616},"console.log(service.sid); \u002F\u002F VAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n",{"type":42,"tag":247,"props":618,"children":619},{},[620],{"type":42,"tag":51,"props":621,"children":622},{},[623,629],{"type":42,"tag":127,"props":624,"children":626},{"className":625},[],[627],{"type":48,"value":628},"friendlyName",{"type":48,"value":630}," 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.",{"type":42,"tag":51,"props":632,"children":633},{},[634],{"type":42,"tag":57,"props":635,"children":636},{},[637],{"type":48,"value":638},"Step 2 — Send the verification (replaces your existing messaging logic)",{"type":42,"tag":51,"props":640,"children":641},{},[642,644,650,652,658,660,666],{"type":48,"value":643},"This single ",{"type":42,"tag":127,"props":645,"children":647},{"className":646},[],[648],{"type":48,"value":649},"verifications.create",{"type":48,"value":651}," call replaces steps you did by hand:\nVerify generates the code, tracks it with a 10-min expiry, and delivers it over a\nmanaged sender — so whatever you used to produce the code, persist it, and send\nthe SMS can all go. A ",{"type":42,"tag":127,"props":653,"children":655},{"className":654},[],[656],{"type":48,"value":657},"status",{"type":48,"value":659}," of ",{"type":42,"tag":127,"props":661,"children":663},{"className":662},[],[664],{"type":48,"value":665},"pending",{"type":48,"value":667}," means the code is on its way.",{"type":42,"tag":51,"props":669,"children":670},{},[671],{"type":42,"tag":57,"props":672,"children":673},{},[674],{"type":48,"value":485},{"type":42,"tag":487,"props":676,"children":678},{"className":489,"code":677,"language":491,"meta":492,"style":492},"verification = client.verify.v2 \\\n    .services(os.environ[\"VERIFY_SERVICE_SID\"]) \\\n    .verifications \\\n    .create(to=\"+15558675310\", channel=\"sms\")\n\nprint(verification.status)  # pending\n",[679],{"type":42,"tag":127,"props":680,"children":681},{"__ignoreMap":492},[682,690,698,706,714,721],{"type":42,"tag":498,"props":683,"children":684},{"class":500,"line":501},[685],{"type":42,"tag":498,"props":686,"children":687},{},[688],{"type":48,"value":689},"verification = client.verify.v2 \\\n",{"type":42,"tag":498,"props":691,"children":692},{"class":500,"line":510},[693],{"type":42,"tag":498,"props":694,"children":695},{},[696],{"type":48,"value":697},"    .services(os.environ[\"VERIFY_SERVICE_SID\"]) \\\n",{"type":42,"tag":498,"props":699,"children":700},{"class":500,"line":519},[701],{"type":42,"tag":498,"props":702,"children":703},{},[704],{"type":48,"value":705},"    .verifications \\\n",{"type":42,"tag":498,"props":707,"children":708},{"class":500,"line":529},[709],{"type":42,"tag":498,"props":710,"children":711},{},[712],{"type":48,"value":713},"    .create(to=\"+15558675310\", channel=\"sms\")\n",{"type":42,"tag":498,"props":715,"children":716},{"class":500,"line":538},[717],{"type":42,"tag":498,"props":718,"children":719},{"emptyLinePlaceholder":523},[720],{"type":48,"value":526},{"type":42,"tag":498,"props":722,"children":723},{"class":500,"line":546},[724],{"type":42,"tag":498,"props":725,"children":726},{},[727],{"type":48,"value":728},"print(verification.status)  # pending\n",{"type":42,"tag":51,"props":730,"children":731},{},[732],{"type":42,"tag":57,"props":733,"children":734},{},[735],{"type":48,"value":568},{"type":42,"tag":487,"props":737,"children":739},{"className":571,"code":738,"language":573,"meta":492,"style":492},"const verification = await client.verify.v2\n    .services(process.env.VERIFY_SERVICE_SID)\n    .verifications.create({ to: \"+15558675310\", channel: \"sms\" });\n\nconsole.log(verification.status); \u002F\u002F pending\n",[740],{"type":42,"tag":127,"props":741,"children":742},{"__ignoreMap":492},[743,751,759,767,774],{"type":42,"tag":498,"props":744,"children":745},{"class":500,"line":501},[746],{"type":42,"tag":498,"props":747,"children":748},{},[749],{"type":48,"value":750},"const verification = await client.verify.v2\n",{"type":42,"tag":498,"props":752,"children":753},{"class":500,"line":510},[754],{"type":42,"tag":498,"props":755,"children":756},{},[757],{"type":48,"value":758},"    .services(process.env.VERIFY_SERVICE_SID)\n",{"type":42,"tag":498,"props":760,"children":761},{"class":500,"line":519},[762],{"type":42,"tag":498,"props":763,"children":764},{},[765],{"type":48,"value":766},"    .verifications.create({ to: \"+15558675310\", channel: \"sms\" });\n",{"type":42,"tag":498,"props":768,"children":769},{"class":500,"line":529},[770],{"type":42,"tag":498,"props":771,"children":772},{"emptyLinePlaceholder":523},[773],{"type":48,"value":526},{"type":42,"tag":498,"props":775,"children":776},{"class":500,"line":538},[777],{"type":42,"tag":498,"props":778,"children":779},{},[780],{"type":48,"value":781},"console.log(verification.status); \u002F\u002F pending\n",{"type":42,"tag":51,"props":783,"children":784},{},[785,787,792,794,800,801,807,809,815,817,823,825,831,833,838,840,845],{"type":48,"value":786},"No code generation, no DB write, no owned number. Switch channel by changing\n",{"type":42,"tag":127,"props":788,"children":790},{"className":789},[],[791],{"type":48,"value":230},{"type":48,"value":793}," to ",{"type":42,"tag":127,"props":795,"children":797},{"className":796},[],[798],{"type":48,"value":799},"\"call\"",{"type":48,"value":154},{"type":42,"tag":127,"props":802,"children":804},{"className":803},[],[805],{"type":48,"value":806},"\"whatsapp\"",{"type":48,"value":808},", or ",{"type":42,"tag":127,"props":810,"children":812},{"className":811},[],[813],{"type":48,"value":814},"\"email\"",{"type":48,"value":816}," (also ",{"type":42,"tag":127,"props":818,"children":820},{"className":819},[],[821],{"type":48,"value":822},"\"sna\"",{"type":48,"value":824}," and ",{"type":42,"tag":127,"props":826,"children":828},{"className":827},[],[829],{"type":48,"value":830},"\"auto\"",{"type":48,"value":832},").\nRCS isn't a ",{"type":42,"tag":127,"props":834,"children":836},{"className":835},[],[837],{"type":48,"value":230},{"type":48,"value":839}," value — Verify upgrades an ",{"type":42,"tag":127,"props":841,"children":843},{"className":842},[],[844],{"type":48,"value":18},{"type":48,"value":846}," verification to RCS where\nthe device supports it and falls back to SMS automatically.",{"type":42,"tag":51,"props":848,"children":849},{},[850],{"type":42,"tag":57,"props":851,"children":852},{},[853],{"type":48,"value":854},"Step 3 — Check the code (replaces your existing validation logic)",{"type":42,"tag":51,"props":856,"children":857},{},[858],{"type":42,"tag":57,"props":859,"children":860},{},[861],{"type":48,"value":485},{"type":42,"tag":487,"props":863,"children":865},{"className":489,"code":864,"language":491,"meta":492,"style":492},"check = client.verify.v2 \\\n    .services(os.environ[\"VERIFY_SERVICE_SID\"]) \\\n    .verification_checks \\\n    .create(to=\"+15558675310\", code=\"123456\")\n\nif check.status == \"approved\":\n    grant_access()\nelse:\n    reject()  # \"pending\" = wrong\u002Fnot-yet-submitted, \"expired\" = past TTL\n",[866],{"type":42,"tag":127,"props":867,"children":868},{"__ignoreMap":492},[869,877,884,892,900,907,915,923,932],{"type":42,"tag":498,"props":870,"children":871},{"class":500,"line":501},[872],{"type":42,"tag":498,"props":873,"children":874},{},[875],{"type":48,"value":876},"check = client.verify.v2 \\\n",{"type":42,"tag":498,"props":878,"children":879},{"class":500,"line":510},[880],{"type":42,"tag":498,"props":881,"children":882},{},[883],{"type":48,"value":697},{"type":42,"tag":498,"props":885,"children":886},{"class":500,"line":519},[887],{"type":42,"tag":498,"props":888,"children":889},{},[890],{"type":48,"value":891},"    .verification_checks \\\n",{"type":42,"tag":498,"props":893,"children":894},{"class":500,"line":529},[895],{"type":42,"tag":498,"props":896,"children":897},{},[898],{"type":48,"value":899},"    .create(to=\"+15558675310\", code=\"123456\")\n",{"type":42,"tag":498,"props":901,"children":902},{"class":500,"line":538},[903],{"type":42,"tag":498,"props":904,"children":905},{"emptyLinePlaceholder":523},[906],{"type":48,"value":526},{"type":42,"tag":498,"props":908,"children":909},{"class":500,"line":546},[910],{"type":42,"tag":498,"props":911,"children":912},{},[913],{"type":48,"value":914},"if check.status == \"approved\":\n",{"type":42,"tag":498,"props":916,"children":917},{"class":500,"line":30},[918],{"type":42,"tag":498,"props":919,"children":920},{},[921],{"type":48,"value":922},"    grant_access()\n",{"type":42,"tag":498,"props":924,"children":926},{"class":500,"line":925},8,[927],{"type":42,"tag":498,"props":928,"children":929},{},[930],{"type":48,"value":931},"else:\n",{"type":42,"tag":498,"props":933,"children":935},{"class":500,"line":934},9,[936],{"type":42,"tag":498,"props":937,"children":938},{},[939],{"type":48,"value":940},"    reject()  # \"pending\" = wrong\u002Fnot-yet-submitted, \"expired\" = past TTL\n",{"type":42,"tag":51,"props":942,"children":943},{},[944],{"type":42,"tag":57,"props":945,"children":946},{},[947],{"type":48,"value":568},{"type":42,"tag":487,"props":949,"children":951},{"className":571,"code":950,"language":573,"meta":492,"style":492},"const check = await client.verify.v2\n    .services(process.env.VERIFY_SERVICE_SID)\n    .verificationChecks.create({ to: \"+15558675310\", code: \"123456\" });\n\nif (check.status === \"approved\") grantAccess();\nelse reject();\n",[952],{"type":42,"tag":127,"props":953,"children":954},{"__ignoreMap":492},[955,963,970,978,985,993],{"type":42,"tag":498,"props":956,"children":957},{"class":500,"line":501},[958],{"type":42,"tag":498,"props":959,"children":960},{},[961],{"type":48,"value":962},"const check = await client.verify.v2\n",{"type":42,"tag":498,"props":964,"children":965},{"class":500,"line":510},[966],{"type":42,"tag":498,"props":967,"children":968},{},[969],{"type":48,"value":758},{"type":42,"tag":498,"props":971,"children":972},{"class":500,"line":519},[973],{"type":42,"tag":498,"props":974,"children":975},{},[976],{"type":48,"value":977},"    .verificationChecks.create({ to: \"+15558675310\", code: \"123456\" });\n",{"type":42,"tag":498,"props":979,"children":980},{"class":500,"line":529},[981],{"type":42,"tag":498,"props":982,"children":983},{"emptyLinePlaceholder":523},[984],{"type":48,"value":526},{"type":42,"tag":498,"props":986,"children":987},{"class":500,"line":538},[988],{"type":42,"tag":498,"props":989,"children":990},{},[991],{"type":48,"value":992},"if (check.status === \"approved\") grantAccess();\n",{"type":42,"tag":498,"props":994,"children":995},{"class":500,"line":546},[996],{"type":42,"tag":498,"props":997,"children":998},{},[999],{"type":48,"value":1000},"else reject();\n",{"type":42,"tag":247,"props":1002,"children":1003},{},[1004],{"type":42,"tag":51,"props":1005,"children":1006},{},[1007,1009,1014,1016,1022,1024,1030],{"type":48,"value":1008},"A wrong code does ",{"type":42,"tag":57,"props":1010,"children":1011},{},[1012],{"type":48,"value":1013},"not",{"type":48,"value":1015}," throw — it returns ",{"type":42,"tag":127,"props":1017,"children":1019},{"className":1018},[],[1020],{"type":48,"value":1021},"status: \"pending\"",{"type":48,"value":1023},". You must\ntest ",{"type":42,"tag":127,"props":1025,"children":1027},{"className":1026},[],[1028],{"type":48,"value":1029},"status === \"approved\"",{"type":48,"value":1031}," explicitly. After approval the verification is\nsingle-use; re-checking it returns 404. Delete your OTP database table once\ntraffic is fully on Verify.",{"type":42,"tag":297,"props":1033,"children":1034},{},[],{"type":42,"tag":43,"props":1036,"children":1038},{"id":1037},"key-patterns",[1039],{"type":48,"value":1040},"Key patterns",{"type":42,"tag":448,"props":1042,"children":1044},{"id":1043},"format-numbers-as-e164-verify-is-strict",[1045],{"type":48,"value":1046},"Format numbers as E.164 (Verify is strict)",{"type":42,"tag":51,"props":1048,"children":1049},{},[1050,1052,1057,1059,1065,1067,1072,1074,1080],{"type":48,"value":1051},"Programmable Messaging tolerated loose formats; ",{"type":42,"tag":57,"props":1053,"children":1054},{},[1055],{"type":48,"value":1056},"Verify requires E.164",{"type":48,"value":1058},"\n(",{"type":42,"tag":127,"props":1060,"children":1062},{"className":1061},[],[1063],{"type":48,"value":1064},"+15558675310",{"type":48,"value":1066},"). If your DB stores national-format numbers, normalize them\nbefore sending or you'll get error 60200. Use the ",{"type":42,"tag":57,"props":1068,"children":1069},{},[1070],{"type":48,"value":1071},"Lookup API",{"type":48,"value":1073}," to convert user\ninput to E.164 and capture the ISO country code — useful for fraud allow\u002Fblock\nlists. See ",{"type":42,"tag":127,"props":1075,"children":1077},{"className":1076},[],[1078],{"type":48,"value":1079},"twilio-lookup-phone-intelligence",{"type":48,"value":285},{"type":42,"tag":448,"props":1082,"children":1084},{"id":1083},"keep-your-own-codes-bring-your-own-otp",[1085],{"type":48,"value":1086},"Keep your own codes (bring-your-own OTP)",{"type":42,"tag":51,"props":1088,"children":1089},{},[1090,1092,1098],{"type":48,"value":1091},"If you can't rip out your existing generation logic on day one, pass your code\nvia ",{"type":42,"tag":127,"props":1093,"children":1095},{"className":1094},[],[1096],{"type":48,"value":1097},"custom_code",{"type":48,"value":1099}," so Verify still handles delivery, templates, and localization.",{"type":42,"tag":51,"props":1101,"children":1102},{},[1103],{"type":42,"tag":57,"props":1104,"children":1105},{},[1106],{"type":48,"value":485},{"type":42,"tag":487,"props":1108,"children":1110},{"className":489,"code":1109,"language":491,"meta":492,"style":492},"verification = client.verify.v2 \\\n    .services(os.environ[\"VERIFY_SERVICE_SID\"]) \\\n    .verifications \\\n    .create(to=\"+15558675310\", channel=\"sms\", custom_code=\"123456\")\n",[1111],{"type":42,"tag":127,"props":1112,"children":1113},{"__ignoreMap":492},[1114,1121,1128,1135],{"type":42,"tag":498,"props":1115,"children":1116},{"class":500,"line":501},[1117],{"type":42,"tag":498,"props":1118,"children":1119},{},[1120],{"type":48,"value":689},{"type":42,"tag":498,"props":1122,"children":1123},{"class":500,"line":510},[1124],{"type":42,"tag":498,"props":1125,"children":1126},{},[1127],{"type":48,"value":697},{"type":42,"tag":498,"props":1129,"children":1130},{"class":500,"line":519},[1131],{"type":42,"tag":498,"props":1132,"children":1133},{},[1134],{"type":48,"value":705},{"type":42,"tag":498,"props":1136,"children":1137},{"class":500,"line":529},[1138],{"type":42,"tag":498,"props":1139,"children":1140},{},[1141],{"type":48,"value":1142},"    .create(to=\"+15558675310\", channel=\"sms\", custom_code=\"123456\")\n",{"type":42,"tag":51,"props":1144,"children":1145},{},[1146],{"type":48,"value":1147},"Caveats — read before relying on this:",{"type":42,"tag":307,"props":1149,"children":1150},{},[1151,1184,1209,1221],{"type":42,"tag":311,"props":1152,"children":1153},{},[1154,1159,1161,1167,1169,1175,1177,1182],{"type":42,"tag":57,"props":1155,"children":1156},{},[1157],{"type":48,"value":1158},"Enable it per-Service first",{"type":48,"value":1160}," — it is not on by default. Set ",{"type":42,"tag":127,"props":1162,"children":1164},{"className":1163},[],[1165],{"type":48,"value":1166},"CustomCodeEnabled=true",{"type":48,"value":1168}," when creating\u002Fupdating the Service via the API (",{"type":42,"tag":127,"props":1170,"children":1172},{"className":1171},[],[1173],{"type":48,"value":1174},"services.update(custom_code_enabled=True)",{"type":48,"value":1176},"), or toggle \"Enable Custom Verification Code\" in the Console (",{"type":42,"tag":57,"props":1178,"children":1179},{},[1180],{"type":48,"value":1181},"Verify → Services → your Service → General",{"type":48,"value":1183},").",{"type":42,"tag":311,"props":1185,"children":1186},{},[1187,1192,1194,1199,1201,1208],{"type":42,"tag":57,"props":1188,"children":1189},{},[1190],{"type":48,"value":1191},"Do not pass a custom message body.",{"type":48,"value":1193}," Send only the code via ",{"type":42,"tag":127,"props":1195,"children":1197},{"className":1196},[],[1198],{"type":48,"value":1097},{"type":48,"value":1200},"; Verify maps it into an approved template. Custom message text returns ",{"type":42,"tag":468,"props":1202,"children":1205},{"href":1203,"rel":1204},"https:\u002F\u002Fwww.twilio.com\u002Fdocs\u002Fapi\u002Ferrors\u002F60243",[472],[1206],{"type":48,"value":1207},"error 60243",{"type":48,"value":285},{"type":42,"tag":311,"props":1210,"children":1211},{},[1212,1214,1219],{"type":48,"value":1213},"You are billed per ",{"type":42,"tag":57,"props":1215,"children":1216},{},[1217],{"type":48,"value":1218},"attempted",{"type":48,"value":1220}," send (not per completed verification), and custom-code flows tend to generate more sends than checks (abandoned\u002Fre-requested codes) — expect higher cost than standard Verify.",{"type":42,"tag":311,"props":1222,"children":1223},{},[1224,1229,1231,1237],{"type":42,"tag":57,"props":1225,"children":1226},{},[1227],{"type":48,"value":1228},"Report the outcome so Twilio can keep deliverability high.",{"type":48,"value":1230}," Verify's route-intelligence engine uses whether a code gets validated as a signal — it automatically retries different carriers\u002Froutes when codes go unvalidated or delivery underperforms. Tell Verify the result by updating the verification's status to ",{"type":42,"tag":127,"props":1232,"children":1234},{"className":1233},[],[1235],{"type":48,"value":1236},"approved",{"type":48,"value":1238}," so Twilio can keep optimizing routing:",{"type":42,"tag":51,"props":1240,"children":1241},{},[1242],{"type":42,"tag":57,"props":1243,"children":1244},{},[1245],{"type":48,"value":485},{"type":42,"tag":487,"props":1247,"children":1249},{"className":489,"code":1248,"language":491,"meta":492,"style":492},"client.verify.v2 \\\n    .services(os.environ[\"VERIFY_SERVICE_SID\"]) \\\n    .verifications(verification.sid) \\\n    .update(status=\"approved\")  # after your own code check passes\n",[1250],{"type":42,"tag":127,"props":1251,"children":1252},{"__ignoreMap":492},[1253,1261,1268,1276],{"type":42,"tag":498,"props":1254,"children":1255},{"class":500,"line":501},[1256],{"type":42,"tag":498,"props":1257,"children":1258},{},[1259],{"type":48,"value":1260},"client.verify.v2 \\\n",{"type":42,"tag":498,"props":1262,"children":1263},{"class":500,"line":510},[1264],{"type":42,"tag":498,"props":1265,"children":1266},{},[1267],{"type":48,"value":697},{"type":42,"tag":498,"props":1269,"children":1270},{"class":500,"line":519},[1271],{"type":42,"tag":498,"props":1272,"children":1273},{},[1274],{"type":48,"value":1275},"    .verifications(verification.sid) \\\n",{"type":42,"tag":498,"props":1277,"children":1278},{"class":500,"line":529},[1279],{"type":42,"tag":498,"props":1280,"children":1281},{},[1282],{"type":48,"value":1283},"    .update(status=\"approved\")  # after your own code check passes\n",{"type":42,"tag":51,"props":1285,"children":1286},{},[1287],{"type":48,"value":1288},"Treat custom codes as a transition aid, not the end state — migrating to Verify-generated codes removes all these caveats.",{"type":42,"tag":448,"props":1290,"children":1292},{"id":1291},"translate-your-error-handling",[1293],{"type":48,"value":1294},"Translate your error handling",{"type":42,"tag":51,"props":1296,"children":1297},{},[1298],{"type":48,"value":1299},"Verify uses different error codes than Messaging. Map them during migration:",{"type":42,"tag":80,"props":1301,"children":1302},{},[1303,1324],{"type":42,"tag":84,"props":1304,"children":1305},{},[1306],{"type":42,"tag":88,"props":1307,"children":1308},{},[1309,1314,1319],{"type":42,"tag":92,"props":1310,"children":1311},{},[1312],{"type":48,"value":1313},"Situation",{"type":42,"tag":92,"props":1315,"children":1316},{},[1317],{"type":48,"value":1318},"Verify error",{"type":42,"tag":92,"props":1320,"children":1321},{},[1322],{"type":48,"value":1323},"Handle by",{"type":42,"tag":103,"props":1325,"children":1326},{},[1327,1353,1371,1389,1412,1430],{"type":42,"tag":88,"props":1328,"children":1329},{},[1330,1335,1340],{"type":42,"tag":110,"props":1331,"children":1332},{},[1333],{"type":48,"value":1334},"Malformed \u002F non-E.164 number, bad param",{"type":42,"tag":110,"props":1336,"children":1337},{},[1338],{"type":48,"value":1339},"60200",{"type":42,"tag":110,"props":1341,"children":1342},{},[1343,1345,1351],{"type":48,"value":1344},"Fix ",{"type":42,"tag":127,"props":1346,"children":1348},{"className":1347},[],[1349],{"type":48,"value":1350},"to",{"type":48,"value":1352}," format (use Lookup)",{"type":42,"tag":88,"props":1354,"children":1355},{},[1356,1361,1366],{"type":42,"tag":110,"props":1357,"children":1358},{},[1359],{"type":48,"value":1360},"Too many sends to a number",{"type":42,"tag":110,"props":1362,"children":1363},{},[1364],{"type":48,"value":1365},"60203",{"type":42,"tag":110,"props":1367,"children":1368},{},[1369],{"type":48,"value":1370},"Back off; surface \"try again later\"",{"type":42,"tag":88,"props":1372,"children":1373},{},[1374,1379,1384],{"type":42,"tag":110,"props":1375,"children":1376},{},[1377],{"type":48,"value":1378},"Too many check attempts",{"type":42,"tag":110,"props":1380,"children":1381},{},[1382],{"type":48,"value":1383},"60202",{"type":42,"tag":110,"props":1385,"children":1386},{},[1387],{"type":48,"value":1388},"Issue a new verification",{"type":42,"tag":88,"props":1390,"children":1391},{},[1392,1397,1402],{"type":42,"tag":110,"props":1393,"children":1394},{},[1395],{"type":48,"value":1396},"Service SID wrong",{"type":42,"tag":110,"props":1398,"children":1399},{},[1400],{"type":48,"value":1401},"60212",{"type":42,"tag":110,"props":1403,"children":1404},{},[1405,1407],{"type":48,"value":1406},"Check ",{"type":42,"tag":127,"props":1408,"children":1410},{"className":1409},[],[1411],{"type":48,"value":361},{"type":42,"tag":88,"props":1413,"children":1414},{},[1415,1420,1425],{"type":42,"tag":110,"props":1416,"children":1417},{},[1418],{"type":48,"value":1419},"Country not enabled",{"type":42,"tag":110,"props":1421,"children":1422},{},[1423],{"type":48,"value":1424},"60410",{"type":42,"tag":110,"props":1426,"children":1427},{},[1428],{"type":48,"value":1429},"Enable geo-permission in Console",{"type":42,"tag":88,"props":1431,"children":1432},{},[1433,1438,1443],{"type":42,"tag":110,"props":1434,"children":1435},{},[1436],{"type":48,"value":1437},"Custom message passed",{"type":42,"tag":110,"props":1439,"children":1440},{},[1441],{"type":48,"value":1442},"60243",{"type":42,"tag":110,"props":1444,"children":1445},{},[1446,1448,1453],{"type":48,"value":1447},"Use ",{"type":42,"tag":127,"props":1449,"children":1451},{"className":1450},[],[1452],{"type":48,"value":1097},{"type":48,"value":1454}," only, not a body",{"type":42,"tag":51,"props":1456,"children":1457},{},[1458,1460,1465],{"type":48,"value":1459},"Full Verify error handling and built-in protections: ",{"type":42,"tag":127,"props":1461,"children":1463},{"className":1462},[],[1464],{"type":48,"value":267},{"type":48,"value":285},{"type":42,"tag":448,"props":1467,"children":1469},{"id":1468},"rate-limits-fraud-delete-your-custom-throttling",[1470],{"type":48,"value":1471},"Rate limits & fraud — delete your custom throttling",{"type":42,"tag":51,"props":1473,"children":1474},{},[1475,1477,1482,1484,1489,1491,1496,1498,1503,1505,1512,1514,1519,1521,1527],{"type":48,"value":1476},"Verify enforces, per phone per service, out of the box: ",{"type":42,"tag":57,"props":1478,"children":1479},{},[1480],{"type":48,"value":1481},"5 sends \u002F 10 min",{"type":48,"value":1483}," and\n",{"type":42,"tag":57,"props":1485,"children":1486},{},[1487],{"type":48,"value":1488},"5 check attempts",{"type":48,"value":1490}," per verification. Codes live ",{"type":42,"tag":57,"props":1492,"children":1493},{},[1494],{"type":48,"value":1495},"10 minutes",{"type":48,"value":1497}," and are\nimmutable within that window (a re-request inside 10 min returns the ",{"type":42,"tag":271,"props":1499,"children":1500},{},[1501],{"type":48,"value":1502},"same",{"type":48,"value":1504},"\ncode). This replaces hand-rolled resend counters. Need different limits? Define\ncustom ",{"type":42,"tag":468,"props":1506,"children":1509},{"href":1507,"rel":1508},"https:\u002F\u002Fwww.twilio.com\u002Fdocs\u002Fverify\u002Fapi\u002Fservice-rate-limits",[472],[1510],{"type":48,"value":1511},"Rate Limits \u002F Buckets",{"type":48,"value":1513},"\non the Service. Enable ",{"type":42,"tag":57,"props":1515,"children":1516},{},[1517],{"type":48,"value":1518},"Fraud Guard",{"type":48,"value":1520}," in the Console to retire your SMS-pumping\ndefenses. See ",{"type":42,"tag":127,"props":1522,"children":1524},{"className":1523},[],[1525],{"type":48,"value":1526},"twilio-security-hardening",{"type":48,"value":1528}," for the pumping-attack background.",{"type":42,"tag":448,"props":1530,"children":1532},{"id":1531},"safe-incremental-cutover",[1533],{"type":48,"value":1534},"Safe incremental cutover",{"type":42,"tag":51,"props":1536,"children":1537},{},[1538],{"type":48,"value":1539},"Don't flip 100% of traffic at once:",{"type":42,"tag":1541,"props":1542,"children":1543},"ol",{},[1544,1554,1572,1582],{"type":42,"tag":311,"props":1545,"children":1546},{},[1547,1552],{"type":42,"tag":57,"props":1548,"children":1549},{},[1550],{"type":48,"value":1551},"Stand up Verify in parallel.",{"type":48,"value":1553}," Keep the Messaging path live; route a small % of new OTP requests (or one region \u002F internal users) through Verify.",{"type":42,"tag":311,"props":1555,"children":1556},{},[1557,1562,1564,1570],{"type":42,"tag":57,"props":1558,"children":1559},{},[1560],{"type":48,"value":1561},"Dual-run the check.",{"type":48,"value":1563}," If a user was issued a legacy code, validate against your DB; if issued via Verify, use ",{"type":42,"tag":127,"props":1565,"children":1567},{"className":1566},[],[1568],{"type":48,"value":1569},"verification_checks",{"type":48,"value":1571},". Track which system issued each code so checks hit the right validator.",{"type":42,"tag":311,"props":1573,"children":1574},{},[1575,1580],{"type":42,"tag":57,"props":1576,"children":1577},{},[1578],{"type":48,"value":1579},"Drain, then delete.",{"type":48,"value":1581}," Once no legacy codes remain within their 10-min TTL, stop issuing them, ramp Verify to 100%, then decommission the OTP number\u002FMessaging Service and drop the OTP DB table.",{"type":42,"tag":311,"props":1583,"children":1584},{},[1585,1590],{"type":42,"tag":57,"props":1586,"children":1587},{},[1588],{"type":48,"value":1589},"Reuse one Verify Service",{"type":48,"value":1591}," across the app — do not create a Service per request.",{"type":42,"tag":51,"props":1593,"children":1594},{},[1595,1597,1604],{"type":48,"value":1596},"For testing without tripping rate limits during the cutover, see Twilio's\n",{"type":42,"tag":468,"props":1598,"children":1601},{"href":1599,"rel":1600},"https:\u002F\u002Fwww.twilio.com\u002Fdocs\u002Fverify\u002Fquickstarts\u002Ftest",[472],[1602],{"type":48,"value":1603},"Verify testing docs",{"type":48,"value":285},{"type":42,"tag":297,"props":1606,"children":1607},{},[],{"type":42,"tag":43,"props":1609,"children":1611},{"id":1610},"cannot",[1612],{"type":48,"value":1613},"CANNOT",{"type":42,"tag":307,"props":1615,"children":1616},{},[1617,1627,1644,1670,1685,1695,1705,1715,1732,1749],{"type":42,"tag":311,"props":1618,"children":1619},{},[1620,1625],{"type":42,"tag":57,"props":1621,"children":1622},{},[1623],{"type":48,"value":1624},"Cannot retrieve the code Verify generated",{"type":48,"value":1626}," — never returned by any API (by design for security). If your old flow logged or displayed the code, that stops working.",{"type":42,"tag":311,"props":1628,"children":1629},{},[1630,1635,1637,1642],{"type":42,"tag":57,"props":1631,"children":1632},{},[1633],{"type":48,"value":1634},"Cannot reuse your owned number as the Verify sender by default",{"type":48,"value":1636}," — Verify picks from managed sender pools per country. (You ",{"type":42,"tag":271,"props":1638,"children":1639},{},[1640],{"type":48,"value":1641},"can",{"type":48,"value":1643}," bring your own number\u002FMessaging Service for specific channels\u002FWhatsApp, but that's not the default.)",{"type":42,"tag":311,"props":1645,"children":1646},{},[1647,1652,1654,1660,1662,1668],{"type":42,"tag":57,"props":1648,"children":1649},{},[1650],{"type":48,"value":1651},"Cannot pass a raw custom message body",{"type":48,"value":1653}," — the ",{"type":42,"tag":127,"props":1655,"children":1657},{"className":1656},[],[1658],{"type":48,"value":1659},"customMessage",{"type":48,"value":1661}," param is deprecated; raw text → error 60243. To customize the copy, use an approved template (",{"type":42,"tag":127,"props":1663,"children":1665},{"className":1664},[],[1666],{"type":48,"value":1667},"TemplateSid",{"type":48,"value":1669},") instead.",{"type":42,"tag":311,"props":1671,"children":1672},{},[1673,1683],{"type":42,"tag":57,"props":1674,"children":1675},{},[1676,1681],{"type":42,"tag":127,"props":1677,"children":1679},{"className":1678},[],[1680],{"type":48,"value":1097},{"type":48,"value":1682}," has strings attached",{"type":48,"value":1684}," — must be enabled per-Service (Console → Verify → Services → General), bills per attempt (higher cost than standard Verify), and mandates updating Verification Status manually.",{"type":42,"tag":311,"props":1686,"children":1687},{},[1688,1693],{"type":42,"tag":57,"props":1689,"children":1690},{},[1691],{"type":48,"value":1692},"Cannot change the 10-minute token TTL yourself",{"type":48,"value":1694}," — to change the default (adjustable between 2 minutes and 24 hours), you must contact Twilio Support to set it on your Service.",{"type":42,"tag":311,"props":1696,"children":1697},{},[1698,1703],{"type":42,"tag":57,"props":1699,"children":1700},{},[1701],{"type":48,"value":1702},"Cannot re-check an approved verification",{"type":48,"value":1704}," — single-use; a second check returns 404.",{"type":42,"tag":311,"props":1706,"children":1707},{},[1708,1713],{"type":42,"tag":57,"props":1709,"children":1710},{},[1711],{"type":48,"value":1712},"Cannot send to non-E.164 numbers",{"type":48,"value":1714}," — normalize first (Lookup).",{"type":42,"tag":311,"props":1716,"children":1717},{},[1718,1723,1725,1730],{"type":42,"tag":57,"props":1719,"children":1720},{},[1721],{"type":48,"value":1722},"No push webhook on completion",{"type":48,"value":1724}," — poll ",{"type":42,"tag":127,"props":1726,"children":1728},{"className":1727},[],[1729],{"type":48,"value":1569},{"type":48,"value":1731}," (rate-limited: 60\u002Fmin, 180\u002Fhr, 250\u002Fday) or use the Verify Events API.",{"type":42,"tag":311,"props":1733,"children":1734},{},[1735,1740,1742,1748],{"type":42,"tag":57,"props":1736,"children":1737},{},[1738],{"type":48,"value":1739},"Cannot skip WhatsApp sender setup",{"type":48,"value":1741}," — as of March 1, 2024 there is no shared Verify WhatsApp sender; bring your own. See ",{"type":42,"tag":127,"props":1743,"children":1745},{"className":1744},[],[1746],{"type":48,"value":1747},"twilio-whatsapp-manage-senders",{"type":48,"value":285},{"type":42,"tag":311,"props":1750,"children":1751},{},[1752,1757],{"type":42,"tag":57,"props":1753,"children":1754},{},[1755],{"type":48,"value":1756},"Verify traffic is A2P-exempt, but your remaining non-OTP Messaging is not",{"type":48,"value":1758}," — keep 10DLC registration for any other traffic on that number.",{"type":42,"tag":297,"props":1760,"children":1761},{},[],{"type":42,"tag":43,"props":1763,"children":1765},{"id":1764},"next-steps",[1766],{"type":48,"value":1767},"Next Steps",{"type":42,"tag":307,"props":1769,"children":1770},{},[1771,1786,1800,1814,1828,1842,1856],{"type":42,"tag":311,"props":1772,"children":1773},{},[1774,1779,1781],{"type":42,"tag":57,"props":1775,"children":1776},{},[1777],{"type":48,"value":1778},"Full Verify API reference (all channels, service config):",{"type":48,"value":1780}," ",{"type":42,"tag":127,"props":1782,"children":1784},{"className":1783},[],[1785],{"type":48,"value":267},{"type":42,"tag":311,"props":1787,"children":1788},{},[1789,1794,1795],{"type":42,"tag":57,"props":1790,"children":1791},{},[1792],{"type":48,"value":1793},"Decide product fit \u002F architecture first:",{"type":48,"value":1780},{"type":42,"tag":127,"props":1796,"children":1798},{"className":1797},[],[1799],{"type":48,"value":283},{"type":42,"tag":311,"props":1801,"children":1802},{},[1803,1808,1809],{"type":42,"tag":57,"props":1804,"children":1805},{},[1806],{"type":48,"value":1807},"Normalize numbers to E.164 + fraud signals:",{"type":48,"value":1780},{"type":42,"tag":127,"props":1810,"children":1812},{"className":1811},[],[1813],{"type":48,"value":1079},{"type":42,"tag":311,"props":1815,"children":1816},{},[1817,1822,1823],{"type":42,"tag":57,"props":1818,"children":1819},{},[1820],{"type":48,"value":1821},"Bring a WhatsApp sender for WhatsApp OTP:",{"type":48,"value":1780},{"type":42,"tag":127,"props":1824,"children":1826},{"className":1825},[],[1827],{"type":48,"value":1747},{"type":42,"tag":311,"props":1829,"children":1830},{},[1831,1836,1837],{"type":42,"tag":57,"props":1832,"children":1833},{},[1834],{"type":48,"value":1835},"Retire SMS-pumping defenses safely:",{"type":48,"value":1780},{"type":42,"tag":127,"props":1838,"children":1840},{"className":1839},[],[1841],{"type":48,"value":1526},{"type":42,"tag":311,"props":1843,"children":1844},{},[1845,1850,1851],{"type":42,"tag":57,"props":1846,"children":1847},{},[1848],{"type":48,"value":1849},"Credential setup:",{"type":48,"value":1780},{"type":42,"tag":127,"props":1852,"children":1854},{"className":1853},[],[1855],{"type":48,"value":369},{"type":42,"tag":311,"props":1857,"children":1858},{},[1859,1864,1865],{"type":42,"tag":57,"props":1860,"children":1861},{},[1862],{"type":48,"value":1863},"Source blog post:",{"type":48,"value":1780},{"type":42,"tag":468,"props":1866,"children":1869},{"href":1867,"rel":1868},"https:\u002F\u002Fwww.twilio.com\u002Fen-us\u002Fblog\u002Fmigrate-programmable-messaging-to-verify",[472],[1870],{"type":48,"value":1871},"Migrate from Programmable Messaging to Verify",{"type":42,"tag":1873,"props":1874,"children":1875},"style",{},[1876],{"type":48,"value":1877},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":1879,"total":2057},[1880,1893,1913,1924,1936,1951,1968,1982,1998,2011,2027,2045],{"slug":326,"name":326,"fn":1881,"description":1882,"org":1883,"tags":1884,"stars":26,"repoUrl":27,"updatedAt":1892},"configure Twilio accounts and credentials","Create and configure a Twilio account from scratch. Covers free trial signup, trial limitations, getting credentials (Account SID and Auth Token), buying a phone number, verifying recipient numbers for trial use, SDK installation, first API call, subaccount management (creation, inheritance, credential isolation, limits), and enabling specific products (AI Assistants, Conversations, Verify, ConversationRelay, WhatsApp). Use this skill before any other Twilio skill if you do not yet have a Twilio account or need to enable a product. For Organization-level governance (SSO, SCIM, multi-team), see `twilio-organizations-setup`.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1885,1888,1891],{"name":1886,"slug":1887,"type":15},"API Development","api-development",{"name":1889,"slug":1890,"type":15},"Communications","communications",{"name":17,"slug":18,"type":15},"2026-08-01T05:43:28.968968",{"slug":1894,"name":1894,"fn":1895,"description":1896,"org":1897,"tags":1898,"stars":26,"repoUrl":27,"updatedAt":1912},"twilio-agent-augmentation-architect","augment human agents with AI intelligence","Planning skill for augmenting human agents with real-time AI intelligence. Qualifies the developer's use case across coaching, compliance, QA, and routing to recommend the right Conversation Intelligence + Conversation Memory + TaskRouter architecture. Handles both \"I want to add AI coaching to my call center\" and \"configure Conversation Intelligence operators for script adherence.\"\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1899,1902,1905,1908,1911],{"name":1900,"slug":1901,"type":15},"Agents","agents",{"name":1903,"slug":1904,"type":15},"AI","ai",{"name":1906,"slug":1907,"type":15},"Coaching","coaching",{"name":1909,"slug":1910,"type":15},"QA","qa",{"name":9,"slug":8,"type":15},"2026-07-17T06:07:58.250609",{"slug":1914,"name":1914,"fn":1915,"description":1916,"org":1917,"tags":1918,"stars":26,"repoUrl":27,"updatedAt":1923},"twilio-agent-connect","connect AI agents to Twilio channels","Connect third-party AI agents (OpenAI, Bedrock, LangChain, Microsoft Foundry) to Twilio's communication channels using the Twilio Agent Connect SDK. Covers identity resolution, memory and context management via Conversation Memory, conversation orchestration via Conversation Orchestrator, multi-channel handling (Voice, SMS, RCS, WhatsApp, Chat), and AI-to-human escalation. Use this skill when integrating an existing LLM agent with Twilio services.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1919,1920,1921,1922],{"name":1900,"slug":1901,"type":15},{"name":1886,"slug":1887,"type":15},{"name":1889,"slug":1890,"type":15},{"name":9,"slug":8,"type":15},"2026-07-17T06:06:05.217098",{"slug":1925,"name":1925,"fn":1926,"description":1927,"org":1928,"tags":1929,"stars":26,"repoUrl":27,"updatedAt":1935},"twilio-ai-agent-architect","plan Twilio conversational AI agents","Planning skill for AI-powered conversational agents. Qualifies the developer's use case across outcome sophistication, entry point, and customer profile to recommend the right Twilio Conversations architecture and implementation skills. Handles both high-level requests (\"build me a voice AI assistant\") and specific ones (\"integrate ConversationRelay with my OpenAI backend\").\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1930,1931,1934],{"name":1900,"slug":1901,"type":15},{"name":1932,"slug":1933,"type":15},"Architecture","architecture",{"name":9,"slug":8,"type":15},"2026-07-17T06:07:48.883723",{"slug":1937,"name":1937,"fn":1938,"description":1939,"org":1940,"tags":1941,"stars":26,"repoUrl":27,"updatedAt":1950},"twilio-call-recordings","record and manage Twilio voice calls","Record Twilio voice calls correctly. Covers the critical distinction between Record verb (voicemail) and Dial record (call recording), dual-channel for QA, mid-call pause for PCI, Conference recording, and the ConversationRelay workaround. Use this skill whenever you need to capture call audio for compliance, QA, or analytics.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1942,1945,1948,1949],{"name":1943,"slug":1944,"type":15},"Audio","audio",{"name":1946,"slug":1947,"type":15},"Compliance","compliance",{"name":1909,"slug":1910,"type":15},{"name":9,"slug":8,"type":15},"2026-07-17T06:07:55.268412",{"slug":1952,"name":1952,"fn":1953,"description":1954,"org":1955,"tags":1956,"stars":26,"repoUrl":27,"updatedAt":1967},"twilio-cli-reference","manage Twilio resources via CLI","Twilio CLI reference for managing Twilio resources from the terminal. Covers installation, credential profiles, phone number provisioning, sending SMS and email, webhook configuration, local development with a tunneling service, debugging with watch and logs, serverless deployment, and plugin ecosystem. Use when the developer asks to \"just do it\", \"set this up\", \"run a command\", mentions \"CLI\", \"command line\", or \"terminal\", or when an AI agent can execute a task directly instead of writing application code.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1957,1960,1963,1966],{"name":1958,"slug":1959,"type":15},"CLI","cli",{"name":1961,"slug":1962,"type":15},"Local Development","local-development",{"name":1964,"slug":1965,"type":15},"Operations","operations",{"name":9,"slug":8,"type":15},"2026-07-17T06:07:54.925664",{"slug":1969,"name":1969,"fn":1970,"description":1971,"org":1972,"tags":1973,"stars":26,"repoUrl":27,"updatedAt":1981},"twilio-compliance-onboarding","manage Twilio messaging and voice compliance","Registrations required BEFORE Twilio traffic works. Covers messaging programs (A2P 10DLC, toll-free verification, WhatsApp WABA, RCS, short code, alphanumeric sender) and voice trust programs (STIR\u002FSHAKEN, Voice Integrity, Branded Calling, CNAM). Each number\u002Fsender type has its own program — registration blocks traffic until complete.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1974,1975,1976,1977,1978],{"name":1946,"slug":1947,"type":15},{"name":23,"slug":24,"type":15},{"name":17,"slug":18,"type":15},{"name":9,"slug":8,"type":15},{"name":1979,"slug":1980,"type":15},"WhatsApp","whatsapp","2026-07-17T06:05:47.897229",{"slug":1983,"name":1983,"fn":1984,"description":1985,"org":1986,"tags":1987,"stars":26,"repoUrl":27,"updatedAt":1997},"twilio-compliance-traffic","ensure compliance for Twilio messaging traffic","Rules you must follow for Twilio messaging and voice traffic. Covers TCPA (consent tiers, quiet hours, DNC), GDPR (EU consent, right to deletion), PCI DSS (payment recording, Pay verb), HIPAA (BAA, PHI), FDCPA (debt collection limits), CAN-SPAM, WhatsApp policies, SHAKEN\u002FSTIR, and consent management patterns. Use this skill proactively when developers have working traffic to ensure they follow the rules.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1988,1989,1990,1993,1996],{"name":1946,"slug":1947,"type":15},{"name":23,"slug":24,"type":15},{"name":1991,"slug":1992,"type":15},"Regulatory Compliance","regulatory-compliance",{"name":1994,"slug":1995,"type":15},"Security","security",{"name":9,"slug":8,"type":15},"2026-07-17T06:07:55.952779",{"slug":1999,"name":1999,"fn":2000,"description":2001,"org":2002,"tags":2003,"stars":26,"repoUrl":27,"updatedAt":2010},"twilio-conference-calls","build multi-party calls with Twilio Conference","Build multi-party calls using Twilio Conference. Covers warm transfer, cold transfer, coaching (whisper), hold vs mute, participant modes, and supervisor barge. Use this skill for any contact center, support line, or scenario requiring transfers, holds, or multi-party calls.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2004,2005,2006,2009],{"name":1943,"slug":1944,"type":15},{"name":1889,"slug":1890,"type":15},{"name":2007,"slug":2008,"type":15},"Meetings","meetings",{"name":9,"slug":8,"type":15},"2026-07-17T06:07:55.603708",{"slug":2012,"name":2012,"fn":2013,"description":2014,"org":2015,"tags":2016,"stars":26,"repoUrl":27,"updatedAt":2026},"twilio-content-template-builder","create and send message templates with Twilio","Create, manage, and send message templates using Twilio's Content API. Covers template creation for WhatsApp, SMS, RCS, and MMS; variable usage; WhatsApp Meta approval; and sending templates via ContentSid. Use this skill when building structured messages that require pre-approval or consistent formatting across channels.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2017,2020,2021,2022,2025],{"name":2018,"slug":2019,"type":15},"Email","email",{"name":23,"slug":24,"type":15},{"name":17,"slug":18,"type":15},{"name":2023,"slug":2024,"type":15},"Templates","templates",{"name":9,"slug":8,"type":15},"2026-07-17T06:04:26.637309",{"slug":2028,"name":2028,"fn":2029,"description":2030,"org":2031,"tags":2032,"stars":26,"repoUrl":27,"updatedAt":2044},"twilio-conversation-intelligence","build conversation intelligence pipelines","Twilio Conversation Intelligence development guide. Use when building real-time or post-call conversation analysis, language operator pipelines, sentiment analysis, agent assist, cross-channel analytics, or querying aggregated conversation insights (sentiment trends, escalation rates, dashboards).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2033,2034,2037,2040,2043],{"name":1900,"slug":1901,"type":15},{"name":2035,"slug":2036,"type":15},"Analytics","analytics",{"name":2038,"slug":2039,"type":15},"Monitoring","monitoring",{"name":2041,"slug":2042,"type":15},"NLP","nlp",{"name":9,"slug":8,"type":15},"2026-07-17T06:07:52.545387",{"slug":2046,"name":2046,"fn":2047,"description":2048,"org":2049,"tags":2050,"stars":26,"repoUrl":27,"updatedAt":2056},"twilio-conversation-memory","manage conversation memory with Twilio","Store and retrieve conversation context using Twilio Conversation Memory. Covers Memory Store provisioning, profile management, traits, observations, conversation summaries, and semantic Recall. Use this skill to give AI agents or human agents persistent memory of conversations across sessions and channels.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2051,2052,2055],{"name":1900,"slug":1901,"type":15},{"name":2053,"slug":2054,"type":15},"Memory","memory",{"name":9,"slug":8,"type":15},"2026-07-17T06:04:19.526724",57,{"items":2059,"total":2057},[2060,2066,2074,2081,2087,2094,2101],{"slug":326,"name":326,"fn":1881,"description":1882,"org":2061,"tags":2062,"stars":26,"repoUrl":27,"updatedAt":1892},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2063,2064,2065],{"name":1886,"slug":1887,"type":15},{"name":1889,"slug":1890,"type":15},{"name":17,"slug":18,"type":15},{"slug":1894,"name":1894,"fn":1895,"description":1896,"org":2067,"tags":2068,"stars":26,"repoUrl":27,"updatedAt":1912},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2069,2070,2071,2072,2073],{"name":1900,"slug":1901,"type":15},{"name":1903,"slug":1904,"type":15},{"name":1906,"slug":1907,"type":15},{"name":1909,"slug":1910,"type":15},{"name":9,"slug":8,"type":15},{"slug":1914,"name":1914,"fn":1915,"description":1916,"org":2075,"tags":2076,"stars":26,"repoUrl":27,"updatedAt":1923},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2077,2078,2079,2080],{"name":1900,"slug":1901,"type":15},{"name":1886,"slug":1887,"type":15},{"name":1889,"slug":1890,"type":15},{"name":9,"slug":8,"type":15},{"slug":1925,"name":1925,"fn":1926,"description":1927,"org":2082,"tags":2083,"stars":26,"repoUrl":27,"updatedAt":1935},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2084,2085,2086],{"name":1900,"slug":1901,"type":15},{"name":1932,"slug":1933,"type":15},{"name":9,"slug":8,"type":15},{"slug":1937,"name":1937,"fn":1938,"description":1939,"org":2088,"tags":2089,"stars":26,"repoUrl":27,"updatedAt":1950},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2090,2091,2092,2093],{"name":1943,"slug":1944,"type":15},{"name":1946,"slug":1947,"type":15},{"name":1909,"slug":1910,"type":15},{"name":9,"slug":8,"type":15},{"slug":1952,"name":1952,"fn":1953,"description":1954,"org":2095,"tags":2096,"stars":26,"repoUrl":27,"updatedAt":1967},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2097,2098,2099,2100],{"name":1958,"slug":1959,"type":15},{"name":1961,"slug":1962,"type":15},{"name":1964,"slug":1965,"type":15},{"name":9,"slug":8,"type":15},{"slug":1969,"name":1969,"fn":1970,"description":1971,"org":2102,"tags":2103,"stars":26,"repoUrl":27,"updatedAt":1981},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2104,2105,2106,2107,2108],{"name":1946,"slug":1947,"type":15},{"name":23,"slug":24,"type":15},{"name":17,"slug":18,"type":15},{"name":9,"slug":8,"type":15},{"name":1979,"slug":1980,"type":15}]