Twilio compatibility
OneML is compatible with Twilio's TwiML, and PressOne's REST surface mirrors Twilio's 2010-04-01 Calls API, so most existing TwiML documents and Twilio integrations run against PressOne unmodified. This page is the exception to the rest of this documentation: it names Twilio and TwiML directly, and exists specifically to help developers migrating an existing Twilio integration understand where PressOne's behavior differs.
Supported verbs
Each OneML verb below links to its full reference page.
| OneML verb | Twilio verb(s) | Notes |
|---|---|---|
| Say | <Say> | Voice and language defaults differ. See behavioral divergences. |
| Play | <Play> | |
| Gather | <Gather> | |
| Record | <Record> | |
| Dial | <Dial> | Only a bare number or <Number> children are dialed. See not supported. |
| Redirect | <Redirect> | |
| Pause | <Pause> | |
| Hangup | <Hangup> | |
| Reject | <Reject> | |
| Stream | <Connect><Stream> | Bidirectional, blocking media session. |
| Stream | <Start><Stream> / <Stop><Stream> | One-way, non-blocking media session. |
| Disconnect | No Twilio equivalent | PressOne extension. Returns control of the call to the platform dialplan without hanging up. |
Not supported
The elements and attributes below are not fully implemented. They fail in different ways depending on the case: some reject the document outright, others are silently skipped, and others are accepted but never take effect. Knowing which applies to a given document matters when migrating: a hard error is visible immediately, while a silent skip is not.
Rejected with a validation error
| Verb | What happens |
|---|---|
<Pay> | Parses as valid XML, but fails semantic validation with the error <Pay> is not supported yet when PressOne executes the document: the call ends as failed. This applies wherever a <Pay>-containing document reaches the engine: an initial webhook response, a <Redirect> target, or an inline Twiml document. For inline Twiml supplied on create-call specifically, this check does not run synchronously against the request: the parser only confirms the document is well-formed XML at that point, so POST .../Calls.json still returns 201 Created. The <Pay> rejection only happens later, when the engine actually executes the call, and surfaces as status: failed on the call resource or on a status callback, not as a create-call error. |
Accepted, skipped without effect
The following elements parse and validate successfully (a document containing them passes OneML's strict parser), but the executor has no handler for them. Each falls through to a generic case that logs a warning on the server and continues with the next verb. There is no error, no action callback, and no signal to your webhook or to the caller that anything was skipped.
| Element | Context | Notes |
|---|---|---|
<Enqueue> | Direct child of <Response> | |
<Leave> | Direct child of <Response> | |
<Refer> | Direct child of <Response> | Its sendDigits attribute is validated for format, but the verb itself never executes. |
<Siprec> | Child of <Start> or <Stop> | |
<Transcription> | Child of <Start> or <Stop> | |
<ConversationRelay> | Child of <Connect> | Only <Connect><Stream> is implemented; other <Connect> targets fall through the same way. |
<Room> | Child of <Connect> | |
<VirtualAgent> | Child of <Connect> |
<Start> and <Stop> still require at least one recognized child (<Siprec>, <Stream>, or <Transcription>) to pass validation: an empty <Start> or <Stop> is rejected before execution. Once past that check, only the <Stream> child actually runs.
Dial targets dropped with a warning
<Dial> executes only a bare phone number or <Number> children. The following nested targets parse without error, but PressOne drops them with a server-side warning when it builds the list of numbers to dial. If a <Dial> contains only unsupported targets, PressOne skips the dial entirely and continues with the next verb, as if <Dial> were never present.
| Target | Notes |
|---|---|
<Conference> | |
<Client> | |
<Sip> | |
<Queue> | |
<Application> |
Dial attributes parsed but ignored
The following <Dial> attributes are recognized and validated on the parsed document, but setting them has no effect on the placed call.
| Attribute | Notes |
|---|---|
trim | Recording trim behavior is not configurable through <Dial>. |
referUrl | |
recordingTrack |
REST API differences
PressOne's REST API mirrors Twilio's 2010-04-01 Calls resource closely enough that most existing integrations run unmodified, with the following differences.
ApplicationSidis rejected. Twilio's create-call endpoint acceptsApplicationSidto run a pre-configured Twilio Application instead ofUrl/Twiml. PressOne has no equivalent concept: supplyingApplicationSidalways fails the request with error21605. UseUrlorTwimlinstead.Timeoutdefaults to 60 seconds. The ring timeout on create-call defaults to 60 seconds before PressOne treats the call as unanswered, matching Twilio's default.- Updating a live call requires a JSON body, not form encoding. Every other endpoint in this API accepts
application/x-www-form-urlencodedwith PascalCase field names, matching Twilio's convention. The update-call endpoint is the one exception: it requires anapplication/jsonbody with snake_case fields (voice_url,voice_method,twiml). Twilio's official helper libraries send update-call requests form-encoded, so they cannot update a live call against PressOne without modification: issue a custom HTTP request with a JSON body instead of using the helper library's built-in call-update method. - The recordings list is not paginated. Twilio paginates the recordings list. PressOne's equivalent endpoint returns every recording captured during the call in a single response, with no
Page/PageSizeparameters and no page metadata in the response body. - Error response bodies are not uniform across operations. Only create-call returns a Twilio-style JSON error body (
code,message,more_info,status). Every other operation (list calls, get call, update call, end call, list recordings, get recording) returns a plain-text body (UnauthorizedorNot Found) instead of JSON on failure. In addition, update call, end call, and list recordings collapse every failure, including an unknownCallSid, to a401response; they do not expose a separate404for a call that does not exist.
Behavioral divergences
<Dial record>dual-channel variants record a single mixed channel. The from-answer and from-ringing start moments are honored, butrecord-from-answer-dualandrecord-from-ringing-dualdo not produce separate channels per party.<Say>uses Google Cloud Text-to-Speech, not Amazon Polly. Twilio's own TwiML reference documents Amazon Polly as the underlying engine for its Twilio-style voice names. PressOne synthesizes every<Say>voice, including theman/woman/alicealiases, through Google Cloud Text-to-Speech.<Say voice>is not validated against an enum. PressOne accepts any string invoice, not onlyman,woman, andalice. The bare wordman(the default) and any unrecognized bare string select a default Google male voice;womanandalice, matched exactly in lowercase, select a default Google female voice. See Voice selection for the full set of rules.<Say voice>supports aProvider.<name>namespace with no Twilio equivalent. SettingvoicetoGoogle.<voice-name>orElevenlabs.<voice-id>selects a specific Google Cloud Text-to-Speech or ElevenLabs voice by name or ID. Twilio's TwiML has no equivalent mechanism for choosing a non-default voice by provider. See Voice selection.<Say language>is accepted but not applied. Speech synthesis always uses the default voice's language, regardless of the value you set.<Number sendDigits>is accepted but not applied. PressOne validates the attribute's format but does not send the specified DTMF tones to the dialed party after the call connects.<Connect>'sactionandmethodare accepted but never requested. When a<Connect><Stream>session ends, PressOne continues with the next verb in the document; it does not request theactionURL.<Start><Stream>metadata omitsaccount_sid. The metadata PressOne sends to your WebSocket endpoint when a stream starts includesaccount_sidfor<Connect><Stream>but not for<Start><Stream>. See Stream for the full metadata comparison.- Status callbacks fire only for calls created through the REST API. Inbound calls never emit status callbacks, regardless of any
StatusCallbackconfigured elsewhere. See status callbacks. StatusCallbackEventdoes not validate its values server-side. Any string is accepted, not onlyinitiated,ringing,answered, andcompleted. Values outside that set are accepted at request time but never match an event PressOne emits, so subscribing to them has no effect.- The signature header and webhook user agent keep their Twilio names. PressOne signs webhook requests with a header literally named
X-Twilio-Signature, and identifies itself with aUser-AgentofTwilioProxy/1.1. Both are retained as-is so that existing Twilio SDKs and signature-verification libraries work without modification. See verify webhook signatures.
Next steps
- OneML overview: every OneML verb and attribute.
- Webhooks: request format, standard parameters, and signature verification.
- Status callbacks: call lifecycle events and delivery.
- API reference: complete REST endpoint documentation.