SIP Mediaserver Service - SIP Mediaserver Examples
Download OpenAPI specification:Download
This document provides practical examples of how to use the SIP Mediaserver Service from iotcomms.io to build advanced voice applications. The service enables fine-grained control over call handling and media interactions through REST APIs and real-time callbacks. This document combines key concepts, API functionalities, and practical use cases to demonstrate how developers can leverage the SIP Mediaserver Service in their applications.
The SIP Mediaserver Service provides a robust and scalable solution for managing real-time voice communications. It allows developers to handle incoming and outgoing calls, route participants, play media, capture input, and record conversations. Applications communicate with the SIP Mediaserver Service through REST APIs and callbacks to define call flows dynamically.
Core Concepts
The service operates using three main entities:
- Sessions – Represent a single call session, which may contain multiple connections and participants.
- Connections – Link two or more participants within a session.
- Participants – Individual entities engaged in the call, including SIP endpoints and IVR systems.
A session can be initiated in two ways:
- A new call arrives at the SIP Mediaserver Service, creating an inbound participant.
- An outbound call is placed using the
/placeivrcallAPI, creating a participant that connects to a SIP destination.
The state logic determining how calls should be handled is implemented in the customer application. When state changes for sessions, connections, or participants, the customer application is informed about this by REST callback requests.
The key callbacks are:
/newCall- sent to inform that a new call arrives to media service. The application should pass anactionproperty in the response to tell how Mediaservice should handle the call:drop- Disconnect the incoming callcall- Connect the call with another destination SIP participantivr- Connect the call with an IVR participant to play and receive media and tones
/callNotify- these are informative events for progress updates for participants, for example, inform about ringing state/callEvent- these are events sent to inform that a connection or participant state has changed, and the application responds with anactionto inform how to proceed. For example, if two participants are in a call with an associated connection, and one of the parties hangs up the call there is a/callEventsent to inform about this. Theactionin the response then tells what to do with the remaining participant in the call.drop- Disconnect the remaining participantcall- Connect the remaining participant with another destination SIP participantivr- Connect the remaining participant with an IVR participant to play and receive media and tones
/callAnswered- This callback request is sent when a connection has been established between two participants in a session/sessionCompleted- This callback request is sent once all participants have left a session and therefore the session is deleted.
A special type of a participant in a session is the IVR participant. This participant act as an endpoint in a connection which can:
- Listen to DTMF tones sent by the other participant in the connection
- Play out phrases to be heard by the other participant in the connection, for example:
- Phrases generated by text to speech
- Play out files previously recorded or uploaded using the
/promptfileAPI - Play out file fetched from a remote server from a provided HTTP URL
- Record audio sent by the other participant
- Set a background audio that should be played continuously while no other commands are executed, for example playing music on hold.´
Once a connection have been established with an IVR participant it will trigger a /getIvrCommand callback request. The response to this request should have an command that provide instructions to the IVR participant.
{body} Application->>Mediaservice: 200 OK
{command body} Mediaservice->>Application: POST /getIvrCommand
{body} Application->>Mediaservice: 200 OK
{command body} Mediaservice->>Application: POST /getIvrCommand
{body} Application->>Mediaservice: 200 OK
{command body,hangup:true}
The body of the /getIvrCommand request may contain information from a previous command execution, for example collected DTMF digits.
The body of the /getIvrCommand response contain the command to be executed, for example to play out a phrase. Once a command have been executed a new /getIvrCommand request is sent until the response contain a hangup:true property. This will hang up the IVR participant from the connection and trigger a /callEvent callback to get information what to do with the party previously connected with the IVR participant.
/getIvrCommand response object
Details of /getIvrCommand callback requests and responses can be found in the Mediaservice API documentation.
The response to the callback object contain a command that provides instructions to the IVR participant. It is a JSON object of the format:
{
"dtmf": {},
"phrases": [],
"backgroundPrompt": {},
"hangup": false,
"getIVRCommandAfter": 20,
}
Where:
dtmfproperty is an object telling the IVR participant to listen for and report DTMF digits received. Received digits are reported in subsequent/getIvrCommandrequestsphrasesis an array of phrase objects. A phrase object instructs the IVR participant to play out audio to the connected participant. Once all phrases have been played a new/getIvrCommandcallback request is sent to get further commands.backgroundPromptis an object with information telling the IVR participant to loop audio in between command execution.hangupis a boolean property telling the IVR participant to drop out from the connection, i.e. when either connect the connected participant with another party or to end the session. When this property is set the command provided will be executed but no further/getIvrCommandcallbacks will be sent. Instead a/callEventcallback request will be sent indicating that the IVR participant have left the connection.getIVRCommandAfteris a property telling the IVR participant to pause the next/getIvrCommandrequest the time specified in seconds.
The list above is an overview of the most commonly used properties, for more detailed information, please look at the API documentation.
This example demonstrates how to implement an IVR-based call routing system that greets the caller, captures input, and routes them to the correct department.
Below is an example of a typical message flow where the Mediaservice interacts with a customer's application implementing an auto-attendant function.
- A call arrives to the phone number provisioned for the Mediaservice. This trigger a
/newCallcallback request - The application respond with a 200 Ok to the callback having a body telling the service to connect the caller with an IVR participant.
- The SIP call is established and a
/callAnsweredcallback request is sent to indicate that a connection have been established between caller and IVR participant. - Media path is established between caller and Mediaservice
- A
/getIvrCommandcallback request is sent to the application to get instructions. - Application responds back with a command to listen for a single DTMF tone and play a greeting phrase using Amazon Polly text to speech. IVR participant will wait until the DTMF matches one digit.
- Caller press digit 2. This trigger a new
/getIvrCommandwhich contain the matched digit. - Application responds with a connection phrase and set
hangup:trueproperty to hang up the IVR participant. - This triggers a
/callEventcallback indicating that IVR participant have been disconnected. - Application responds with a
action:"call"and a destination to connect the caller with. - Mediaservice creates a new participant representing the callee and establish a SIP call with the provided destination.
- Callee answers the call which creates a new connection in the session for caller and callee. This triggers a
/callAnsweredcallback. - Caller decides to hang up the call. This triggers a
/callEventto the application. - Application responds with a
action:dropto hang up the remaining participant representing the callee. This will trigger a SIP Bye to be sent for that call leg. - No more participants exists for the session which triggers a
/sessionCompletedcallback to be sent to the application.
Below is a simplified sequence diagram where some callbacks such as /callNotify have been omitted. A detailed description of callbacks and API can be found in the Mediaservice API documentation.
{action:ivr} iotcomms.io->>Caller: SIP 200 Ok Caller->>iotcomms.io: SIP Ack iotcomms.io->>Application: HTTPS POST /callAnswered callback Application->>iotcomms.io: HTTPS 200 OK Caller-->iotcomms.io: Media established iotcomms.io->>Application: HTTPS POST /getIvrCommand callback Application->>iotcomms.io: HTTPS 200 OK
{
dtmf:{maxDigits:1}
phrases:({format:"awstts",
awsTTSParameters:{
"Text": "Welcome. Press 1 for sales and 2 for support."
}})
} Caller-->iotcomms.io: DTMF digit two pressed iotcomms.io->>Application: HTTPS POST /getIvrCommand callback
{dtmfParams:{dtmfMatch:"2",reason:"maxDigits"}} Application->>iotcomms.io: HTTPS 200 OK
{
phrases:({format:"awstts",
awsTTSParameters:{
"Text": "Thank you, you will get connected to support."
}})
hangup:true
} iotcomms.io->>Application: HTTPS POST /callEvent callback
{event:"calledPartyDrop"} Application->>iotcomms.io: HTTPS 200 OK
{action:"call", destAddr:"sip:callee@{customer}.prod-eu-north-1.iotcomms.io" } iotcomms.io->>Caller: SIP Re-nvite iotcomms.io->>Callee: SIP Invite Callee->>iotcomms.io: SIP 200 Ok Caller->>iotcomms.io: SIP 200 Ok iotcomms.io->>Callee: Ack iotcomms.io->>Caller: Ack Caller-->Callee: Media established iotcomms.io->>Application: HTTPS POST /callAnswered callback Application->>iotcomms.io: HTTPS 200 OK Caller->>iotcomms.io: SIP Bye iotcomms.io->>Caller: SIP 200 Ok iotcomms.io->>Application: HTTPS POST /callEvent callback
{event:"callingPartyDrop"} Application->>iotcomms.io: HTTPS 200 OK
{action:"drop"} iotcomms.io->>Callee: SIP Bye Callee->>iotcomms.io: SIP 200 Ok iotcomms.io->>Application: HTTPS POST /sessionCompleted
- When iotcomms.io Mediaservice receives an incoming call to a destination to be recorded, a /newCall callback request to the application. This will send a response telling the service to connect the call with the callee and enable recording. Recording is enabled by setting the
callRecording:trueproperty in the/newCallcallback response. A filename is also provided in the response. - Mediaservice places an outbound call to the callee.
- Call is connected, and parties talk to each other.
- Call is disconnected.
- Mediaservice sends a
/recordingReadycallback to indicate that the recording is ready for download. - Recording is downloaded using the
/promptfileAPI
Below is a simplified sequence diagram where some callbacks such as /callNotify have been omitted. A detailed description of callbacks and API can be found in the Mediaservice API documentation.
callRecording:true
filename:{name} iotcomms.io->>Caller: SIP 200 Ok Caller->>iotcomms.io: SIP Ack iotcomms.io->>Callee: SIP Invite Callee->>iotcomms.io: SIP 200 Ok iotcomms.io->>Callee: SIP Ack iotcomms.io->>Application: /callAnswered callback Caller->>iotcomms.io: SIP Bye iotcomms.io->>Caller: SIP 200 Ok iotcomms.io->>Application: /callEvent callback Application->>iotcomms.io: action:drop iotcomms.io->>Callee: SIP Bye Callee->>iotcomms.io: SIP 200 Ok iotcomms.io->>Application: /sessionCompleted callback iotcomms.io->>Application: /recordingReady callback Application->>iotcomms.io: GET /promptfile
Call monitoring allows a third party to listen to an ongoing call without participating in the conversation. This is enabled by providing a monitorDestAddr property in the /newCall response. The SIP Mediaserver Service will establish a new SIP call to this address after the initial /callAnswered event is sent. The monitor user will receive an audio stream that contains a mix of the audio from all active participants in the call.
Message Flow
- A call arrives at the Mediaservice, triggering a
/newCallcallback. - The application responds with an action to connect the caller with a callee and includes
monitorDestAddrto enable call monitoring. - Mediaservice places an outbound call to the callee.
- Once the callee answers, a
/callAnsweredcallback is sent to the application. - Mediaservice then initiates a SIP call to the monitor user.
- The monitor user answers the call and begins receiving the mixed audio stream.
- When the caller or callee disconnects, a
/callEventcallback is sent, and the application decides how to proceed. - If all participants disconnect, a
/sessionCompletedcallback is sent.
If you wish to make outbound calls when there are no inbound call that initiated the process it is possible to use the /placeivrcall API. It will create a participant in the same way as a call participant was created when a call comes in and the application is notified using the /newCall callback.
Here is an example on how to use the /placeivrcall API:
The sequence is initiated with a placeivrcall.
While the call is progressing a number of callbacks will be generated towards the application. Most of them are just status information indicating the progress of the call. The only callbacks that really need handling is the getIvrCommand which needs to be responded with a parameter action with value call and then a destination address parameter with the phone number of the ARC and then at the end of the call there will be a callEvent callback indicating that either party of the call has hung up. A proper response is then to send a property action with value that specify to drop either the calling party or called party.
Message flow
- A /placeivrcall is initiated from the application with the destination address set to the phone number of the device to call.
- As the call progress towards the device there will be a /callNotify callback telling that it is ringing in the device.
- When the device answer there will be a /callAnswered callback telling that the user has answered.
- The system now need to know how to process the call and sends a /getIvrCommand callback. This callback can be responded with a "action": "call" which then connects the device to another device as specified in the response to the getIvrCommand.
- The call is now connected to the other device and the progress is reported in /callNotify callbacks.
- Eventually the call is answered by the other device and the two devices are now able to talk to each other.
- One of the devices hang up and there will be a /callEvent callback to ask what to do with call now. In this case the application decides do drop the call.
- Finally there will be a /sessionCompleted callback telling that the session has ended and the call has completed.
{destAddr: "the number to call"} iotcomms.io->>Application: HTTPS 200 Ok iotcomms.io->>Alarm device: SIP Invite Alarm device->>iotcomms.io: SIP 180 Ringing iotcomms.io->>Application: HTTPS POST /callNotify callback
{event:"ringing"} Application->>iotcomms.io: HTTPS 200 OK Alarm device->>iotcomms.io: SIP 200 OK iotcomms.io->>Alarm device: SIP Ack iotcomms.io->>Application: HTTPS POST /callAnswered callback Application->>iotcomms.io: HTTPS 200 OK iotcomms.io->>Application: HTTPS POST /getIvrCommand callback Application->>iotcomms.io: HTTPS 200 OK
{action:"call", destAddr:"sip:ARC@{customer}.prod-eu-north-1.iotcomms.io" } iotcomms.io->>Alarm device: SIP Re-invite Alarm device->>iotcomms.io: SIP 200 Ok iotcomms.io->>Alarm device: Ack Alarm device-->ARC: Media established for ringback tone iotcomms.io->>ARC: SIP Invite ARC->>iotcomms.io: SIP 180 Ringing iotcomms.io->>Application: HTTPS POST /callNotify callback
{event: "ringing"} Application->>iotcomms.io: HTTPS 200 OK ARC->>iotcomms.io: SIP 200 Ok iotcomms.io->>Alarm device: SIP Re-invite Alarm device->>iotcomms.io: SIP 200 Ok iotcomms.io->>Alarm device: Ack iotcomms.io->>ARC: Ack Alarm device-->ARC: Media established for voice call iotcomms.io->>Application: HTTPS POST /callAnswered callback Application->>iotcomms.io: HTTPS 200 OK Note over ARC,Alarm device: ARC is talking to Alarm device Alarm device->>iotcomms.io: SIP Bye iotcomms.io->>Alarm device: SIP 200 Ok iotcomms.io->>Application: HTTPS POST /callEvent callback
{event:"callingPartyDrop"} Application->>iotcomms.io: HTTPS 200 OK
{action:"drop"} iotcomms.io->>ARC: SIP Bye ARC->>iotcomms.io: SIP 200 Ok iotcomms.io->>Application: HTTPS POST /sessionCompleted
Attended call transfer allows a participant to be placed on hold while another call is established, and then either completed (transfer) or canceled (return to original call). This is implemented using the concept of splitting an ongoing connection, invoke IVR parking, and re-connect with another party.
The SIP Mediaserver Service enables full control of attended transfer flows by letting the application manage each participant independently through /callEvent callback responses.
Key concepts
Attended transfer is built using:
/splitconnectionto trigger/callEventcallbacks for participants active in a connection/callEvent action:"ivr"to connect the party put on hold with hold music/callEvent action:"call"to connect create the inquiry call/connectto connect two participants to complete the call transfer
Message flow – Attended transfer
The example below shows a standard attended transfer where User A calls User B, B initiates a transfer to User C, and then completes the transfer so A and C are connected. When parties are connected and transferred the SIP re-invite sequences are left out for readability.
{action:"call", destAddr:"sip:userb@customer.prod-eu-north-1.iotcomms.io"} Mediaservice->>A: SIP 200 Ok A->>Mediaservice: SIP Ack Mediaservice->>B: SIP Invite sip:userb@customer.prod-eu-north-1.iotcomms.io B->>Mediaservice: SIP 200 OK Mediaservice->>B: SIP Ack Mediaservice->>Application: HTTPS POST /callAnswered
{
connectionId:AwithB,
"replyId":"replyIdStringAwithB"
} Application->>Mediaservice: HTTPS 200 Ok Note over A,Application: A and B are connected, keep a reference to the connectionId and replyId for later use
in /splitconnection API call Application->>Mediaservice: HTTPS POST /splitconnection
{
"connectionId": "AwithB",
"replyId":"replyIdStringAwithB"
} Mediaservice->>Application: HTTPS 200 Ok Mediaservice->>Application: HTTPS POST /callEvent {
"event":"callerConnectionSplit",
"user":"userA"
} Application->>Mediaservice: HTTPS 200 Ok {"action":"ivr"} Note over A,Application: /callEvent related to participant userA, reply with action:ivr to put party on hold. This will create a new connection a sequence ot /getIvrCommand callbacks while party is on hold. Mediaservice->>Application: HTTPS POST /callAnswered
{
"connectionId":"AwithIVR",
"replyId":"replyIdStringAwithIVR"
"callerParticipantId":"participantIdA"
} Application->>Mediaservice: HTTPS 200 Ok Mediaservice->>Application: HTTPS POST /getIvrCommand Application->>Mediaservice: HTTPS 200 Ok
{
"backgroundPrompt":
{"format":"file",
"fileName":"holdmusic.mp3",
"repeatPause":0.5
},
"hangup":false
} Note over A,Application: A and IVR are now connected, keep a reference to the connectionId and replyId for later use
in /splitconnection API call. Save a reference to participantIdA for later use when A should be connected with C. Mediaservice->>Application: HTTPS POST /callEvent
{
"event":"calleeConnectionSplit"
"user":userB
} Application->>Mediaservice: HTTPS 200 Ok
{
action:"call"
, destAddr:"sip:userc@customer.prod-eu-north-1.iotcomms.io"
} Note over A,Application: /callEvent related to participant userB, reply with action:call to set up an inquiry call with userC Mediaservice->>C: SIP Invite sip:userc@customer.prod-eu-north-1.iotcomms.io C->>Mediaservice: SIP 200 OK Mediaservice->>C: SIP Ack Mediaservice->>Application: HTTPS POST /callAnswered
{
"connectionId":"BwithC",
"replyId":"replyIdStringBwithC"
} Application->>Mediaservice: HTTPS 200 Ok Note over A,Application: B and C are now connected, keep a reference to the connectionId and replyId for later use
in /splitconnection API call Note over A,Application: B inquiries C about to be connected with A and decides to do so. The connection is process is initiated using the /splitconnection API Application->>Mediaservice: HTTPS POST /splitconnection
{
"connectionId":"BwithC",
"replyId":"replyIdStringBwithC"
} Mediaservice->>Application: HTTPS POST /callEvent {
"event":"callerConnectionSplit",
"user":"userB"
} Application->>Mediaservice: HTTPS 200 Ok {"action":"drop"} Mediaservice->>B: SIP Bye B->>Mediaservice: SIP 200 Ok Note over A,Application: Drop the call for userB Mediaservice->>Application: HTTPS POST /callEvent {
"event":"calleeConnectionSplit",
"user":"userC"
,"replyId":"replyIdBwithCSplit"
} Application->>Mediaservice: HTTPS 200 Ok {"action":"connect","participantId":"participantIdA","replyId":"replyIdBwithCSplit"} Mediaservice->>Application: HTTPS POST /callAnswered
{
"connectionId":"AwithC",
"replyId":"replyIdStringAwithC"
} Application->>Mediaservice: HTTPS 200 Ok Note over A,Application: The /connect API call connects userC with userA and drops its connection with the hold music IVR.
The SIP Mediaserver Service supports dynamic audio conferencing by allowing an application to merge multiple participants into a shared media connection. A conference is created by combining existing call legs into a conference connection using /createconferenceconnection, /splitconnection, and /callEvent callback responses.
Conference handling builds on the same primitives as attended transfer:
- Splitting existing connections to isolate participants
- Using IVR to park participants (hold music or announcements)
- Creating new call legs
- Joining participants into a shared conference connection
This allows the application to fully control how and when participants are added to the conference.
Key concepts
Conference is built using the same primitives as attended transfer:
/splitconnectionto isolate participants and trigger/callEvent/callEvent action:"ivr"to park participants during setup/callEvent action:"call"to establish new call legs/createconferenceconnectionto create the shared media connection/callEvent action:"joinconferenceconnection"to add participants to the conference
Important identifiers to track:
connectionIdandreplyIdfor all connections (used in/splitconnectionand forjoinconferenceconnection)participantIdfor participants that will later be joined into the conference
IVR is used to:
- park participants (hold)
- provide hold music or announcements
Once joined, all participants receive a mixed audio stream from the conference connection
Message flow – Conference with park and retrieve
The example below shows how a conference between User A, B, and C is established. User A is first connected with B. A then puts B on hold and calls C. Once A is connected with C, a conference connection is created and A, B and C are joined into the same conference. When parties are connected the SIP re-invite sequences are left out for readability.
{action:"call", destAddr:"sip:userb@customer.prod-eu-north-1.iotcomms.io"} Mediaservice->>A: SIP 200 Ok A->>Mediaservice: SIP Ack Mediaservice->>B: SIP Invite sip:userb@customer.prod-eu-north-1.iotcomms.io B->>Mediaservice: SIP 200 OK Mediaservice->>B: SIP Ack Mediaservice->>Application: HTTPS POST /callAnswered
{
"connectionId":"AwithB",
"replyId":"replyIdAwithB"
} Application->>Mediaservice: HTTPS 200 Ok Note over A,Application: A and B are connected, keep connectionId and replyId Application->>Mediaservice: HTTPS POST /splitconnection
{
"connectionId":"AwithB",
"replyId":"replyIdAwithB"
} Mediaservice->>Application: HTTPS 200 Ok Mediaservice->>Application: HTTPS POST /callEvent {
"event":"calleeConnectionSplit",
"user":"userB"
} Application->>Mediaservice: HTTPS 200 Ok {"action":"ivr"} Note over A,Application: B is parked (hold) using IVR Mediaservice->>Application: HTTPS POST /callAnswered
{
"connectionId":"BwithIVR",
"replyId":"replyIdBwithIVR",
"callerParticipantId":"participantIdB"
} Application->>Mediaservice: HTTPS 200 Ok Mediaservice->>Application: HTTPS POST /getIvrCommand Application->>Mediaservice: HTTPS 200 Ok
{
"backgroundPrompt":{
"format":"file",
"fileName":"holdmusic.mp3"
},
"hangup":false
} Note over A,Application: Store participantIdB for later conference join Mediaservice->>Application: HTTPS POST /callEvent {
"event":"callerConnectionSplit",
"user":"userA"
} Application->>Mediaservice: HTTPS 200 Ok
{
"action":"call",
"destAddr":"sip:userc@customer.prod-eu-north-1.iotcomms.io"
} Mediaservice->>C: SIP Invite sip:userc@customer.prod-eu-north-1.iotcomms.io C->>Mediaservice: SIP 200 OK Mediaservice->>C: SIP Ack Mediaservice->>Application: HTTPS POST /callAnswered
{
"connectionId":"AwithC",
"replyId":"replyIdAwithC",
"callerParticipantId":"participantIdA",
"calleeParticipantId":"participantIdC"
} Application->>Mediaservice: HTTPS 200 Ok Note over A,Application: A and C are connected, store participantIdA and participantIdC Application->>Mediaservice: HTTPS POST /createconferenceconnection Mediaservice->>Application: HTTPS 200 Ok { "connectionId":"conferenceId" } Note over A,Application: Conference connection created Application->>Mediaservice: HTTPS POST /splitconnection
{
"connectionId":"AwithC",
"replyId":"replyIdAwithC"
} Mediaservice->>Application: HTTPS POST /callEvent {
"event":"callerConnectionSplit",
"user":"userA"
} Application->>Mediaservice: HTTPS 200 Ok
{
"action":"joinconferenceconnection",
"connectionId":"conferenceId"
} Mediaservice->>Application: HTTPS POST /callEvent {
"event":"calleeConnectionSplit",
"user":"userC"
} Application->>Mediaservice: HTTPS 200 Ok
{
"action":"joinconferenceconnection",
"connectionId":"conferenceId"
} Note over A,Application: A and C joined conference Application->>Mediaservice: HTTPS POST /splitconnection
{
"connectionId":"BwithIVR",
"replyId":"replyIdBwithIVR"
} Mediaservice->>Application: HTTPS POST /callEvent {
"event":"callerConnectionSplit",
"user":"userB"
} Application->>Mediaservice: HTTPS 200 Ok
{
"action":"joinconferenceconnection",
"connectionId":"conferenceId"
} Mediaservice->>Application: HTTPS POST /callEvent {
"event":"calleeConnectionSplit"
} Application->>Mediaservice: HTTPS 200 Ok {"action":"drop"} Note over A,Application: A, B and C are now connected in a shared conference