Managing Subscribers

In this tutorial, we will get an overview of the various APIs and steps to manage Subscribers.

Preparation

You must own a Tenant Administrator user account for an existing tenant company. Only Tenant Administrators can create Subscribers. Make sure you have this account's credentials before you continue with this tutorial.

Introduction

The BUZZ Administration API lets you create, modify and delete Subscriber accounts. While you can accomplish this manually using the BUZZ administration console, using the API is useful when automating administrative tasks.

This section does not discuss guests which are temporary Subscribers invited to IM rooms. It doesn't cover company and extension administration. These topics will be covered in a future tutorial.

Subscriber Data Model

This section describes the relevant data entities of the Subscriber provisioning APIs.

Creating a Subscriber affects a few types of entities.

  • The Subscriber, which encapsulates the subscriber.
  • The UserExtension, with the Subscriber's phone settings.
  • The Subscriber's contact information (address, telephone number) is in the subscriberDetails entity.
  • Then there's also his tag line (the text that should appear next to his name) in the subscriberTagLine entity.
  • The Subscriber purchased a number of different phone services. These services are listed in the classesOfService entity.

Next, are a number of different preferences AKA attributes. These are categorized by:

  • subscriberBuzzAttributes: BUZZ permissions
  • subscriberVoicemailAttributes: Preferences for the voicemail system.
  • subscriberInboundAttributes: Telephony settings for incoming calls.
  • subscriberOutboundAttributes: Telephony settings for outgoing calls.

Creating and Configuring a Subscriber

The process for creating a new Subscriber is done by following these steps:

  1. Log in as a Tenant Administrator with the /api/UserSecurityLogin endpoint.
  2. Create a Subscriber with the /api/SubscriberProvisioning endpoint. This will create the baseSubscriber and subscriber entities. This may create a new temporary password, in which case the user must reset his password (see next steps). Otherwise, the process is finished.
  3. To reset the user's password, log into the new Subscriber's account with the /api/subscribersecuritylogin command with the credentials of the newly created Subscriber. This will return an error including a reset token.
  4. If there is a reset token, change this Subscriber's temporary password to his real password using the /api/subscribersecuritychangePassword endpoint.

Let us revisit the Subscriber creation steps one by one.

1. Log In as a Tenant Administrator

Since the Tenant Administrator account is not a Subscriber, we will not use the normal /api/subscriberSecurityLogin endpoint. Instead, we will send a GET message to the /api/usersecuritylogin endpoint, with basic authentication. The Authorization header should contain the administrator's username and password.

If successful, this will return an object with the following format:

{
    "data": {
        "bearerToken": "<administrator authentication token>"
    }
}

Keep this token handy and use it in the Authentication header for step 3.

The following is an example of this step:

@Autowired
private RestTemplate rest;

@Autowired
private ObjectMapper mapper;

private String server;
private String token;

public String buzzSubscriberAdminLogin(String username, String password, String server) {
    String plainCreds = username + ":" + password;
    byte[] plainCredsBytes = plainCreds.getBytes();
    byte[] base64CredsBytes = Base64.getEncoder().encode(plainCredsBytes);
    String base64Creds = new String(base64CredsBytes);

    HttpHeaders headers = new HttpHeaders();
    headers.add("Authorization", "Basic " + base64Creds);

    HttpEntity<String> request = new HttpEntity<>(headers);
    this.server = server;
    if (!this.server.endsWith("/")) {
        this.server += "/";
    }

    try {
        ResponseEntity<String> response = rest.exchange(this.server + "api/usersecuritylogin", HttpMethod.GET, request, String.class);
        if (response.getStatusCode().is2xxSuccessful()) {
            JsonNode loginResponse = mapper.readValue(response.getBody(), JsonNode.class);
            return subscriberAdminLoginSuccess(loginResponse);
        }
        return "Error logging into BUZZ server: " + response.toString();
    } catch (HttpClientErrorException e) {
        return "Error logging into BUZZ server: " + e.getStatusCode().toString();
    } catch (Exception e2) {
        return "Problem logging into BUZZ server: " + e2.toString();
    }
}

