Signals Docs
Programmable Voice

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

TwilioSignals
Base URLhttps://api.twilio.comhttps://api.signals.pressone.africa
Path/2010-04-01/Accounts/{AccountSid}/...Same
UsernameAccount SID, or an API key SID (SK...)Voice Account AccountSid only. It must equal the AccountSid in the path.
PasswordAuth token, or API key secretVoice Account auth token
Webhook signature keyAuth tokenVoice Account auth token. X-Twilio-Signature is computed as on Twilio. See Request signatures.
Inbound call setupPhone number voice URL, or a TwiML AppA 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

ResourceMethodsNotes
Calls.jsonGET, POSTList and create calls.
Calls/{CallSid} (.json optional)GET, POST, DELETEFetch, update and end a call. PATCH is not accepted.
Calls/{CallSid}/Recordings.jsonGETList a call's recordings.
Calls/{CallSid}/Events.jsonGETThe requests Signals made to your URLs during the call, with your responses. See Inspect requests with call events.
Recordings/{RecordingSid} (.json optional)GETRecording metadata.
Recordings/{RecordingSid}.wavGET, HEADRecording 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 verbTwiML verbNotes
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.
DisconnectNoneOneML 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

VerbWhat 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.

ElementContextNotes
<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.

TargetNotes
<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.

AttributeNotes
trimRecording trim is not configurable through <Dial>.
referUrl
recordingTrack

REST API differences

  • The Basic username must be the path AccountSid. Signals rejects any other username with 401 before the request goes further. Twilio API key SIDs (SK...) and parent-account credentials on a subaccount path are not accepted.
  • SIDs are lowercase. AccountSid, CallSid and RecordingSid must be two uppercase letters followed by 32 lowercase hexadecimal characters. Any other form returns 404.
  • ApplicationSid is rejected. Create-call fails with error 21605 when ApplicationSid is present. Use Url or Twiml. 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.
  • Timeout defaults to 60 seconds on create-call, the same as Twilio.
  • Call updates use form fields. Send POST with application/x-www-form-urlencoded and exactly one of Url, Twiml, or Status. Method accepts GET or POST with Url. Status=completed ends an in-progress call; Status=canceled cancels 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 Page or PageSize and no page metadata.
  • Recording audio needs authentication. RecordingUrl and the .wav resource require your AccountSid and 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 at Calls/{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+json documents with a code such as unauthenticated or voice_route_not_proxied. Create-call validation errors are Twilio-style JSON (code, message, more_info, status). Other voice platform errors are plain text (Unauthorized or Not Found). Call-update and end-call errors use Twilio-style JSON with the matching HTTP status. List-recordings errors retain the older 401 behaviour.

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. Without callerId, Signals uses To for inbound calls and From for 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, but record-from-answer-dual and record-from-ringing-dual do not produce a channel per party.
  • <Say> uses Google Cloud Text-to-Speech, not Amazon Polly, including for the man, woman and alice aliases.
  • <Say voice> is not checked against a list. man (the default) and any unrecognized plain string select a Google male voice; woman and alice, in lowercase, select a Google female voice. See Voice selection.
  • <Say voice> accepts a provider prefix. Google.<voice-name> and Elevenlabs.<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 support en and en-US. For another Google language, select a matching Google.<voice-name>. The current ElevenLabs integration accepts en and en-US.
  • <Number sendDigits> is accepted but not applied. Signals checks its format but does not send the tones.
  • <Connect> action and method are never requested. When a <Connect><Stream> session ends, the next verb runs.
  • <Start><Stream> metadata omits account_sid. <Connect><Stream> includes it. See Stream.
  • Inbound status callbacks come from the voice application. An inbound call sends a completed status callback only when its voice application has a status callback URL. API-created calls use StatusCallback as on Twilio. See Status callbacks.
  • StatusCallbackEvent is not validated. Any string is accepted; only initiated, ringing, answered and completed ever match an event.
  • The webhook user agent keeps its Twilio name. Requests carry User-Agent: TwilioProxy/1.1.

Next steps

Last modified on