Expertsystemen

 

API Reference

The xxllnc Expertsystemen Server API serves to interact with decision trees published to xxllnc Expertsystemen Web Servers.

Where is the xxllnc Expertsystemen Server API currently used?

  • the client-side framework from xxllnc Expertsystemen (‘presentation layer’) and
  • third parties that need to create and manage cases, but let the actual user-interaction be handled by the client-side framework from xxllnc Expertsystemen.
  • automated regression tests of decision trees

The part of the API described here covers authentication, model querying, and session management: all things necessary to create an administrative shell or portal to the xxllnc Expertsystemen models and sessions.

The part of the API necessary to create a client framework for running an actual decision tree is left out of this documentation. The administrative part that is used for managing users, domains and model rights and changing server settings is not covered.

Getting started

The API responds in JSON format when fmt=json. Without this parameter, the API responds in HTML.

Requests to the API are in the form of HTTP parameter=value requests, since the API has its roots in talking to a browser (so no such things as a JSON body inside requests).

All requests that expect JSON as output need fmt=json as a parameter.

Note on documentation

Since the API responses can get quite large, the example responses focus on just the parts of interest to a certain section.

Since fmt=json is always required, it is considered understood and left out in the endpoint documentation for brevity’s sake. The example sections do list this parameter.

Quick start

In the example below, we will use the API to create a link with which a user without an account can create a case for a model that is publicly accessible.

let modelname = 'klantendag',
  signature = { method : "POST" },
  server = 'yourserver',
  template = 'bb.html';

fetch('/login?fmt=json', signature)
  .then(res => res.json())
  .then(({uniqueid, models}) => {
    let model = models.find((m)=>m.realmodelname === modelname);
    if (!model)
      throw `No such model: ${modelname}`;
    return `${server}/${template}?uniqueid=${uniqueid}&modelid=${model.modelid}`
  })
  .then(link => console.log(link))
  .catch(err => console.warn(err))

Analysis of the above code

First, one logs in without any credentials with /login?fmt=json.

The response is a JSON object, which lists all models that are publicly available in the models property.

We then search the model list for the model we are interested in.

If we do not find our model, we throw an error.

If we do, we create a link, using the uniqueid that is a top-level property of the returned JSON and the modelid property of the model we just found.

You see the link created uses the ‘template’ bb.html. This is the default HTML entry page for a xxllnc Expertsystemen Web Server. This HTML page may be variable if the server hosts multiple presentation layers.

NOTE the method in the request signature is set to POST here; the GET method would be equally valid though.

You can use this link as a regular link for the user to follow:

<a href="https://example.com/bb.html?uniqueid=UID&modelid=MID">link text</a>

… or as the src attribute of an <iframe> with a fallback link inside:

<iframe src="https://example.com/bb.html?uniqueid=UID&modelid=MID">
  <a href="https://example.com/bb.html?uniqueid=UID&modelid=MID">link text</a>
</iframe>

In this example, we will create a session for a user with an account.

let modelname = 'klantendag',
  server = 'yourserver',
  signature = { method : "POST" },
  template = 'bb.html',
  username = 'john',
  password = 'secret passphrase never to be guessed';

fetch(`/login?fmt=json&username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`, signature)
  .then(res => res.json())
  .then(({uniqueid, models}) => {
    let model = models.find((m) => m.realmodelname === modelname);
    if (!model)
      throw `No such model: ${modelname}`;
    return `${server}/${template}?uniqueid=${uniqueid}&modelid=${model.modelid}`
  })
  .then(link => console.log(link))
  .catch(err => console.warn(err))

Analysis of the above code

First, as with the anonymous example above, one logs in with /login?fmt=json. This time however, we do provide their credentials.

The response is a JSON object, which lists all models that are available for that user in the models property.

We then search the model list for the model we are interested in.

If we do not find our model, we throw an error.

If we do, we create a link, using the uniqueid that is a top-level property of the returned JSON and the modelid property of the model we just found.

You see the link created uses the ‘template’ bb.html. This is the default HTML entry page for a xxllnc Expertsystemen Web Server. This HTML page may be variable if the server hosts multiple presentation layers.

NOTE the method in the request signature is set to POST here; the GET method would be equally valid though.

You can use this link as a regular link for the user to follow:

<a href="https://example.com/bb.html?uniqueid=UID&modelid=MID">link text</a>