private String subscriberAdminLoginSuccess(JsonNode response) {
    this.token = response.get("bearerToken").asText();
    return null;
}
var server;

function buzz_subscriber_admin_login(user, pass, adminServer, callback) {
    server = adminServer;
    if (!server.endsWith("/")) {
        server += "/";
    }
    $.ajax({
            url: server+"api/usersecuritylogin",
            headers: {
            "Authorization": "Basic " + btoa(user + ":" + pass)
        },
        method: "GET",
        success: function(result) {
            buzz_admin_login_success(result, callback);
        },
        error: function (xhr, status, errorThrown) {
            console.log("Error logging into BUZZ. " + xhr.status + " " + xhr.text + " " + errorThrown);
            alert("Cannot log into BUZZ. Please check your credentials and server address.");
        }
    });
}

var adminToken;

function buzz_admin_login_success(response, callback) {
    adminToken = response.bearerToken;
    callback();
}

2. Create a New Subscriber

Send a POST message to the endpoint /api/SubscriberProvisioning, with the body containing the following:

{
    "email": "<subscriber’s email address>",
    "username": "<subscriber’s unique user name>",
    "firstName": "<subscriber’s first name>",
    "lastName": "<subscriber’s last name>",
    "overridepasswordresetonfirstlogin": false,
    "overridUserLocalOverDomain": true
}

If successful, this will return an object with the following format:

{
    "data": {
        "id": "<subscriber’s new record ID>",
        "password": "<subscriber’s temporary password. If missing, the subscriber creation is complete.>"
    }
}

There could be errors coming back if the email address or the username is already used by another Subscriber, so please ensure that these are unique.

private HttpEntity<String> createBuzzEntity(String body) {
    HttpHeaders headers = new HttpHeaders();
    headers.add("Authorization", "Bearer " + token);

    if (body == null) {
        return new HttpEntity<>(headers);
    } else {
        headers.add("Content-Type", "application/json");
        return new HttpEntity<>(body, headers);
    }
}

public void createSubscriber(String username, String firstName, String lastName, String email, String userExtension, String password) throws Exception {
    ObjectNode subscriberNode = mapper.createObjectNode();
    subscriberNode.set("username", new TextNode(username));
    subscriberNode.set("firstName", new TextNode(firstName));
    subscriberNode.set("lastName", new TextNode(lastName));
    subscriberNode.set("email", new TextNode(email));
    subscriberNode.set("userExtension", new TextNode(userExtension));

    HttpEntity<String> request = createBuzzEntity(mapper.writeValueAsString(subscriberNode));

    try {
        ResponseEntity<String> response = rest.exchange(server + "api/SubscriberProvisioning", HttpMethod.POST, request, String.class);
        if (response.getStatusCode().is2xxSuccessful()) {
            JsonNode result = mapper.readValue(response.getBody(), JsonNode.class);
            //keep this for later if needed
            int newSubscriberId = result.get("id").asInt();
            if(result.has("password")) {
                String tempPassword = result.get("password").asText();
                buzzLogin(username, tempPassword, password);
            }
            return;
        }
        throw new Exception("Error creating user: " + response.toString());
    } catch (HttpClientErrorException e) {
        throw new Exception("Error creating user: " + e.getStatusCode().toString());
    } catch (Exception e2) {
        throw new Exception("Problem creating user: " + e2.toString());
    }
}
function create_subscriber(username, firstName, lastName, email, userExtension, password, callback) {
    var subscriber = {
        "username": username,
        "firstName": firstName,
        "lastName": lastName,
        "email": email,
        "userExtension": userExtension
    };
    
    $.ajax({
        url: server+"api/SubscriberProvisioning",
        method: "POST",
        headers: {
            "Authorization": "Bearer " + adminToken
        },
        data: subscriber,
        success: function(result) {
            //keep this for later if needed
            var newSubscriberId = result.id;
            if(result.password) {
                var tempPassword = result.password;
                buzz_login(username, tempPassword, password, callback);
                return;
            }
            callback(); //Success
        },
        error: function (xhr, status, errorThrown) {
            console.log("Error creating subscriber. " + xhr.status + " " + xhr.text + " " + errorThrown);
        }
    });

}

