Alemba API Programmers’ Guide

The RESTful API is built on top of a schema that encompasses the primary data entities in the ASM System.

API structure

Additionally, a number of logical entities have been added that allow similar entities to be grouped together. The hierarchical structure allows sub-entities to inherit common properties, allowing for consistent meaning and behavior.

The structure is indicated in the Alemba API Entity Details Pane in the API explorer.

Languages

The API is platform independent - it can be accessed from any kind of web client, using a range of languages. The API Explorer provides language neutral examples of the structures sent and received as part of a web request/response, for each action, for each entity.

Authentication

The API uses standard oAuth 2.0 authentication, via /alemba.web/oauth/login.

The authentication workflow issues a short-lived (10 minutes) Access Token and a longer-lived (24 hours) Refresh Token on Login. The Access Token is used for authentication on the REST API. The Refresh Token is used to renew the Access Token and therefore maintain the session. The Refresh Token allocates a ASM Core Session and consumes a licence.

The process is illustrated as follows, flowing from the top downwards.

When the Refresh Token is used to renew an Access Token, a new Refresh Token is also issued, and the ASM Core Session is extended. The used Refresh Token becomes invalid and should be discarded. Clients should store the new Refresh Token for subsequent usage. If the ASM Core Session expires or is removed, the Refresh Token will become invalid. If the Refresh Token expires, the ASM Core Session is terminated.

The single use Refresh Token and short lived Access Token ensures that compromised tokens quickly become invalid - protecting the security of individual users, and preventing unauthorized Access Token reuse.

The Access Token must be presented in the Authorization header of the HTTP request:

Authorization: Bearer <Access Token>

Note that the Authorization service supports the following oAuth 2.0 Grant Types:

  • authorization_code – Authenticate using Single Sign On (SSO), for Windows authentication and SAML

  • client_credentials – Authenticate using integrated security for anonymous access to the API, not full access

  • password – Authenticate using a username and password

  • refresh_token – Authenticate using an existing Refresh Token

Logging in

UserName and Password

Below is an example of the code used to login with a username and password.

For more information on client ids, see Configuring Authentication for the Alemba API.

