12. Provisioning interfaces

12.1. REST API
12.1.1. API Workflows
12.1.1.1. Managing Customers and Subscribers
12.2. SOAP and XMLRPC API

The sip:provider CE provides two kinds of provisioning interfaces for easy interconnection with 3rd party tools. The one recommended by Sipwise is the REST API, and the other (soon deprecated) one is SOAP and XMLRPC. Any new functionality is only added to the REST interface, so do not base any new development on SOAP or XMLRPC.

12.1. REST API

The sip:provider CE provides a REST API to provision various functionality of the platform. The entry point - and at the same time the official documentation - is at https://<your-ip>:1443/api. It allows both administrators and resellers (in a limited scope) to manage the system.

You can either authenticate via username and password of your administrative account you’re using to access the admin panel, or via SSL client certificates. Find out more about client certificate authentication in the online api documentation.

12.1.1. API Workflows

The typical tasks done on the API involve managing customers and subscribers. The following chapter focuses on creating, changing and deleting these resources.

12.1.1.1. Managing Customers and Subscribers

The classical life-cycle of a customer and subscriber is:

  1. Create customer contact
  2. Create customer
  3. Create subscribers within customer
  4. Modify subscribers
  5. Modify subscriber preferences (features)
  6. Terminate subscriber
  7. Terminate customer

The boiler-plate to access the REST API is described in the online API documentation at /api/#auth. A simple example in perl using password authentication looks as follows:

#!/usr/bin/perl -w
use strict;
use v5.10;

use LWP::UserAgent;
use JSON qw();

my $uri = 'https://ngcp.example.com:1443';
my $ua = LWP::UserAgent->new;
my $user = 'myusername';
my $pass = 'mypassword';
$ua->credentials('ngcp.example.com:1443', 'api_admin_http', $user, $pass);
my ($req, $res);

For each customer you create, you need to assign a billing profile id. You either have the id stored somewhere else, or you need to fetch it by searching for the billing profile handle.

my $billing_profile_handle = 'my_test_profile';
$req = HTTP::Request->new('GET', "$uri/api/billingprofiles/?handle=$billing_profile_handle");
$res = $ua->request($req);
if($res->code != 200) {
    die "Failed to fetch billing profile: ".$res->decoded_content."\n";
}
my $billing_profile = JSON::from_json($res->decoded_content);
my $billing_profile_id = $billing_profile->{_embedded}->{'ngcp:billingprofiles'}->{id};
say "Fetched billing profile, id is $billing_profile_id";

A customer is mainly a billing container for subscribers without a real identification other than the external_id property you might have stored somewhere else (e.g. the id of the customer in your CRM). In order to still easily identify a customer, a customer contact is required. It is created using the /api/customercontacts/ resource.

$req = HTTP::Request->new('POST', "$uri/api/customercontacts/");
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
    firstname => 'John',
    lastname => 'Doe',
    email => 'john.doe\@example.com'
}));
$res = $ua->request($req);
if($res->code != 201) {
    die "Failed to create customer contact: ".$res->decoded_content."\n";
}
my $contact_id = $res->header('Location');
$contact_id =~ s/^.+\/(\d+)$/$1/; # extract id from Location header
say "Created customer contact, id is $contact_id";
[Important]

To get the id of a just created resource, you need to parse the Location header. This will change in the future for POST requests to optionally also return the resource in the response, controlled via the Prefer: return=representation header as it is already the case for PUT and PATCH.

[Warning]

The example above implies the fact that the API is accessed via a reseller user. If you are accessing the API as admin user, you also have to provide a reseller_id parameter defining the reseller this contact belongs to.

Once the customer contact is created, you can create the actual customer.

$req = HTTP::Request->new('POST', "$uri/api/customers/");
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
    status => 'active',
    contact_id => $contact_id,
    billing_profile_id => $billing_profile_id,
    type => 'sipaccount',
    external_id => undef, # can be set to your crm's customer id
}));
$res = $ua->request($req);
if($res->code != 201) {
    die "Failed to create customer: ".$res->decoded_content."\n";
}
my $customer_id = $res->header('Location');
$customer_id =~ s/^.+\/(\d+)$/$1/; # extract id from Location header
say "Created customer, id is $customer_id";