At this point, there are two cases to consider:

  • If a temporary password was returned in the result, this means the user must reset his password. Follow the next steps to do this programmatically.
  • If no temporary password was returned, this means the authentication method for this Tenant already has a password set up. Skip the next steps.

3. Log Into the New Subscriber's Account

At this point, the user needs to reset his password.

To do this, we'll log in exactly as we did in the Logging In tutorial, but at this point, the user will be forced to replace his temporary password with one of his own choosing.

In this step use the password returned from step 2. You will get an error back, but the body of the error will contain a special token, that can be used to change the password in the next step.

Send a GET message to the /api/subscriberSecurityLogin endpoint with basic authentication. The Authorization header should contain the username and the temporary password from step 2.

At this point, you should get a 401 (Unauthorized) error, and the body should contain:

{
    "data": {
        "passwordResetToken": "<temporary token to reset the password.>"
    }
}

Keep this reset token handy for the next step.

public String buzzLogin(String username, String oldPassword, String newPassword) throws Exception {
    String plainCreds = username + ":" + oldPassword;
    byte[] plainCredsBytes = plainCreds.getBytes();
    byte[] base64CredsBytes = Base64.getEncoder().encode(plainCredsBytes);
    String base64Creds = new String(base64CredsBytes);

    HttpHeaders headers = new HttpHeaders();
    headers.add("Authorization", "Basic " + base64Creds);

    HttpEntity<String> request = new HttpEntity<>(headers);

    try {
        ResponseEntity<String> response = rest.exchange(server + "api/subscriberSecurityLogin", HttpMethod.GET, request, String.class);
        if (response.getStatusCode().is2xxSuccessful()) {
            JsonNode loginResponse = mapper.readValue(response.getBody(), JsonNode.class);
            return loginSuccess(loginResponse);
        }
        return "Error logging into BUZZ server: " + response.toString();
    } catch (HttpClientErrorException e) {
        if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) {
            //Need to exchange the reset token with the real password
            JsonNode result = null;
            try {
                result = mapper.readValue(e.getResponseBodyAsString(), JsonNode.class);
            } catch (IOException e1) {
                return "Error logging into BUZZ server: " + e.getStatusCode().toString();
            }
            //keep this for later if needed
            String resetToken = result.get("passwordResetToken").asText();
            return changePassword(username, resetToken, oldPassword, newPassword);
        }
        return "Error logging into BUZZ server: " + e.getStatusCode().toString();
    } catch (Exception e2) {
        return "Problem logging into BUZZ server: " + e2.toString();
    }
}

private String loginSuccess(JsonNode response) {
    //No need to change the password, already changed.
    return null; //Success
}
function buzz_login(username, oldPassword, newPassword, callback) {
    $.ajax({
            url: server+"api/subscriberSecurityLogin",
            headers: {
            "Authorization": "Basic " + btoa(username + ":" + oldPassword)
        },
        method: "GET",
        success: function(result) {
            buzz_login_success(result, callback); //No error, change password.
        },
        error: function (xhr, status, errorThrown) {
            if(xhr.status == 401) { //UNAUTHORIZED
                var resetToken = xhr.responseJSON.passwordResetToken;
                change_password(username, resetToken, oldPassword, newPassword, callback);
                return;
            }
            console.log("Error logging into BUZZ. " + xhr.status + " " + xhr.text + " " + errorThrown);
            alert("Cannot log into BUZZ. Please check your credentials and server address.");
        }
    });
}

function buzz_login_success(response, callback) {
    //No need to change the password, already changed.
    callback(); //Success
}

4. Change the Password

Send a PUT message to the /api/SubscriberSecurityChangePassword endpoint, with the body containing the following:

{
    "username": "<subscriber’s user name>",
    "resetToken": "<the token from previous step>",
    "oldpassword": "<temporary password>",
    "newpassword": "<new password>"
}

If successful, the Subscriber is now fully created.

In case of an error, there will be an error code returned:

