4.0.0 was published to npm on January 25, 2023, the date of its entry in Twilio's UPGRADE.md. Twilio's changelog for the release says the source moved to TypeScript, that removing large dependencies cut the bundle size by 31%, that lazy loading is now on by default, and that the library adopted Twilio's OpenAPI specification. The list of breaking changes is short, but each item fails differently: some throw on the first request, some change XML output, some only show up in TypeScript. Later majors are lighter: UPGRADE.md says 5.0.0 has no breaking changes to existing APIs, and 6.0.0 only raises the minimum Node.js version to 20. This guide follows UPGRADE.md and Twilio's release notes, linked at the end.
What changed
- Node.js 14 or later is required. Support for older versions was dropped, and Node.js 18 support was added.
- Lazy loading of Twilio modules is on by default; pass
lazyLoading: falseto turn it off. new AccessToken(...)requires anidentityin its options.IpMessagingGrantis removed andConversationsGrantis deprecated.- The
ssml*helpers on<Say>are renamed, for examplesay.ssmlEmphasis()tosay.emphasis(), andRefer.referSip()becomesRefer.sip(). - TaskRouter cumulative and real-time Workers statistics no longer accept a Worker SID.
- Some response properties use
Recordtypes with string keys instead ofobject, includingsubresourceUrisandlinks.
Migration steps
Check your Node.js version and upgrade
npm install twilio@^4Check the runtime first: 4.x needs Node.js 14 or later, and the 4.x README lists 14, 16 and 18 as supported. If you plan to go straight to the latest release, 6.x needs Node.js 20, so you may as well upgrade Node once. When 4.x shipped, Twilio's changelog noted that it was not compatible with Twilio Functions; if you deploy there, check the current Functions runtime notes before upgrading. The main entry points (
require("twilio")(accountSid, authToken),client.messages.create(...)) are not on UPGRADE.md's list of changes.Pass identity when you create access tokens
Before
const AccessToken = require("twilio").jwt.AccessToken; const token = new AccessToken(accountSid, apiKeySid, apiKeySecret); token.identity = "alice";After
const AccessToken = require("twilio").jwt.AccessToken; const token = new AccessToken(accountSid, apiKeySid, apiKeySecret, { identity: "alice", });In 3.x
identitywas optional and could be assigned after construction. In 4.x the constructor throwsidentity is required to be specified in optionswhen it is missing. Token endpoints usually run per request, so this fails the first time a client asks for a token, not when the server starts.Replace removed grants
Before
token.addGrant(new AccessToken.IpMessagingGrant({ serviceSid }));After
token.addGrant(new AccessToken.ChatGrant({ serviceSid }));IpMessagingGrantis removed; the 3.x deprecation warning already pointed toChatGrant.ConversationsGrantis listed as deprecated, and neither class appears in the currentAccessTokensource. UPGRADE.md namesVoiceGrantas its replacement while the 3.x deprecation warning namedVideoGrant, so pick the grant that matches what the token is for.Rename SSML helpers in TwiML
Before
const response = new VoiceResponse(); const say = response.say("Hello"); say.ssmlEmphasis("you"); say.ssmlBreak({ time: "500ms" });After
const response = new VoiceResponse(); const say = response.say("Hello"); say.emphasis("you"); say.break({ time: "500ms" });The full list of renames on
<Say>and<Refer>:ssmlBreak()andbreak_()tobreak()ssmlEmphasis()toemphasis(),ssmlLang()tolang(),ssmlP()top()ssmlPhoneme()tophoneme(),ssmlProsody()toprosody(),ssmlS()tos()ssmlSayAs()tosayAs(),ssmlSub()tosub(),ssmlW()tow()Refer.referSip()toRefer.sip()
Update TaskRouter worker statistics
Before
client.taskrouter.v1.workspaces("WS...").workers("WK...").cumulativeStatistics(); client.taskrouter.v1.workspaces("WS...").workers("WK...").realTimeStatistics();After
client.taskrouter.v1.workspaces("WS...").workers.cumulativeStatistics(); client.taskrouter.v1.workspaces("WS...").workers.realTimeStatistics();These operations map to
GET /v1/Workspaces/{WorkspaceSid}/Workers/CumulativeStatisticsand.../RealTimeStatistics. Code that passed a Worker SID to them needs to drop it.Review lazy loading and response types
3.x loaded every module up front unless you passed
lazyLoading: true; 4.x does the reverse. That option can simply be deleted. If you relied on eager loading, for example to pay the loading cost at startup rather than on the first request, turn it off explicitly:const client = require("twilio")(accountSid, authToken, { lazyLoading: false, });In TypeScript, some response properties changed from
objecttoRecordtypes with string keys, includingsubresourceUrison v2010 APIs andlinkselsewhere. Code that indexes them usually gets simpler, but explicit annotations or casts written against the old types may need updating.
How to find every affected call
Every breaking change has a distinctive call shape, so ast-grep can find them, including calls split across lines:
sg run --pattern 'new AccessToken($SID, $KEY, $SECRET)' --lang js
sg run --pattern '$TOKEN.identity = $ID' --lang js
sg run --pattern 'new $NS.IpMessagingGrant($$$ARGS)' --lang js
sg run --pattern '$SAY.ssmlEmphasis($$$ARGS)' --lang js
sg run --pattern '$REFER.referSip($$$ARGS)' --lang js
sg run --pattern '$WORKERS($SID).cumulativeStatistics($$$ARGS)' --lang jsRepeat the SSML search for each renamed helper you use, and the last pattern for realTimeStatistics. If you destructure grants (const { IpMessagingGrant } = AccessToken), search for new IpMessagingGrant($$$ARGS) as well. Run each pattern with --lang ts too. $TOKEN.identity matches any object with an identity property, so check the hits.
Testing the change
- Run CI on the Node.js version you deploy, at least 14 for 4.x and 20 if you continue to 6.x.
- Generate an access token in a unit test and decode it to assert the identity and grants, since a missing identity throws only at runtime.
- Compare TwiML output: call
response.toString()on your voice responses before and after the upgrade and diff the XML for every SSML helper you renamed. - Run
tsc --noEmitto catch code written against the oldobjecttypes.
Automating it
We found no official codemod for this upgrade in Twilio's UPGRADE.md, changelog or release announcement. The breaking changes are few enough that UPGRADE.md lists them one by one, which makes them good targets for search and replace, with the exception of the access token change.
Versyka finds these call sites deterministically with ast-grep, rewrites them together with the code around them (token construction, TwiML builders, mocks), then runs your repository's install, typecheck, build and tests in a disposable sandbox. A pull request is opened only when those pass.
Was this guide helpful?
Check your own repository
Paste your package.json, requirements.txt or pyproject.toml into the free upgrade check to see which API SDKs are behind, with no account. Or let Versyka run this migration on one repository and open a pull request that has already passed your tests.