Once the customer is created, you can add subscribers to it. One customer can hold multiple subscribers, up to the max_subscribers property which can be set via /api/customers/. If this property is not defined, a virtually unlimited number of subscribers can be added.

$req = HTTP::Request->new('POST', "$uri/api/subscribers/");
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
    status => 'active',
    customer_id => $customer_id,
    primary_number => { cc => 43, ac => 9876, sn => 10001 }, # the main number
    alias_numbers => [ # as many alias numbers the subscriber can be reached at (or skip param if none)
        { cc => 43, ac => 9877, sn => 10001 },
        { cc => 43, ac => 9878, sn => 10001 }
    ],
    username => 'test_10001'
    domain => 'ngcp.example.com',
    password => 'secret subscriber pass',
    webusername => 'test_10001',
    webpassword => undef, # set undef if subscriber shouldn't be able to log into sipwise csc
    external_id => undef, # can be set to the operator crm's subscriber id
}));
$res = $ua->request($req);
if($res->code != 201) {
    die "Failed to create subscriber: ".$res->decoded_content."\n";
}
my $subscriber_id = $res->header('Location');
$subscriber_id =~ s/^.+\/(\d+)$/$1/; # extract id from Location header
say "Created subscriber, id is $subscriber_id";
[Important]

The domain has to exist prior to creating a subscriber and can be created via /api/domains/.

At that stage, the subscriber can connect both via SIP and XMPP, and can be reached via the primary number, all alias numbers, as well as via the SIP URI.

If you want to set call forwards for the subscribers, then perform an API call as follows.

$req = HTTP::Request->new('PUT', "$uri/api/callforwards/$subscriber_id");
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => "return=minimal"); # use return=representation to get full json response
$req->content(JSON::to_json({
    cfna => { # set a call-forward if subscriber is not registered
        destinations => [
            { destination => "4366610001", timeout => 10  }, # ring this for 10s
            { destination => "4366710001", timeout => 300 }, # if no answer, ring that for 300s
        ],
        times => undef # no time-based call-forward, trigger cfna always
    }
}));
$res = $ua->request($req);
if($res->code != 204) { # if return=representation, it's 200
    die "Failed to set cfna for subscriber: ".$res->decoded_content."\n";
}

You can set cfu, cfna, cft and cft via this api call, also all at once. Destinations can be hunting lists as described above, or just a single number. Also a time set can be provided in order to trigger call forwards only during specific time periods.

To provision certain features of a subscriber, you can manipulate the subscriber preferences. A full list of preferences available for a subscriber is available at /api/subscriberpreferencedefs/.

$req = HTTP::Request->new('GET', "$uri/api/subscriberpreferences/$subscriber_id");
$res = $ua->request($req);
if($res->code != 200) {
    die "Failed to fetch subscriber preferences: ".$res->decoded_content."\n";
}
my $prefs = JSON::from_json($res->decoded_content);
delete $prefs->{_links}; # not needed in update

$prefs->{prepaid_library} = 'libinewrate'; # switch to inew billing
$prefs->{block_in_clir} = JSON::true; # reject incoming anonymous calls
$prefs->{block_in_list} = [ # reject calls from the following numbers:
    '4366412345', # this particular number
    '431*', # all vienna/austria numbers
];
$req = HTTP::Request->new('PUT', "$uri/api/subscriberpreferences/$subscriber_id");
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => "return=minimal"); # use return=representation to get full json response
$req->content(JSON::to_json($prefs));
$res = $ua->request($req);
if($res->code != 204) {
    die "Failed to update subscriber preferences: ".$res->decoded_content."\n";
}
say "Updated subscriber preferences";

Modifying numbers assigned to a subscriber, changing the password, locking a subscriber etc. can be done directly on the subscriber resource.