{
    "statusMessage": "<human-readable error message>",
    "returnCode": "<the reason for the rejection, see below>"
}

Here is the list of possible error codes:

  • The password is too weak. Look at statusMessage for the details.
  • Password has been used before. Choose another password.
  • This user doesn't exist. Check the username.
  • The old password is incorrect.
  • The resetToken has expired. Redo the previous step to get a new one.

At this point, the Subscriber can log into his account normally as described in the Logging In tutorial.

private String changePassword(String username, String resetToken, String oldPassword, String newPassword) throws Exception {
    ObjectNode subscriberNode = mapper.createObjectNode();
    subscriberNode.set("username", new TextNode(username));
    subscriberNode.set("resetToken", new TextNode(resetToken));
    subscriberNode.set("oldPassword", new TextNode(oldPassword));
    subscriberNode.set("newPassword", new TextNode(newPassword));

    HttpHeaders headers = new HttpHeaders();
    headers.add("Content-Type", "application/json");
    HttpEntity<String> request = new HttpEntity<>(mapper.writeValueAsString(subscriberNode), headers);

    try {
        ResponseEntity<String> response = rest.exchange(server + "api/subscribersecuritychangePassword", HttpMethod.PUT, request, String.class);
        if (response.getStatusCode().is2xxSuccessful()) {
            return null; //Success!
        }
        return "Error changing password: " + response.toString();
    } catch (HttpClientErrorException e) {
        return "Error changing password: " + e.getStatusCode().toString();
    } catch (Exception e2) {
        return "Problem changing password: " + e2.toString();
    }
}
function change_password(username, resetToken, oldPassword, newPassword, callback) {
    var subscriber = {
        "username": username,
        "resetToken": resetToken,
        "oldPassword": oldPassword,
        "newPassword": newPassword
    };
    
    $.ajax({
        url: server+"api/subscribersecuritychangePassword",
        method: "PUT",
        data: subscriber,
        success: function(result) {
            callback(); //Success
        },
        error: function (xhr, status, errorThrown) {
            console.log("Error creating subscriber. " + xhr.status + " " + xhr.text + " " + errorThrown);
        }
    });
}

Modifying a Subscriber

At this point, the primary Subscriber objects are created but the user's attributes are set to the defaults. You can use the following endpoints to modify settings and attributes:

  • /api/UserExtensionProvisioning
  • /api/SubscriberInboundAttributeProvisioning
  • /api/SubscriberOutboundAttributeProvisioning
  • /api/SubscriberVoiceMailAttributeProvisioning
  • Of course, you can also change the subscriber's password with /api/subscribersecuritychangePassword, or modify a class of service with /api/ClassOfServiceProvisioning.

We will not cover these attributes as they are the topic of other tutorials.

To modify the Subscriber data directly, you can send a PUT message to the endpoint /api/SubscriberProfileProvisioning/{id} with the following body:

{
    "firstName": "<first name, optional>",
    "lastName": "<last name, optional>",
    "phoneNumber": "<telephone number, optional>",
    "email": "<email address, optional>",
    "oldPassword": "<current password, mandatory>",
    "newPassword": "<the new password, optional>"
}

In this data, a missing field indicate you want to preserve the existing value. In the following example, we will change the email address of a Subscriber.

public void changeEmail(String id, String email) throws Exception {
    ObjectNode subscriberNode = mapper.createObjectNode();
    subscriberNode.set("id", new TextNode(id));
    subscriberNode.set("email", new TextNode(email));

    HttpEntity<String> request = createBuzzEntity(mapper.writeValueAsString(subscriberNode));

    try {
        ResponseEntity<String> response = rest.exchange(server + "api/SubscriberProvisioning/" + id, HttpMethod.PUT, request, String.class);
        if (response.getStatusCode().is2xxSuccessful()) {
            return; //Success
        }
        throw new Exception("Error updating user: " + response.toString());
    } catch (HttpClientErrorException e) {
        throw new Exception("Error updating user: " + e.getStatusCode().toString());
    } catch (Exception e2) {
        throw new Exception("Problem updating user: " + e2.toString());
    }
}
function change_email(id, email, callback) {
    var subscriber = {
        "id": id,
        "email": email,
    };
    
    $.ajax({
        url: server+"api/SubscriberProvisioning/"+id,
        method: "PUT",
        headers: {
            "Authorization": "Bearer " + adminToken
        },
        data: subscriber,
        success: function(result) {
            callback(); //Success
        },
        error: function (xhr, status, errorThrown) {
            console.log("Error changing email. " + xhr.status + " " + xhr.text + " " + errorThrown);
        }
    });
}

