VoiceML Reference

VoiceML is the JSON instruction format for Edesy calls. Reference for all 11 verbs - say, play, gather, record, dial, stream, redirect, enqueue, reject, hangup, pause - with examples.

6 min read

Base URL
https://voice-api.edesy.in/v1
Auth
Authorization: Bearer sk_...Get a key

VoiceML is the JSON format that tells an Edesy call what to do. It is an object with an instructions array; each instruction has a verb and that verb's fields, and the call runs them in order. Your answer URL returns VoiceML, and you can also send it inline as the voiceml field of POST/v1/calls. VoiceML is JSON, not TwiML XML.

{
  "instructions": [
    { "verb": "say", "text": "Welcome to Acme." },
    { "verb": "gather", "text": "Press 1 to confirm your order, or 2 to cancel.", "num_digits": 1, "timeout": 8, "action": "https://example.com/edesy/menu" },
    { "verb": "say", "text": "We did not get your answer. Goodbye." },
    { "verb": "hangup" }
  ]
}

Verbs at a glance#

Verb What it does
say Speak text with text-to-speech
play Play an audio file from a URL
gather Collect keypad digits
record Record a message from the caller
dial Connect the call to another phone number
stream Send the call audio to a WebSocket
redirect Continue with instructions from another URL
enqueue Place the caller in a named queue (see the note)
reject Decline an inbound call without answering
hangup End the call
pause Wait in silence

Rules that apply to every document:

  • Instructions run top to bottom. When the last one finishes, the call ends.
  • An instruction with an unknown verb is skipped. If nothing valid is left, the caller hears a short error message and the call ends.
  • Numbers are JSON numbers ("timeout": 8), not strings.
  • Host audio files and callback URLs on public https:// addresses.

say#

Speaks text with text-to-speech.

{ "verb": "say", "text": "आपका ऑर्डर कल पहुंच जाएगा।", "language": "hi-IN" }
Field Type Required Description
text string Required What to say
language string Optional BCP-47 code, for example en-IN, hi-IN, ta-IN
voice string Optional Voice ID. Omit to use the platform default voice

Characters spoken with say are metered. Premium voices cost Rs 0.03 per character on top of call minutes. See Pricing and Limits.

play#

Plays an audio file.

{ "verb": "play", "url": "https://cdn.example.com/welcome.mp3", "loop": 2 }
Field Type Required Description
url string Required Public URL of a WAV or MP3 file
loop integer Optional How many times to play it. Default 1

play carries no TTS charge, so pre-recorded audio is the cheapest way to deliver a fixed message.

gather#

Speaks an optional prompt and collects keypad (DTMF) digits.

{
  "verb": "gather",
  "text": "Please enter your 4-digit PIN, then press hash.",
  "num_digits": 4,
  "finish_on_key": "#",
  "timeout": 10,
  "action": "https://example.com/edesy/pin"
}
Field Type Required Description
text string Optional Prompt spoken while waiting for input
num_digits integer Optional Stop after this many digits
finish_on_key string Optional Key that ends input, for example #
timeout integer Optional Seconds to wait for input
action string Optional URL that receives the digits

If the caller enters nothing before timeout, the call moves on to the next instruction, so put a fallback say after the gather.

gather collects keypad digits. For spoken answers, use Conversation Relay (Edesy transcribes speech for you) or Media Streams (you run your own speech recognition).

record#

Records the caller, for example a voicemail. The recording is stored and listed by GET/v1/recordings.

{ "verb": "record", "max_length": 120, "play_beep": true, "finish_on_key": "#" }
Field Type Required Description
max_length integer Optional Maximum length in seconds
play_beep boolean Optional Play a beep before recording starts
finish_on_key string Optional Key that stops the recording
action string Optional URL notified when recording ends

If a recording cannot be captured, the caller hears a short message instead and the call continues. To record a whole call, turn on recording for the number or application instead. See Recordings and Transcriptions.

dial#

Connects the caller to another phone number.

{ "verb": "dial", "number": "+9198XXXXXXXX", "caller_id": "+9180XXXXXXXX", "timeout": 30 }
Field Type Required Description
number string Required E.164 number to dial
caller_id string Optional Caller ID shown to the dialled party. Use a number in your workspace
timeout integer Optional Seconds to ring, up to 600
action string Optional URL notified when the dialled leg ends

To send inbound calls to a SIP endpoint rather than a phone number, use the SIP target in Inbound Routing.

stream#

Sends the call's audio to a WebSocket.

{ "verb": "stream", "url": "wss://bot.example.com/ws" }
Field Type Required Description
url string Required wss:// endpoint

For a two-way voice bot, prefer stream_url on POST/v1/calls or the media-stream inbound target. Those set up a bidirectional L16 stream with your choice of 8 kHz or 16 kHz and record call.stream.* events. See Media Streams.

redirect#

Continues the call with instructions from another URL.

{ "verb": "redirect", "url": "https://example.com/edesy/next-step" }
Field Type Required Description
url string Required URL to fetch next

enqueue#

{ "verb": "enqueue", "queue_name": "support" }
Field Type Required Description
queue_name string Required Queue name

enqueue is part of the VoiceML schema, but the answer-URL runtime does not execute it today and skips it. To queue callers for live agents, use the Edesy contact center at ccaas.edesy.in, which has queues, routing strategies, hold music and an agent desktop.

reject#

Declines an inbound call without answering it, so neither side is billed for talk time.

{ "verb": "reject", "status": 486, "reason": "Busy" }
Field Type Required Description
status integer Optional SIP status from 400 to 699. Default 603 (decline). Use 486 for busy
reason string Optional Reason phrase sent with the status

reject only works before the call is answered, so make it the first instruction your inbound webhook returns.

hangup#

Ends the call.

{ "verb": "hangup" }

No fields.

pause#

Waits in silence.

{ "verb": "pause", "length": 2 }
Field Type Required Description
length number Optional Seconds of silence. Default 1

Callback URLs (action, redirect)#

The answer URL is the documented, supported way to control a call: Edesy fetches it once and runs the whole document it returns. The action URLs on gather, record and dial, and the redirect target, are called directly by the media layer. Their request and response format is different from the answer URL and is not covered in these docs yet.

For a keypad menu with several steps, build the flow in IVR Studio and route the number to it, or run the conversation from your own server with Conversation Relay or Media Streams.

Examples#

Delivery notice with a recorded jingle

{
  "instructions": [
    { "verb": "play", "url": "https://cdn.example.com/brand-jingle.mp3" },
    { "verb": "say", "text": "Your Acme parcel will be delivered today between 2 and 5 PM.", "language": "en-IN" },
    { "verb": "pause", "length": 1 },
    { "verb": "say", "text": "Thank you." },
    { "verb": "hangup" }
  ]
}

After-hours voicemail on an inbound number

{
  "instructions": [
    { "verb": "say", "text": "Our office is closed. Please leave a message after the beep." },
    { "verb": "record", "max_length": 90, "play_beep": true },
    { "verb": "say", "text": "Thank you. We will call you back." },
    { "verb": "hangup" }
  ]
}

Forward an inbound call to a mobile

{
  "instructions": [
    { "verb": "say", "text": "Connecting you to our team." },
    { "verb": "dial", "number": "+9198XXXXXXXX", "caller_id": "+9180XXXXXXXX", "timeout": 25 },
    { "verb": "say", "text": "Nobody is available right now. Please call again later." },
    { "verb": "hangup" }
  ]
}
Was this page helpful?