$req = HTTP::Request->new('GET', "$uri/api/subscribers/$subscriber_id");
$res = $ua->request($req);
if($res->code != 200) {
    die "Failed to fetch subscriber: ".$res->decoded_content."\n";
}
my $sub = JSON::from_json($res->decoded_content);
delete $sub->{_links}; # not needed in update
push @{ $sub->{alias_numbers} }, { cc => 1, ac => 5432, sn => $t }; # add this number
push @{ $sub->{alias_numbers} }, { cc => 1, ac => 5433, sn => $t }; # add another number

$req = HTTP::Request->new('PUT', "$uri/api/subscribers/$subscriber_id");
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => "return=minimal"); # use return=representation to get full json response
$req->content(JSON::to_json($sub));
$res = $ua->request($req);
if($res->code != 204) {
    die "Failed to update subscriber: ".$res->decoded_content."\n";
}
say "Updated subscriber";

At the end of a subscriber life cycle, it can be terminated. Once terminated, you can NOT recover the subscriber anymore.

$req = HTTP::Request->new('DELETE', "$uri/api/subscribers/$subscriber_id");
$res = $ua->request($req);
if($res->code != 204) {
    die "Failed to terminate subscriber: ".$res->decoded_content."\n";
}
say "Terminated subscriber";

Note that certain basic information is still available on the internal database in order to perform billing/rating of calls done by this subscriber, but a subscriber is not able to connect to the system, login or do calls/chats, as the data is removed from the operational tables of the database.

Modifications to resources can not only be done via a GET/PUT combination, you can also add, modify or delete single properties of a resource without actually fetching the whole resource. An example is given below where we terminate the status of a customer using the PATCH method.

$req = HTTP::Request->new('PATCH', "$uri/api/customers/$customer_id");
$req->header('Content-Type' => 'application/json-patch+json');
$req->header('Prefer' => "return=minimal"); # use return=representation to get full json response
$req->content(JSON::to_json([
    { op => 'replace', path => '/status', value => 'terminated' }
]));
$res = $ua->request($req); # this will also terminate all still active subscribers
if($res->code != 204) {
    die "Failed to terminate customer: ".$res->decoded_content."\n";
}
say "Terminated customer";

12.2. SOAP and XMLRPC API

[Important]

SOAP and XMLRPC API are deprecated and disabled by default since mr3.6.1. Please consider using REST API as SOAP and XMLRPC API will be deleted in upcoming release(s). To enable SOAP and XMLRPC change /etc/ngcp-config/config.yml by setting ossbssfrontendfcgi and execute ngcpcfg apply 'enable SOAP API'.

The sip:provider CE provides two (soon deprecated) XML based provisioning interfaces - SOAP and XMLRPC. The server provides online documentation about all the functions available. To access the online documentation for the first time, you need to follow the following instructions:

  • Generate a password for http access to the provisioning interfaces:
htpasswd -nbs myuser mypassword
[Note]

Also see man 1 htpasswd on how to generate crypt or MD5 passwords if you like. Of course you may use any other process to generate crypt, MD5 or SHA hashed passwords. But using htpasswd ensures the hashes are also understood by Nginx. To install htpasswd please run apt-get install apache2-utils on your system.

  • Edit /etc/ngcp-config/config.yml. Under section ossbsshtpasswd, replace user and pass with your new values and execute ngcpcfg apply 'change SOAP credentials' as usual.
  • Access https://<ip>:2443/SOAP/Provisioning.wsdl and login with your new credentials.
[Note]

The default port for provisioning interfaces is 2443. You can change it in /etc/ngcp-config/config.yml by modifying ossbssapacheport and execute ngcpcfg apply.

[Important]

The displayed online API documentation shows all the currently available functionalities. Enabling or disabling features in /etc/ngcp-config/config.yml will directly reflect in the functions being available via the APIs.

[Important]

If your SOAP client throws errors because of the inline <documentation> tags (e.g. Visual Studio and the stock PHP SOAP client complain about this), try to use the WSDL URL https://<ip>:2443/SOAP/Provisioning.wsdl?plain instead, which supresses the output of these tags.