Retrieving Subscribers

There are multiple ways to retrieve Subscriber information.

  • A complete Subscriber object is returned at the time of logging in
  • You can also retrieve it again by doing a GET on the /api/SubscriberProvisioning/{id} or /api/apiSubscribers/{id} endpoints
  • You can retrieve a full list of Subscribers for the same company as the currently logged in Subscriber by calling the endpoint /api/apiSubscribers

Let's retrieve a Subscriber by ID using the /api/SubscriberProvisioning/{id} endpoint:

public Subscriber getSubscriber(String id) throws Exception {
    HttpEntity<String> request = createBuzzEntity(null);

    try {
        ResponseEntity<String> response = rest.exchange(server + "api/SubscriberProvisioning/" + id, HttpMethod.GET, request, String.class);
        if (response.getStatusCode().is2xxSuccessful()) {
            JsonNode subscribersResponse = mapper.readValue(response.getBody(), JsonNode.class);
            return extractSubscriber((ObjectNode) subscribersResponse);
        }
        throw new Exception("Error getting users: " + response.toString());
    } catch (HttpClientErrorException e) {
        throw new Exception("Error getting users: " + e.getStatusCode().toString());
    } catch (Exception e2) {
        throw new Exception("Problem getting users: " + e2.toString());
    }
}

private Subscriber extractSubscriber(ObjectNode subscriberObject) {
    Subscriber subscriber = new Subscriber();
    subscriber.setId("" + subscriberObject.get("id").asInt());
    subscriber.setUsername(subscriberObject.get("username").asText());
    subscriber.setFirstName(subscriberObject.get("firstName").asText());
    subscriber.setLastName(subscriberObject.get("lastName").asText());
    subscriber.setEmail(subscriberObject.get("email").asText());
    subscriber.setUserExtension(subscriberObject.get("userExtension").asText());
    return subscriber;
}
function get_subscriber(id, callback) {
    $.ajax({
        url: server+"api/SubscriberProvisioning/" + id,
        method: "GET",
        headers: {
            "Authorization": "Bearer " + adminToken
        },
        success: function(result) {
            extract_subscriber(result, callback);
        },
        error: function (xhr, status, errorThrown) {
            console.log("Error getting subscribers. " + xhr.status + " " + xhr.text + " " + errorThrown);
        }
    });
}

function extract_subscriber(data, callback) {
    var subscriberObject = data.results;
    var subscriber = {
        "id": subscriberObject.id,
        "username": subscriberObject.userName,
        "firstName": subscriberObject.firstName,
        "lastName": subscriberObject.lastName,
        "email": subscriberObject.email,
        "userExtension": subscriberObject.userExtension
        };
    subscribers.push(subscriber);
    callback(subscriber);
}

The response body of this function has the following format:

"id": ,
    "firstName": "<first name>",
    "lastName": "<last name>",
    "phoneNumber": "<telephone number>",
    "email": "<email address>"
}

Deleting a Subscriber

To delete a Subscriber, send a DELETE message to the endpoint /api/SubscriberProvisioning/{id}

function deleteSubscriber(subscriberID) {
    $.ajax({
        url: server+"/api/SubscriberProvisioning/" + subscriberID,
        method: "DELETE",
        headers: {
            "Authorization": "Bearer " + adminToken
        },
        success: function(result) {
            extract_subscriber(result, callback);
        },
        error: function (xhr, status, errorThrown) {
            console.log("Error getting subscribers. " + xhr.status + " " + xhr.text + " " + errorThrown);
        }
    });
}