Skip to content

Python client

This page builds a small, complete Python 3 script on the call() helper from the API overview. The script manages the trusted numbers of an account: it adds a CLI mapping, changes it, lists the account’s numbers and removes the mapping again. The methods are described in Manage trusted numbers.

  • Enable API access for the customer that owns the account, as described in Enable API access.

  • Install Python 3.9 or later and the requests library:

    Terminal window
    python3 -m pip install requests
  • Find the i_account of the account, for example with getAccountInfo.

The script reads the endpoint and the credentials from environment variables, so the API password stays out of the code.

trusted_numbers.py
"""Add, change, list and remove trusted numbers (CLI mappings) of an account."""
import os
import sys
import xmlrpc.client
import requests
from requests.auth import HTTPDigestAuth
URL = os.environ.get("FLYSIP_URL", "https://panel.example.com/xmlapi/xmlapi")
AUTH = HTTPDigestAuth(os.environ["FLYSIP_LOGIN"], os.environ["FLYSIP_API_PASSWORD"])
# If the panel uses a certificate of your own CA, set FLYSIP_CA to its PEM file.
VERIFY = os.environ.get("FLYSIP_CA", True)
def call(method: str, params: dict) -> dict:
"""Call one API method; raises xmlrpc.client.Fault on a fault."""
body = xmlrpc.client.dumps((params,), method, allow_none=True)
resp = requests.post(URL, data=body.encode(), auth=AUTH, verify=VERIFY,
headers={"Content-Type": "text/xml"}, timeout=30)
resp.raise_for_status()
return xmlrpc.client.loads(resp.content, use_builtin_types=True)[0][0]
def list_numbers(i_account: int) -> list[tuple[str, str]]:
"""Return the (cli, lang) pairs mapped to the account."""
res = call("listCLIMappings", {"i_account": i_account})
return [(cli, lang) for cli, lang in res["list"]]
def main() -> int:
i_account = 3092
old_cli, new_cli = "12065550123", "12065550124"
try:
call("addCLIMapping", {"i_account": i_account, "cli": old_cli, "lang": "en"})
print(f"added {old_cli}")
call("updateCLIMapping", {"i_account": i_account, "old_cli": old_cli,
"new_cli": new_cli, "lang": "en"})
print(f"changed {old_cli} to {new_cli}")
for cli, lang in list_numbers(i_account):
print(f"trusted number {cli}, voice menu language {lang}")
call("delCLIMapping", {"i_account": i_account, "cli": new_cli})
print(f"removed {new_cli}")
except xmlrpc.client.Fault as fault:
# The switch rejected the call, for example a wrong i_account
# or a missing parameter. faultString says what went wrong.
print(f"API fault {fault.faultCode}: {fault.faultString}", file=sys.stderr)
return 1
except requests.HTTPError as exc:
# HTTP 401 means the digest credentials were rejected.
print(f"HTTP error: {exc}", file=sys.stderr)
return 2
except requests.RequestException as exc:
print(f"Cannot reach {URL}: {exc}", file=sys.stderr)
return 2
return 0
if __name__ == "__main__":
sys.exit(main())

Run it:

Terminal window
export FLYSIP_URL=https://panel.example.com/xmlapi/xmlapi
export FLYSIP_LOGIN=acme-web
export FLYSIP_API_PASSWORD='API-PASSWORD'
python3 trusted_numbers.py
output
added 12065550123
changed 12065550123 to 12065550124
trusted number 12065550124, voice menu language en
removed 12065550124

There are two kinds of failure, and the script reports them differently:

  • An XML-RPC fault. The request reached the switch, but the call failed. xmlrpc.client.loads() raises xmlrpc.client.Fault with faultCode and faultString. The fault codes tell you the class of the problem; log faultString.
  • An HTTP or network error. The call never ran. raise_for_status() raises requests.HTTPError, for example for HTTP 401 when the Web Login or API Password is wrong or Allow API Calls is off. A timeout or a refused connection raises another requests.RequestException.

The script stops at the first fault. To go on with the other numbers, catch xmlrpc.client.Fault around each call instead.

Calls from the switch itself or from a host it trusts need no digest authentication, see Trusted mode. Such a call does not identify a customer, so add i_customer, the ID of the customer that owns the account, to every call:

trusted_mode.py
import xmlrpc.client
import requests
URL = "https://panel.example.com/xmlapi/xmlapi"
def call_trusted(method: str, params: dict) -> dict:
"""Call one API method without authentication, from a trusted host."""
body = xmlrpc.client.dumps((params,), method, allow_none=True)
resp = requests.post(URL, data=body.encode(),
headers={"Content-Type": "text/xml"}, timeout=30)
resp.raise_for_status()
return xmlrpc.client.loads(resp.content, use_builtin_types=True)[0][0]
call_trusted("addCLIMapping", {"i_customer": 3, "i_account": 3092,
"cli": "12065550123", "lang": "en"})
res = call_trusted("listCLIMappings", {"i_customer": 3, "i_account": 3092})
for cli, lang in res["list"]:
print(cli, lang)