LaML / Compatibility API
SignalWire's Compatibility API is a drop-in alternative for Twilio TwiML, REST API, and helper-library code. Existing Twilio apps run on SignalWire with three changes — credentials, endpoint, webhook URLs.
LaML (SignalWire Markup Language) and cXML are SignalWire's XML voice-control format. They're functionally compatible with Twilio's TwiML for the supported feature surface.
The three migration changes
1. Update credentials
Replace Twilio Account SID and Auth Token with SignalWire Project ID and API Token from your SignalWire Dashboard.
| Twilio | SignalWire |
|---|---|
| Account SID | Project ID |
| Auth Token | API Token |
2. Change the API endpoint
If you're hitting Twilio's REST API directly, swap the base URL:
| Twilio | SignalWire |
|---|---|
| https://api.twilio.com/... | https://your-space.signalwire.com/... |
your-space is the SignalWire Space name in your dashboard URL.
If you're using the SignalWire Compatibility SDK (@signalwire/compatibility-api), the base URL is set automatically — no manual swap needed.
3. Update webhook URLs
Point your phone number webhooks to your SignalWire Space. Existing TwiML/cXML response handlers work without modification — the markup is compatible.
Critical caveat: change the from number
When migrating, replace the from number on outbound API calls with a valid SignalWire DID. A Twilio-owned number sent through SignalWire won't work — the number must live on your SignalWire project.
Node.js example
// Replace these lines:
const twilio = require('twilio');
const response = new twilio.twiml.VoiceResponse();
// With:
const { RestClient } = require('@signalwire/compatibility-api');
const response = new RestClient.LaML.VoiceResponse();
// Now use response like you did before
response.say('Hey, welcome to SignalWire!');
response.dial('+15555551234');
console.log(response.toString());
The LaML.VoiceResponse API matches Twilio's VoiceResponse for say, play, dial, gather, record, conference, enqueue, pause, redirect, hangup, and the rest of the core voice surface.
Sending a call (REST)
const { RestClient } = require('@signalwire/compatibility-api');
const client = new RestClient(
process.env.SIGNALWIRE_PROJECT_ID,
process.env.SIGNALWIRE_API_TOKEN,
{ signalwireSpaceUrl: 'your-space.signalwire.com' }
);
client.calls.create({
url: 'https://your.api/voice-handler',
to: '+15555550101',
from: '+15555550102', // MUST be a SignalWire DID
}).then((call) => console.log(call.sid));
Sending an SMS (REST)
client.messages.create({
to: '+15555550101',
from: '+15555550102', // MUST be a SignalWire DID
body: 'Hello from SignalWire LaML',
}).then((msg) => console.log(msg.sid));
Supported feature surface
| Category | Features | |---|---| | Voice | Inbound/outbound calls, call control, conferencing, queues, recordings, transcriptions | | Messaging | SMS, MMS, status callbacks | | Fax | Send and receive faxes | | Phone Numbers | Purchase, configure, manage DIDs | | Applications | Manage voice and messaging application configurations |
LaML vs SWML — which to use post-migration?
After the migration, you can keep running on LaML indefinitely. But SignalWire's native SWML unlocks:
- Native AI verb (ai) — voice AI agents in JSON, no separate Media Stream setup.
- Native expression substitution (
%{call.from}everywhere). - JSON/YAML format choice.
- First-class background recording (
record_call) and live transcription (live_transcribe). - SWAIG function calling.
A common path: migrate on LaML first to get to parity quickly, then move flows that need AI or advanced features to SWML one at a time.
Twilio Media Streams → SignalWire SWML AI
If you were using Twilio Media Streams to wire an external AI provider, the SignalWire equivalent is the native ai verb — no separate WebSocket pipeline. See the AI verb for the full shape, or use the Python Agents SDK for programmatic agents.
Anti-patterns
- Leaving Twilio
fromnumbers on outbound calls — sends will fail or be rejected. - Mixing LaML and SWML for the same DID — pick one per number. (Different numbers can use different formats.)
- Hand-coding the base URL with
api.twilio.comafter migration — change the endpoint, or rely on the SDK. - Assuming Twilio Studio export works as-is — Studio JSON does not run on SignalWire. Re-build flows in Call Flow Builder or convert to SWML.
- Forgetting that webhook signature validation differs — verify with SignalWire's signature header, not Twilio's.