Migrating to v1.25.0
Developer actions required when upgrading the Twilio in-browser call plugin to version 1.25.0.
Version 1.25.0 is a security release. It tightens what the plugin accepts and what it sends to third parties. Most apps upgrade without changes, but four changes can break a working app, and two security issues can only be fixed by you — they live in your Twilio Functions, not in the plugin.
Please read Step 1 even if you change nothing else.
The Access Token Function published in earlier versions of our Getting Started guide mints a token for whatever identity the caller asks for, from a public endpoint with CORS open to every origin. If you followed that guide, anyone who opens your app can request a token as one of your agents — receiving that agent's inbound calls and placing outbound calls billed to your Twilio account. No password is needed.
This applies to older plugin versions too. Upgrading to 1.25.0 does not fix it on its own.
Budget about 30 minutes: 10 for the plugin changes, 20 for your Twilio Functions.
At a glance
Access Token Function trusts event.identity
Yes — highest priority
Voice Function trusts appCallerId and To
Yes
Agent Identifier left blank
Yes — the default is a single shared identity
Identity is now validated
Yes, if yours contains anything outside A-Z a-z 0-9 _ . + @ -
agent now reaches your Voice Function
Yes, if your function reads event.agent
instance.data.token removed
Yes, if you have custom JavaScript reading it
Token lifetime capped at 24 hours
Only if you set a longer TTL
Telemetry no longer sends your email or Account SID
No — but update your own privacy policy if it mentioned this
Before you upgrade
Note your current plugin version so you can roll back.
Copy your Access Token Function and Voice Function code somewhere safe.
Upgrade on a development version of your app first.
Have the Twilio Console open.
Step 1: Secure your Access Token Function
The plugin requests a token like this, and accessTokenURL is a client-safe key — it is visible in your app's page source, and the identity parameter is trivially editable:
The function we previously documented used const identity = event.identity, so it returned a valid Voice token for any identity requested. Replace it with a version that decides the identity itself.
What changed and why:
Authenticate before minting
The endpoint is public and its URL is in your page source.
identity derived server-side
Stops a visitor requesting a token as another agent.
CORS restricted to your domain
* let any website call the endpoint from a victim's browser.
Removed console.log(jwt)
Wrote live access tokens into your Twilio function logs.
Short ttl
A leaked token stays usable until it expires.
incomingAllow: false where possible
An outbound-only token cannot intercept another agent's calls.
Using a Bubble backend workflow instead of a Twilio Function? Uncheck "This workflow can be run without authentication", delete the identity URL parameter, and set the Voice Access Token action's Identity to Current User's unique id.
How to check whether you are exposed: open your app, view source, find your accessTokenURL, and request it in a private window with an identity that is not yours. If a token comes back, you are exposed.
Step 2: Authorize caller ID and destination in your Voice Function
Start Call sends To, appCallerId and agent as TwiML parameters. All three come from the browser. Our earlier guidance — replace context.CALLER_ID with event.appCallerId — hands the choice of presented caller ID to the client.
An allowlist of countries, or a block on premium-rate prefixes, is usually enough for isAllowedDestination.
event.agent is self-reported by the browser. Never use it for authorization or billing. The identity embedded in the access token is the value you can trust.
Step 3: Set an explicit Agent Identifier
If you leave Agent Identifier blank, the plugin uses the literal string the_user_id — not the current user's id. Every user of your app then shares one identity, which means they can all receive each other's inbound calls.
Set it explicitly, to the same value your Twilio routing uses:
Current User's unique id
✅ Recommended
Current User's email
✅ Yes
support_team, agent_42
✅ Yes
Current User's Name (e.g. Jane Doe)
❌ Contains a space
Anything with : / # & or accents
❌ Rejected
If you change an identity, update your Twilio Studio routing to match — the identity is the address Twilio routes inbound calls to, so inbound calls stop arriving otherwise.
Step 4: Breaking changes in v1.25.0
Identity is now validated
Symptom: the Voice Access Token action fails with "identity" may only contain letters, digits, and the characters _ . + @ - or "identity" must be 121 characters or fewer.
The identity is signed into the token and decides which calls it can place and receive, so whitespace, newlines, quotes and path separators are now rejected. Leading and trailing spaces are trimmed automatically. See the table in Step 3 for what passes.
agent now reaches your Voice Function
Symptom: your Voice Function behaves differently on outbound calls.
Start Call always sent agent as an undefined value — the plugin read it from the wrong place. That is fixed, so event.agent now carries the element's Agent Name for the first time. If your function has if (event.agent) logic, re-check it: a branch that never ran before will start running.
instance.data.token has been removed
Symptom: custom JavaScript that read the access token off the element gets undefined.
The plugin stored the live token on the element where any script on the page could read it, and never read it back — the Twilio SDK keeps its own copy. If you need a token in your own workflow, call the Voice Access Token server-side action and use its returned token. instance.data.tokenURL is unchanged.
Token lifetime is capped
Time to live is now clamped to 86400 seconds (24 hours, Twilio's own ceiling), and blank, zero or non-numeric values fall back to 3600. Prefer a short lifetime — the Token Expiring event and the Update token action handle renewal for you.
Misconfigured credentials fail immediately
Symptom: "API Key" does not look right... instead of Twilio error 31202.
The action now checks Twilio's SID prefixes — Account Sid starts AC, API Key starts SK, Application Sid starts AP. API Key must be the API Key SID (SK…) from API Keys — not your Account SID, and not your Account Auth Token.
Step 5: Two smaller hardening steps
Sub Account Token — element properties live in page state, so any token bound to this field is readable by your end users. Use short lifetimes and the narrowest grants. See Subaccounts.
Scoped API Key — the bundled APIs authenticate with your Account SID and Auth Token, which carry full account authority. Move to a scoped Twilio API Key where you can.
Changes that need no action
Telemetry no longer includes your email or Twilio Account SID. Both were previously read in browser code and sent to our analytics on every page load. They have been removed; we now receive only the plugin name and version and the Bubble app name and version. If your own privacy policy mentioned the old behaviour, update it. The App Owner Email plugin field is no longer sent anywhere.
DTMF digits are no longer logged to the console. Callers key card numbers and PINs into IVRs, and browser consoles are captured by monitoring tools.
The Agent Identifier is URL-encoded before it is added to the token request, so special characters can no longer alter that request.
Scripts load over HTTPS rather than protocol-relative URLs.
Reset Twilio Device had an incorrect internal signature; corrected, with no behaviour change.
After upgrading: verification checklist
On a development version of your app:
Only then promote to live.
Rolling back
Revert to your previous plugin version in the Bubble plugin editor.
The issues in Steps 1 and 2 exist in older plugin versions too. Rolling back the plugin does not undo the fixes you make in your Twilio Functions, and you should keep those regardless.
Getting help
Plugin support — please include your plugin version, the exact error text from the Twilio Error event, and whether your token endpoint is a Twilio Function or a Bubble backend workflow.