For the complete documentation index, see llms.txt. This page is also available as Markdown.

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.

Budget about 30 minutes: 10 for the plugin changes, 20 for your Twilio Functions.

At a glance

Change
Do you need to act?

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

  1. Note your current plugin version so you can roll back.

  2. Copy your Access Token Function and Voice Function code somewhere safe.

  3. Upgrade on a development version of your app first.

  4. 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:

Change
Reason

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.

Step 3: Set an explicit Agent Identifier

If you leave Agent Identifier blank, the plugin uses the literal string the_user_idnot 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:

Value
Valid?

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.

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.