Developing Call Services on Creacode SIP Application Server


Advanced call services can be developed in a very short time period with the capabilities of Creacode SIP Application Server's CCS scripting language. With CCS's event-driven architecture, SIP signal states are abstracted from service developer as much as possible, so that she can primarily focus on implementing call service business.

The syntax and semantics of CCS looks like major programming languages such as C, C# or Javascript. If you can build simple programs with these programming languages, this means that you are ready to build call services on the server.

Suppose that your server will receive a call with SIP INVITE request, then your script will automatically jump to an event called NewCall.

NewSession.ccs script file
// INVITE received from originator
EVENT NewCall()
    LOG("New Call received. Session has started!");
    // Print SIP from-user to log file
    LOG("Calling Number:" + _CALLING_NR_);
    // Send 180 SIP message to Calling Party
    // Send 200 OK SIP message to Calling Party

Click here to look at related signalling case.

If you would like to know that Calling Party has received your response and acknowledged with an ACK message to establish a call, it is time to catch this state and continue the call service.

// ACK received from originator
EVENT CallActive()
    LOG("Call is active right now");
    // Open an audio channel towards calling party
    bool bRet = OpenAudioChannel(_LEG_A_);
    if (bRet == FALSE) {
        LOG("Couldn't open an audio channel");
        // I would like to send a BYE message to end this call

    // Play a welcome voice prompt. 
    PlayAudio(_LEG_A_, "welcome.wav");
    // Now, collect maximum 14 digits from calling party while prompting "Please enter your destination number" voice file
    string strDigits = GetDigits(_LEG_A_, 14, "enter_destination.wav");
    // It's time to make a new call towards destination number with the received digits.
    // This call can be sent to a destination by route-plan result, registered subscriber or configured default proxy. 
	StartCall(_LEG_A_, _LEG_B_, strDigits);
    // Now jump to another script file to catch new events...

If called party's telephone rings, then an event named CallRinging will be thrown similar to above process to be caught by service developer.

Ringing.ccs script file
// 180 Ringing received from Called Party
EVENT CallRinging()
    LOG("Call is ringing right now");

Creacode SIP Application Server comes with template scripts for B2BUA and IVR call scenarios. So you can use various internal functions and catch events in those template scripts according to your call service requirements.


Call flows are implemented in script files and all files are compiled to build a final call service binary which would be run by Creacode SIP Application Server.
Internal events (NewCalls, CallRinging, CallActive etc.), timer events or user defined events can be caught in script files where current call state is executed.
Service-wide or script-wide global functions can be implemented and global variables can be defined in string, integer, bool or double data types.
Functions can take parameters and return values.
"if" statements, "for" and "while" loops can be utilized.

How To Communicate with an External Web Application

HTTP module allows call service developers to post data to or retrieve data from a web application such as php, jsp/servlet, asp/aspx etc. in order to communicate with an external node such as billing server, reporting server a database itself. Attribute-value paired parameters can be posted and retrieved in XML forms via HTTP post/get communication protocol. Secure SSL connections can also be utilized by this module.

In the following example, maximum call duration is asked to web server by sending calling number and called number.

FUNCTION FindDuration()
    AddXMLParam("Origination",  _CALLING_NR_);
    AddXMLParam("Destination",  _CALLED_NR_);  

    // Send added parameters to the web page running on
    bool bRet = PostHttpXML("", "/MyServlet.aspx");
    if (bRet != TRUE) {
        LOG("PostHttpXML Failed. Error Description:" + 

    // MyServlet.aspx returns maximum call duration value in MaxCallDuration attribute
    string strDuration = GetXMLParamValue("MaxCallDuration");

    // Reset http session for further http requests

How To Communicate with a Radius Server

Authentication, Authorization and Accounting services of a Radius server can be called by the Radius client module of Creacode SIP Application Server. Standard Radius attribute types are preloaded for Radius client. But if Vendor Specific Attributes (VSA) needs to be used, relevant VSA dictionary file is added into Radius client's dictionary path. So those VSA parameters will be automatically loaded when server starts up and utilized when sending/retrieving Radius requests/responses.

For example, calling and called number can be added into Radius packet by this way:

AddRadiusAttr(31, "+905327654321");	// Adds calling number into RADIUS packet. 
                                        // 31 is known as calling number related parameter in Radius
AddRadiusAttr(30, "+902126090484");	// Adds called number into RADIUS packet. 
                                        // 30 is known as calling number related parameter in Radius

Sending synchronous authorization packet to Radius server is performed by SendRadiusRequest script function.
SendRadiusRequest(1, "", 1812); // 1=Authorization, then radius ip and port is specified

Following example shows how to send an authorization request and receive response parameters via VSA attributes

FUNCTION bool AuthorizeUser()
    // Standart attribute values are added into authorization packet
    AddRadiusAttr(1, "MyUserName");         // User
    AddRadiusAttr(2, "MyPassword");         // Password
    AddRadiusAttr(4, "");        // Nas Ip Address
    AddRadiusAttr(5, i2str(_SESSION_ID_));  // Nas Port
    AddRadiusAttr(61, "0");                 // Nas Port Type
    AddRadiusAttr(31, "05327654321");       // Calling Id 
    AddRadiusAttr(30, "02127654321");       // Called Id 
    AddRadiusAttr(44, i2str(_SESSION_ID_)); // Acct Session Id

    // Adds a VSA attribute value as well
    AddRadiusVSAAttr(24, "h323-conf-id=" + g_strh323confid, 9); 

    // Sends Radius request to Radius server. 
    bool bRet = FALSE;
    int iRet = SendRadiusRequest(1, "", 1812); 
    if (iRet == 0) { // ACCESS_ACCEPT
        // VSA(103) returns as result code from the server
        string strReturnCode = GetRadiusVSAAttrValue(103);

        if (str2i(strReturnCode) > 0) {
            // An internal error returned by Radius server
            bRet = FALSE;
        else { 
            // User authorized and other parameters are fetched from Radius response
            string strCreditTime = GetRadiusVSAAttrValue(102);
            string strBillingModel = GetRadiusVSAAttrValue(109);
            bRet = TRUE;
    if (iRet == 1) { // ACCESS_REJECT
        // Authentication rejected
        string strReturnCode = GetRadiusVSAAttrValue(103);
        LOG("Radius reject");
    if (iRet == 2) { // PACKET_SEND_PROBLEM
        // For exp: Couldn't connect to server
        LOG("Technical problem occured when sending radius packet!");

    // Reset radius session for further radius requests

    return bRet;

Direct Access To Database

Any data can be direcly stored to and read from an ODBC supported database during the execution of call service. ODBC connection is established using configured DSN name, username and password.

Below you can find an example of connecting postgresql database and executing select and insert queries.

FUNCTION bool ConnectDatabase()
    // Save connection id in g_nConnectionId global variable
    g_nConnectionId = ODBCConnect("DSN=creacodesas_pg;UID=postgres;PWD=postgres");
    LOG("g_ConnectionId: " + i2str(g_ConnectionId));
    if (g_ConnectionId < 1) {
        LOG("odbc connection failed");
        return false;
    return true;

FUNCTION void ListAnswerTable()
    int nRet = ODBCExecuteSelect(g_ConnectionId, "select * from answer"); 
    if (nRet > 0) {
        while (ODBCIsEOF(g_ConnectionId) == 0) {
            LOG("id: " + " answer_id:" + i2str(ODBCGetIntField(g_ConnectionId, "answer_id")) + 
                " called_number:" + i2str(ODBCStringField(g_ConnectionId, "called_number")));
    } else {
        LOG("Select query failed");

FUNCTION void ExecuteQuery()
    string strSQL = "insert into answer (id, answer_id, called_number) " + 
        "values (nextval('answer_id_seq'), 1, "+41795632538")";
    LOG("strSQL: " + strSQL);
    // Insert data into answer table
    int nRet = ODBCExecute(g_ConnectionId, strSQL);
    if (nRet != 0) {
         LOG("Record inserted successfully.");
    } else {
        LOG("Record insert failed!!!");


The List of Scripting Functions and Events

Signalling Functions HTTP Messaging String Manupulation Functions
AcceptCall AddXMLParam strlen
AcceptCallWithMedia PostHTTPXML str2i
AnswerCall GetXMLParamValue i2str
StartCall ResetXML strfind
RejectCall strleft
JoinLegs Radius Messaging strright
SendDigit AddRadiusAttr strmid
RedirectCall AddRadiusVSAAttr
EndCall SendRadiusRequest Signalling Events
ReleaseSession GetRadiusAttrValue EVENT NewCall( )
GetRadiusVSAAttrValue EVENT CallRinging( )
Voice Functions ResetRadius EVENT CallRingingWithMedia( )
OpenAudioChannel EVENT CallReject( )
CloseAudioChannel VoiceMail Functions EVENT CallAnswered( )
PlayAudio RecordVoice EVENT CallEstablished( )
PlayBackgroundAudio RecordnMailVoice EVENT CallActive( )
StopBackgroundAudio EVENT CallEnd( )
GetDigits Database Connectivity Functions EVENT OnDigit( )
PlayCredit ODBCConnect EVENT OnCallChangedByLegA( )
PlayTime ODBCDisconnect EVENT OnCallChangedByLegB( )
Utility Functions ODBCExecuteSelect Scripting Events
ReturnScript ODBCIsEOF EVENT < [TimerFunctionName] >( )
StartTimer ODBCMoveNext EVENT < [UserEventName] >( )
GetTime ODBCGetIntField  
FormatTime ODBCGetDobuleField
TimeDiff ODBCGetStringField  
Sleep Mathematical Functions  
GetStatusMsg neg
LOG rand
SendMail randHex