function passwordLogin() {
var args = {
client_id: "clientid",
grant_type: "password",
scope: "scope",
password: "username",
username: "password"
var xhr = $.ajax({
url: 'alemba.web/oauth/login',
type: "POST",
data: args,
contentType: 'application/x-www-form-urlencoded'
});
xhr.done(onGrantSuccess).fail(function (err) { return onGrantFailure(err, "password"); });
return xhr;
}

Access Token

Or, to refresh your access token:

function refreshTokenLogin(refreshToken) {
var args = {
client_id: "clientid",grant_type: "refresh_token",scope: "scope",refresh_token: refreshToken
};
var xhr = $.ajax({
url: 'alemba.web/oauth/login',
type: 'POST',
data: args,contentType: 'application/x-www-form-urlencoded'
});
xhr.done(onGrantSuccess).fail(function (err) {
if (err.status == 401) {
}
else {
onGrantFailure(err, "refresh_token");
}
});
return xhr;
}

scope should be set to session-type:Analyst or session-type:User. It is case sensitive.

Login Responses

For successful Logins the response will be:

{
expires_in: number, // number of seconds until access_token expiry
access_token: string, // token used for data access
refresh_token: string, // token used for access_token renewal
scope: string, // The actual scope of the token
}​

Note that there is a new refresh_token in the response. The old one will no longer work and must be discarded. It is acceptable to renew your access_token before it is due to expire, but you must not do so with every request.

Login Response Errors

If the authorization request is not successful, clients can expect to receive a suitable HTTP response code and JSON formatted data containing an error code.

The response data may also include error_description, which gives the developer a clue as to the precise cause of the failure.


{
error: string, // one of the oauth 2.0 error codes
error_description: string, // a description of the error if applicable
}

In these cases, the response is deliberately vague so as to protect the integrity of the authorization server.

HTTP Status Code

Error Code

Reasons

401

invalid_client

The client_id is incorrect or the client is not enabled

​400

invalid_grant

The credentials are not correct, the user is not allowed to login

If you receive a 401 response, it is because your access token has expired, and you must refresh it using the refresh token. If you receive a 401 response when using a refresh token, you must login again with username and password.

Logging out

You can logout as follows:

function logout() {
var deferred = $.Deferred();
var args = {
token: exports.grant.refresh_token
};
var xhr = $.ajax({
url: 'alemba.web/oauth/logout',
type: "POST",
data: args,
contentType: 'application/x-www-form-urlencoded',
headers: {
"Authorization": "Bearer " + exports.grant.access_token
}
});
xhr.done(function () {
//Logout success
deferred.resolve();
}).fail(function (err) {
switch (err.status) {
case 404:
deferred.resolve();
break;
case 400:
deferred.reject("Invalid token");
break;
case 401:
refreshTokenLogin(exports.grant.refresh_token).done(function () {
//We've successfully refreshed the access token
//Now we can try to invalidate the refresh token again
logout().done(function () {
deferred.resolve();
}).fail(function (err) {
//Logout is still not working.
//The session may still be active and may still be consuming a license.
//The session can be terminated by an administrator from logon control, so this error should be reported
deferred.reject(err);
});
}).fail(function () {
//If you cant log in its because the refresh token has expired.
//This can be considered a successful logout
deferred.resolve();
});
break;
case 403: //Not allowed, probably because the refresh token is not related to the access token
default:
deferred.reject(err.responseJSON.Message);
break;
}
});
return deferred.promise();
}​

Logout Responses

Logout will give one of the following responses:

ResponseMeaning

200

Logged out successfully

400

Token invalid or missing

401

Not authorized because it’s not been possible to validate your ownership of the refresh token. In this case you must refresh the access token and try again

403

That refresh token doesn’t belong to you

404

The refresh token is valid but has already been removed. Maybe you logged in elsewhere

Base RESTful API methods

The following HTTP verbs are used by the API to perform the listed actions.

CreatePOST

Read

GET

Update

PUT

Delete

DELETE

The API supports delete where it is appropriate, including delete for attachments and person images (avatars)

A Programmatically Discoverable API

The API can be used to return information about itself, in the form of hypermedia – machine readable descriptions and links to further similar information, allowing a developer to progressively explore the breadth and depth of the API for themselves. What is more, these descriptions are used by the API itself, guaranteeing that this “documentation” is always current.

Root Level Information on API Scope

For example, to discover root level information on the scope of the API, invoke

http://localhost/alemba.api/api?$metadata&$options

Note that this response will also be returned for any request which does not specify a resource, for example:

GET http://localhost/alemba.api
GET http://localhost/alemba.api/api
GET http://localhost/alemba.api/api/v1

​Metadata

The metadata will be returned in JSON format and will contain links to the metadata for top level entities exposed by the API, for example:

{
"_links": {
"Approval": [
{
"_self": "api:v1/approval/$metadata"
}
],
"Call": [
{
"_self": "api:v1/call/$metadata"
}
],
...
},
"description": "A description of the API, the links at this level and the ResourceDescriptor response type."
}

​All metadata responses may include the following properties:

children

An array of descendant types for the current response

Call may list children including Incident

The metadata for each child only includes "_self"

description

A description of the current metadata response

name

The name of the entity that is the subject of the metadata response

properties

An array of property descriptions for the subject entity. These properties provide the minimum information required to understand the data model and basic constraints.

Each entry in the array may include the following properties:

name

The name of the entity property

displayName

The default display name of the property. This could be used in table column headers or form fields

type

A description of the data type of this property. The type property is a complex type which has the following properties:

displayTypes

The suggested display types for this property

dataType

The type of data this property represents

class

The kind of property

description

A description of the purpose of the property

usage

Internal when the property is used for internal business logic, otherwise Public

isKey

The property represents the unique identifier (primary key) for the entity

noSearch

When true, this property is not supported in searches

defaultValue

An indication of the default value for this property

length

The maximum length of this field. Text fields only

uppercase

When true, this indicates that the Text value will be capitalized.

Non capitalized input may cause validation errors in a future release.

status

Indicates the current release status of the subject entity. "Alpha", "Beta", "GA"

_actions

A hash map of action name and an array of action metadata

_context

A reference to the entity metadata of a record or of the metadata of the parent of the subject entity

_links

A hash map of name and an array of link metadata. These links must be requested using the http verb GET

_self

A reference to the current response link and action metadata will always include a link to _self and will often include an "href" property

displayTypes

The suggested display types for this property

dataType

The type of data this property represents

class

The kind of property

description

A description of the purpose of the property

usage

Internal when the property is used for internal business logic, otherwise Public

isKey

The property represents the unique identifier (primary key) for the entity

noSearch

When true, this property is not supported in searches

defaultValue

An indication of the default value for this property

length

The maximum length of this field. Text fields only

uppercase

When true, this indicates that the Text value will be capitalized.

Non capitalized input may cause validation errors in a future release.

status

Indicates the current release status of the subject entity. "Alpha", "Beta", "GA"

_actions

A hash map of action name and an array of action metadata

_context

A reference to the entity metadata of a record or of the metadata of the parent of the subject entity

_links

A hash map of name and an array of link metadata. These links must be requested using the http verb GET

_self

A reference to the current response link and action metadata will always include a link to _self and will often include an "href" property

HREF Templated

The "href" may be templated, as denoted by the syntax {id}. The templated values must be replaced by the client.

{id} in "api:v1/call/{id}" should be replaced with the Ref of a call.

{id} always indicates the primary key field for the target entity. All other entity properties may also be referenced in the template. e.g. {Partition} where Partition is the name of the property in the entity referenced by "_context".

"_context" and "_self" will always define a medialink to an API resource. For brevity, the links are prefixed with "api:"

Clients should replace this prefix with the actual API base url

Given an api base url of http://web-server/core-system/alemba.api/api, api:v1/call/$metadata should be interpreted as http://web-server/core-system/alemba.api/api/v1/call/$metadata

All API medialinks can be invoked using the $options suffix.

These links can then be followed to explore further details about the entity, what it is and what it can do.

All API entities support simple and predictable RESTful actions.

"api:v1/call" supports GET for searching and POST for create
"api:v1/call/1​" supports PUT for update and GET to get that instance of a call.

Discovering the entity actions

Many entities also support more complex actions, such as forward.

These actions are typically accessed using "api:v1/call/1/forward"

Details of the required inputs and supported HTTP method can be found in the metadata for that action.

For example the Call Create action metadata can be accessed with

http://localhost/alemba.api/api/call/$create?$options

This metadata gives you info about the Create action, including a list of mandatory and optional properties and parameters. $create in the above example can be substituted with any action that is supported by the Call entity.

What is more, you can use the same principles not just as an aid to programming, but at runtime too. The metadata that is returned about a specific object contains links for the list of actions for that object instance in its current state. (An exception is the Search action, which will only return links to the relevant Get action for each record.) For example you will only see the Reopen action if a Call is in a closed state.

Searching

The Rest API supports expressive searching of most entities

The query parameters and syntax applies to all Search actions for all entities.​

To start, it is possible to simple request a resource using HTTP GET.

​GET api:v1/call

This will return a reference to all accessible calls in JSON format

{
"results": [
{
"_context": "api:v1/call/$metadata",
"_self": "api:v1/call/3"
},
{
"_context": "api:v1/incident/$metadata",
"_self": "api:v1/incident/4"
}
],
"_self": "api:v1/call?$top=2147483647",
"__count": 275
}

The response contains the following properties

  • "results": This is an array of search results. Each result will always include a _context url (so you know what it is) and a _self url (so you know how to get that item).

  • ​"_self": This is a url refering to the current response​

Notice that the _self url includes a query string parameter

$top=2147483647​

The search actually ran without any row limit. It will try to return every row (accessible to the current session).

This query string parameter is added to the response as a hint.

Paging

The Rest API supports flexible paging of search results.

$top

$top accepts a positive signed integer value (Int32) and is used to define a row count.

// SGET api:v1/call​?$top=30

This will limit the response to the top 30 records. If you don’t explicitly specify the row count, then it will default to $top=100.

$orderby

$orderby accepts a comma separated list of property names and optional sort direction (see $select for more details on property names) and is used to define the order of the results​

GET api:v1/call​?$orderby=Ref​

​This will return all Calls ordered by Ref

By default the orderby clause will be applied in ascending order, but this can be overridden

//GET api:v1/call​?$orderby=Ref​ asc

​​​This will return all Calls ordered by Ref in ascending order

GET api:v1/call​?$orderby=Ref​ desc

This will return all Calls ordered by Ref​ in descending order

$skip

$skip accepts a positive signed integer value (Int32) and is used to define a number of rows to skip

GET api:v1/call​?$top=30

This will limit the response to the top 30 records

and GET api:v1/call​?$skip=30&$top=30

This will skip the first 30 records and return the next 30 (ie rows 31 to 60)

These parameters can be combined (in any order) to control page size and contents

GET api:v1/call​?$top=30&$skip=30&$orderby=Ref​ desc

This will return the top 30 Calls ordered by Ref in descending order

$count

The API also supports counting, which can be used to calculate the total number of pages

$count must be set to true and is used to instruct the api to return a count only

GET api:v1/call​?$count=true

This will return a text/plain response containing a number which represents the total number of rows.

$inlinecount

Alternatively, $inlinecount can be used to have the count be returned with a set of results

GET api:v1/call​?$top=30&$inlinecount=true
{
"results": [
{
"_context": "api:v1/call/$metadata",
"_self": "api:v1/call/3"
},
{
"_context": "api:v1/incident/$metadata",
"_self": "api:v1/incident/4"
}
],
"_self": "api:v1/call?$top=2147483647",
"__count": 275
}

Note that "__count" is included in the response body.​

​Paging Best Practice

Use of these paging features is critical for individual client and application wide performance and should be utilized by all API consumers for all searches.

Even where it is assumed that there are only a handful of records.

Using $inlinecount results in two executions of the database query; one to get the count; and one to get the result set. Therefore it is important that this parameter is not added to every request.​

Selecting columns​

The Rest API supports configurable column selection in search results.

$select

$select accepts a comma separated list of property paths to include in the search results

// SGET api:v1/call​?$select=Ref,Description​

​This instructs the API to include Ref and Description in the search results​

{
"results": [
{
"Ref": 3,
"Description": "Microsoft Windows 2000 needs to be installed across all client machines.",
"_context": "api:v1/call/$metadata",
"_self": "api:v1/call/3"
},
{
"Ref": 4,
"Description": "Cannot access intranet.",
"_context": "api:v1/incident/$metadata",
"_self": "api:v1/incident/4"
}
],
"_self": "api:v1/call?$select=Ref,Description&$top=2147483647"
}

Note that as well as retrieving properties from the entity, you can directly retrieve properties from related entities. This is extremely powerful.

In the Classic WCF API, you would first need to get the raw foreign key ref from the main entity and then do a lookup on the related one, or you would have to write your own custom query, including joining objects and ensuring that the related object is not locked.

The RESTful API is built on an underlying schema that takes care of these complexities and allows you to get the data you need in the simplest possible way.

In fact you can traverse the entity relationships as far as you need, so getting a call priority’s name can be achieved with:

GET api:v1/call​?$select=Ref,Description​​,Priority.Name
{
"Ref": 4,
"Description": "Cannot access intranet."​,
"Priority": {
"Name": "Priority 3",
"_context": "api:v1/call-priority/$metadata",
"_self": "api:v1/call-priority/3"
},
"_context": "api:v1/incident/$metadata",
"_self": "api:v1/incident/4"
}

Linked Fields from Linked Relations

Notice that the linked entity includes metadata links. This also applies to linked fields from linked relations.

GET api:v1/call​?$select=Ref,Description​​,Service.Location.Name

​Returns

{
"Ref": 4,
"Description": "Cannot access intranet.",
"Service": {
"Location": {
"Name": "San Francisco",
"_context": "api:v1/location/$metadata",
"_self": "api:v1/location/9"
},
"_context": "api:v1/service/$metadata",
"_self": "api:v1/service/1"
},
"_context": "api:v1/incident/$metadata",
"_self": "api:v1/incident/4"
}

Property paths in $select can be aliased using a prefix to simplify the response

GET api:v1/call​?$select=Ref,Description​​,LocationName:Service.Location.Name​
{
"Ref": 4,
"Description": "Cannot access intranet.",
"LocationName": "San Francisco",
"_context": "api:v1/incident/$metadata",
"_self": "api:v1/incident/4"
}

$select will also accept named Extension Augmenters

GET api:v1/call​?$select=Ref,@AssignmentState

This will add the computed assignment state to the response ​​

{
"Ref": 4,
"AssignmentState": "Assigned to Me",
"_context": "api:v1/incident/$metadata",
"_self": "api:v1/incident/4"
}

To help with orientation during development, $select will also accept *. This will return all properties for the entity.

$select=*

Best Practice $select=*

$select=* is only intended for development and should not be used in production.

Selecting values from extension fields is supported, but carries a significant overhead and so should be avoided.

Filtering

​The Rest API supports expressive filtering of search results.

$filter

$filter accepts a C# LINQ style predicate which is translated to parameterized SQL and applied as a search filter

GET api:v1/call​?$filter=Priority==1

This would return all accessible calls where the Priority is equal to 1

Equality Operators and Methods

All data types support basic equality comparison

==

Is equal to

=

Is equal to

!=

Not equal to

Binary data types support basic equality comparison but in practice, this can only be used to compare the property value with null.

GET api:v1/call/1/attachment​?$filter=BinaryData!=null

Boolean data types

Boolean data types support basic equality operators

When comparing with true or false, the right hand side of a boolean property equality expression can be omitted.

Binary equality expressions can also be negated with !

GET api:v1/person​?$filter=IsLoggedIn==true

is equivalent to

GET api:v1/person​?$filter=IsLoggedIn

or

GET api:v1/person​?$filter=IsLoggedIn==false

is equivalent to

GET api:v1/person​?$filter=!IsLoggedIn

DateTime data type filters

DateTime data type filters must be used with one of the applicable augmenters

GET api:v1/call​?$filter=CreatedDate>@DateTime(2017-01-01T00:00:00.000+1)

This will return all Calls which were created after Midnight on January 1st 2017 (UTC+1)

Note that the date value must be expressed in ISO8601 format

@Now

The @Now augmenter can be used to compare a date value with the current time.

GET api:v1/call​?$filter=CreatedDate==@Now

This is most useful where a query will be designed and then subsequently reused.

@NowOffset

The @NowOffset augmenter can be used to compare a date value with the current time and an offset expressed in days hours and minutes

GET api:v1/call​?$filter=CreatedDate>@NowOffset(0,-1,-30)

This will return Calls created in the last 0 days, 1 hours and 30 minutes (in the last hour and a half).

As with all filters, the expressions can be combined using logical And (&&) and Or (||) operators

GET api:v1/call​?$filter=CreatedDate>=@DateTime(2017-01-01T00:00:00.000+1)&&CreatedDate<=@NowOffset(0,0,-30)​​

This will return Calls created between Midnight on January 1st 2017 (UTC+1)​ and half an hour ago.

Dates do not have to be defined in UTC format, but MUST include the timezone.

If no timezone is supplied, dates are assumed to be in local server time.

Text and RichText data types

Text and RichText data types support basic equality and can also be used with some string comparison methods

Contains

The Contains method can be used to match records where a string property contains a word or phrase

GET api:v1/call​?$filter=ShortDescription.Contains("email")

StartsWith

The StartsWith method can be used to match records where a string property starts with a word or phrase​

GET api:v1/call​?$filter=ShortDescription.StartsWith("email")

EndsWith

​The EndsWith method can be used to match records where a string property ends with a word or phrase​​

GET api:v1/call​?$filter=ShortDescription.EndsWith("email")

As with all filters, the expressions can be combined using logical And (&&) and Or (||) operators​

GET api:v1/call​?$filter=hortDescription.Contains("email")||ShortDescription.StartsWith("outlook")

​Number Data Types

Number data types support more complex equality comparison operators

>

Greater than

<

Less than

>=

Greater than or equal to

<=

Less than or equal to

GET api:v1/call​?$filter=Number1>=3

Combining Expressions

Filter expressions can be combined using logical And (&&) and Or (||) operators​​ and can be grouped using ​parentheses ( and )

GET api:v1/call​?$filter=((Number1>=3​||Number2==1)&&(Priority==3||Priority==1))||(CreatedDate>@NowOffset(0,0,-30)​​​&&@IsAssignedToMe)

​Note that query string parameter values must be url encoded

GET api:v1/call​?$filter=((Number1%3E%3D3%E2%80%8B%7C%7CNumber2%3D%3D1)%26%26(Priority%3D%3D3%7C%7CPriority%3D%3D1))%7C%7C(CreatedDate%3E%40NowOffset(0,0,-30)%E2%80%8B%E2%80%8B%E2%80%8B%26%26%40IsAssignedToMe)

Sorting

$order

The API supports sorting using $order, and you can use several qualifiers, as illustrated in the following examples.

$orderby=FirstName would sort the result by the FirstName property in descending (a-z) order.

$orderby=FirstName desc would also sort the result by the FirstName property in descending (a-z) order.

$orderby=FirstName asc would sort the result by the FirstName property in ascending (z-a) order.

It is also possible to specify multiple order properties:

$orderby=FirstName, LastName asc would sort the result by the FirstName property in descending (a-z) order and then sort by the LastName property in ascending (z-a) order, e.g. Andrew Anderson, John Smith, John Jones

Joining

$leftjoin & $innerJoin

You can use $leftJoin and $innerJoin to link to entities that are not already linked as part of the schema definition. This is the case with entities that have multi-column keys e.g. CallHistory, which has a composite key of Ref and LastHistoryOrder. Once you create such a join, you will want to refer to properties of that joined entity, so part of the definition of the join is an alias for that join, in the format

Alias:TargetEntity(TargetProperty==SourceProperty)

… where the clause in brackets can occur multiple times. For example:

&$leftJoin=LastAction:CallHistory(TicketId==Ref && Order==LastHistoryOrder)

You can then use the alias in your select clause just as if it were a property of the entity with a Data Type that is the target entity, e.g. LastAction.Description.

Note that this technique should be used sparingly, because as with extension fields the relationship is not indexed and so may result in reduced performance.

Case sensitivity

Everything after any of the $ functions above is case sensitive.