Note: The Python examples below now use requests instead of httplib2. By using requests.post() with the necessary authentication, headers, and SSL options, the code is simpler and compatible with modern Python versions (e.g., 3.11). Successful HTTP 200 responses are parsed with getparser() from xmlrpc.client; otherwise, a ProtocolError is raised. This approach streamlines SSL handling, authentication, and header management. Also, requests by default writes more information about its internal work for example, errors or warnings about SSL settings.


  * Digest Authentication for Trusted Numbers Manipulation and CLI Mapping

See introduction to XMLAPI for details on Authentication:

https://support.sippysoft.com/a/solutions/articles/106909

#!/usr/bin/env python
## this script requires requests to be installed
## username should be Web Login of the Customer, password - API password
## 1.2.3.4 should be replaced with target IP/hostname of Sippy

import requests
from requests.auth import HTTPDigestAuth
from urllib.parse import urlparse
try:
    from xmlrpc.client import ServerProxy, getparser, ProtocolError
except ImportError:
    from xmlrpclib import ServerProxy, getparser, ProtocolError

class HTTPSDigestAuthTransport:
    def request(self, host, handler, request_body, verbose=0):
        full_url = f"https://{host}"
        parsed = urlparse(full_url)
        username = parsed.username
        password = parsed.password
        hostname = parsed.hostname
        port = parsed.port or 443

        url = f"https://{hostname}:{port}{handler}"

        if verbose:
            print(f"Making request to: {url}")
            print(f"Username: {username}, Password: {password}")

        response = requests.post(url, data=request_body, headers={"content-type": "text/xml"}, auth=HTTPDigestAuth(username, password), verify=True)

        if response.status_code != 200:
            raise ProtocolError(url, response.status_code, response.reason, None)

        p, u = getparser(use_datetime=0)
        p.feed(response.content)
        return u.close()

if __name__ == "__main__":
    transport = HTTPSDigestAuthTransport()
    client = ServerProxy("https://username:[email protected]/xmlapi/xmlapi", transport)

    ## add a mapping
    res = client.addCLIMapping({"i_account": 35, "cli": "123456", "lang": "en"})
    print(res["result"])

    ## retrieve list of trusted numbers
    res = client.listCLIMappings({"i_account": 35})
    if res["result"] == "OK":
        for cli, lang in res["list"]:
            print(cli, lang)


* Trusted Authentication for Trusted Numbers Manipulation and CLI Mapping

In this example i_customer should be supplied to selected XMLAPI method, which is mandatory for most methods in trusted mode. 


#!/usr/bin/env python
## this script requires requests to be installed
## 1.2.3.4 should be replaced with target IP/hostname of Sippy

import requests
try:
    from xmlrpc.client import ServerProxy, getparser, ProtocolError
except ImportError:
    from xmlrpclib import ServerProxy, getparser, ProtocolError

class HTTPSTrustedAuthTransport:
    def request(self, host, handler, request_body, verbose=0):
        url = f"https://{host}{handler}"

        if verbose:
            print(f"Making request to: {url}")

        response = requests.post(url, data=request_body, headers={"content-type": "text/xml"}, verify=True)

        if response.status_code != 200:
            raise ProtocolError(url, response.status_code, response.reason, None)

        p, u = getparser(0)
        p.feed(response.content)
        return u.close()

transport = HTTPSTrustedAuthTransport()
client = ServerProxy("https://1.2.3.4/xmlapi/xmlapi", transport)

## add a mapping
res = client.addCLIMapping({'i_customer': 1, 'i_account': 35, 'cli': '123456', 'lang': 'en'})
print(res['result'])

## retrieve list of trusted numbers
res = client.listCLIMappings({'i_customer': 1, 'i_account': 35})
if res['result'] == 'OK':
    for cli, lang in res['list']:
        print(cli, lang)


* Manage Call-Back Calls

#!/usr/bin/env python
## this script requires requests to be installed
## 1.2.3.4 should be replaced with target IP/hostname of Sippy

import requests
from requests.auth import HTTPDigestAuth
try:
    from urllib.parse import urlparse
    from xmlrpc.client import ServerProxy, getparser, ProtocolError
except ImportError:
    from urlparse import urlparse
    from xmlrpclib import ServerProxy, getparser, ProtocolError

my_number = '12061234567'
destination_number = '12067654321'
authname = '000125'

class HTTPSDigestAuthTransport:
    def request(self, host, handler, request_body, verbose=0):
        parsed = urlparse("https://" + host)
        username = parsed.username
        password = parsed.password
        hostname = parsed.hostname
        port = parsed.port or 443

        url = f"https://{hostname}:{port}{handler}"

        if verbose:
            print(f"Making request to: {url}")

        response = requests.post(url, data=request_body, headers={'content-type': 'text/xml'}, auth=HTTPDigestAuth(username, password), verify=True)

        if response.status_code != 200:
            raise ProtocolError(url, response.status_code, response.reason, None)

        p, u = getparser(0)
        p.feed(response.content)
        return u.close()

transport = HTTPSDigestAuthTransport()
client = ServerProxy("https://username:[email protected]/xmlapi/xmlapi", transport)

res = client.make2WayCallback({
    'authname'   : authname,
    'cld_first'  : my_number,
    'cld_second' : destination_number,
    'credit_time': 3600 # call should not continue more than an hour
})

if res['result'] == 'OK':
    print(res['i_callback_request'])


* Refunding an Account

#!/usr/bin/env python
## this script requires requests to be installed
## 1.2.3.4 should be replaced with target IP/hostname of Sippy

import requests
from requests.auth import HTTPDigestAuth
try:
    from urllib.parse import urlparse
    from xmlrpc.client import ServerProxy, getparser, ProtocolError
except ImportError:
    from urlparse import urlparse
    from xmlrpclib import ServerProxy, getparser, ProtocolError

class HTTPSDigestAuthTransport:
    def request(self, host, handler, request_body, verbose=0):
        parsed = urlparse("https://" + host)
        username = parsed.username
        password = parsed.password
        hostname = parsed.hostname
        port = parsed.port or 443

        url = f"https://{hostname}:{port}{handler}"

        if verbose:
            print(f"Making request to: {url}")

        response = requests.post(url, data=request_body, headers={'content-type': 'text/xml'}, auth=HTTPDigestAuth(username, password), verify=True)

        if response.status_code != 200:
            raise ProtocolError(url, response.status_code, response.reason, None)

        p, u = getparser(0)
        p.feed(response.content)
        return u.close()

transport = HTTPSDigestAuthTransport()
client = ServerProxy("https://username:[email protected]/xmlapi/xmlapi", transport)

res = client.accountAddFunds({ 'i_account' : 35, 'amount' : 1.23, 'currency' : 'USD', 'payment_notes' : 'Test credit' })
print(res['result'])

* Python multicall example

https://support.sippysoft.com/a/solutions/articles/3000108533