Moving from Twilio
The Calls API follows Twilio's 2010-04-01 Calls and Recordings resources, and OneML is compatible with TwiML, so most Twilio voice integrations run on Signals after you change the base URL and credentials. This page names Twilio and TwiML directly. It lists what you change, what Signals serves, and where behaviour differs.
What you change
| Twilio | Signals | |
|---|---|---|
| Base URL | https://api.twilio.com | https://api.signals.pressone.africa |
| Path | /2010-04-01/Accounts/{AccountSid}/... | Same |
| Username | Account SID, or an API key SID (SK...) | Voice Account AccountSid only. It must equal the AccountSid in the path. |
| Password | Auth token, or API key secret | Voice Account auth token |
| Webhook signature key | Auth token | Voice Account auth token. X-Twilio-Signature is computed as on Twilio. See Request signatures. |
| Inbound call setup | Phone number voice URL, or a TwiML App | A voice application, with a number routed to it. See Receiving calls. |
Configure your HTTP client, or your helper library's HTTP layer, to send requests to api.signals.pressone.africa. Find the AccountSid and auth token in the Signals console under Developers, Voice Account.
Resources Signals serves
| Resource | Methods | Notes |
|---|---|---|
Calls.json | GET, POST | List and create calls. |
Calls/{CallSid} (.json optional) | GET, POST, DELETE | Fetch, update and end a call. PATCH is not accepted. |
Calls/{CallSid}/Recordings.json | GET | List a call's recordings. |
Calls/{CallSid}/Events.json | GET | The requests Signals made to your URLs during the call, with your responses. See Inspect requests with call events. |
Recordings/{RecordingSid} (.json optional) | GET | Recording metadata. |
Recordings/{RecordingSid}.wav | GET, HEAD | Recording audio. .mp3 and other formats are not served. |
Every other Twilio resource, such as Messages, IncomingPhoneNumbers, Applications, Conferences or Queues, returns 404 with code voice_route_not_proxied. Manage numbers and voice applications through the Signals API instead.
Supported verbs
Each OneML verb links to its reference page.
| OneML verb | TwiML verb | Notes |
|---|---|---|
| Say | <Say> | Voice and language handling differ. See Behaviour differences. |
| Play | <Play> | |
| Gather | <Gather> | |
| Record | <Record> | |
| Dial | <Dial> | Bare numbers and <Number> children only. |
| Redirect | <Redirect> | |
| Pause | <Pause> | |
| Hangup | <Hangup> | |
| Reject | <Reject> | |
| Stream | <Connect><Stream> | Two-way, blocking media session. |
| Stream | <Start><Stream>, <Stop><Stream> | One-way, non-blocking media session. |
| Disconnect | None | OneML extension. |
Not supported
The elements and attributes below are not implemented. They fail in different ways: some reject the document, others are skipped, and others are accepted with no effect. A rejected document is visible at once; a skipped element is not, so check your documents for these before you move.
Rejected with a validation error
| Verb | What happens |
|---|---|
<Pay> | Parses as XML but fails validation with <Pay> is not supported yet when Signals runs the document, and the call ends as failed. This applies to a webhook response, a <Redirect> target and inline Twiml. For inline Twiml on create-call, the request itself still returns 201 Created, because the request only checks that the XML is well formed. The failure appears later as status: failed on the call resource or in a status callback. |
Accepted and skipped
These elements pass validation, but nothing runs them. Signals moves on to the next verb with no error, no action request and no signal to your server or the caller.
| Element | Context | Notes |
|---|---|---|
<Enqueue> | Child of <Response> | |
<Leave> | Child of <Response> | |
<Refer> | Child of <Response> | Its sendDigits format is checked, but the verb never runs. |
<Siprec> | Child of <Start> or <Stop> | |
<Transcription> | Child of <Start> or <Stop> | |
<ConversationRelay> | Child of <Connect> | Only <Connect><Stream> is implemented. |
<Room> | Child of <Connect> | |
<VirtualAgent> | Child of <Connect> |
<Start> and <Stop> still need at least one recognized child (<Siprec>, <Stream> or <Transcription>) to pass validation. An empty <Start> or <Stop> is rejected. Only the <Stream> child runs.
Dial targets that are dropped
<Dial> dials bare phone numbers and <Number> children. The following targets parse without error but are dropped when Signals builds the list of numbers to dial. If a <Dial> contains only these targets, Signals reports a failed dial to its action URL when one is set.
| Target | Notes |
|---|---|
<Conference> | |
<Client> | |
<Queue> | |
<Application> | |
<Sip> | Not available for Programmable Voice. To reach your own PBX, dial a Signals number that is routed to your SIP trunk; Signals connects it on net. |
Dial attributes that are ignored
These <Dial> attributes are validated but have no effect.
| Attribute | Notes |
|---|---|
trim | Recording trim is not configurable through <Dial>. |
referUrl | |
recordingTrack |
REST API differences
- The Basic username must be the path
AccountSid. Signals rejects any other username with401before the request goes further. Twilio API key SIDs (SK...) and parent-account credentials on a subaccount path are not accepted. - SIDs are lowercase.
AccountSid,CallSidandRecordingSidmust be two uppercase letters followed by 32 lowercase hexadecimal characters. Any other form returns404. ApplicationSidis rejected. Create-call fails with error21605whenApplicationSidis present. UseUrlorTwiml. A Signals voice application ID is not accepted there either; voice applications serve inbound numbers.- Inbound numbers point at voice applications. A Signals number has no voice URL of its own. Create a voice application and route the number to it.
Timeoutdefaults to 60 seconds on create-call, the same as Twilio.- Call updates use form fields. Send
POSTwithapplication/x-www-form-urlencodedand exactly one ofUrl,Twiml, orStatus.MethodacceptsGETorPOSTwithUrl.Status=completedends an in-progress call;Status=canceledcancels before answer. Success returns HTTP 200 and the call resource. The final status follows the switch event. JSON update clients must adopt the form contract. - The recordings list is not paginated. It returns every recording for the call in one response, with no
PageorPageSizeand no page metadata. - Recording audio needs authentication.
RecordingUrland the.wavresource require yourAccountSidand auth token. Recording metadata carries no media URL. See Download recording audio. - Call resources link to their events. Call responses include
subresource_uris.events, pointing atCalls/{CallSid}/Events.json. - Request bodies are limited to 1 MiB, and Signals waits at most 15 seconds for the voice platform to answer before returning
504. - Error bodies have three shapes. Errors Signals raises itself (bad credentials format, unknown account or path, timeouts) are
application/problem+jsondocuments with acodesuch asunauthenticatedorvoice_route_not_proxied. Create-call validation errors are Twilio-style JSON (code,message,more_info,status). Other voice platform errors are plain text (UnauthorizedorNot Found). Call-update and end-call errors use Twilio-style JSON with the matching HTTP status. List-recordings errors retain the older401behaviour.
Behaviour differences
<Dial>works only inside an active call. As with Twilio,<Dial>does not start an outbound call. Create the call through the Calls API, then use<Dial>in its OneML.<Dial>caller IDs must be yours. The caller ID must be a number your project owns. WithoutcallerId, Signals usesTofor inbound calls andFromfor calls created through the Calls API. The selected number must be eligible and owned by the same tenant. It does not treat an arbitrary incoming caller number as owned. Dialed numbers go through your project's outbound policy.<Dial record>dual-channel modes record one mixed channel. The from-answer and from-ringing start points are honoured, butrecord-from-answer-dualandrecord-from-ringing-dualdo not produce a channel per party.<Say>uses Google Cloud Text-to-Speech, not Amazon Polly, including for theman,womanandalicealiases.<Say voice>is not checked against a list.man(the default) and any unrecognized plain string select a Google male voice;womanandalice, in lowercase, select a Google female voice. See Voice selection.<Say voice>accepts a provider prefix.Google.<voice-name>andElevenlabs.<voice-id>select a specific Google Cloud Text-to-Speech or ElevenLabs voice.<Say language>must match the voice. A mismatch returns a clear error. The built-in aliases supportenanden-US. For another Google language, select a matchingGoogle.<voice-name>. The current ElevenLabs integration acceptsenanden-US.<Number sendDigits>is accepted but not applied. Signals checks its format but does not send the tones.<Connect>actionandmethodare never requested. When a<Connect><Stream>session ends, the next verb runs.<Start><Stream>metadata omitsaccount_sid.<Connect><Stream>includes it. See Stream.- Inbound status callbacks come from the voice application. An inbound call sends a
completedstatus callback only when its voice application has a status callback URL. API-created calls useStatusCallbackas on Twilio. See Status callbacks. StatusCallbackEventis not validated. Any string is accepted; onlyinitiated,ringing,answeredandcompletedever match an event.- The webhook user agent keeps its Twilio name. Requests carry
User-Agent: TwilioProxy/1.1.
Next steps
- Quickstart: place a call with your Signals credentials.
- Receiving calls: move an inbound number to a voice application.
- OneML overview: every OneML verb and attribute.
- Calls API reference: every Calls API endpoint.