… or as the src attribute of an <iframe> with a fallback link inside:

<iframe src="https://example.com/bb.html?uniqueid=UID&modelid=MID">
  <a href="https://example.com/bb.html?uniqueid=UID&modelid=MID">link text</a>
</iframe>

Basic concepts used in this API

Before delving any further, let’s pause and look at the core concepts used inside this API.

Model

A model is a decision tree.

Models get created by authors using the desktop software xxllnc Expertsystemen Studio. These authors publish the models to a xxllnc Expertsystemen Web server.

This API takes it from there.

Case

A case is a specific run-through of a model (a decision tree), and contains all answered questions. A case may be (auto-)saved, listed, re-opened, copied, used as a template and, finally, deleted.

A case may last as long as a user session does, or remain on the server until explicitly deleted.

Unique ID

What other APIs call a session token.

Session ID

Id for a specific case.

Model name

modelname represents the model filename, as in modelname.mdl. It can be considered stable and your best bet to use for long-term reference.

Ready for some confusion? In the response to /menu this one is called realmodelname.

Model description

modeldescription represents what is actually the model description, i.e. a pretty, free-form name, configurable in the server.

This is prone to change at an author’s or administrator’s whim. Do not use it for long-time reference, unless maybe you are very close with the author or administrator.

Ready for some more confusion? In the response to /menu this is called modelname.

Model ID

The modelid is the id used in API communication to reference a model within a session. Chances are high that it may point to another model entirely when used with a different server, or to no model at all.

Logging in

The API uses anonymous login, user credentials or SSO solutions in order to create a session.

The use of SSO solutions is explained elsewhere.

Anonymously

Logging in anonymously:

# Logging in anonymously
curl "https://example.com/login?fmt=json"

The simplest case of logging in is to create an anonymous session.

With an anonymous session one can access publicly accessible decision trees (models).

HTTP Request

GET/POST https://example.com/login

Logging in with just /login will give you an anonymous session, ready to experiment

With user credentials

Logging in with user credentials:

# Provide user credentials
curl "https://example.com/login?fmt=json&password=secret&username=johndoe"

# Or with newer, leaner, API:
curl -d username=johndoe -d password=secret https://example.com/api/login

Make sure to replace secret and johndoe with your credentials.

Registered users may access all publicly accessible decision trees and decision trees they have published or to which they have explicitly been given access.

Registered users are also allowed to save and re-open sessions on the server and to change their account details. These are options that can be turned on or off on the Server.

HTTP Request

GET/POST https://example.com/login

Query Parameters

Parameter Default
password empty
username empty

Logging in with a /login?username=...&password=... will give you a session for a specific user

Skipping login

As a shortcut, the login step may be skipped, if what you want to do first is to create a new session for a known modelname or modelid.

After logging in

A successful login returns a uniqueid:

{ "uniqueid": "DTTAYWFVKQXIJWOKGGRLDHXVPPBBDI", ... }

After logging in, the API returns a JSON object with a temporary token called uniqueid.

The API expects this uniqueid as a parameter to all subsequent requests, and also returns it in all of its successful responses.

All requests other than login use a uniqueid parameter, e.g. uniqueid=UHIEHQUIWYWEIHUIWE.

Listing decision trees

Get list of available models

curl "https://example.com/menu?uniqueid=DTTAYWFVKQXIJWOKGGRLDHXVPPBBDI&fmt=json"

Sample response for /menu

{
  "models": [
    {
      "realmodelname": STRING,
      "modelname": STRING,
      "modelid": INT,
      "version": GUID,
      "yours": BOOL,
      "language": STRING,
      "authorname": STRING,
      "authorinfo": STRING,
      "modelinfo": STRING,
      "serverinstructions": STRING,
      "completed": true,
      "lastupdate": "/Date(INT)/",
      "company": STRING
    }
  ]
}

Once you have logged in and acquired the uniqueid, use /menu to get a list of available models.

realmodelname represents the model name, as in realmodelname.mdl.

modelname represents what is actually the model description, i.e. a pretty, free-form name, configurable in the server.

In all other parts of the API modelname refers to what is here called realmodelname. In those areas modeldescription refers what is here called modelname.

The modelid is the id used subsequently in API communication.

Use modelid to talk with the API about a model. Use realmodelname if you need to look up the model in this list, and use modelname to show to an end user.

yours means that the account used for authentication is the (only) one allowed to publish this model.

version is a GUID uniquely identifying the current version of the model. It is used for cache-busting otherwise equally named resources.

Working with cases

A case is a specific run-through of a model (a decision tree), and contains all answered questions. A case may be (auto-)saved, listed, re-opened, copied, used as a template and, finally, deleted.

Creating a new case

/open without a sessionid opens a new case

curl "https://example.com/open?modelid=M&fmt=json"
{
  "sessionid" : GUID,
  "casename"  : STRING // Human name to identify this session
}

To create a new case, call the endpoint /open

HTTP Request

GET/POST https://example.com/open

Query Parameters

Parameter Description Required
uniqueid empty if model is private
modelid model to create case for yes

Sending along custom parameters when creating a new case

It is possible to send custom parameters along with the request to create a new case. As long as you prefix your parameters to give it a ‘namespace’ and do not use bb: as a prefix, there is no change of colliding with internal parameters.

Custom parameters are any prefixed parameters you may want to make available to the model author.

You may for example choose my: as a prefix and use open?modelname=mymodel&my:param=3 as a request.

The model author then may pick up the parameter my:param with the function getparambyname('my:param').

The prefix bb: is a reserved prefix, i.e. do not use it, since it may collide with parameters used by (future versions of) this API

JavaScript example sending my:param along

You can test the following JavaScript after publishing the accompanying model to your own server. Remember to replace 'https://yourserver' with your own address.

let server = "https://yourserver",
  signature = { method : "POST" };

fetch("/login?fmt=json", signature)
.then(res => res.json())
.then(({uniqueid}) => fetch(`/menu?uniqueid=${uniqueid}&fmt=json`, signature))
.then(res => res.json())
.then(({uniqueid, models}) => {
  let model = models.find((m) => m.realmodelname === "customparam"),
      url = `/open?uniqueid=${uniqueid}&modelid=${model.modelid}&my:param=3&fmt=json`;
  return fetch(url, signature);
})
.then(res => res.json())
  .then(({uniqueid, modelid, sessionid}) => {
    return `${server}/bb.html?uniqueid=${uniqueid}&modelid=${modelid}&sessionid=${sessionid}`
  })
.then(link => console.log(link))

When following the link logged at the end of this code in a browser, the model will echo your custom parameter back to you as follows:

You entered: 3

Analysis of the above code

First, one logs in with /login?fmt=json. Credentials may be given here too (username=USERNAME&password=PASSWORD). Otherwise login will be anonymous.

Then, with the uniqueid, a request is done to /menu. This will list all models available to the logged in user in the models property.

We then search the model list for the model we are interested in.

After that, we perform a request to /open a new case on the server, with the uniqueid, the modelid we just got and our custom parameter my:param.

When that request returns, we have a running session that may access the parameter my:param with the Studio function getparambyname('my:param').

The last step creates a link, using the uniqueid, the modelid and the sessionid returned in the request that creates the new case. The sessionid uniquely identifies the case.

You see the link created uses bb.html. This is the default HTML entry page for a xxllnc Expertsystemen Web Server. This HTML page may be variable if the server hosts multiple presentation layers.

NOTE the method in the request signature is set to POST here; the GET method would be equally valid though.

Listing cases for a model

A generic call to /menu gives some general info about cases: how many there are, and whether or not the user is allowed to get a listing of all previous runs through a certain model (caselistingallowed below):

{
  "models": [
    {
      "modelid": INT,
      "selected": BOOL,
      "casecount": INT,
      "caselistingallowed": BOOL
    }
  ]
}

If case listing is allowed, we can fire the /menu request with the model in question:

curl "https://example.com/menu?modelid=MODELID&uniqueid=DTTAYWFVKQXIJWOKGGRLDHXVPPBBDI&fmt=json"

The server response will then contain an array of cases for the model in question, and also whether the user is supposed to

  • delete a case from this listing (showdeleteinmenu)
  • see the creation date for the case (showdatecreated)
  • obtain an audit trail from within this listing (showreport)
  • use cases as templates for new cases (showcopycase)
{
  "cases" : [
    {"name"      : STR,
     "dateenter" : TIME,
     "modelid"   : INT,
     "sessionid" : STR
    }
  ],
  "showdeleteinmenu" : true | false,
  "showdatecreated"  : true | false,
  "showreport"       : true | false,
  "showcopycase"     : true | false
}