NAV
shell nodejs

Introduction

Lumian gives you the tools to develop high-quality unified telecom applications. The REST API interface provides a simple way for external application to talk to Lumian by making HTTP requests.

API Basics

Give it a REST

Crossbar is the REST API server, from which developers can build applications that configure Lumian's myriad services and functionalities.

Almost most of the time requests and responses for Crossbar endpoint are in JSON format, unless other format are expected for specific endpoint which you can find in the documentation.

Crossbar REST API is based on open standards like JSON-Schema (for data validation), you can use any web developer language to work with the API. Although for quick access to API tools like cURL (to work with HTTP protocol from terminal) and Postman (A GUI application to work with HTTP APIs) are come in handy.

A non-exhaustive list of all the things you can do with REST API:

Getting Started

After on-boarding is completed an account with default setting is setup and ready to use for you.

Prerequisites

Before you begin you need to know the main URL path to the API which is usually provided to you by e-mail during on-boarding process. If you don’t have this URL you can ask a your reseller salesperson to give you the URL.

Crossbar API requires the request to be authenticated, so you have to first get an authentication token before making any HTTP request to the API. The are various way to get this authentication token, you can learn more about them in Authenticate your REST requests.

Accessing to REST API resources

Let's assume we want to get our own account settings. For doing this we can simply use this cURL command:

$ curl -x GET \
     -H "X-Auth-Token: {AUTH_TOKEN}" \
     'http://crossbar_server.com:8000/v2/accounts/{ACCOUNT_ID}'

Here the explanation of the command:

  • -x GET is telling cURL to perform a HTTP GET request.
  • -H "X-Auth-Token: {AUTH_TOKEN}" is adding a HTTP header to the request. Here we add X-Auth-Token header required by Crossbar for authentication. {AUTH_TOKEN} is your authentication token.
  • http://... part is telling cURL to which URL make the request.
  • crossbar_server.com/8000 is the main Crossbar API URL.
  • v2 is the API version.
  • accounts is the name of the resource we want to have access.
  • {ACCOUNT_ID} is a specific instance of this resource we want. Since we want to get our account information (we are using accounts resource here) we give our own account ID.

API resources are available at below location:

/{VERSION}/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}

Here the explanation:

Resources

There are two parts to how a request is routed in Crossbar: the REST endpoint and the resource ID. Let's break down a common URI and see how Crossbar figures out what is an endpoint and what is a resource ID.

Given a URI of /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}:

  1. First, strip the version off the URI:
    • Version: v2
    • URI Remaining: /accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}
  2. See if the next token is a REST endpoint module. It is, so track the module for later routing:
    • Version: v2
    • Modules: {accounts: []}
    • URI Remaining: /{ACCOUNT_ID}/devices/{DEVICE_ID}
  3. See if the next token is a REST endpoint module. It is not, so add the token to the last module's data:
    • Version: v2
    • Modules: {accounts: [{ACCOUNT_ID}]}
    • URI Remaining: /devices/{DEVICE_ID}
  4. Repeat parsing. devices is a REST endpoint:
    • Version: v2
    • Modules: {accounts: [{ACCOUNT_ID}], devices: []}
    • Remaining URI: /{DEVICE_ID}
  5. Repeat parsing. {DEVICE_ID} is an argument:
    • Version: v2
    • Modules: {accounts: [{ACCOUNT_ID}], devices: [{DEVICE_ID}]}

So we have a request to account {ACCOUNT_ID} to do something with a device {DEVICE_ID}.

HTTP Verbs

The HTTP verb will determine the class of actions to take against the resource. Generically speaking, the verbs map thusly:

PATCH

Some resources are supporting the PATCH verb, allowing partial updates instead of requiring the request to include the full version of the document. /users/{USER_ID}, for instance, supports PATCH:

Sample Request:

curl -v -X PATCH \
   -H "Content-Type: application/json" \
   -H "X-Auth-Token: {AUTH_TOKEN}" \
   'http://api.lumian.net:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}' \
   -d '{"data":{"vm_to_email_enabled":true}}'
import axios from 'axios';

const response = await axios.patch(
  'http://api.lumian.net:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}',
  {
    'data': {
      'vm_to_email_enabled': true
    }
  },
  {
    headers: {
      'Content-Type': 'application/json',
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

This cURL request will patch the user's document and set vm_to_email_enabled to true. All normal validation will occur after patching the document; this also means clients can PATCH documents with their own data only.

If a resource does not support PATCH yet, clients can expect to receive a 405 Method Not Allowed error.

Tunneling the HTTP Verb

Some clients do not support the full range of HTTP verbs, and are typically limited to GET and POST. To access the functionalities of PUT and DELETE, you can tunnel the verb in a POST in a couple of ways:

  1. As part of the request envelope: {"data":{...}, "verb":"PUT"}
  2. As a query string parameter: /v2/accounts/{ACCOUNT_ID}/resources?verb=PUT

Tunneling the Accept Header

Some clients do not support the ability to set the Accept header in the request, meaning they will not necessarily receive the response in the format they wish. Clients can append accept=text/html to the request body or query string to indicate they'd like the response processed as if the Accept header was text/html.

!!! note accept=csv is retained for backwards-compatibility but it is encouraged to use a proper media type going forward.

Request Envelope

When issuing a PUT, POST or PUT, a request body is needed. When submitting a JSON (the most common body), Crossbar expects a request envelope with a few bits of metadata:

Sample Request Envelope:

Sample Response:

{
    "data": {
        "foo": "bar"
    },
    "auth_token": "{AUTH_TOKEN}",
    "verb": "delete"
}

Request Data

When using PATCH to edit entities, if you want to remove a field from the entity, set it to null:

Sample Response:

{
    "data": {
        "update":"this",
        "exists": null
    }
}

This request would set update to this and would remove exists from the entity.

Response Envelope

When receiving JSON responses, clients will receive the response in an envelope. The response includes some duplicated data from the HTTP Response headers, since some clients do not have access to those headers.

Sample Response Envelope:

Sample Response:

{
    "data": {
        "the": "response",
        "data": "is here"
    },
    "auth_token": "{AUTH_TOKEN}",
    "status": "success",
    "request_id": "{REQUEST_ID}"
}

Pagination

All listing APIs in v2 will be paginated by default (v1 will operate as before without pagination).

Let's take a look at the CDRs API to see how to interpret pagination.

CDR Pagination

We start with the typical CDR request for a listing of CDRs:

Sample Request:

curl -v \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    http://{SERVER_URL}:8000/v2/accounts/{ACCOUNT_ID}/cdrs
import axios from 'axios';

const response = await axios.get('http://{SERVER_URL}:8000/v2/accounts/{ACCOUNT_ID}/cdrs', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'application/json'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {CDR_OBJECT},
        {CDR_OBJECT},
        ...
    ],
    "next_start_key": "g2wAAAACbgUAvn1W1A5tAAAACDk4MDE2ODAwag",
    "page_size": 25,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "start_key": "g2wAAAACbgUAb0ZX1A5oAWpq",
    "status": "success"
}

The pagination response keys are next_start_key, page_size, and start_key.

Assuming no changes are made to the underlying documents, start_key will get you this page of results, and next_start_key will give you a pointer to the next page (imagine a linked-list).

Encoded Start Keys (Lumian 4.2+ Only)

As you can see from the response above, both the start_key and next_start_key are encoded as URL-safe Base64 strings of their Erlang term representation. A couple character substitutions (_ for / and _ for +) and one character removal (=) ensures a string that plays nice in URLs.

In practice, the client should treat these keys as opaque and supply them as-is in future requests.

Requesting next page

Using the next_start_key value, let's request the next page of CDRs:

Sample Request:

curl -v \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    http://{SERVER_URL}:8000/v2/accounts/{ACCOUNT_ID}/cdrs?start_key=g2wAAAACbgUAb0ZX1A5oAWpq
import axios from 'axios';

const response = await axios.get('http://{SERVER_URL}:8000/v2/accounts/{ACCOUNT_ID}/cdrs', {
  params: {
    'start_key': 'g2wAAAACbgUAb0ZX1A5oAWpq'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'application/json'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {CDR_OBJECT},
        {CDR_OBJECT},
        ...
    ],
    "next_start_key": "g2wAAAACbgUAbyYO1A5tAAAACDYwMTIzYjdiag",
    "page_size": 25,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "start_key": "g2wAAAACbgUAb0ZX1A5oAWpq",
    "status": "success"
}

Observe now that start_key is the requested start_key and next_start_key points to the start of the next page of results.

!!! note If next_start_key is missing from the response envelope, the response represents the last page of results.

You can also choose to receive pages in bigger or smaller increments by specifying page_size on the request. Do take care, as the next_start_key will probably vary if you use the same start_key but differing page_size values.

Setting Page Size

By default, API requests have a page size of 50 results. This value is customizable by system administrator in the crossbar.maximum_range system config setting.

For individual API request, you can also include a page_size query string parameter. For example: http://{SERVER}:8000/v2/{API_URL}?page_size=25.

Setting sorting order

By default, Crossbar returns the results in descending order. To get results in ascending order either set ascending=true (Lumian 4.2+ only) or descending=false in the request query string.

!!! note The field used to sort the individual API results depends on the internal implementation of the API endpoint and is not controllable by the client.

Disabling Pagination

If you want to disable pagination for a request, simply include paginate=false on the query string.

Protecting from (un)intentional abuse

Since pagination can be turned off by a client-supplied query string parameter, it is important that Lumian still protect itself from overly large datasets being loaded. Examples seen include large CDR listings, call recording listings, and ledger listings.

Therefore, during a non-paginated request, Lumian monitors memory consumption of the handling server process and will abort the request if the processing is exceeding a high watermark setting (configured by the system operator). The client can expect to receive an HTTP "416 Range Not Satisfiable" error as a result of exceeding the limit.

Chunked Response

Starting with Lumian 4.2, most of the summary API endpoints can send chunked responses. Some known APIs, which tend to have larger datasets, are chunked by default (e.g. /cdrs/interaction and /ledgers/{LEDGER}).

The query string parameter is_chunked (boolean value) can be used to enable or disable chunking per-request.

To set the default chunk size, you can use chunk_size in the query string. Default value is 50.

Pretty Printing

If needed, the JSON response to be pretty printed, the server can can do so.

Include pretty printing inside the header.

Sample Request:

curl -v \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -H "X-Pretty-Print:true" \
    http://{SERVER_URL}:8000/v2/accounts/{ACCOUNT_ID}/
import axios from 'axios';

const response = await axios.get('http://{SERVER_URL}:8000/v2/accounts/{ACCOUNT_ID}/', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'application/json',
    'X-Pretty-Print': 'true'
  }
});

If the client cannot use headers the options can be included inside the URI.

Sample Request:

curl -v \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    http://{SERVER_URL}:8000/v2/accounts/{ACCOUNT_ID}?pretty_print=true
import axios from 'axios';

const response = await axios.get('http://{SERVER_URL}:8000/v2/accounts/{ACCOUNT_ID}', {
  params: {
    'pretty_print': 'true'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'application/json'
  }
});

Requesting a range of binary data

It is useful to be able to get just a section of a file when streaming or resuming a download. This can be accomplished with the range header, e.g.:

Sample Request:

curl -v \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -H "Accept: audio/mpeg" \
    -H "Range: bytes={START_BYTE}-{END_BYTE}" \
    http://{SERVER_URL}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VMBOX_ID}/messages/{MESSAGE_ID}/raw
import axios from 'axios';

const response = await axios.get('http://{SERVER_URL}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VMBOX_ID}/messages/{MESSAGE_ID}/raw', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'application/json',
    'Accept': 'audio/mpeg',
    'Range': 'bytes={START_BYTE}-{END_BYTE}'
  }
});

Requesting data in CSV format

In some cases (e.g. CDR) its possible to request data in CSV format You must define the Content-type in the header you can define the file name in the request header or URL (Optional)

Sample Request:

curl -v -X GET \
    -H "Accept: text/csv" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "X-File-Name: {FILE_NAME}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs', {
  headers: {
    'Accept': 'text/csv',
    'X-Auth-Token': '{AUTH_TOKEN}',
    'X-File-Name': '{FILE_NAME}'
  }
});

or

Sample Request:

curl -v -X GET \
    -H "Accept: text/csv" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs?file_name={FILE_NAME}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs?file_name={FILE_NAME}', {
  headers: {
    'Accept': 'text/csv',
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

About

About About

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/about

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/about
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/about', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "erlang_version": "19",
        "ports": 21,
        "processes": 1816,
        "used_memory": 89615664,
        "version": "4.0.0"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Accounts

About Accounts

Accounts are the container for most things in Lumian. They typically represent an office, business, family, etc. Lumian arranges accounts into a tree structure, where parent accounts can access their sub accounts but not their ancestor accounts.

About the Account Tree

Since accounts can be the child of 0 or more parent accounts, it is necessary to track each account's lineage. This is tracked in the account document (_id = ID of the account) in the pvt_tree array. The order of the list is from most-ancestral to parent.

So given "pvt_tree":["1", "2", "3"], it can be determined that "3" is the parent account, "2" the grand-parent, and "1" is the great-grandparent. "pvt_tree":[] indicates the master (or Highlander) account; there should only be one!

Schema

Accounts represent tenants or customers on the system. Each account represents an individual dataset or sandbox that only one tenant can access. The data set is architecturally independent from other tenants.

Key Description Type Default Required
blacklists.[] string() false
blacklists A list blacklist ids that apply to the account array(string()) false
call_recording.account endpoint recording settings #/definitions/call_recording false
call_recording.endpoint endpoint recording settings #/definitions/call_recording false
call_recording call recording configuration object() false
call_restriction Account level call restrictions for each available number classification object() {} false
call_waiting Parameters for server-side call waiting #/definitions/call_waiting false
caller_id The account default caller ID parameters #/definitions/caller_id false
caller_id_options.outbound_privacy Determines what appears as caller id for offnet outbound calls. Values: full - hides name and number; name - hides only name; number - hides only number; none - hides nothing `string('full' 'name' 'number'
caller_id_options.show_rate Whether to show the rate boolean() false
caller_id_options custom properties for configuring caller_id object() false
dial_plan A list of default rules used to modify dialed numbers #/definitions/dialplans false
do_not_disturb.enabled The default value for do-not-disturb boolean() false
do_not_disturb object() false
enabled Determines if the account is currently enabled boolean() true false
flags.[] string() false
flags Flags set by external applications array(string()) false
formatters Schema for request formatters #/definitions/formatters false
language The language for this account string() false
metaflows Actions applied to a call outside of the normal callflow, initiated by the caller(s) #/definitions/metaflows false
music_on_hold.media_id The ID of a media object that should be used as the default music on hold string(0..2048) false
music_on_hold The default music on hold parameters object() {} false
name A friendly name for the account string(1..128) true
notifications.first_occurrence.sent_initial_call has the account made their first call boolean() false false
notifications.first_occurrence.sent_initial_registration has the account registered their first device boolean() false false
notifications.first_occurrence send emails on these account-firsts object() false
notifications.low_balance.enabled should the account be checked for this alert boolean() false
notifications.low_balance.last_notification Timestamp, in Gregorian seconds, of when the last low_balance alert was sent integer() false
notifications.low_balance.sent_low_balance has the alert been sent (avoids duplication/spamming) boolean() false
notifications.low_balance.threshold account balance to send alert on number() false
notifications.low_balance Low balance settings object() false
notifications account notification settings object() false
org Full legal name of the organization string() false
preflow.always The ID of a callflow to always execute prior to processing the callflow with numbers/patterns matching the request string() false
preflow Each property provides functionality that can be applied to calls using the callflow application object() {} false
realm The realm of the account, ie: 'account1.lumian.net' string(4..253) false
ringtones.external The alert info SIP header added when the call is from internal sources string(0..256) false
ringtones.internal The alert info SIP header added when the call is from external sources string(0..256) false
ringtones Ringtone Parameters object() {} false
timezone The default timezone string(5..32) false
topup.threshold The account balance when topup occurs number() false
topup Topup settings for the account object() false
voicemail.notify.callback Schema for a callback options #/definitions/notify.callback false
voicemail.notify object() false
voicemail object() false
zones A priority ordered mapping of zones for the account object() false

call_recording

endpoint recording settings

Key Description Type Default Required
any settings for any calls to/from the endpoint #/definitions/call_recording.source false
inbound settings for inbound calls to the endpoint #/definitions/call_recording.source false
outbound settings for outbound calls from the endpoint #/definitions/call_recording.source false

call_recording.parameters

Key Description Type Default Required
enabled is recording enabled boolean() false
format What format to store the recording on disk `string('mp3' 'wav')`
record_min_sec The minimum length, in seconds, the recording must be to be considered successful. Otherwise it is deleted integer() false
record_on_answer Recording should start on answer boolean() false
record_on_bridge Recording should start on bridge boolean() false
record_sample_rate What sampling rate to use on the recording integer() false
time_limit Time limit, in seconds, for the recording integer() false
url The URL to use when sending the recording for storage string() false

call_recording.source

Key Description Type Default Required
any settings for calls from any network #/definitions/call_recording.parameters false
offnet settings for calls from offnet networks #/definitions/call_recording.parameters false
onnet settings for calls from onnet networks #/definitions/call_recording.parameters false

call_waiting

Parameters for server-side call waiting

Key Description Type Default Required
enabled Determines if server side call waiting is enabled/disabled boolean() false

caller_id

Defines caller ID settings based on the type of call being made

Key Description Type Default Required
asserted.name The asserted identity name for the object type string(0..35) false
asserted.number The asserted identity number for the object type string(0..35) false
asserted.realm The asserted identity realm for the object type string() false
asserted Used to convey the proven identity of the originator of a request within a trusted network. object() false
emergency.name The caller id name for the object type string(0..35) false
emergency.number The caller id number for the object type string(0..35) false
emergency The caller ID used when a resource is flagged as 'emergency' object() false
external.name The caller id name for the object type string(0..35) false
external.number The caller id number for the object type string(0..35) false
external The default caller ID used when dialing external numbers object() false
internal.name The caller id name for the object type string(0..35) false
internal.number The caller id number for the object type string(0..35) false
internal The default caller ID used when dialing internal extensions object() false

dialplans

Permit local dialing by converting the dialed number to a routable form

Key Description Type Default Required
system.[] string() false
system List of system dial plans array(string()) false

formatters

Schema for request formatters

Key Description Type Default Required
^[[:alnum:]_]+$ Key to match in the route request JSON `array(#/definitions/formatters.format_options) #/definitions/formatters.format_options`

formatters.format_options

Schema for formatter options

Key Description Type Default Required
direction Only apply the formatter on the relevant request direction `string('inbound' 'outbound' 'both')`
match_invite_format Applicable on fields with SIP URIs. Will format the username portion to match the invite format of the outbound request. boolean() false
prefix Prepends value against the result of a successful regex match string() false
regex Matches against the value, with optional capture group string() false
strip If set to true, the field will be stripped from the payload boolean() false
suffix Appends value against the result of a successful regex match string() false
value Replaces the current value with the static value defined string() false

metaflow

A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to

Key Description Type Default Required
children./.+/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
children Children metaflows object() false
data The data/arguments of the metaflow module object() {} false
module The name of the metaflow module to execute at this node string(1..64) true

metaflows

Actions applied to a call outside of the normal callflow, initiated by the caller(s)

Key Description Type Default Required
binding_digit What DTMF will trigger the collection and analysis of the subsequent DTMF sequence `string('1' '2' '3'
digit_timeout How long to wait between DTMF presses before processing the collected sequence (milliseconds) integer() false
listen_on Which leg(s) of the call to listen for DTMF `string('both' 'self' 'peer')`
numbers./^[0-9]+$/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
numbers A list of static numbers with their flows object() false
patterns./.+/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
patterns A list of patterns with their flows object() false

notify.callback

Schema for a callback options

Key Description Type Default Required
attempts How many attempts without answer will system do integer() false
disabled Determines if the system will call to callback number boolean() false
interval_s How long will system wait between call back notification attempts integer() false
number Number for callback notifications about new messages string() false
schedule Schedules interval between callbacks array(integer()) false
timeout_s How long will system wait for answer to callback integer() false

Create New Account

PUT /v2/accounts

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"name":"child account"}}' \
    http://api.lumian.net:8000/v2/accounts
import axios from 'axios';

const response = await axios.put(
  'http://api.lumian.net:8000/v2/accounts',
  {
    'data': {
      'name': 'child account'
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "billing_mode": "manual",
        "call_restriction": {},
        "caller_id": {},
        "created": 63621662701,
        "dial_plan": {},
        "enabled": true,
        "id": "{ACCOUNT_ID}",
        "is_reseller": false,
        "language": "en-us",
        "music_on_hold": {},
        "name": "child account",
        "preflow": {},
        "realm": "aeac33.sip.lumian.net",
        "reseller_id": "undefined",
        "ringtones": {},
        "superduper_admin": false,
        "timezone": "America/New_York",
        "wnm_allow_additions": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Remove an account

DELETE /v2/accounts/{ACCOUNT_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: \{AUTH_TOKEN\}" \
    http://\{SERVER\}:8000/v2/accounts/\{ACCOUNT_ID\}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}', {
  headers: {
    'X-Auth-Token': '\\{AUTH_TOKEN\\}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "billing_mode": "manual",
        "call_restriction": {},
        "caller_id": {},
        "created": 63621662701,
        "dial_plan": {},
        "enabled": true,
        "id": "{ACCOUNT_ID}",
        "is_reseller": false,
        "language": "en-us",
        "music_on_hold": {},
        "name": "child account",
        "preflow": {},
        "realm": "aeac33.sip.lumian.net",
        "reseller_id": "undefined",
        "ringtones": {},
        "superduper_admin": false,
        "timezone": "America/New_York",
        "wnm_allow_additions": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch the account doc

GET /v2/accounts/{ACCOUNT_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "billing_mode": "manual",
        "call_restriction": {},
        "caller_id": {},
        "created": 63621662701,
        "dial_plan": {},
        "enabled": true,
        "id": "{ACCOUNT_ID}",
        "is_reseller": false,
        "language": "en-us",
        "music_on_hold": {},
        "name": "child account",
        "preflow": {},
        "realm": "aeac33.sip.lumian.net",
        "reseller_id": "undefined",
        "ringtones": {},
        "superduper_admin": false,
        "timezone": "America/New_York",
        "wnm_allow_additions": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Patch the account doc

PATCH /v2/accounts/{ACCOUNT_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"some_key":"some_value"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}',
  '{"data":{"some_key":"some_value"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "billing_mode": "manual",
        "call_restriction": {},
        "caller_id": {},
        "created": 63621662701,
        "dial_plan": {},
        "enabled": true,
        "id": "{ACCOUNT_ID}",
        "is_reseller": false,
        "language": "en-us",
        "music_on_hold": {},
        "name": "child account",
        "preflow": {},
        "realm": "aeac33.sip.lumian.net",
        "reseller_id": "undefined",
        "ringtones": {},
        "some_key":"some_value",
        "superduper_admin": false,
        "timezone": "America/New_York",
        "wnm_allow_additions": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Change the account doc

POST /v2/accounts/{ACCOUNT_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data": {"billing_mode": "manual","call_restriction": {},"caller_id": {},"created": 63621662701,"dial_plan": {},"enabled": true,"is_reseller": false,"language": "en-us","music_on_hold": {},"name": "child account","preflow": {},"realm": "aeac33.sip.lumian.net","reseller_id": "undefined","ringtones": {},"some_key":"some_value","superduper_admin": false,"timezone": "America/New_York","wnm_allow_additions": false}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}',
  // '{"data": {"billing_mode": "manual","call_restriction": {},"caller_id": {},"created": 63621662701,"dial_plan": {},"enabled": true,"is_reseller": false,"language": "en-us","music_on_hold": {},"name": "child account","preflow": {},"realm": "aeac33.sip.lumian.net","reseller_id": "undefined","ringtones": {},"some_key":"some_value","superduper_admin": false,"timezone": "America/New_York","wnm_allow_additions": false}}',
  {
    'data': {
      'billing_mode': 'manual',
      'call_restriction': {},
      'caller_id': {},
      'created': 63621662701,
      'dial_plan': {},
      'enabled': true,
      'is_reseller': false,
      'language': 'en-us',
      'music_on_hold': {},
      'name': 'child account',
      'preflow': {},
      'realm': 'aeac33.sip.lumian.net',
      'reseller_id': 'undefined',
      'ringtones': {},
      'some_key': 'some_value',
      'superduper_admin': false,
      'timezone': 'America/New_York',
      'wnm_allow_additions': false
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "billing_mode": "manual",
        "call_restriction": {},
        "caller_id": {},
        "created": 63621662701,
        "dial_plan": {},
        "enabled": true,
        "id": "{ACCOUNT_ID}",
        "is_reseller": false,
        "language": "en-us",
        "music_on_hold": {},
        "name": "child account",
        "preflow": {},
        "realm": "aeac33.sip.lumian.net",
        "reseller_id": "undefined",
        "ringtones": {},
        "some_key":"some_value",
        "superduper_admin": false,
        "timezone": "America/New_York",
        "wnm_allow_additions": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Create a new child account

Puts the created account under {ACCOUNT_ID}

PUT /v2/accounts/{ACCOUNT_ID}

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"name":"child account"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}',
  {
    'data': {
      'name': 'child account'
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "billing_mode": "manual",
        "call_restriction": {},
        "caller_id": {},
        "created": 63621662701,
        "dial_plan": {},
        "enabled": true,
        "id": "{CHILD_ACCOUNT_ID}",
        "is_reseller": false,
        "language": "en-us",
        "music_on_hold": {},
        "name": "child account",
        "preflow": {},
        "realm": "aeac33.sip.lumian.net",
        "reseller_id": "undefined",
        "ringtones": {},
        "superduper_admin": false,
        "timezone": "America/New_York",
        "wnm_allow_additions": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch the parent account IDs

GET /v2/accounts/{ACCOUNT_ID}/parents

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/parents
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/parents', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "id": "{PARENT_ACCOUNT_ID}",
            "name": "{PARENT_ACCOUNT_NAME}"
        }
    ],
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch an account's ancestor tree

GET /v2/accounts/{ACCOUNT_ID}/tree

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tree
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tree', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "id": "{PARENT_ACCOUNT_ID}",
            "name": "{PARENT_ACCOUNT_NAME}"
        }
    ],
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch the account's API key

The API key is used by the api_auth API to obtain an auth_token. This is intended for use by applications talking to Lumian and provides a mechanism for authentication that does not require storing a username and password in the application. The API key can be obtained via the accounts API's endpoint api_key.

GET /v2/accounts/{ACCOUNT_ID}/api_key

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
     http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/api_key
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/api_key', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "api_key": "{API_KEY}"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Re-create the account's API key

If you think that your account's API key might be exposed you can create a new one with api_key endpoint. Issuing a PUT request to this endpoint will generates a new API key for the account and will returned it in response.

PUT /v2/accounts/{ACCOUNT_ID}/api_key

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
     http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/api_key
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/api_key',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "api_key": "{API_KEY}"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch sibling accounts

By default a user account under an admin/reseller account can view all the other accounts under that reseller. If you would like current account only will be able to query its child accounts' sibling and not other accounts then set allow_sibling_listing in system_config/crossbar.accounts to false. Admin account can unrestrictedly list siblings.

GET /v2/accounts/{ACCOUNT_ID}/siblings

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/siblings
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/siblings', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "descendants_count": 1,
            "id": "{ACCOUNT_ID}",
            "name": "{ACCOUNT_NAME}",
            "realm": "{ACCOUNT_REALM}"
        }
    ],
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "start_key": "",
    "status": "success"
}

Fetch all descendants of an account

This will include children, grandchildren, etc

GET /v2/accounts/{ACCOUNT_ID}/descendants

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/descendants
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/descendants', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "id": "{CHILD_ACCOUNT}",
            "name": "{CHILD_NAME}",
            "realm": "{CHILD_REALM}",
            "tree": [
                "{ACCOUNT_ID}"
            ]
        }
    ],
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "start_key": "",
    "status": "success"
}

Fetch immediate children of an account

GET /v2/accounts/{ACCOUNT_ID}/children

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/children
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/children', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "id": "{CHILD_ACCOUNT}",
            "name": "{CHILD_NAME}",
            "realm": "{CHILD_REALM}",
            "tree": [
                "{ACCOUNT_ID}"
            ]
        }
    ],
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "start_key": "",
    "status": "success"
}

Demote a reseller

Requires superduper admin auth token

DELETE /v2/accounts/{ACCOUNT_ID}/reseller

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/reseller
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/reseller', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Promote a reseller

Requires superduper admin auth token

PUT /v2/accounts/{ACCOUNT_ID}/reseller

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/reseller
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/reseller',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Move an account

An account can only be moved by a "superduper_admin" or if enabled by anyone above the desired account.

You can enable that feature by editing the document crossbar.accounts in your system_config database and set the value to tree.

Key Value Description
allow_move enum("tree", "superduper_admin") Who can move a sub-account

POST /v2/accounts/{ACCOUNT_ID}/move

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"to": "{ACCOUNT_ID_DESTINATION}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/move
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/move',
  '{"data": {"to": "{ACCOUNT_ID_DESTINATION}"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "billing_mode": "manual",
        "call_restriction": {},
        "caller_id": {},
        "created": 63621662701,
        "dial_plan": {},
        "enabled": true,
        "id": "{ACCOUNT_ID}",
        "is_reseller": false,
        "language": "en-us",
        "music_on_hold": {},
        "name": "child account",
        "preflow": {},
        "realm": "aeac33.sip.lumian.net",
        "reseller_id": "undefined",
        "ringtones": {},
        "superduper_admin": false,
        "timezone": "America/New_York",
        "wnm_allow_additions": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Access Lists

SBC level per-account and per-device access lists allow setting individual IP-based access filtering rules which significantly increases security for users working on-premise.

Rules can be applied at account level or at individual device level

About Access Lists

access_lists API works at the level of both accounts and devices documents.

Sections:

Schema

Access Control List entries for device or account

Key Description Type Default Required Support Level
cidrs.[] string() true
cidrs Classless Inter-Domain Routing IP notation for use on the access lists array(string()) true
order Allow-Deny or Deny-Allow? `string('allow,deny' 'deny,allow')` true
user_agent Regexp to match valid user agent strings string() false

Fetch account-level access lists

GET /v2/accounts/{ACCOUNT_ID}/access_lists

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/access_lists
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/access_lists', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {},
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Update account-level access lists

POST /v2/accounts/{ACCOUNT_ID}/access_lists

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"order": "allow,deny","cidrs": ["127.0.0.3/32"]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/access_lists
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/access_lists',
  '{"data": {"order": "allow,deny","cidrs": ["127.0.0.3/32"]}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cidrs": [
            "127.0.0.3/32"
        ],
        "order": "allow,deny"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Remove account-level access lists

DELETE /v2/accounts/{ACCOUNT_ID}/access_lists

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/access_lists
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/access_lists', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch device-level access lists

GET /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/access_lists

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/access_lists
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/access_lists', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cidrs": [
            "127.0.0.3/32"
        ],
        "order": "allow,deny"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Update device-level access lists

POST /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/access_lists

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"order": "deny,allow","cidrs": ["127.0.0.3/32"]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/access_lists
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/access_lists',
  '{"data": {"order": "deny,allow","cidrs": ["127.0.0.3/32"]}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cidrs": [
            "127.0.0.3/32"
        ],
        "order": "deny,allow"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Remove device-level access lists

DELETE /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/access_lists

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/access_lists
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/access_lists', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Acdc Call Stats

About Acdc Call Stats

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/acdc_call_stats

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/acdc_call_stats
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/acdc_call_stats', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get a time range of ACDC Call Stats (using Gregorian seconds for timestamps):

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/acdc_call_stats?created_from={FROM_TIMESTAMP}&created_to={TO_TIMESTAMP}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/acdc_call_stats?created_from={FROM_TIMESTAMP}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get ACDC Call Stats as CSV:

Sample Request:

curl -v -X GET \
    -H "Accept: text/csv" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/acdc_call_stats
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/acdc_call_stats', {
  headers: {
    'Accept': 'text/csv',
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Acls

About Acls

Schema

Access Control List entries

Key Description Type Default Required Support Level
cidr Classless Inter-Domain Routing IP notation for use on the ACL string() true
description Will be added as a comment for quick identification later string(0..30) false
network-list-name The trusted list should represent anything that can issue calls without authorization. The authoritative list should indicate inter-network routing equipment (SBC, etc). `string('authoritative' 'trusted')` true
type Allow or deny this CIDR `string('allow' 'deny')` allow true

Fetch

GET /v2/accounts/{ACCOUNT_ID}/acls

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/acls
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/acls', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

API for managing agent status

This API provides convenient way for agent status management without need to dial feature codes. It's useful for various call center agent/supervisor interfaces.

Log In/Log Out agent to/from some queue

/agents/AID/queue_status (GET, POST):

Sample Response:

{
  "data":{
    "action":{{action}},
    "queue_id":{{queue_id}}
  }
}

where

{{action}} - "login" | "logout" and {{queue_id}} is an ID of the queue

Set agent status:

/agents/AID/status (GET, POST):

Sample Response:

{
  "data":{
    "status":{{status}},
    "timeout":{{timeout}},
    "presence_id":{{id}},
    "presence_state":{{state}}
  }
}

where {{status}} - "login" | "logout" | "pause" | "resume" {{timeout}} - timeout for "pause" status presence_id и presence_state - optional fields for presence information

If the agent is on call in time of request, then "pause", "resume" and "logout" commands will be executed right after the agent is back from the call.

Alerts

About Alerts

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/alerts

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/alerts
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/alerts', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/alerts

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/alerts
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/alerts',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/alerts/{ALERT_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/alerts/{ALERT_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/alerts/{ALERT_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/alerts/{ALERT_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/alerts/{ALERT_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/alerts/{ALERT_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Allotments

Allotments Explanation

Each object have name (outbound_national) which build from direction (inbound or outbound) and classification from number manager configuration.

Properties

Examples

increment", "minimum" and "no_consume_time"

Sample Response:

{
    "outbound_local": {
       "increment": 10,
       "minimum": 60,
       "no_consume_time": 5
    }
}

Call consumed time rounded before store it to DB. Call with duration 40 seconds will be count as 60 seconds.

69 seconds -> 70
75 seconds -> 80
5 seconds -> 0
6 seconds -> 60

"group_consume"

Sample Response:

{
  "Class1": {
       "amount": 600,
       "group_consume": [
           "Class2"
       ]
   },
   "Class2": {
       "amount": 600,
       "group_consume": [
           "Class1"
       ]
   }
}

Here we have 2 classifiers which share same counter. If Class1 already consumed 400 seconds and Class2 consumed 150 seconds, next call with classifier Class2 (or Class1) will have 50 free seconds.

Little more complex example:

Sample Response:

{
  "Class1": {
       "amount": 600,
       "group_consume": [
           "Class2",
           "Class3"
       ]
   },
   "Class2": {
       "amount": 120,
       "group_consume": [
           "Class1"
       ]
   },
   "Class3": {
       "amount": 300,
       "group_consume": [
            "Class2"
       ]
   }
}

So if we already have consumed calls:

Class1 - 300
Class2 - 60
Class3 - 180

As result next call will have this free seconds:

Class1 - 60 (300 Class1 + 60 Class2 + 180 Class3 = 540, 600-540 = 60)
Class2 - 0 (60 Class2 + 300 Class1 = 360, 360 > 120)
Class3 - 60 (180 Class3 + 60 Class2 = 240, 300-240 = 60)

Schema

Create buckets of minutes per time-period

Key Description Type Default Required Support Level
^\w+$.amount integer() false
^\w+$.cycle `string('minutely' 'hourly' 'daily' 'weekly'
^\w+$.group_consume.[] string() false
^\w+$.group_consume array(string()) false
^\w+$.increment integer() false
^\w+$.minimum integer() false
^\w+$.no_consume_time integer() false
^\w+$ object() false

Fetch

GET /v2/accounts/{ACCOUNT_ID}/allotments

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/allotments
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/allotments', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "outbound_national": {
            "amount": 600,
            "cycle": "hourly",
            "increment": 60,
            "minimum": 60,
            "no_consume_time": 2,
            "group_consume": [
                "outbound_local"
            ]
        },
        "outbound_local": {
            "amount": 600,
            "cycle": "hourly",
            "increment": 60,
            "minimum": 60,
            "no_consume_time": 2,
            "group_consume": [
                "outbound_national"
            ]
        }
    },
    "status": "success"
}

Update allotments configuration for a given account

POST /v2/accounts/{ACCOUNT_ID}/allotments

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/allotments
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/allotments',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Sample Response:

{
    "data": {
        "outbound_national": {
            "amount": 3600,
            "cycle": "monthly",
            "increment": 60,
            "minimum": 60,
            "no_consume_time": 2,
            "group_consume": [
                "outbound_local"
            ]
        },
        "outbound_local": {
            "amount": 3600,
            "cycle": "monthly",
            "increment": 60,
            "minimum": 60,
            "no_consume_time": 2,
            "group_consume": [
                "outbound_national"
            ]
        }
    }
}

Get consumed allotments for a given account

GET /v2/accounts/{ACCOUNT_ID}/allotments/consumed

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/allotments/consumed
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/allotments/consumed', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "outbound_local": {
            "consumed": 120,
            "consumed_to": 63608284800,
            "consumed_from": 63605606400,
            "cycle": "monthly"
        },
        "outbound_national": {
            "consumed": 120,
            "consumed_to": 63606384000,
            "consumed_from": 63605779200,
            "cycle": "weekly"
        }
    },
    "status": "success",
}

Get consumed allotments for a certain period of time

{TIMESTAMP} - Gregorian epoch seconds.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/allotments/consumed?created_from={TIMESTAMP}&created_to={TIMESTAMP}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/allotments/consumed?created_from={TIMESTAMP}&created_to={TIMESTAMP}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "outbound_local": {
            "consumed": 180,
            "consumed_to": 63607728001,
            "consumed_from": 63605046001,
            "cycle": "manual"
        },
        "outbound_national": {
            "consumed": 120,
            "consumed_to": 63607728001,
            "consumed_from": 63605046001,
            "cycle": "manual"
        }
    },
    "status": "success",
}

Get consumed allotments at certain time

{TIMESTAMP} - Gregorian epoch seconds.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/allotments/consumed?created_from={TIMESTAMP}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/allotments/consumed?created_from={TIMESTAMP}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

or

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/allotments/consumed?created_to={TIMESTAMP}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/allotments/consumed?created_to={TIMESTAMP}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response:

{
    "data": {
        "outbound_local": {
            "consumed": 180,
            "consumed_to": 63692087455, // month2_end_timestamp
            "consumed_from": 63691988379, // month2_start_timestamp
            "cycle": "monthly"
        },
        "outbound_national": {
            "consumed": 60,
            "consumed_to": 63692088370, // week4_end_timestamp
            "consumed_from": 63692078446, // week4_start_timestamp
            "cycle": "weekly"
        }
    },
    "status": "success",
}

Example Time Diagram

                                 {TIMESTAMP}
                                     ||
----+--------------------+-----------||-------+--------------------+--------
    | week3              | week4     ||       | week5              | week6
----+------------+-------+-----------||-------+--------------------+--------
 month1          | month2            ||
-----------------+-------------------||-------------------------------------

API Authentication

About API Authentication

Schema

Provides an auth-token via an Account API key

Key Description Type Default Required Support Level
api_key The Accounts API key string(64) true

Create

HTTP Request

PUT /v2/api_auth

Sample Request:

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/api_auth
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/api_auth',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Account API Authentication

Use your account's API token to instruct Crossbar to create an authentication token to be used on subsequent requests requiring authentication.

Getting your API key from the API:

Use Account API to get API key for your account:

!!! note Must already authenticated as a user.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/api_key
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/api_key', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
   "auth_token":"{AUTH_TOKEN}",
   "data":{
      "api_key":"{API_KEY}"
   },
   "revision":"{REQUEST_ID}",
   "request_id":"{REQUEST_ID}",
   "status":"success"
}

Schema

Provides an auth-token via an Account API key

Key Description Type Default Required Support Level
api_key The Accounts API key string(64) true

Create

PUT /v2/api_auth

Note:

Sample Request:

curl -v -X PUT \
    -d '{"data": {"api_key":"{API_KEY}"} }' \
    http://{SERVER}:8000/v2/api_auth
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/api_auth',
  '{"data": {"api_key":"{API_KEY}"} }',
  {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "account_id": "{ACCOUNT_ID}",
        "apps": [...],
        "is_reseller": true,
        "language": "en-US",
        "owner_id": "{OWNER_ID}",
        "reseller_id": "{RESELLER_ID}",
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/apps_link/authorize

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_link/authorize
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_link/authorize', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "account": {
            "account_id": "{ACCOUNT_ID}",
            "account_name": "{ACCOUNT_NAME}",
            "is_master": false,
            "is_reseller": false,
            "language": "en-us",
            "reseller_id": "6b71cb72c876b5b1396a335f8f8a2594"
        },
        "auth_token": {
            "account_id": "{ACCOUNT_ID}",
            "account_name": "{ACCOUNT_NAME}",
            "apps": [
                {
                    "api_url": "http://localhost:8000/v2",
                    "id": "44c076738144b5f4f80542ce49035a27",
                    "label": "Accounts Manager",
                    "name": "accounts"
                },
                {
                    "api_url": "http://localhost:8000/v2",
                    "id": "06815c7173aafa575bcf3c68ee124a77",
                    "label": "Callflows",
                    "name": "callflows"
                },
                {
                    "api_url": "http://localhost:8000/v2",
                    "id": "41f5eba03d33fc10740df7f541745f1d",
                    "label": "Number Manager",
                    "name": "numbers"
                },
                {
                    "api_url": "http://localhost:8000/v2",
                    "id": "3eb4f9230f95a7a60ac825021ad0affe",
                    "label": "Smart PBX",
                    "name": "voip"
                },
                {
                    "api_url": "http://localhost:8000/v2",
                    "id": "bbd90ad61193b2a9a3529b4f78038b5e",
                    "label": "Webhooks",
                    "name": "webhooks"
                }
            ],
            "is_master": false,
            "is_reseller": false,
            "language": "en-us",
            "method": "cb_user_auth",
            "owner_id": "8e248327b85591955749e53ea45b6baa",
            "reseller_id": "6b71cb72c876b5b1396a335f8f8a2594"
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Apps Store

Apps Store list apps allowed by your service plan.

Apps Structure

Cannot be modified, only accessible by GET requests.

Ex:

Sample Response:

{
    "name": "numbers",
    "i18n": {
        "en-US": {
            "label": "Number Manager",
            "description": "Number Manager app allows you to easily buy, port and manage all numbers in your account.",
            "extended_description": "When you're managing a PBX or running a business there are more numbers in your hands than you can manage. Number Manager allows you to easily purchase and delete numbers, port numbers away from other carriers, and assign them to your own clients.",
            "features": ["Purchase new numbers", "Delete numbers", "Port numbers from other carriers", "Set a Caller ID for each number", "Set a Failover Number", "Associate e911 address to each number"]
        }
    },
    "tags": ["reseller", "developer"],
    "icon": "NumberManager_app.png",
    "api_url": "http://{{SERVER}}:8000/v2/",
    "source_url": "http://{{SERVER}}/monster-apps/numbers",
    "author": "Lumian",
    "version": "1.0",
    "license": "-",
    "price": 0,
    "screenshots": ["numbermanager1.png", "numbermanager2.png", "numbermanager3.png"],
    "urls": {
        "documentation": "{documentation_url}",
        "howto": "{howto_video_url}"
    },
    "id": "d5c75363d3d188f08dfcf5f5b80f645f"
}

Install Master applications

Assuming you've installed your Monster applications to /path/to/monster-ui/apps, you can run the following SUP command on the {{SERVER}}:

sup crossbar_maintenance init_apps '/path/to/monster-ui/apps' 'http://your.api.{{SERVER}}:8000/v2'

This will load the apps (and let you know which apps it couldn't automatically load) into the master account (including icons, if present). For any apps that failed to be loaded automatically, you can follow the manual instructions below.

If you want to install a single Monster application:

sup crossbar_maintenance init_app '/path/to/monster-ui/apps/{{APP}}' 'http://{{SERVER}}:8000/v2'

App Permission

This is located on the account document.

Sample Response:

{
    "apps": {
        "{{application_id}}": {
            "allowed_users": "specific",
            "users": []
        },
        "{{application_id}}": {
            "allowed_users": "specific",
            "users": [{
                "id": {{user_id}}
            }]
        },
        "{{application_id}}": {
            "allowed_users": "admins"
        },
        "{{application_id}}": {
            "allowed_users": "all"
        }
    }
}
Allowed Users To key
Specific with no user No one specific
Specific with user(s) Only listed users specific
All Everyone in the account all
Admins Only Admins admins

Fetch App(s):

GET /v2/accounts/{ACCOUNT_ID}/apps_store

GET /v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": [
        {APP}
    ],
    "status": "success"
}

Install App:

PUT /v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}

Install app on your account.

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"allowed_users": "specific", "users": []}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}',
  '{"data": {"allowed_users": "specific", "users": []}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {
        "name": "{APP_ID}",
        "allowed_users": "specific",
        "users": []
    }
}

Update an App permission:

POST /v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}

Update app permission on your account.

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"allowed_users": "all"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}',
  '{"data": {"allowed_users": "all"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {
        "allowed_users": "all"
    },
    "status": "success"
}

Uninstall an App:

DELETE /v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}

Uninstall app on your account (remove permission for all users).

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {},
    "status": "success"
}

Fetch App icon

GET /v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}/icon

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}/icon
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}/icon', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Streams application icon back.

Fetch App screen shots

GET /v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}/screenshot/{APP_SCREENSHOT_INDEX}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}/screenshot/{APP_SCREENSHOT_INDEX}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/{APP_ID}/screenshot/{APP_SCREENSHOT_INDEX}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Streams application screenshot number {APP_SCREENSHOT_INDEX} back.

Get Blacklist

GET /v2/accounts/{ACCOUNT_ID}/apps_store/blacklist

Need to be reseller.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/blacklist
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/blacklist', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "blacklist": [
            "{APP_1}",
            "{APP_2}"
        ]
    },
    "status": "success"
}

Update Blacklist

POST /v2/accounts/{ACCOUNT_ID}/apps_store/blacklist

Need to be reseller.

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"blacklist": [{APP_3}]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/blacklist
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/apps_store/blacklist',
  '{"data": {"blacklist": [{APP_3}]}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {
        "blacklist": [
            "{APP_1}",
            "{APP_2}",
            "{APP_3}"
        ]
    },
    "status": "success"
}

Attachment Handlers Errors

Retrieve attachment handlers errors

List all the stored errors

GET /v2/accounts/{ACCOUNT_ID}/att_handlers_errors

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/att_handlers_errors
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/att_handlers_errors', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "page_size" : 5,
  "start_key" : "g24FANAHFtQO",
  "data" : [
    {
      "id" : "201803-586ce197ae32cfa5f16e94736f47fbd3",
      "attachment_name" : "99a81a3436ce569631a4f8e82f0afae0_test_credentials_file.txt",
      "resp_code" : 403,
      "reason" : "The AWS Access Key Id you provided does not exist in our records."
    },
    {
      "id" : "201803-586ce197ae32cfa5f16e94736f47ec5f",
      "attachment_name" : "18cdfa814a6451bff0604b86f0dc76f9_test_credentials_file.txt",
      "resp_code" : 403,
      "reason" : "The AWS Access Key Id you provided does not exist in our records."
    },
    {
      "id" : "201803-c371e3fdd10f5cdb88b1756e6ce0d877",
      "attachment_name" : "579a7ceb533bd63391ce5c035e626781_test_credentials_file.txt",
      "resp_code" : 403,
      "reason" : "The AWS Access Key Id you provided does not exist in our records."
    },
    {
      "id" : "201803-6d670f21a8184140cb31770959e09dbd",
      "attachment_name" : "01c5f64e1c8f14d1123cacf6921e43b4_test_credentials_file.txt",
      "resp_code" : 403,
      "reason" : "The AWS Access Key Id you provided does not exist in our records."
    },
    {
      "id" : "201803-461d4a97783105a46deb559e45d12266",
      "attachment_name" : "c669cd4df0cb57625a8634562dd994e6_test_credentials_file.txt",
      "resp_code" : 403,
      "reason" : "The AWS Access Key Id you provided does not exist in our records."
    }
  ],
  "revision" : "ef20ee5deff0a85f34e5c5555faa4903",
  "timestamp" : "2018-03-08T19:15:28",
  "version" : "4.3.1",
  "node" : "p1vlV1a_qodo9eHpALZAKw",
  "request_id" : "b70dbb60666e83a6316df8e0681fa417",
  "status" : "success",
  "auth_token" : "..."
}

Get all the information for a given stored error

GET /v2/accounts/{ACCOUNT_ID}/att_handlers_errors/{ERROR_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/att_handlers_errors/{ERROR_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/att_handlers_errors/{ERROR_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "data" : {
    "attachment_name" : "c669cd4df0cb57625a8634562dd994e6_test_credentials_file.txt",
    "db_name" : "account%2F67%2Fc6%2Fd3eb4b5f3486ac51dea16e8064a9-201803",
    "document_id" : "aeda8252cb95a4943d91c11d0fd1ac04",
    "handler_props" : {
      "bucket" : "my_S3_BUCKET",
      "key" : "my_AWS_ACCESS_KEY",
      "secret" : "my_AWS_SECRET_KEY"
    },
    "options" : {
      "rev" : "1-b9b24bdddc20aa1a8702a78b5f111406",
      "doc_type" : "storage_settings_probe",
      "error_verbosity" : "verbose"
    },
    "req_url" : "account%2F67%2Fc6%2Fd3eb4b5f3486ac51dea16e8064a9-201803/aeda8252cb95a4943d91c11d0fd1ac04_c669cd4df0cb57625a8634562dd994e6_test_credentials_file.txt",
    "resp_body" : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>InvalidAccessKeyId</Code><Message>The AWS Access Key Id you provided does not exist in our records.</Message><AWSAccessKeyId>my_AWS_ACCESS_KEY</AWSAccessKeyId><RequestId>F9ABFC34EDBD7CF4</RequestId><HostId>v9p8GqEAolAyMc39sByE5hsDw9BQMbRxV0D6fajkCASWs5zTx0poP42AdlHTcRdojEkaOwLHjFM=</HostId></Error>",
    "resp_code" : 403,
    "handler_id" : "custom-handler-id",
    "reason" : "The AWS Access Key Id you provided does not exist in our records.",
    "id" : "201803-461d4a97783105a46deb559e45d12266"
  },
  "revision" : "1-1d947805e5c21477d094cbb678fdcaea",
  "timestamp" : "2018-03-08T19:18:37",
  "version" : "4.3.1",
  "node" : "p1vlV1a_qodo9eHpALZAKw",
  "request_id" : "c9e00eeeeb28508114697e0d1e7200b0",
  "status" : "success",
  "auth_token" : "..."
}

Get all the stored errors generated by the given handler-id

GET /v2/accounts/{ACCOUNT_ID}/att_handlers_errors/handler/{HANDLER_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/att_handlers_errors/handler/{HANDLER_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/att_handlers_errors/handler/{HANDLER_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "page_size" : 3,
  "start_key" : "g2wAAAACbQAAABFjdXN0b20taGFuZGxlci1pZG4FABgGFtQOag",
  "data" : [
    {
      "id" : "201803-c371e3fdd10f5cdb88b1756e6ce0d877",
      "attachment_name" : "579a7ceb533bd63391ce5c035e626781_test_credentials_file.txt",
      "resp_code" : 403,
      "reason" : "The AWS Access Key Id you provided does not exist in our records."
    },
    {
      "id" : "201803-6d670f21a8184140cb31770959e09dbd",
      "attachment_name" : "01c5f64e1c8f14d1123cacf6921e43b4_test_credentials_file.txt",
      "resp_code" : 403,
      "reason" : "The AWS Access Key Id you provided does not exist in our records."
    },
    {
      "id" : "201803-461d4a97783105a46deb559e45d12266",
      "attachment_name" : "c669cd4df0cb57625a8634562dd994e6_test_credentials_file.txt",
      "resp_code" : 403,
      "reason" : "The AWS Access Key Id you provided does not exist in our records."
    }
  ],
  "revision" : "7eebf3cd4c24826feeb59f9d59ab2493",
  "timestamp" : "2018-03-08T19:08:08",
  "version" : "4.3.1",
  "node" : "p1vlV1a_qodo9eHpALZAKw",
  "request_id" : "573f2d76601744af316f1e2fc24c9304",
  "status" : "success",
  "auth_token" : "..."
}

Authentication Token Operations

Crossbar module for operations on JWT Token, SSO/OAuth tokens.

About Authentication

Schema for auth.callback

Key Description Type Default Required
client_id client id, usually application id for OAuth providers string true
code access code emitted by provider string true true
provider provider string true true
redirect_uri redirect URI string true
state state string false

Schema for auth.provider

Key Description Type Default Required
id id string true

Schema for auth.app

Key Description Type Default Required
client_id client id, usually application id for OAuth providers string true
email email for application string true false
provider provider string true true
secret secret for application string true

Resetting System (Lumian) Signature Secret

System signature secret and the subject signature is being used to sign the signature in the JWT token that Lumian is issuing.

If you feel that this system secret is compromised, use this API to reset it.

!!! danger Resetting system signature secret will invalidate all issued token! In other words all login users will be logout from the system and can't make any further request until login again. Use this API if you feel the system secret is compromised only.

!!! note Only super duper admin can reset system secret!

PUT /v2/auth

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ "action": "reset_signature_secret", "data": {} }' \
    http://{SERVER}:8000/v2/auth
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/auth',
  '{ "action": "reset_signature_secret", "data": {} }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Returns an empty success response.

Sample Response:

{
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Resetting an Account or a User Signature Secret

System signature secret and the subject signature is being used to sign the signature in the JWT token that Lumian is issuing.

If you feel that an account or a user secret is compromised, use this API to reset it.

!!! danger Resetting signature secret will invalidate user's issued token! In other words if the user is already login, it will be logout from the system and can't make any further request until login again.

!!! note Only the user or an account admin can reset the user's secret.

To Reset an Account Signature Secret

PUT /v2/accounts/{ACCOUNT_ID}/auth

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ "action": "reset_signature_secret", "data": {} }' \
    http://{SERVER}:8000/v2/accounts/290ac723eb6e73dd4a0adcd77785e04e/auth
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/290ac723eb6e73dd4a0adcd77785e04e/auth',
  '{ "action": "reset_signature_secret", "data": {} }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

To Reset a User Signature Secret

PUT /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/auth

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ "action": "reset_signature_secret", "data": {} }' \
    http://{SERVER}:8000/v2/accounts/290ac723eb6e73dd4a0adcd77785e04e/users/3bedb94b3adfc4873a548b41d28778b5/auth
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/290ac723eb6e73dd4a0adcd77785e04e/users/3bedb94b3adfc4873a548b41d28778b5/auth',
  '{ "action": "reset_signature_secret", "data": {} }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Response

Returns an empty success response.

Sample Response:

{
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Request a new token while current token is still valid

!!! note This will fail if your password or User Signature Secret was reset.

PUT /v2/auth

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ "action": "refresh_token", "data": {} }' \
    http://{SERVER}:8000/v2/auth
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/auth',
  '{ "action": "refresh_token", "data": {} }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Get a List of Registered SSO App

This list all registered Single Sign On applications for the account's reseller. Account ID is determined in order by first from the query string account_id, or from request path (/accounts/{ACCOUNT_ID}) or from authenticated user's account id.

GET /v2/auth/apps

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/apps
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/auth/apps', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "page_size": 1,
  "data": [
    {
      "id": "iamatestclientid.apps.googleusercontent.com",
      "provider": "google"
    }
  ],
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

List SSO Provider

Get a list of all Single Sign On provider.

GET /v2/auth/providers

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/providers
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/auth/providers', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "page_size": 3,
  "start_key": [
    "oauth"
  ],
  "data": [
    {
      "id": "salesforce",
      "provider_type": "oauth"
    },
    {
      "id": "office365",
      "provider_type": "oauth"
    },
    {
      "id": "google",
      "provider_type": "oauth"
    },
    {
      "id": "duo",
      "enabled": false,
      "name": "System Default Provider",
      "provider_name": "duo",
      "provider_type": "multi_factor"
    }
  ],
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

List User's Linked SSO Applications

Get a list of the linked Single Sign On applications for the authenticated user.

GET /v2/auth/links

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/links
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/auth/links', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "page_size": 1,
  "data": [
    {
      "id": "{LINKED_USER_ID}",
      "app": "{LINKED_OAUTH_APP_ID}",
      "provider": "{LINKED_OAUTH_PROVIDER}",
      "scopes": [
        "https://www.googleapis.com/auth/plus.me",
        "https://www.googleapis.com/auth/userinfo.profile",
        "https://www.googleapis.com/auth/userinfo.email"
      ],
      "email": "{USER_EMAIL}"
    }
  ],
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

List Public Key Cryptography

Lists a list of keys which is being used to sign and validate JWT tokens.

GET /v2/auth/keys

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/keys
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/auth/keys', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "page_size": 1,
  "data": [
    "96247ed90b4bec8294c09ae6ece923a2"
  ],
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Authenticate a User with a SSO (Create a Token from SSO Response)

After a user authenticate with Single Sign On provider, use this API to send the provider response to Crossbar to login and create a token.

PUT /v2/auth/callback

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ "data": { "redirect_uri": "{MONSTER_UI_IP}", "client_id": "{OAUTH_CLIENT_ID}", "provider": "{OAUTH_PROVIDER_NAME}", "code": "{OAUTH_RESP_CODE}" } }' \
    http://{SERVER}:8000/v2/auth/callback
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/auth/callback',
  '{ "data": { "redirect_uri": "{MONSTER_UI_IP}", "client_id": "{OAUTH_CLIENT_ID}", "provider": "{OAUTH_PROVIDER_NAME}", "code": "{OAUTH_RESP_CODE}" } }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Response when no User is Linked yet

If this is the first time that the user is authenticating using this SSO provider, Lumian returns and empty response indicating that user should first login using it's own Lumian credentials first to link the SSO application with its user. After login the user is linked with the application and it no need to manual login again.

Sample Response:

{
  "page_size": 1,
  "data": {},
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Response when the User is Linked

Sample Response:

{
  "page_size": 1,
  "data": {
    "owner_id": "{OWNER_ID}",
    "account_id": "{ACCOUNT_ID}",
    "reseller_id": "{RESELLER_ID}",
    "account_name": "{ACCOUNT_NAME}",
    "language": "{LANG}",
    "apps": []
  },
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Validate and Authorize a Foreign SSO Token/App

PUT /v2/auth/authorize

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/authorize
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/auth/authorize',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Get Token Information (Query String version)

Returns the information encoded in the specified authentication token (from query string token parameter).

GET /v2/auth/tokeninfo

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/tokeninfo?token={AN_AUTH_TOKEN}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/auth/tokeninfo?token={AN_AUTH_TOKEN}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "page_size": 1,
  "data": {
    "owner_id": "{OWNER_ID}",
    "account_id": "{ACCOUNT_ID}",
    "reseller_id": "{RESELLER_ID}",
    "account_name": "{ACCOUNT_NAME}",
    "language": "{LANG}",
    "apps": []
  },
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Get Token Information (Request Body version)

Returns the information encoded in the specified authentication token (from request body token parameter).

POST /v2/auth/tokeninfo

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ "data": { "token": "{AN_AUTH_TOKEN}" } }' \
    http://{SERVER}:8000/v2/auth/tokeninfo
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/auth/tokeninfo',
  '{ "data": { "token": "{AN_AUTH_TOKEN}" } }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Sample Response:

{
  "page_size": 1,
  "data": {
    "owner_id": "{OWNER_ID}",
    "account_id": "{ACCOUNT_ID}",
    "reseller_id": "{RESELLER_ID}",
    "account_name": "{ACCOUNT_NAME}",
    "language": "{LANG}",
    "apps": []
  },
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Get a SSO Application

Get a Single Sign On application by it's ID.

GET /v2/auth/apps/{APP_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/apps/{APP_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/auth/apps/{APP_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "page_size": 1,
  "data": {
    "id": "{APP_ID}"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Change a SSO Application

POST /v2/auth/apps/{APP_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/apps/{APP_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/auth/apps/{APP_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove a SSO Application

DELETE /v2/auth/apps/{APP_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/apps/{APP_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/auth/apps/{APP_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get a Public Key

Get a public key used for signing the JWT tokens issued by system.

!!! note To get the public key in form of a PEM file set Accept header as application/x-pem-file.

GET /v2/auth/keys/{KEY_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/keys/96247ed90b4bec8294c09ae6ece923a2
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/auth/keys/96247ed90b4bec8294c09ae6ece923a2', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "page_size": 1,
  "data": {
     "public_key_pem": "-----BEGIN RSA PUBLIC KEY-----\n{A_PUBLIC_KEY}\n-----END RSA PUBLIC KEY-----\n\n"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Reset a Private Key

Reset the private key of the system used to signing and verifying issued JWT tokens. If you feel that the private key is compromised, use this API to generate a new private and public key.

!!! danger Resetting system private will invalidate all issued token! In other words all login users will be logout from the system and can't make any further request until login again. Use this API if you feel the system private key is compromised only.

!!! note Only super duper admin can reset system private key!

PUT /v2/auth/keys/{KEY_ID}

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ "action": "reset_private_key", "data": {} }' \
    http://{SERVER}:8000/v2/auth/keys/96247ed90b4bec8294c09ae6ece923a2
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/auth/keys/96247ed90b4bec8294c09ae6ece923a2',
  '{ "action": "reset_private_key", "data": {} }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Sample Response:

{
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Fetch a SSO Provider Information

GET /v2/auth/providers/{PROVIDER_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/providers/google
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/auth/providers/google', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": {
    "access_code_url": "https://accounts.google.com/o/oauth2/token",
    "auth_module": "oauth",
    "auth_url": "https://accounts.google.com/o/oauth2/token",
    "discovery": "https://accounts.google.com/.well-known/openid-configuration",
    "issuer_domains": [
      "accounts.google.com"
    ],
    // rest of the provider information...
    "id": "google"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Make changes to SSO Provider

!!! note Only super duper admin can make changes to SSO provider!

POST /v2/auth/providers/{PROVIDER_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/providers/{PROVIDER_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/auth/providers/{PROVIDER_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove a SSO Provider

!!! note Only super duper admin can delete a SSO provider!

DELETE /v2/auth/providers/{PROVIDER_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/providers/{PROVIDER_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/auth/providers/{PROVIDER_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get an User's Linked SSO Applications Information

GET /v2/auth/links/{LINK_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/links/d6faaff6b054393f28356ab7b38ad1bf-116254222860442180295
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/auth/links/d6faaff6b054393f28356ab7b38ad1bf-116254222860442180295', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": {
    "email": "{USER_EMAIL}",
    "verified_email": true,
    "scope": "{PROVIDER_SCOPE}",
    "scopes": ["{SCOPES}"],
    "profile": { //user's profile },
    "display_name": "{Test Person}",
    "id": "d6faaff6b054393f28356ab7b38ad1bf-116254222860442180295"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

When the user is signing on with A Single Sign On provider for the first time, it should login with its own Lumian credentials one more time, and then make a request to this API to link its Lumian's user to the SSO. After that the user can sign in with SSO regularly and no need to use Lumian credentials again.

PUT /v2/auth/links/{LINK_ID}

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/links/d6faaff6b054393f28356ab7b38ad1bf-116254222860442180295
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/auth/links/d6faaff6b054393f28356ab7b38ad1bf-116254222860442180295',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Responses

Sample Response:

{
  "data": {
    "email": "{USER_EMAIL}",
    "verified_email": true,
    "scope": "{PROVIDER_SCOPE}",
    "scopes": ["{SCOPES}"],
    "profile": { //user's profile },
    "display_name": "{Test Person}",
    "id": "d6faaff6b054393f28356ab7b38ad1bf-116254222860442180295"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

DELETE /v2/auth/links/{LINK_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/auth/links/d6faaff6b054393f28356ab7b38ad1bf-116254222860442180295
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/auth/links/d6faaff6b054393f28356ab7b38ad1bf-116254222860442180295', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": {
    "email": "{USER_EMAIL}",
    "verified_email": true,
    "scope": "{PROVIDER_SCOPE}",
    "scopes": ["{SCOPES}"],
    "profile": { //user's profile },
    "display_name": "{Test Person}",
    "id": "d6faaff6b054393f28356ab7b38ad1bf-116254222860442180295"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "4.0.0",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Basic Auth

As an alternative for generated Tokens, you can make request with HTTP Basic Auth using your account ID and user name and password.

Basic Auth Username

Should be set to account_id of the authorizing account.

Basic Password Password

Should be set to MD5 hash of your username:password

Sample Request:

PASSWORD=`echo -n username:password | md5sum | awk '{print $1}'`
import axios from 'axios';

const response = await axios.get('http://PASSWORD=ca664ea481da78792223bf19f17824ae');

Sample cURL Requests

Sample Request:

curl -v \
    --basic --user {AUTH_ACCOUNT_ID}:$PASSWORD \
    http://server.com:8000/v2/accounts/{ACCOUNT_ID}/devices
import axios from 'axios';

const response = await axios.get('http://server.com:8000/v2/accounts/{ACCOUNT_ID}/devices', {
  auth: {
    username: '{AUTH_ACCOUNT_ID}'
  }
});

This is useful for reseller to execute requests against a their sub-account quickly.

Blacklists

About Blacklists

A blacklist is a map of caller id numbers that can be then apply to the account to block these callers to call the system.

Schema

Schema for a blacklists

Key Description Type Default Required Support Level
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
name A friendly name for the temporal rule set string(1..128) true supported
numbers Map of caller id number to block object() {} false supported
should_block_anonymous Should block Anonymous call boolean() false supported

Fetch

GET /v2/accounts/{ACCOUNT_ID}/blacklists

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/blacklists
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/blacklists', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/blacklists

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/blacklists
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/blacklists',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/blacklists/{BLACKLIST_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/blacklists/{BLACKLIST_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/blacklists/{BLACKLIST_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/blacklists/{BLACKLIST_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/blacklists/{BLACKLIST_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/blacklists/{BLACKLIST_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Patch

PATCH /v2/accounts/{ACCOUNT_ID}/blacklists/{BLACKLIST_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/blacklists/{BLACKLIST_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/blacklists/{BLACKLIST_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/blacklists/{BLACKLIST_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/blacklists/{BLACKLIST_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/blacklists/{BLACKLIST_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Braintree

About Braintree

DISCLAIMER: Please read the docs available at braintree to know how braintree works and how its APIs are intended to work.

Braintree provides good docs to make you knowledgeable about what are important fields and what fields can be left from the API requests.

This doc just provides examples as what is possible with the API but you should consult braintree and their API documentation before using Lumian's braintree modules.

Test out braintree using sandbox account before deploying it in production as the module is considered NOT PRODUCTION READY.

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/braintree/client_token

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/client_token
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/client_token', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "client_token": "{BRAINTREE_CLIENT_TOKEN}"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Show this account's credits

GET /v2/accounts/{ACCOUNT_ID}/braintree/credits

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/credits
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/credits', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "amount": 500.0,
        "billing_account_id": "31a05ddba6a9df166c7d50fc4b683606"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Add credit to this account

PUT /v2/accounts/{ACCOUNT_ID}/braintree/credits

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"amount":200.00}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/credits
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/credits',
  // '{"data":{"amount":200.00}}',
  {
    'data': {
      'amount': 200
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "bookkeeper_info": {
            "add_ons": [],
            "amount": "20.00",
            "avs_postal_response": "M",
            "avs_street_response": "I",
            "card": {
                "bin": "411111",
                "card_type": "Visa",
                "customer_location": "US",
                "default": false,
                "expiration_month": "11",
                "expiration_year": "2020",
                "expired": false,
                "id": "7gz3qw",
                "last_four": "1111"
            },
            "ccv_response_code": "I",
            "created_at": "2016-09-29T14:22:54Z",
            "currency_code": "USD",
            "customer": {
                "company": "ACME Corp",
                "first_name": "John",
                "id": "31a05ddba6a9df166c7d50fc4b657854",
                "last_name": "Doe",
                "phone": "9122475533"
            },
            "discounts": [],
            "id": "0gmy6jrw",
            "is_api": true,
            "is_automatic": false,
            "is_recurring": false,
            "merchant_account_id": "romanat",
            "order_id": "a6268d1a31a76d53857fae0d",
            "processor_authorization_code": "XJ3YL9",
            "processor_response_code": "1000",
            "processor_response_text": "Approved",
            "purchase_order": "3001",
            "status": "submitted_for_settlement",
            "tax_exempt": false,
            "type": "sale",
            "update_at": "2016-09-29T14:22:54Z"
        },
        "description": "credit addition from credit card",
        "id": "80591de5690374ebba7adc4ffaaf51c1",
        "order_id": "a6268d1a31a76d53857fae0d310552ab",
        "sub_account_id": "22f1b082e505064cc930e944bf9a0728",
        "sub_account_name": "romana"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

List this account's transactions

GET /v2/accounts/{ACCOUNT_ID}/braintree/transactions

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/transactions
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/transactions', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [{
        "id": "{TRANSACTION_ID}",
        "status": "{STATUS_OF_TRANSACTION}",
        "type": "sale",
        "currency_code": "{CURRENCY_CODE}",
        "amount": "20.00",
        "merchant_account_id": "{BRAINTREE_MERCHANT_ID}",
        "order_id": "{ORDER_ID}",
        "purchase_order": "3001",
        "created_at": "2016-09-29T14:22:54Z",
        "update_at": "2016-09-29T14:22:54Z",
        "avs_postal_response": "M",
        "avs_street_response": "I",
        "ccv_response_code": "I",
        "processor_authorization_code": "XJ3LR9",
        "processor_response_code": "1000",
        "processor_response_text": "Approved",
        "tax_exempt": false,
        "billing_address": {
            {BRAINTREE_CUSTOMER_ADDRESS}
        },
        "shipping_address": {
            {BRAINTREE_CUSTOMER_SHIPPING_ADDRESS}
        },
        "customer": {
            {BRAINTREE_CUSTOMER_INFO}
        },
        "card": {
            {BRAINTREE_CREDIT_CARD_DETAILS}
        },
        "add_ons": [
            {ADDONS_IN_ORDER}
        ],
        "discounts": [
            {DISCOUNTS_APPLIED_TO_ORDER}
        ],
        "is_api": true,
        "is_automatic": {AUTOMATIC_BILLING},
        "is_recurring": {RECURRING_SUBSCRIPTION}
    }],
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

List addresses

GET /v2/accounts/{ACCOUNT_ID}/braintree/addresses

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/addresses
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/addresses', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "company": "{CUSTOMER_COMPANY}",
            "country_code": "{BRAINTREE_COUNTRY_CODE}",
            "country_code_three": "{BRAINTREE_COUNTRY_CODE_THREE}",
            "country_code_two": "{BRAINTREE_COUNTRY_CODE_TWO}",
            "country_name": "{BRAINTREE_COUNTRY_NAME}",
            "created_at": "2016-09-23T00:20:51Z",
            "customer_id": "{ACCOUNT_ID}",
            "extended_address": "{EXTENDED_CUSTOMER_ADDRESS}",
            "first_name": "{CUSTOMER_FIRST_NAME}",
            "id": "7x",
            "last_name": "{CUSTOMER_LAST_NAME}",
            "locality": "{CUSTOMER_LOCALITY}",
            "postal_code": "{CUSTOMER_POSTAL_CODE}",
            "region": "{BRAINTREE_REGION}",
            "street_address": "{CUSTOMER_ADDRESS}",
            "updated_at": "2016-09-23T00:20:51Z"
        }
    ],
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Add an address

PUT /v2/accounts/{ACCOUNT_ID}/braintree/addresses

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"{ADDRESS_INFORMATION}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/addresses
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/addresses',
  '{"data":{"{ADDRESS_INFORMATION}"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "company": "{CUSTOMER_COMPANY}",
        "country_code": "{BRAINTREE_COUNTRY_CODE}",
        "country_code_three": "{BRAINTREE_COUNTRY_CODE_THREE}",
        "country_code_two": "{BRAINTREE_COUNTRY_CODE_TWO}",
        "country_name": "{BRAINTREE_COUNTRY_NAME}",
        "created_at": "2016-09-23T00:20:51Z",
        "customer_id": "{ACCOUNT_ID}",
        "extended_address": "{EXTENDED_CUSTOMER_ADDRESS}",
        "first_name": "{CUSTOMER_FIRST_NAME}",
        "id": "7x",
        "last_name": "{CUSTOMER_LAST_NAME}",
        "locality": "{CUSTOMER_LOCALITY}",
        "postal_code": "{CUSTOMER_POSTAL_CODE}",
        "region": "{BRAINTREE_REGION}",
        "street_address": "{CUSTOMER_ADDRESS}",
        "updated_at": "2016-09-23T00:20:51Z"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

List credit cards

GET /v2/accounts/{ACCOUNT_ID}/braintree/cards

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/cards
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/cards', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [{
        "id": "{CARD_ID}",
        "bin": "{CARD_FIRST_SIX_DIGITS}",
        "card_type": "Visa",
        "created_at": "2016-09-23T00:20:51Z",
        "updated_at": "2016-09-29T14:22:54Z",
        "default": true,
        "expiration_month": "11",
        "expiration_year": "2020",
        "expired": false,
        "customer_location": "US",
        "last_four": "1111",
        "customer_id": "{ACCOUNT_ID}",
        "created_at": "2016-09-23T00:20:51Z",
        "updated_at": "2016-09-29T14:22:54Z",
        "billing_address": {
            {BRAINTREE_ADDRESS}
        },
        "billing_address_id": "{BRAINTREE_ADDRESS_ID}"
    }],
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Add a credit card

PUT /v2/accounts/{ACCOUNT_ID}/braintree/cards

To add a credit card, the information about a credit card can be sent in the request or payment_token_nonce.

with payment method nonce

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"payment_method_nonce":"valid-nonce"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/cards
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/cards',
  {
    'data': {
      'payment_method_nonce': 'valid-nonce'
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "id": "{CARD_ID}",
        "bin": "{CARD_FIRST_SIX_DIGITS}",
        "card_type": "Visa",
        "created_at": "2016-09-23T00:20:51Z",
        "updated_at": "2016-09-29T14:22:54Z",
        "default": true,
        "expiration_month": "11",
        "expiration_year": "2020",
        "expired": false,
        "customer_location": "US",
        "last_four": "1111",
        "customer_id": "{ACCOUNT_ID}",
        "created_at": "2016-09-23T00:20:51Z",
        "updated_at": "2016-09-29T14:22:54Z",
        "billing_address": {
            {BRAINTREE_ADDRESS}
        },
        "billing_address_id": "{BRAINTREE_ADDRESS_ID}"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

with credit card info

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"{CREDIT_CARD_INFO}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/cards
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/cards',
  '{"data":{"{CREDIT_CARD_INFO}"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "id": "{CARD_ID}",
        "bin": "{CARD_FIRST_SIX_DIGITS}",
        "card_type": "Visa",
        "created_at": "2016-09-23T00:20:51Z",
        "updated_at": "2016-09-29T14:22:54Z",
        "default": true,
        "expiration_month": "11",
        "expiration_year": "2020",
        "expired": false,
        "customer_location": "US",
        "last_four": "1111",
        "customer_id": "{ACCOUNT_ID}",
        "created_at": "2016-09-23T00:20:51Z",
        "updated_at": "2016-09-29T14:22:54Z",
        "billing_address": {
            {BRAINTREE_ADDRESS}
        },
        "billing_address_id": "{BRAINTREE_ADDRESS_ID}"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Get this account's details

GET /v2/accounts/{ACCOUNT_ID}/braintree/customer

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/customer
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/customer', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "id": "{ACCOUNT_ID}",
        "first_name": "John",
        "last_name": "Doe",
        "company": "Presentation",
        "phone": "9122475533",
        "created_at": "2016-09-17T21:08:01Z",
        "updated_at": "2016-09-23T00:20:53Z",
        "credit_cards": [{
            {BRAINTREE_CREDIT_CARD}
        }],
        "addresses": [{
            {BRAINTREE_CUSTOMER_ADDRESS}
        }]
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Add a customer

POST /v2/accounts/{ACCOUNT_ID}/braintree/customer

To add a customer we can send the customer's info as with just customer's name, company and phone or can add a payment_method_nonce with it, or add a credit card with the customer info with card's info or with payment_method_nonce token.

the user can be added without any credit card

Sample Response:

{
    "data": {
        "first_name": "John",
        "last_name": "Doe",
        "company": "ACME CORP",
        "phone": "6000000000"
    }
}

without any credit card and contains payment method nonce in their json request

Sample Response:

{
    "data": {
        "first_name": "John",
        "last_name": "Doe",
        "company": "ACME CORP",
        "phone": "6000000000",
        "payment_method_nonce": "valid-nonce"
    }
}

payment method nonce is added to the credit card section

Sample Response:

{
    "data": {
        "first_name": "John",
        "last_name": "Doe",
        "company": "ACME CORP",
        "phone": "6000000000",
        "credit_card":{
            "payment_method_nonce": "valid-nonce"
        }
    }
}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"{CUSTOMER_INFO}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/customer
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/customer', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'application/json'
  },
  data: '{"data":{"{CUSTOMER_INFO}"}}'
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "id": "{ACCOUNT_ID}",
        "first_name": "John",
        "last_name": "Doe",
        "company": "Presentation",
        "phone": "9122475533",
        "created_at": "2016-09-17T21:08:01Z",
        "updated_at": "2016-09-23T00:20:53Z",
        "credit_cards": [{
            {BRAINTREE_CREDIT_CARD}
        }],
        "addresses": [{
            {BRAINTREE_CUSTOMER_ADDRESS}
        }]
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch details on a transaction

GET /v2/accounts/{ACCOUNT_ID}/braintree/transactions/{TRANSACTION_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/transactions/{TRANSACTION_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/transactions/{TRANSACTION_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "id": "{TRANSACTION_ID}",
        "status": "{STATUS_OF_TRANSACTION}",
        "type": "sale",
        "currency_code": "{CURRENCY_CODE}",
        "amount": "20.00",
        "merchant_account_id": "{BRAINTREE_MERCHANT_ID}",
        "order_id": "{ORDER_ID}",
        "purchase_order": "3001",
        "created_at": "2016-09-29T14:22:54Z",
        "update_at": "2016-09-29T14:22:54Z",
        "avs_postal_response": "M",
        "avs_street_response": "I",
        "ccv_response_code": "I",
        "processor_authorization_code": "XJ3LR9",
        "processor_response_code": "1000",
        "processor_response_text": "Approved",
        "tax_exempt": false,
        "billing_address": {
            {BRAINTREE_CUSTOMER_ADDRESS}
        },
        "shipping_address": {
            {BRAINTREE_CUSTOMER_SHIPPING_ADDRESS}
        },
        "customer": {
            {BRAINTREE_CUSTOMER_INFO}
        },
        "card": {
            {BRAINTREE_CREDIT_CARD_DETAILS}
        },
        "add_ons": [
            {ADDONS_IN_ORDER}
        ],
        "discounts": [
            {DISCOUNTS_APPLIED_TO_ORDER}
        ],
        "is_api": true,
        "is_automatic": {AUTOMATIC_BILLING},
        "is_recurring": {RECURRING_SUBSCRIPTION}
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Remove an address

DELETE /v2/accounts/{ACCOUNT_ID}/braintree/addresses/{ADDRESS_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/addresses/{ADDRESS_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/addresses/{ADDRESS_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {},
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch an address' details

GET /v2/accounts/{ACCOUNT_ID}/braintree/addresses/{ADDRESS_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/addresses/{ADDRESS_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/addresses/{ADDRESS_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "id": "7x",
        "customer_id": "{ACCOUNT_ID}",
        "first_name": "{CUSTOMER_FIRST_NAME}",
        "last_name": "{CUSTOMER_LAST_NAME}",
        "company": "{CUSTOMER_COMPANY}",
        "street_address": "{CUSTOMER_ADDRESS}",
        "extended_address": "{EXTENDED_CUSTOMER_ADDRESS}",
        "locality": "{CUSTOMER_LOCALITY}",
        "region": "{BRAINTREE_REGION}",
        "postal_code": "{CUSTOMER_POSTAL_CODE}",
        "country_code": "{BRAINTREE_COUNTRY_CODE}",
        "country_code_two": "{BRAINTREE_COUNTRY_CODE_TWO}",
        "country_code_three": "{BRAINTREE_COUNTRY_CODE_THREE}",
        "country_name": "{BRAINTREE_COUNTRY_NAME}",
        "created_at": "2016-09-23T00:20:51Z",
        "updated_at": "2016-09-23T00:20:51Z"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Update an address

POST /v2/accounts/{ACCOUNT_ID}/braintree/addresses/{ADDRESS_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"{ADDRESS_INFORMATION}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/addresses/{ADDRESS_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/addresses/{ADDRESS_ID}',
  '{"data":{"{ADDRESS_INFORMATION}"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "id": "7x",
        "customer_id": "{ACCOUNT_ID}",
        "first_name": "{CUSTOMER_FIRST_NAME}",
        "last_name": "{CUSTOMER_LAST_NAME}",
        "company": "{CUSTOMER_COMPANY}",
        "street_address": "{CUSTOMER_ADDRESS}",
        "extended_address": "{EXTENDED_CUSTOMER_ADDRESS}",
        "locality": "{CUSTOMER_LOCALITY}",
        "region": "{BRAINTREE_REGION}",
        "postal_code": "{CUSTOMER_POSTAL_CODE}",
        "country_code": "{BRAINTREE_COUNTRY_CODE}",
        "country_code_two": "{BRAINTREE_COUNTRY_CODE_TWO}",
        "country_code_three": "{BRAINTREE_COUNTRY_CODE_THREE}",
        "country_name": "{BRAINTREE_COUNTRY_NAME}",
        "created_at": "2016-09-23T00:20:51Z",
        "updated_at": "2016-09-23T00:20:51Z"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Remove a credit card

DELETE /v2/accounts/{ACCOUNT_ID}/braintree/cards/{CARD_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/cards/{CARD_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/cards/{CARD_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": { },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch a credit card's details

GET /v2/accounts/{ACCOUNT_ID}/braintree/cards/{CARD_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/cards/{CARD_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/cards/{CARD_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "id": "{CARD_ID}",
        "bin": "{CARD_FIRST_SIX_DIGITS}",
        "card_type": "Visa",
        "created_at": "2016-09-23T00:20:51Z",
        "updated_at": "2016-09-29T14:22:54Z",
        "default": true,
        "expiration_month": "11",
        "expiration_year": "2020",
        "expired": false,
        "customer_location": "US",
        "last_four": "1111",
        "customer_id": "{ACCOUNT_ID}",
        "created_at": "2016-09-23T00:20:51Z",
        "updated_at": "2016-09-29T14:22:54Z",
        "billing_address": {
            {BRAINTREE_ADDRESS}
        },
        "billing_address_id": "{BRAINTREE_ADDRESS_ID}"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Update a credit card

POST /v2/accounts/{ACCOUNT_ID}/braintree/cards/{CARD_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"{CREDIT_CARD_INFO_OR_PAYMENT_NONCE}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/cards/{CARD_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/braintree/cards/{CARD_ID}',
  '{"data":{"{CREDIT_CARD_INFO_OR_PAYMENT_NONCE}"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "id": "{CARD_ID}",
        "bin": "{CARD_FIRST_SIX_DIGITS}",
        "card_type": "Visa",
        "created_at": "2016-09-23T00:20:51Z",
        "updated_at": "2016-09-29T14:22:54Z",
        "default": true,
        "expiration_month": "11",
        "expiration_year": "2020",
        "expired": false,
        "customer_location": "US",
        "last_four": "1111",
        "customer_id": "{ACCOUNT_ID}",
        "created_at": "2016-09-23T00:20:51Z",
        "updated_at": "2016-09-29T14:22:54Z",
        "billing_address": {
            {BRAINTREE_ADDRESS}
        },
        "billing_address_id": "{BRAINTREE_ADDRESS_ID}"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Bulk

About Bulk

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/bulk

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"ids": ["{ID1}", "{ID2}", "{ID3}"]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/bulk
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/bulk', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  data: '{"data": {"ids": ["{ID1}", "{ID2}", "{ID3}"]}}'
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        ...
    ],
    "page_size": 0,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Call Inspector

About Call Inspector

The Call Inspector Crossbar resource allows the client to query and inspect data related to the Call Inspector application.

The Call Inspector endpoint is not loaded on start in a default Lumian installation.

Note: adding cb_call_inspector to the crossbar system_config doc will not start the endpoint; only on restarting Crossbar will cb_call_inspector be loaded. Use the sup command above to start the endpoint at runtime.

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/call_inspector

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/call_inspector
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/call_inspector', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {CALL_ID1},
        {CALL_ID2}
    ]
    "status": "success"
}

Read a call's SIP dialogue

GET /v2/accounts/{ACCOUNT_ID}/call_inspector/{CALL_ID}

Note: {CHUNKS} is an array of JSON-formatted chunks.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/call_inspector/{CALL_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/call_inspector/{CALL_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "analysis": [],
        "messages": {CHUNKS}
    }
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Callflows

About Callflows

Callflows are the instructions Lumian uses to process a call. A callflow includes a list of numbers or regex patterns used by Lumian to determine what callflow is used when a call comes in for an account. The flow parameter defines the tree of actions, allowing branching (such as in the menu action) and chaining actions together. You can also branch to other callflows and execute its flow (useful to avoid recreating the same sub-flow structure).

Schema

Call flows describe steps to take in order to process a phone call. They are trees of information related to a phone call such as "answer, play file, record file" etc. that are logically grouped together and ordered.

Key Description Type Default Required Support Level
featurecode.name string(1..128) false
featurecode.number string(1..30) false
featurecode When the callflow is used as a featurecode this object tracks the intended match of the pattern and name of the feature object() false
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
flow A callflow node defines a module to execute, data to provide to that module, and zero or more children to branch to #/definitions/callflows.action false
metaflow Actions applied to a call outside of the normal callflow, initiated by the caller(s) #/definitions/metaflows false
numbers.[] string(1..36) false
numbers A list of static numbers that the callflow should execute for array(string(1..36)) [] false
patterns.[] string(1..) false
patterns A list of regular expressions that the callflow should execute for, with optional capture groups array(string(1..)) [] false

callflows.action

Call flows describe steps to take in order to process a phone call. They are trees of information related to a phone call such as "answer, play file, record file" etc. that are logically grouped together and ordered.

Key Description Type Default Required Support Level
children./.+/ Call flows describe steps to take in order to process a phone call. They are trees of information related to a phone call such as "answer, play file, record file" etc. that are logically grouped together and ordered. #/definitions/callflows.action false
children Children callflows object() false
data The data/arguments of the callflow module object() {} true
module The name of the callflow module to execute at this node string(1..64) true

metaflow

A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to

Key Description Type Default Required Support Level
children./.+/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
children Children metaflows object() false
data The data/arguments of the metaflow module object() {} false
module The name of the metaflow module to execute at this node string(1..64) true

metaflows

Actions applied to a call outside of the normal callflow, initiated by the caller(s)

Key Description Type Default Required Support Level
binding_digit What DTMF will trigger the collection and analysis of the subsequent DTMF sequence `string('1' '2' '3' '4'
digit_timeout How long to wait between DTMF presses before processing the collected sequence (milliseconds) integer() false
listen_on Which leg(s) of the call to listen for DTMF `string('both' 'self' 'peer')`
numbers./^[0-9]+$/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
numbers A list of static numbers with their flows object() false
patterns./.+/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
patterns A list of patterns with their flows object() false

Fetch

GET /v2/accounts/{ACCOUNT_ID}/callflows

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/callflows
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/callflows', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create a new callflow

PUT /v2/accounts/{ACCOUNT_ID}/callflows

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/callflows
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/callflows',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove a callflow

DELETE /v2/accounts/{ACCOUNT_ID}/callflows/{CALLFLOW_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/callflows/{CALLFLOW_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/callflows/{CALLFLOW_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch a callflow's details

GET /v2/accounts/{ACCOUNT_ID}/callflows/{CALLFLOW_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/callflows/{CALLFLOW_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/callflows/{CALLFLOW_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Patch a callflow object

PATCH /v2/accounts/{ACCOUNT_ID}/callflows/{CALLFLOW_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/callflows/{CALLFLOW_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/callflows/{CALLFLOW_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Change a callflow object

POST /v2/accounts/{ACCOUNT_ID}/callflows/{CALLFLOW_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/callflows/{CALLFLOW_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/callflows/{CALLFLOW_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Cccps

About Cccps

Schema

Calling cards callback platform user's info

Key Description Type Default Required Support Level
active Show's whether CID/PIN active boolean() false false
cid CID to authorize string() false
comment Some notes regarding what this pin/cid for string() false
max_concurent_calls_per_user Calls per user limit. Counts all user's legs and compares to max_concurrent_calls_per_user multiplied by 2 integer() false
pin PIN to authorize string() false
retain_cid Pass initial caller number to the callee boolean() false
user_id The ID of the user object that 'owns' cid/pin string(32) false

Fetch

GET /v2/accounts/{ACCOUNT_ID}/cccps

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cccps
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cccps', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Call Detail Records

CDRs (Call Detail Records) provide a summary view of a call leg.

Schema

Call Detail Records

Key Description Type Default Required Support Level
app_name The Lumian application that issued the CDR string() false
app_version The internal Lumian version number of the application that issued the CDR string() false
billing_seconds The number of seconds the call leg can be billed for (typically from when the call leg is answered integer() false
call_direction Direction of the call, relative to the media switch `string('inbound' 'outbound')` false
call_id Unique identifier of the call leg string() true
callee_id_name The indicated name of the callee string() false
callee_id_number The indicated number of the callee string() false
caller_id_name The indicated name of the caller string() false
caller_id_number The indicated number of the caller string() false
channel_call_state Media Server channel call state string() false
channel_created_time Media Server channel creation time integer() false
channel_name Media Server channel name string() false
channel_state Media Server channel state string() false
custom_application_vars Any custom-set values object() false
custom_channel_vars Lumian-specific key/value pairs set on the channel object() false
custom_sip_headers.in Custom SIP Headers to be applied to calls inbound to Lumian from the endpoint #/definitions/custom_sip_headers false
custom_sip_headers.out Custom SIP Headers to be applied to calls outbound from Lumian to the endpoint #/definitions/custom_sip_headers false
custom_sip_headers.^[a-zA-z0-9_\-]+$ The SIP header to add string() false
custom_sip_headers A property list of SIP headers object() false
digits_dialed All the DTMF tones detected on this leg of the call string() false
disposition Who sent the SIP BYE message string() false
duration_seconds The duration of the call leg, in seconds integer() false
event_category Lumian specific key (Event-Category) included on each payload it publishes string() false
event_name Lumian specific key (Event-Name) included on each payload it publishes string() false
fax_bad_rows Number of rows that failed to transfer string() false
fax_ecm_used Was ECM (error correction mode) used on the fax string() false
fax_result_code Media Server's result code of the transmission string() false
fax_result_text Error String, if any, or 'OK' if successful string() false
fax_success Whether the fax was considered a success or not string() false
fax_total_pages Number of pages in the fax string() false
fax_transfer_rate Baud of the fax transfer string() false
fax_transferred_pages Number of pages transferred string() false
from Built by Lumian, depending on direction, to represent the From user string() false
from_tag SIP From TAG string() false
from_uri The From SIP URI string() false
hangup_cause The reason for the call leg's termination string() false
hangup_code The SIP hangup code, if available string() false
interaction_id correlating ID among related call legs string() false
interaction_key Unique portion of the interaction ID string() false
interaction_time Timestamp of the creation of the interaction ID integer() false
local_sdp The SDP negotiated by the local agent string() false
media_server The hostname of the media server that processed the call string() false
msg_id Lumian specific key (Msg-Id) assigned to each payload it publishes string() false
node The ecallmgr which issued the CDR string() false
other_leg_call_id If this leg was bridged, the call-id of the opposite leg string() false
other_leg_caller_id_name Caller ID name of the bridged leg string() false
other_leg_caller_id_number Caller ID number of the bridged leg string() false
other_leg_destination_number Dialed number of the other leg string() false
other_leg_direction direction of the other leg, relative to the media server string() false
presence_id ID used in NOTIFY SIP messages string() false
remote_sdp The SDP negotiated by the remote agent string() false
request Built by Lumian this is the processed request URI string() false
ringing_seconds How many seconds the leg was ringing (pre-answer) integer() false
switch_hostname Media Server hostname (as reported by the switch) string() false
switch_nodename Media Server node name (as known in ecallmgr) string() false
switch_uri Media Server URI string() false
switch_url Media Server URL string() false
timestamp UTC timestamp, in Gregorian seconds, of when the CDR was generated integer() false
to Built by Lumian, depending on direction, to represent the To user string() false
to_tag SIP TO Tag string() false
to_uri The To SIP URI string() false
user_agent User agent header from SIP packet string() false

custom_sip_headers

Custom SIP headers applied to an INVITE

Key Description Type Default Required Support Level
^[a-zA-z0-9_\-]+$ The SIP header to add string() false

Fetch

GET /v2/accounts/{ACCOUNT_ID}/cdrs

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get a time range of CDRs (using Gregorian seconds for timestamps):

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs?created_from={FROM_TIMESTAMP}&created_to={TO_TIMESTAMP}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs?created_from={FROM_TIMESTAMP}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get CDRs and update datetime field to local time zone (using seconds for timeoffset from UTC time):

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs?created_from={FROM_TIMESTAMP}&created_to={TO_TIMESTAMP}&utc_offset={SECONDS_OFFSET}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs?created_from={FROM_TIMESTAMP}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get CDRs as CSV:

Sample Request:

curl -v -X GET \
    -H "Accept: text/csv" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs', {
  headers: {
    'Accept': 'text/csv',
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get CDRs as CSV and define filename:

Sample Request:

curl -v -X GET \
    -H "Accept: text/csv" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "X-File-Name: {FILE_NAME}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs', {
  headers: {
    'Accept': 'text/csv',
    'X-Auth-Token': '{AUTH_TOKEN}',
    'X-File-Name': '{FILE_NAME}'
  }
});

or

Sample Request:

curl -v -X GET \
    -H "Accept: text/csv" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs?file_name={FILE_NAME}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs?file_name={FILE_NAME}', {
  headers: {
    'Accept': 'text/csv',
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch a CDR's details

GET /v2/accounts/{ACCOUNT_ID}/cdrs/{CDR_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs/{CDR_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs/{CDR_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch interaction summary

GET /v2/accounts/{ACCOUNT_ID}/cdrs/interaction

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs/interaction
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs/interaction', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Crossbar cdrs was extended to provide simplified interaction call detail records. It groups all CDRs that interacted with each other to form a list of calls.

GET /v2/accounts/{ACCOUNT_ID}/cdrs/legs/{INTERACTION_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs/legs/{INTERACTION_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/cdrs/legs/{INTERACTION_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Variations

You can select CDRs/interactions for a specific user by adding them to the URI:

GET /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/cdrs

Notes on fields

Some fields need a little more explanation to help you understand what they are telling you about the call leg.

Lumian-specific properties

These are properties set by Lumian for internal purposes. These are the properties found under the custom_channel_vars property at the top-level of the CDR JSON object. The non-exhaustive list of properties:

These properties relate to how the leg was rated and billed. Some of these properties are not accessible via Crossbar, but may exist on the CDR

Fax-specific Properties

These properties may exist on a CDR for a fax request (inbound or outbound):

Resource Properties

All resource properties are set by the Stepswitch application.

Call Channels

About Call Channels

The Channels API allows queries to find active channels for an account, a user, or a device. Given a call-id for a channel, a limited set of commands are allowed to be executed against that channel (such as hangup, transfer, or play media).

NOTE: Konami is an outdated and unsupported Lumian module. If you need support on this module, please ensure you are signed up for Konami Pro.

Fetch active channels system wide.

!!! note For super duper admin only. Be sure to set system_config->crossbar.channels->system_wide_channels_list flag to true

GET /v2/channels

Sample Request:

curl -v -X GET \
    -H "Content-Type: application/json" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/channels
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/channels', {
  headers: {
    'Content-Type': 'application/json',
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch active channels for an account

GET /v2/accounts/{ACCOUNT_ID}/channels

Sample Request:

curl -v -X GET \
    -H "Content-Type: application/json" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/channels
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/channels', {
  headers: {
    'Content-Type': 'application/json',
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "answered": true,
            "authorizing_id": "63fbb9ac78e11f3ccb387928a423798a",
            "authorizing_type": "device",
            "destination": "user_zu0bf7",
            "direction": "outbound",
            "other_leg": "d220c187-e18edc42-bab2459d@10.26.0.91",
            "owner_id": "72855158432d790dfb22d03ff64c033e",
            "presence_id": "user_zu0bf7@account.realm.com",
            "timestamp": 63573977746,
            "username": "user_zu0bf7",
            "uuid": "dab25c76-7479-4ed2-ba92-6b725d68e351"
        }
    ],
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch channels for a user or device

GET /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/channels

For user with {USER_ID}:

Sample Request:

curl -v -X GET \
    -H "Content-Type: application/json" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/channels
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/channels', {
  headers: {
    'Content-Type': 'application/json',
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

For device with {DEVICE_ID}:

GET /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/channels

Sample Request:

curl -v -X GET \
    -H "Content-Type: application/json" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/channels
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/channels', {
  headers: {
    'Content-Type': 'application/json',
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch a channel's details

GET /v2/accounts/{ACCOUNT_ID}/channels/{UUID}

Sample Request:

curl -v -X GET \
    -H "Content-Type: application/json" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/channels/{UUID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/channels/{UUID}', {
  headers: {
    'Content-Type': 'application/json',
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Execute an application against a Channel

!!! note This API requires Konami Pro to be running and metaflows to be enabled on the call

POST /v2/accounts/{ACCOUNT_ID}/channels/{UUID}

Sample Request:

curl -v -X POST \
    -H "Content-Type: application/json" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"action": "transfer", "target": "Lumian", "takeback_dtmf": "*1", "moh": "media_id" }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/channels/{UUID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/channels/{UUID}',
  // '{"data": {"action": "transfer", "target": "Lumian", "takeback_dtmf": "*1", "moh": "media_id" }}',
  {
    'data': {
      'action': 'transfer',
      'target': 'Lumian',
      'takeback_dtmf': '*1',
      'moh': 'media_id'
    }
  },
  {
    headers: {
      'Content-Type': 'application/json',
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Available action values are transfer, hangup, break, callflow, move and intercept.

Move

Sample Request:

curl -v -x POST \
    -H "Content-Type: application/json" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"action": "move", "owner_id": "2e04e3205b36b6291f854995e80985b0", "device_id": "c27b0a86c5e7b0f2a5999967fd8bbf09", "auto_answer": true, "can_call_self": true, "dial_strategy": "simultaneous"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/channels/{UUID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/channels/{UUID}',
  // '{"data": {"action": "move", "owner_id": "2e04e3205b36b6291f854995e80985b0", "device_id": "c27b0a86c5e7b0f2a5999967fd8bbf09", "auto_answer": true, "can_call_self": true, "dial_strategy": "simultaneous"}}',
  {
    'data': {
      'action': 'move',
      'owner_id': '2e04e3205b36b6291f854995e80985b0',
      'device_id': 'c27b0a86c5e7b0f2a5999967fd8bbf09',
      'auto_answer': true,
      'can_call_self': true,
      'dial_strategy': 'simultaneous'
    }
  },
  {
    headers: {
      'Content-Type': 'application/json',
      'X-Auth-Token': '{AUTH_TOKEN}'
    },
    proxy: {
      protocol: 'http',
      host: 'POST',
      port: 1080
    }
  }
);
Key Description Type Default Required
auto_answer Whether to auto-answer the new leg boolean() false false
can_call_self Can intercept devices of the same targeted user boolean() true false
device_id Move the call to a specific device string() false
dial_strategy How to ring the endpoints, if multiple string() simultaneous false
owner_id User ID to use for finding endpoints string() false

Transfer

Sample Request:

curl -v -X POST \
    -H "Content-Type: application/json" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"module":"transfer","data":{"target":"Lumian","Transfer-Type":"blind","leg":"bleg"}},"action":"metaflow"}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/channels/{UUID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/channels/{UUID}',
  {
    'data': {
      'module': 'transfer',
      'data': {
        'target': 'Lumian',
        'Transfer-Type': 'blind',
        'leg': 'bleg'
      }
    },
    'action': 'metaflow'
  },
  {
    headers: {
      'Content-Type': 'application/json',
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);
Key Description Type Default
leg Defines which leg of the call to take action against `string('self' 'bleg')`
target Extension/DID to transfer the {UUID} string()
transfer-type What type of transfer to perform `string('attended' 'blind')`
moh Music on hold to play while transferring string()

Put a feature (metaflow) on a channel

!!! note This API requires Konami Pro to be running and metaflows to be enabled on the call

PUT /v2/accounts/{ACCOUNT_ID}/channels/{UUID}

Sample Request:

curl -v -X PUT \
    -H "Content-Type: application/json" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"action":"metaflow", "data": {"data": { "module": "hangup" }}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/channels/{UUID}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/channels/{UUID}',
  // '{"action":"metaflow", "data": {"data": { "module": "hangup" }}}',
  {
    'action': 'metaflow',
    'data': {
      'data': {
        'module': 'hangup'
      }
    }
  },
  {
    headers: {
      'Content-Type': 'application/json',
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

The Metaflow feature is a metaflow object which validates with its corresponding JSON schema.

Reasoning

The POST action requires that every Metaflow action would have to be coded into the module.

Benefits

The Metaflow feature allows adding new types of Metaflows without changing the code. It also allows full Metaflows and not only single actions, i.e., the children node is also processed.

Click To Call

Schema

Click-to-call allows you to create URLs that can be POSTed to with a phone number or SIP URI and create a phone call from the provided contact information to a destination you have pre-determined.

Key Description Type Default Required Support Level
auth_required Determines if this click to call requires valid auth-tokens when invoked boolean() true false
bypass_media Default bypass media mode (The string type is deprecated, please use this as a boolean) `boolean() string('true' 'false' 'auto')`
caller_id_number Explicitly set caller id number string() false
custom_application_vars./[a-zA-Z0-9\-_]+/ string() false
custom_application_vars Key-value pairs to set as custom_application_vars on the channel object() {} false
custom_sip_headers.in Custom SIP Headers to be applied to calls inbound to Lumian from the endpoint #/definitions/custom_sip_headers false
custom_sip_headers.out Custom SIP Headers to be applied to calls outbound from Lumian to the endpoint #/definitions/custom_sip_headers false
custom_sip_headers.^[a-zA-z0-9_\-]+$ The SIP header to add string() false
custom_sip_headers A property list of SIP headers object() false
dial_first Determines what will be dialed first: extension or contact `string('extension' 'contact')` false
extension The extension to connect to when the click to call is invoked string() true
media.ignore_early_media The option to determine if early media from the endpoint should always be ignored boolean() false
media object() false
music_on_hold.media_id The ID of a media object that should be used as the music on hold string(0..2048) false
music_on_hold The music on hold parameters used if not a property of the device owner object() false
name A friendly name for the click to call string(1..128) true
outbound_callee_id_name Callee ID Name of the device calling out to the contact number string() false
outbound_callee_id_number Callee ID Number of the device calling out to the contact number string() false
presence_id Static presence ID (used instead of SIP username) string() false supported
ringback Ringback to use string() false
throttle The rate that this click to call can be invoked integer() false
timeout How long, in seconds, to wait for the call to progress integer() false
whitelist.[] string(1..) false
whitelist A list of regular expressions that the click to call can dial to array(string(1..)) false

custom_sip_headers

Custom SIP headers applied to an INVITE

Key Description Type Default Required Support Level
^[a-zA-z0-9_\-]+$ The SIP header to add string() false

List all clicktocall endpoints

GET /v2/accounts/{ACCOUNT_ID}/clicktocall

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token":"{AUTH_TOKEN}",
    "data": [
        {
            "extension": "{EXTENSION}",
            "id": "{C2C_ID}",
            "name": "{NAME}"
        }
    ],
    "node": "{NODE_HASH}",
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "4.3.1"
}

Create a clicktocall endpoint

PUT /v2/accounts/{ACCOUNT_ID}/clicktocall

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"name":"{NAME}, "auth_required":false, "extension":"{EXTENSION}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall',
  '{"data":{"name":"{NAME}, "auth_required":false, "extension":"{EXTENSION}"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token":"{AUTH_TOKEN}",
    "data": {
        "auth_required": false,
        "custom_application_vars": {},
        "extension": "{EXTENSION}",
        "id": "{C2C_ID}",
        "name": "{NAME}"
    },
    "node": "{NODE_HASH}",
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "4.3.1"
}

Fetch

GET /v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token":"{AUTH_TOKEN}",
    "data": {
        "auth_required": false,
        "custom_application_vars": {},
        "extension": "{EXTENSION}",
        "id": "{C2C_ID}",
        "name": "{NAME}"
    },
    "node": "{NODE_HASH}",
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "4.3.1"
}

Change

POST /v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Patch

PATCH /v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

GET /v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}/history

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}/history
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}/history', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Execute the clicktocall with a supplied number

GET/POST /v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}/connect

Non-blocking version

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}/connect?contact={CONTACT}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}/connect?contact={CONTACT}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Will return immediately with a 202

Sample Response:

    "data": {
        "application_data": {
            "route": "{CONTACT}"
        },
        "application_name": "transfer",
        "continue_on_fail": true,
        "custom_application_vars": {
            "contact": "{CONTACT}"
        },
        "custom_channel_vars": {
            "account_id": "{ACCOUNT_ID}",
            "authorizing_id": "{C2C_ID}",
            "authorizing_type": "clicktocall",
            "auto_answer_loopback": true,
            "from_uri": "{EXTENSION}@{ACCOUNT_REALM}",
            "inherit_codec": false,
            "loopback_request_uri": "{CONTACT}@{ACCOUNT_REALM}",
            "request_uri": "{CONTACT}@{ACCOUNT_REALM}",
            "retain_cid": true
        },
        "dial_endpoint_method": "single",
        "endpoints": [
            {
                "invite_format": "loopback",
                "route": "{EXTENSION}",
                "to_did": "{EXTENSION}",
                "to_realm": "{ACCOUNT_REALM}"
            }
        ],
        "export_custom_channel_vars": [
            "Account-ID",
            "Authorizing-ID",
            "Authorizing-Type",
            "Loopback-Request-URI",
            "From-URI",
            "Request-URI"
        ],
        "ignore_early_media": true,
        "loopback_bowout": "false",
        "outbound_call_id": "c2c-{C2C_ID}-{RANDOM}",
        "outbound_callee_id_name": "{EXTENSION}",
        "outbound_callee_id_number": "{EXTENSION}",
        "outbound_caller_id_name": "{C2C NAME}",
        "outbound_caller_id_number": "{CONTACT}",
        "simplify_loopback": "false",
        "start_control_process": "false",
        "timeout": 30
    },
    "node": "{NODE_HASH}",
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "4.3.1"
}

Change

POST /v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}/connect

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}/connect
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/clicktocall/{C2C_ID}/connect',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Comments

About Comments

Allows you to add comments to "any" documents in Lumian.

Fetch

DELETE /v2/accounts/{ACCOUNT_ID}/comments

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/comments
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/comments', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": [
    ],
    "status": "success"
}

Fetch a Comment

GET /v2/accounts/{ACCOUNT_ID}/comments

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/comments
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/comments', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "data": "{COMMENT}",
  "status": "success"
}

Add a Comment

PUT /v2/accounts/{ACCOUNT_ID}/comments

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"comments": [{COMMENT_3}]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/comments
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/comments',
  '{"data": {"comments": [{COMMENT_3}]}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {
        "comments": [
            "{COMMENT_1}",
            "{COMMENT_2}",
            "{COMMENT_3}"
        ]
    },
    "status": "success"
}

Delete a Comment

DELETE /v2/accounts/{ACCOUNT_ID}/comments/{COMMENT_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/comments/{COMMENT_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/comments/{COMMENT_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "comments": [
            "{COMMENT_1}",
            "{COMMENT_2}"
        ]
    },
    "status": "success"
}

Fetch a Comment

GET /v2/accounts/{ACCOUNT_ID}/comments/{COMMENT_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/comments/{COMMENT_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/comments/{COMMENT_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "comments": [
            "{COMMENT_1}",
            "{COMMENT_2}"
        ]
    },
    "status": "success"
}

Update a Comment

POST /v2/accounts/{ACCOUNT_ID}/comments/{COMMENT_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": "{COMMENT}"}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/comments/{COMMENT_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/comments/{COMMENT_ID}',
  '{"data": "{COMMENT}"}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": "{COMMENT}",
    "status": "success"
}

Configuring Conferences

About Conferences

Conference documents are enriched with real-time information along side their configuration, namely showing the number of members, number of moderators, duration of the conference, conference locked status.

The real-time information (if available) is added to conference document under _read_only key (to avoid accident document update).

Schema

Schema for conferences

Key Description Type Default Required Support Level
bridge_password the password used for a conference bridge string() false
bridge_username the username used for a conference bridge string() false
caller_controls caller controls (config settings) string() false
conference_numbers.[] string() false
conference_numbers Defines conference numbers that can be used by members or moderators array(string()) [] false
controls controls object() false
domain domain string() false
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
focus This is a read-only property indicating the media server hosting the conference string() false
language Prompt language to play in the conference string() false
max_members_media Media to play when the conference is full string() false
max_participants The maximum number of participants that can join integer() false
member.join_deaf Determines if a member will join deaf boolean() false false supported
member.join_muted Determines if a member will join muted boolean() true false supported
member.numbers.[] string() false
member.numbers Defines the conference (call in) number(s) for members array(string()) [] false
member.pins.[] string() false
member.pins Defines the pin number(s) for members array(string()) [] false
member.play_entry_prompt Whether to play the entry prompt on member join boolean() false
member Defines the discovery (call in) properties for a member object() {} false
moderator.join_deaf Determines if a moderator will join deaf boolean() false false
moderator.join_muted Determines if a moderator will join muted boolean() false false
moderator.numbers.[] string() false
moderator.numbers Defines the conference (call in) number(s) for moderators array(string()) [] false
moderator.pins.[] string() false
moderator.pins Defines the pin number(s) for moderators array(string()) [] false
moderator Defines the discovery (call in) properties for a moderator object() {} false
moderator_controls profile on the switch for controlling the conference as a moderator string() false
name A friendly name for the conference string(1..128) false supported
owner_id The user ID who manages this conference string(32) false supported
play_entry_tone Whether to play an entry tone, or the entry tone to play `boolean() string()` false
play_exit_tone Whether to play an exit tone, or the exit tone to play `boolean() string()` false
play_name Do we need to announce new conference members? boolean() false false
play_welcome Whether to play the welcome prompt boolean() false
profile Profile configuration object() false
profile_name conference profile name string() false
require_moderator does the conference require a moderator boolean() false
wait_for_moderator should members wait for a moderator before joining the conference boolean() false

conferences.profile

Schema for conference profiles

Key Description Type Default Required Support Level
alone-sound Audio that plays while you are alone in the conference string() false
announce-count Play member count to conference when above this threshold integer() false
caller-controls Name of the caller control group string() false
comfort-noise The volume level of background white noise integer() false
energy-level Energy level required for audio to be sent to other users integer() false
enter-sound Audio to play when entering a conference string() false
exit-sound Audio to play when exiting a conference string() false
interval Milliseconds per frame integer() false
locked-sound Audio to play when the conference is locked string() false
max-members Set the maximum number of members in the conference integer() false
max-members-sound If max-members has been reached, audio to play to caller instead of joining the conference string() false
moderator-controls Name of the moderator control group to use string() false
moh-sound Audio to play, on a loop, while participant count is 1 string() false
muted-sound Audio to play when muted string() false
rate Audio sample rate integer() false
unmuted-sound Audio to play when unmuted string() false

Keys under development

Perform an action on a conference

PUT /v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}

Sample Request:

curl -v -X PUT \
    -d '{"action": "{CONFERENCE_ACTION}", "data": {"ACTION":"DATA"}}' \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}',
  '{"action": "{CONFERENCE_ACTION}", "data": {"ACTION":"DATA"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);
Action Description
lock Lock the conference; no new participants may join
unlock Unlock the conference; new participants may join
dial Dial an endpoint (user/device/DID)
play Play media to the conference (all participants)

Dialing an endpoint

Sometimes you want to dial out from a conference to an endpoint (versus waiting for the caller to dial into the conference). Similar to how the group callflow works, you can include device and user IDs; unlike groups, you can include DIDs as well (similar to quickcall/click2call).

Schema

Schema for conferences

Key Description Type Default Required Support Level
bridge_password the password used for a conference bridge string() false
bridge_username the username used for a conference bridge string() false
caller_controls caller controls (config settings) string() false
conference_numbers.[] string() false
conference_numbers Defines conference numbers that can be used by members or moderators array(string()) [] false
controls controls object() false
domain domain string() false
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
focus This is a read-only property indicating the media server hosting the conference string() false
language Prompt language to play in the conference string() false
max_members_media Media to play when the conference is full string() false
max_participants The maximum number of participants that can join integer() false
member.join_deaf Determines if a member will join deaf boolean() false false supported
member.join_muted Determines if a member will join muted boolean() true false supported
member.numbers.[] string() false
member.numbers Defines the conference (call in) number(s) for members array(string()) [] false
member.pins.[] string() false
member.pins Defines the pin number(s) for members array(string()) [] false
member.play_entry_prompt Whether to play the entry prompt on member join boolean() false
member Defines the discovery (call in) properties for a member object() {} false
moderator.join_deaf Determines if a moderator will join deaf boolean() false false
moderator.join_muted Determines if a moderator will join muted boolean() false false
moderator.numbers.[] string() false
moderator.numbers Defines the conference (call in) number(s) for moderators array(string()) [] false
moderator.pins.[] string() false
moderator.pins Defines the pin number(s) for moderators array(string()) [] false
moderator Defines the discovery (call in) properties for a moderator object() {} false
moderator_controls profile on the switch for controlling the conference as a moderator string() false
name A friendly name for the conference string(1..128) false supported
owner_id The user ID who manages this conference string(32) false supported
play_entry_tone Whether to play an entry tone, or the entry tone to play `boolean() string()` false
play_exit_tone Whether to play an exit tone, or the exit tone to play `boolean() string()` false
play_name Do we need to announce new conference members? boolean() false false
play_welcome Whether to play the welcome prompt boolean() false
profile Profile configuration object() false
profile_name conference profile name string() false
require_moderator does the conference require a moderator boolean() false
wait_for_moderator should members wait for a moderator before joining the conference boolean() false

conferences.profile

Schema for conference profiles

Key Description Type Default Required Support Level
alone-sound Audio that plays while you are alone in the conference string() false
announce-count Play member count to conference when above this threshold integer() false
caller-controls Name of the caller control group string() false
comfort-noise The volume level of background white noise integer() false
energy-level Energy level required for audio to be sent to other users integer() false
enter-sound Audio to play when entering a conference string() false
exit-sound Audio to play when exiting a conference string() false
interval Milliseconds per frame integer() false
locked-sound Audio to play when the conference is locked string() false
max-members Set the maximum number of members in the conference integer() false
max-members-sound If max-members has been reached, audio to play to caller instead of joining the conference string() false
moderator-controls Name of the moderator control group to use string() false
moh-sound Audio to play, on a loop, while participant count is 1 string() false
muted-sound Audio to play when muted string() false
rate Audio sample rate integer() false
unmuted-sound Audio to play when unmuted string() false

Endpoints

Dial-able endpoints are

  1. Devices (by device id or device JSON)
  2. Users (by user id)
  3. Phone Numbers
  4. SIP URIs (sip:user@realm)

Note: Phone numbers will involve some internal legs being generated (loopback legs) to process the number as if it was a call coming in for the desired number. This means billing and limits will be applied just the same as if a user dialed the number from their device.

Dial schema

Schema for conference dial API command

Key Description Type Default Required Support Level
caller_id_name Caller ID Name to use when dialing out to endpoints string() false
caller_id_number Caller ID Number to use when dialing out to endpoints string() false
endpoints.[] `string() #/definitions/devices`
endpoints Endpoints to dial out to and join to the conference array() true
participant_flags.[] `string('mute' 'deaf' 'distribute_dtmf' 'is_moderator'
participant_flags Participant flags applied to each endpoint when it joins the conference `array(string('mute' 'deaf' 'distribute_dtmf' 'is_moderator'
profile_name The profile name to use for configuration string() false
target_call_id Existing UUID to use as a hint for where to start the conference string() false
timeout How long to try to reach the endpoint(s) integer() false

Example payloads

Sample Response:

{
    "action":"dial"
    ,"data":{
        "data":{
            "endpoints":["{DEVICE_ID}","{USER_ID}","{NUMBER}","sip:{URI}"],
            "caller_id_name":"Conference XYZ",
            "caller_id_number":"5551212"
        }
    }
}

As when making QuickCalls, you can include custom_application_vars:

Sample Response:

{
    "action":"dial"
    ,"data":{
        "custom_application_vars":{
            "foo":"bar"
        }
        ,"data":{
            "endpoints":["{DEVICE_ID}","{USER_ID}","{NUMBER}","sip:{URI}"],
            "caller_id_name":"Conference XYZ",
            "caller_id_number":"5551212"
        }
    }
}

You can also include the outbound call id you'd like the leg to use:

Sample Response:

{
    "action":"dial"
    ,"data":{
        "custom_application_vars":{
            "foo":"bar"
        }
        ,"data":{
            "endpoints":["{DEVICE_ID}","{USER_ID}","{NUMBER}","sip:{URI}"],
            "caller_id_name":"Conference XYZ",
            "caller_id_number":"5551212",
            "outbound_call_id":"xyz-abc"
        }
    }
}

Participant Flags

You can specify how a participant will enter a conference with a list of attributes:

Value Description
deaf Participant joins unable to hear conference audio
disable_moh Disable music on hold when the participant is the only one in the conference
distribute_dtmf Send DTMF from participant's leg to all other participants
ghost Uncounted in conference membership total
is_moderator Participant will join as a moderator
join_existing Participant may only join a running conference (won't start a conference)
mute Participant joins muted
video_mute Participant joins with video stream muted

Sample Response:

{
    "action":"dial"
    ,"data":{
        "data":{
            "endpoints":["{DEVICE_ID}","{USER_ID}","{NUMBER}","sip:{URI}"],
            "caller_id_name":"Conference XYZ",
            "caller_id_number":"5551212",
            "participant_flags":["deaf", "mute"]
        }
    }
}

Dialing out to a dynamic conference

Sometimes you want to create ad-hoc conferences and put a participant in there. You can PUT a conference and the endpoints to dial out to create a temporary conference. The {CONFERENCE_ID} you supply will be used to name the conference and any conference schema parameters in the request will be used when creating the conference. For example:

Sample Response:

{
    "action":"dial"
    ,"data":{
        "data":{
            "endpoints":["{DEVICE_ID}","{USER_ID}","{NUMBER}","sip:{URI}"],
            "caller_id_name":"Conference XYZ",
            "caller_id_number":"5551212",
            "play_entry_tone": true,
            "play_exit_tone": true,
            "play_name": false
        }
    }
}

These properties will be merged into a "default" conference document and then executed the same as if the conference was preconfigured.

The API response

Sample Response:

{
    "data": {
        "endpoint_responses": [
            {
                "call_id": "522bb682-da03-11e7-8ce9-5364ba916f96",
                "endpoint_id": "{ENDPOINT_FROM_REQUEST}",
                "job_id": "137b1e9e-d9fb-11e7-8cad-5364ba916f96",
                "message": "dial resulted in call id 522bb682-da03-11e7-8ce9-5364ba916f96",
                "status": "success"
            }
        ]
    }
    ,...
}

Playing media to a conference

Playing a media file to everyone in a conference:

Sample Response:

{
    "action":"play",
    "data": {
        "data":{"media_id":"{MEDIA_ID}"}
    }
}

{MEDIA_ID} can be a pre-uploaded media ID or a URL to fetch media from.

Perform an action on participants

PUT /v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants

Sample Request:

curl -v -X PUT \
    -d '{"data": {"action": {PARTICIPANTS_ACTION}}}' \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants',
  '{"data": {"action": {PARTICIPANTS_ACTION}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);
Action Description
mute Mute all the participants that are currently unmuted
unmute Unmute all the participants that are currently muted
deaf Stop sending conference audio to all participants
undeaf Start sending conference audio to all participants
kick Kick all the participants from the conference
relate Relate two participants

Relate participants

The relate action takes a data object:

{
    "data":{
        "action":"relate"
        ,"data":{
            "participant_id":{ID}
            ,"other_participant":{ID}
            ,"relationship":"{RELATIONSHIP}"
        }
    }
}
Key Description Type Default Required
other_participant The other participant ID to relate `string() integer()`
participant_id The participant ID to relate `string() integer()`
relationship The relationship to establish between the two participants `string('deaf' 'clear' 'mute')`

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: $AUTH_TOKEN" \
    "http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants" \
    -d'{"data":{"action":"relate","data":{"participant_id":23, "other_participant":24}}}'
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants',
  '{"data":{"action":"relate","data":{"participant_id":23, "other_participant":24}}}',
  {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": "relating participants",
    "node": "{NODE}",
    "request_id": "{REQUEST_ID}",
    "revision": "1-100475067fa624422c9a21bd976c7b84",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "4.2.2"
}

Perform an action on participant

PUT /v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants/{PARTICIPANT_ID}

Sample Request:

curl -v -X PUT \
    -d '{"data": {"action": {PARTICIPANT_ACTION}}}' \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants/{PARTICIPANT_ID}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants/{PARTICIPANT_ID}',
  '{"data": {"action": {PARTICIPANT_ACTION}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sometimes you may get a HTTP/1.1 304 Not Modified response from crossbar for similar API calls. If you do, add a random string filter to the end of the call to ensure the request is viewed as 'unique'. For example:

Sample Request:

curl -v -X PUT \
    -d '{"data": {"action": {PARTICIPANT_ACTION}}}' \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants/{PARTICIPANT_ID}?random={RANDOM_BIT}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants/{PARTICIPANT_ID}?random={RANDOM_BIT}',
  '{"data": {"action": {PARTICIPANT_ACTION}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);
Action Description
mute Mute the participant
unmute Unmute the participant
deaf Stop sending conference audio to the participant
undeaf Start sending conference audio to the participant
kick Kick the participant from the conference
play Play media to a single participant

Playing media to a conference

Playing a media file to everyone in a conference:

Sample Response:

{"data":{
    "action":"play",
    "data":{"media_id":"{MEDIA_ID}"}
 }
}

{MEDIA_ID} can be a pare-uploaded media ID or a URL to fetch media from.

List of conferences example

Sample Response:

[
    {
        "id": "",
        "name": "",
        "owner_id": "",
        "member": {
            "join_muted": false,
            "join_deaf": false,
            "numbers": [],
            "pins": []
        },
        "moderator": {
            "join_deaf": false,
            "join_muted": false,
            "numbers": [],
            "pins": []
        },
        "members": 0,
        "admins": 0,
        "duration": 0,
        "is_locked": false
    },
    ...
]

Conference document

Sample Response:

{
    "name": "Conf",
    "id": "",
    "owner_id": "",
    "play_entry_tone": true,
    "play_exit_tone": true,
    "play_name": false,
    "conference_numbers": [],
    "member": {
        "join_muted": false,
        "join_deaf": false,
        "numbers": [],
        "pins": []
    },
    "ui_metadata": {
        "ui": "lumian-ui"
    },
    "moderator": {
        "join_deaf": false,
        "join_muted": false,
        "numbers": [],
        "pins": []
    },
    "_read_only": {
        "members": 0,
        "admins": 0,
        "duration": 0,
        "is_locked": false,
        "participants": [
            {
                "call_id": "",
                "conference_name": "",
                "conference_uuid": "",
                "switch_hostname": "",
                "floor": false,
                "hear": true,
                "speak": true,
                "talking": false,
                "mute_detect": false,
                "participant_id": 1,
                "energy_level": 20,
                "current_energy": 0,
                "video": false,
                "is_moderator": false,
                "join_time": 63635217275,
                "duration": 10
            },
            ...
        ]
    }
}

join_time is participant's join time as epoch, duration is number of seconds participant participate in conference.

Here we can see values set up for a Member, then for a Moderator.

The last field, play_entry_tone, is at the root of the document: meaning this field applies to everyone in the conference.

Available fields

Actions

Actions are JSON objects in format:

Sample Response:

{
    "action": "{ACTION}"
}

Conference actions

lock: lock conference (prevent participants to join) unlock: unlock conference (allow everybody to join)

Participants actions

mute/unmute: mute/unmute all participants except moderators deaf/undeaf: deaf/undeaf all participants except moderators kick: kick every participant out

Participant actions

mute/unmute: mute/unmute participant deaf/undeaf: deaf/undeaf participant kick: kick participant

Web-socket events

A client may subscribe to conference event using websocket connection. Participant events are published as amqp conference.event.{conference_id}.{call_id}, where call_id is participant"s call.

The list of published events is determined by publish_participant_event parameter of ecallmgr configuration, if parameter is unset, then all events are published.

Participant events

add-member del-member stop-talking start-talking mute-member unmute-member deaf-member undeaf-member

Example event

Sample Response:

{
    "custom_channel_vars": {
        "account_id": "9d351ad7ffd6f846313af9eed3bb7b85",
        "authorizing_id": "6507f40b09a61fbb8b025dbad9316eb5",
        "authorizing_type": "device",
        "owner_id": "32d8788da9506b4b1991d5bb86d27b0a",
        "presence_id": "1000@kamailio.lumian",
        "fetch_id": "56507071-a216-4e0a-a28f-ff3bd9c86ac3",
        "bridge_id": "934800819",
        "precedence": 5,
        "realm": "kamailio.lumian",
        "username": "sip1",
        "call_interaction_id": "63635497023-3e247b2e"
    },
    "channel_presence_id": "1000@kamailio.lumian",
    "caller_id_number": "sip1",
    "caller_id_name": "sip1",
    "mute_detect": false,
    "video": false,
    "energy_level": 20,
    "current_energy": 0,
    "talking": false,
    "speak": true,
    "hear": true,
    "floor": false,
    "participant_id": 20,
    "instance_id": "d5765180-53d5-4104-860e-b352f3f8e6b1",
    "conference_id": "5edbfdd3b825314a71b0a05957392edb",
    "focus": "freeswitch@freeswitch.lumian",
    "call_id": "934800819",
    "event": "add-member",
    "node": "lumian@jh460",
    "msg_id": "a6fbbf034b5cd3af",
    "event_name": "participant_event",
    "event_category": "conference",
    "app_version": "4.0.0",
    "app_name": "ecallmgr",
    "routing_key": "participant_event"
}

Conferences

About Conferences

Schema

Schema for conferences

Key Description Type Default Required Support Level
bridge_password the password used for a conference bridge string() false
bridge_username the username used for a conference bridge string() false
caller_controls caller controls (config settings) string() false
conference_numbers.[] string() false
conference_numbers Defines conference numbers that can be used by members or moderators array(string()) [] false
controls controls object() false
domain domain string() false
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
focus This is a read-only property indicating the media server hosting the conference string() false
language Prompt language to play in the conference string() false
max_members_media Media to play when the conference is full string() false
max_participants The maximum number of participants that can join integer() false
member.join_deaf Determines if a member will join deaf boolean() false false supported
member.join_muted Determines if a member will join muted boolean() true false supported
member.numbers.[] string() false
member.numbers Defines the conference (call in) number(s) for members array(string()) [] false
member.pins.[] string() false
member.pins Defines the pin number(s) for members array(string()) [] false
member.play_entry_prompt Whether to play the entry prompt on member join boolean() false
member Defines the discovery (call in) properties for a member object() {} false
moderator.join_deaf Determines if a moderator will join deaf boolean() false false
moderator.join_muted Determines if a moderator will join muted boolean() false false
moderator.numbers.[] string() false
moderator.numbers Defines the conference (call in) number(s) for moderators array(string()) [] false
moderator.pins.[] string() false
moderator.pins Defines the pin number(s) for moderators array(string()) [] false
moderator Defines the discovery (call in) properties for a moderator object() {} false
moderator_controls profile on the switch for controlling the conference as a moderator string() false
name A friendly name for the conference string(1..128) false supported
owner_id The user ID who manages this conference string(32) false supported
play_entry_tone Whether to play an entry tone, or the entry tone to play `boolean() string()` false
play_exit_tone Whether to play an exit tone, or the exit tone to play `boolean() string()` false
play_name Do we need to announce new conference members? boolean() false false
play_welcome Whether to play the welcome prompt boolean() false
profile Profile configuration object() false
profile_name conference profile name string() false
require_moderator does the conference require a moderator boolean() false
wait_for_moderator should members wait for a moderator before joining the conference boolean() false

conferences.profile

Schema for conference profiles

Key Description Type Default Required Support Level
alone-sound Audio that plays while you are alone in the conference string() false
announce-count Play member count to conference when above this threshold integer() false
caller-controls Name of the caller control group string() false
comfort-noise The volume level of background white noise integer() false
energy-level Energy level required for audio to be sent to other users integer() false
enter-sound Audio to play when entering a conference string() false
exit-sound Audio to play when exiting a conference string() false
interval Milliseconds per frame integer() false
locked-sound Audio to play when the conference is locked string() false
max-members Set the maximum number of members in the conference integer() false
max-members-sound If max-members has been reached, audio to play to caller instead of joining the conference string() false
moderator-controls Name of the moderator control group to use string() false
moh-sound Audio to play, on a loop, while participant count is 1 string() false
muted-sound Audio to play when muted string() false
rate Audio sample rate integer() false
unmuted-sound Audio to play when unmuted string() false

Fetch

HTTP Request

GET /v2/accounts/{ACCOUNT_ID}/conferences

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

HTTP Request

PUT /v2/accounts/{ACCOUNT_ID}/conferences

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

HTTP Request

GET /v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

HTTP Request

PUT /v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Change

HTTP Request

POST /v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Patch

HTTP Request

PATCH /v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

HTTP Request

DELETE /v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

HTTP Request

GET /v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

HTTP Request

PUT /v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

HTTP Request

GET /v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants/{PARTICIPANT_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants/{PARTICIPANT_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants/{PARTICIPANT_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

HTTP Request

PUT /v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants/{PARTICIPANT_ID}

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants/{PARTICIPANT_ID}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/conferences/{CONFERENCE_ID}/participants/{PARTICIPANT_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Configs

Certain settings are available per account. What settings are understood by Lumian is determined by account_config.{CONFIG_ID}.json schemas. Please note not all possible configuration options are available on per-account basis, some (most) of them are system-wide.

About Configs

All requests returns merged configuration. Following documents are merged (if they are exists): account-specific configuration, account's reseller account-specific configuration, then default section of system config of the same name, and then default values deduced from system config schema.

On PUT/POST/PATCH operations a difference of parent configuration is calculated and written as account-specific config document. Therefore if a provided value is equal to default it will not be stored in account-specific config document.

If the difference with default is empty, then account-specific configuration document will be deleted.

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "data": {
    "entry_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,659);v=-7;>=3;+=.1;%(800,0,659,783)",
    "exit_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,440);v=-7;>=3;+=.1;%(800,0,349,440)",
    "moderator_entry_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,659);v=-7;>=3;+=.1;%(800,0,659,783)",
    "moderator_exit_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,440);v=-7;>=3;+=.1;%(800,0,349,440)",
    "number_timeout": 5000,
    "pin_timeout": 5000,
    "support_name_announcement": true,
    "id": "configs_conferences"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_ID}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Create

PUT /v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

PUT is completely equal to POST

Change

POST /v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d @data.json \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}',
  '@data.json',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Request's data.json:

Sample Response:

{
  "data": {
    "entry_tone": "tone_stream://%(1000,0,2600)",
    "exit_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,440);v=-7;>=3;+=.1;%(800,0,349,440)",
    "moderator_entry_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,659);v=-7;>=3;+=.1;%(800,0,659,783)",
    "moderator_exit_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,440);v=-7;>=3;+=.1;%(800,0,349,440)",
    "number_timeout": 5000,
    "pin_timeout": 5000,
    "support_name_announcement": true,
    "id": "configs_conferences"
  }
}

Response:

Sample Response:

{
  "data": {
    "entry_tone": "tone_stream://%(1000,0,2600)",
    "exit_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,440);v=-7;>=3;+=.1;%(800,0,349,440)",
    "moderator_entry_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,659);v=-7;>=3;+=.1;%(800,0,659,783)",
    "moderator_exit_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,440);v=-7;>=3;+=.1;%(800,0,349,440)",
    "number_timeout": 5000,
    "pin_timeout": 5000,
    "support_name_announcement": true,
    "id": "configs_conferences"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_ID}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Patch

PATCH /v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"pin_timeout":3000}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}',
  '{"data":{"pin_timeout":3000}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
  "data": {
    "entry_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,659);v=-7;>=3;+=.1;%(800,0,659,783)",
    "exit_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,440);v=-7;>=3;+=.1;%(800,0,349,440)",
    "moderator_entry_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,659);v=-7;>=3;+=.1;%(800,0,659,783)",
    "moderator_exit_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,440);v=-7;>=3;+=.1;%(800,0,349,440)",
    "number_timeout": 5000,
    "pin_timeout": 3000,
    "support_name_announcement": true,
    "id": "configs_conferences"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_ID}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/configs/{CONFIG_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Configuration is effectively reset to default:

Sample Response:

{
  "data": {
    "entry_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,659);v=-7;>=3;+=.1;%(800,0,659,783)",
    "exit_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,440);v=-7;>=3;+=.1;%(800,0,349,440)",
    "moderator_entry_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,659);v=-7;>=3;+=.1;%(800,0,659,783)",
    "moderator_exit_tone": "tone_stream://v=-7;>=2;+=.1;%(300,0,523,440);v=-7;>=3;+=.1;%(800,0,349,440)",
    "number_timeout": 5000,
    "pin_timeout": 5000,
    "support_name_announcement": true,
    "id": "configs_conferences"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_ID}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Crossbar Configuration

System Configs

The following table outlines the configs that can be found in the system_config database, crossbar document:

Key Type Default Description
api_auth_tokens integer() 35 Default token cost of creating an auth token via API key
autoload_modules list(string()) The list of Crossbar modules initially started
cache_ttl integer() 300 Cache TTL, in seconds
cleanup_timer integer() 86400 Time, in seconds, to run the cleanup routines
compress_response_body boolean() true Whether to compress the response body before sending
default_allow_anonymous_quickcalls boolean() true Whether to allow unauthenticated quickcall API requests
default_language string() en-US The default language, if none are defined on the account
magic_path_patterns list(string()) Magic path templates
max_upload_size integer() 8000000 bytes (8Mb) Max upload size for request bodies
maximum_range integer() 2682000 Maximum range, in seconds, for time-based view queries
pagination_page_size integer() 50 Default page size when paginating
port integer() 8000 Port to listen for unencrypted traffic
pretty_metrics boolean() true Pretty-print metrics in logs
request_timeout_ms integer() 10000 Time, in milliseconds, for requests to timeout
reset_id_size integer() 250 Password-reset ID length
schema_strict_validation boolean() false Toggles whether to perform type conversions on client data when validating
soft_delete_pause_ms integer() 10000 Time, in milliseconds, to pause between deletions
ssl_ca_cert string() undefined Path to CA cert file
ssl_cert string() /path/to/crossbar/priv/ssl/crossbar.crt Path to cert file
ssl_key string() /path/to/crossbar/priv/ssl/crossbar.key Path to key file
ssl_password string() "" Cert password
ssl_port integer() 8443 Port to listen for SSL traffic
ssl_workers integer() 100 Number of SSL listeners to start
token_costs integer() 1 Default token cost of an API request
trace_path string() /tmp Path to put trace files when profiling API requests
use_plaintext boolean() true Whether to start unencrypted listener (port 8000 traffic, typically)
use_ssl boolean() false Whether to start an SSL listener
user_auth_tokens integer() 35 Default token cost of creating an auth token via username
workers integer() 100 Number of TCP listeners to start

Additional Configs

Some modules use the crossbar namespace to create a specific system_config document for settings as well.

crossbar.accounts

crossbar.auth

crossbar.braintree

crossbar.callflows

crossbar.cdrs

crossbar.devices

crossbar.fax

crossbar.freeswitch

crossbar.local_resources

crossbar.media

crossbar.notifications

crossbar.onboard

crossbar.port_requests

crossbar.presence

crossbar.provisioner_templates

crossbar.queues

crossbar.resource_selectors

crossbar.resource_templates

crossbar.resources

crossbar.services

crossbar.sms

crossbar.token_restrictions

Connectivity

About Connectivity

Schema

Trunkstore configuration document - this is old stuff; do not recommend building off this if possible

Key Description Type Default Required Support Level
account.caller_id.cid_name string(0..35) false
account.caller_id.cid_number string(0..35) false
account.caller_id object() false
account.emergency_caller_id.cid_name string(0..35) false
account.emergency_caller_id.cid_number string(0..35) false
account.emergency_caller_id object() false
account.trunks The number of two-way trunks this account has purchased integer() false
account Information that applies to the account as a whole object() false
name Human-friendly name of the trunkstore account string() false
servers.[].DIDs./^\+?\d*$/.caller_id.cid_name string(1..35) false
servers.[].DIDs./^\+?\d*$/.caller_id.cid_number string(1..35) false
servers.[].DIDs./^\+?\d*$/.caller_id object() false
servers.[].DIDs./^\+?\d*$/.failover.e164 An E.164 formatted DID to dial for failover string() false
servers.[].DIDs./^\+?\d*$/.failover.sip A SIP URI (sip:user@host) to call for failover string() false
servers.[].DIDs./^\+?\d*$/.failover Route inbound call to another destination if this server fails to handle the call object() false
servers.[].DIDs./^\+?\d*$/.force_outbound boolean() false false
servers.[].DIDs./^\+?\d*$/.options.[] string() false
servers.[].DIDs./^\+?\d*$/.options array(string()) false
servers.[].DIDs./^\+?\d*$/ object() false
servers.[].DIDs object() false
servers.[].auth.auth_method What type of auth mechanism to use `string('password' 'Password' 'IP' 'ip')`
servers.[].auth.auth_password Password of the user@auth_realm string(1..) false
servers.[].auth.auth_user Username for authentication string(1..) false
servers.[].auth.ip IP (sip) address for this device string() false
servers.[].auth.port Port to send SIP traffic for the remote device integer() false
servers.[].auth object() true
servers.[].name Human-friendly name of the server string(1..) false
servers.[].options.caller_id.cid_name string(1..35) false
servers.[].options.caller_id.cid_number string(1..35) false
servers.[].options.caller_id object() false
servers.[].options.delay The time, in seconds, to wait before attempting to call the server integer() 0 false
servers.[].options.dynamic_flags.[] string() false
servers.[].options.dynamic_flags List of function names (or 'zone') that are called on the Call record to populate the 'flags' array sent to the resource(s) for matching array(string()) false
servers.[].options.emergency_caller_id.cid_name string(0..35) false
servers.[].options.emergency_caller_id.cid_number string(0..35) false
servers.[].options.emergency_caller_id object() false
servers.[].options.enabled Is the server ready for sending and receiving calls boolean() true false
servers.[].options.failover.e164 An E.164 formatted DID to dial for failover string() false
servers.[].options.failover.sip A SIP URI (sip:user@host) to call for failover string() false
servers.[].options.failover Route inbound call to another destination if this server fails to handle the call object() false
servers.[].options.flags.[] string() false
servers.[].options.flags List of flags to use when matching resources to route the call array(string()) false
servers.[].options.force_outbound If true, will send the call over configured carriers instead of to the server (as opposed to the 'enabled' flag, which will reject the calls) boolean() false false
servers.[].options.hunt_account_id When using local resources, use this account instead of the account making the call (useful for resellers) string() false
servers.[].options.hunt_non_reconcilable Whether to allow routing to continue on a non-reconcilable TO number boolean() false false
servers.[].options.ignore_early_media boolean() false
servers.[].options.inbound_format Determines how the INVITE is sent to the server `string('e164' 'npan' '1npan' 'username')`
servers.[].options.ip IP (sip) address for this device string() false
servers.[].options.media_handling Determine whether the switch should be in the media path or not `string('process' 'bypass')` bypass false
servers.[].options.port Port to send SIP traffic for the remote device integer() false
servers.[].options.progress_timeout The time, in seconds, to wait for the server to progress in the call, before trying an optionally defined failover route or terminating the call integer() false
servers.[].options.sip_headers List of arbitrary SIP headers to add to the INVITE array(object()) false
servers.[].options.timeout The time, in seconds, to wait for an answer from the server integer() false
servers.[].options object() false
servers What servers will be allowed to make/receive calls via this account array(object()) [] false

Fetch

GET /v2/accounts/{ACCOUNT_ID}/connectivity

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/connectivity
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/connectivity', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/connectivity

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/connectivity
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/connectivity',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/connectivity/{CONNECTIVITY_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/connectivity/{CONNECTIVITY_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/connectivity/{CONNECTIVITY_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/connectivity/{CONNECTIVITY_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/connectivity/{CONNECTIVITY_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/connectivity/{CONNECTIVITY_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Patch

PATCH /v2/accounts/{ACCOUNT_ID}/connectivity/{CONNECTIVITY_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/connectivity/{CONNECTIVITY_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/connectivity/{CONNECTIVITY_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/connectivity/{CONNECTIVITY_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/connectivity/{CONNECTIVITY_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/connectivity/{CONNECTIVITY_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Contact List

About Contact List

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/contact_list

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/contact_list
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/contact_list', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "external_number": "+14157775555",
            "internal_number": "1000",
            "name": "John Quux"
        }
    ],
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Devices

About Devices

Devices are the endpoints assigned to an account that serve that account's needs. Devices like fax machines, SIP phones, soft phone clients, and cell phones (via call forwarding), among others, can be represented by Lumian devices.

Schema

A device be it a SIP phone or landline number

Key Description Type Default Required Support Level
call_forward.direct_calls_only Determines if the calls that are not directly sent to the device should be forwarded boolean() false false supported
call_forward.enabled Determines if the call forwarding should be used boolean() false false supported
call_forward.failover Enable the call-forwarding parameters if the device is offline boolean() false false supported
call_forward.ignore_early_media The option to determine if early media from the call forwarded number should ignored boolean() true false
call_forward.keep_caller_id Determines if the caller id is kept when the call is forwarded, if not the devices caller id is used boolean() true false supported
call_forward.number The number to forward calls to string(0..15) false supported
call_forward.require_keypress Determines if the callee is prompted to press 1 to accept the call boolean() true false supported
call_forward.substitute Determines if the call forwarding replaces the device boolean() true false supported
call_forward The device call forward parameters object() false
call_recording endpoint recording settings #/definitions/call_recording false
call_restriction Device level call restrictions for each available number classification object() {} false
call_waiting Parameters for server-side call waiting #/definitions/call_waiting false
caller_id The device caller ID parameters #/definitions/caller_id false
caller_id_options.outbound_privacy Determines what appears as caller id for offnet outbound calls. Values: full - hides name and number; name - hides only name; number - hides only number; none - hides nothing `string('full' 'name' 'number' 'none')`
caller_id_options custom properties for configuring caller_id object() false
contact_list.exclude If set to true the device is excluded from the contact list boolean() false supported
contact_list Contact List Parameters object() {} false
device_type Arbitrary device type used by the UI and billing system string() false
dial_plan A list of rules used to modify dialed numbers #/definitions/dialplans false
do_not_disturb.enabled Is do-not-disturb enabled for this device? boolean() false
do_not_disturb DND Parameters object() false
enabled Determines if the device is currently enabled boolean() true false supported
exclude_from_queues Do not ring this device when calling user/agent in queue boolean() false false
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
formatters Schema for request formatters #/definitions/formatters false
hotdesk.users./^[a-zA-Z0-9]{32}$/ user-specific hotdesk settings object() false
hotdesk.users The user(s) currently hotdesked into the device object() false
hotdesk The hotdesk status of this device object() false
language The language for the device string() false supported
mac_address The MAC Address of the device (if applicable) string() false supported
media Configure audio/video/etc media options for this device #/definitions/endpoint.media false
metaflows The device metaflow parameters #/definitions/metaflows false
music_on_hold.media_id The ID of a media object that should be used as the music on hold string(0..2048) false
music_on_hold The music on hold parameters used if not a property of the device owner object() {} false
mwi_unsolicited_updates When true enables unsolicited mwi notifications boolean() true false
name A friendly name for the device string(1..128) true supported
outbound_flags List of flags (features) this device requires when making outbound calls `array(string()) object()` false
owner_id The ID of the user object that 'owns' the device string(32) false
presence_id Static presence ID (used instead of SIP username) string() false supported
provision.check_sync_event Value to use in Event header for device reload/reboot string() false
provision.check_sync_reboot Value to append to 'check-sync' event if phone should reboot after reloading settings string() false
provision.check_sync_reload Value to append to 'check-sync' event if phone should not reboot after reloading settings string() false
provision.combo_keys./^[0-9]+$/ Device provisioner Combo/Feature Key #/definitions/devices.combo_key false
provision.combo_keys object() false
provision.endpoint_brand Brand of the phone string() false
provision.endpoint_family Family name of the phone string() false
provision.endpoint_model Model name of the phone `string() integer()` false
provision.feature_keys./^[0-9]+$/ Device provisioner Combo/Feature Key #/definitions/devices.combo_key false
provision.feature_keys object() false
provision Provision data object() false
register_overwrite_notify When true enables overwrite notifications boolean() false false
ringtones.external The alert info SIP header added when the call is from internal sources string(0..256) false
ringtones.internal The alert info SIP header added when the call is from external sources string(0..256) false
ringtones Ringtone Parameters object() {} false
sip.custom_sip_headers.in Custom SIP Headers to be applied to calls inbound to Lumian from the endpoint #/definitions/custom_sip_headers false
sip.custom_sip_headers.out Custom SIP Headers to be applied to calls outbound from Lumian to the endpoint #/definitions/custom_sip_headers false
sip.custom_sip_headers.^[a-zA-z0-9_\-]+$ The SIP header to add string() false
sip.custom_sip_headers A property list of SIP headers object() false
sip.expire_seconds The time, in seconds, sent to the provisioner for the registration period that the device should be configured with. integer() 300 false supported
sip.ignore_completed_elsewhere When set to false the phone should not consider ring group calls answered elsewhere as missed boolean() false
sip.invite_format The SIP request URI invite format `string('username' 'npan' '1npan' 'e164'
sip.ip IP address for this device string() false supported
sip.method Method of authentication `string('password' 'ip')` password false
sip.number The number used if the invite format is 1npan, npan, or e164 (if not set the dialed number is used) string() false
sip.password SIP authentication password string(5..32) false supported
sip.realm The realm this device should use, overriding the account realm. Should rarely be necessary. string(4..253) false
sip.route The SIP URL used if the invite format is 'route' string() false supported
sip.static_route Sends all inbound calls to this string (instead of dialed number or username) string() false
sip.username SIP authentication username string(2..32) false supported
sip SIP Parameters object() {} false
suppress_unregister_notifications When true disables deregister notifications boolean() false false
timezone Device's timezone string() false supported

call_recording

endpoint recording settings

Key Description Type Default Required Support Level
any settings for any calls to/from the endpoint #/definitions/call_recording.source false
inbound settings for inbound calls to the endpoint #/definitions/call_recording.source false
outbound settings for outbound calls from the endpoint #/definitions/call_recording.source false

call_recording.parameters

Key Description Type Default Required Support Level
enabled is recording enabled boolean() false
format What format to store the recording on disk `string('mp3' 'wav')` false
record_min_sec The minimum length, in seconds, the recording must be to be considered successful. Otherwise it is deleted integer() false
record_on_answer Recording should start on answer boolean() false
record_on_bridge Recording should start on bridge boolean() false
record_sample_rate What sampling rate to use on the recording integer() false
time_limit Time limit, in seconds, for the recording integer() false
url The URL to use when sending the recording for storage string() false

call_recording.source

Key Description Type Default Required Support Level
any settings for calls from any network #/definitions/call_recording.parameters false
offnet settings for calls from offnet networks #/definitions/call_recording.parameters false
onnet settings for calls from onnet networks #/definitions/call_recording.parameters false

call_waiting

Parameters for server-side call waiting

Key Description Type Default Required Support Level
enabled Determines if server side call waiting is enabled/disabled boolean() false

caller_id

Defines caller ID settings based on the type of call being made

Key Description Type Default Required Support Level
asserted.name The asserted identity name for the object type string(0..35) false
asserted.number The asserted identity number for the object type string(0..35) false
asserted.realm The asserted identity realm for the object type string() false
asserted Used to convey the proven identity of the originator of a request within a trusted network. object() false
emergency.name The caller id name for the object type string(0..35) false
emergency.number The caller id number for the object type string(0..35) false
emergency The caller ID used when a resource is flagged as 'emergency' object() false
external.name The caller id name for the object type string(0..35) false
external.number The caller id number for the object type string(0..35) false
external The default caller ID used when dialing external numbers object() false
internal.name The caller id name for the object type string(0..35) false
internal.number The caller id number for the object type string(0..35) false
internal The default caller ID used when dialing internal extensions object() false

custom_sip_headers

Custom SIP headers applied to an INVITE

Key Description Type Default Required Support Level
^[a-zA-z0-9_\-]+$ The SIP header to add string() false

devices.combo_key

Device provisioner Combo/Feature Key

Key Description Type Default Required Support Level

dialplans

Permit local dialing by converting the dialed number to a routable form

Key Description Type Default Required Support Level
system.[] string() false
system List of system dial plans array(string()) false

endpoint.media

Schema for endpoint media options

Key Description Type Default Required Support Level
audio.codecs.[] `string('OPUS' 'CELT@32000h' 'G7221@32000h' 'G7221@16000h'
audio.codecs A list of audio codecs the endpoint supports `array(string('OPUS' 'CELT@32000h' 'G7221@32000h' 'G7221@16000h'
audio The audio media parameters object() {} false
bypass_media Default bypass media mode (The string type is deprecated, please use this as a boolean) `boolean() string('true' 'false' 'auto')`
encryption.enforce_security Is Encryption Enabled? boolean() false false
encryption.methods.[] `string('zrtp' 'srtp')` false
encryption.methods Supported Encryption Types `array(string('zrtp' 'srtp'))` [] false
encryption Encryption Parameters object() {} false
fax_option Is T.38 Supported? boolean() false
ignore_early_media The option to determine if early media from the endpoint should always be ignored boolean() false
progress_timeout The progress timeout to apply to the endpoint (seconds) integer() false
video.codecs.[] `string('H261' 'H263' 'H264' 'VP8')`
video.codecs A list of video codecs the endpoint supports `array(string('H261' 'H263' 'H264' 'VP8'))`
video The video media parameters object() {} false

formatters

Schema for request formatters

Key Description Type Default Required Support Level
^[[:alnum:]_]+$ Key to match in the route request JSON `array(#/definitions/formatters.format_options) #/definitions/formatters.format_options` false

formatters.format_options

Schema for formatter options

Key Description Type Default Required Support Level
direction Only apply the formatter on the relevant request direction `string('inbound' 'outbound' 'both')`
match_invite_format Applicable on fields with SIP URIs. Will format the username portion to match the invite format of the outbound request. boolean() false
prefix Prepends value against the result of a successful regex match string() false
regex Matches against the value, with optional capture group string() false
strip If set to true, the field will be stripped from the payload boolean() false
suffix Appends value against the result of a successful regex match string() false
value Replaces the current value with the static value defined string() false

metaflow

A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to

Key Description Type Default Required Support Level
children./.+/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
children Children metaflows object() false
data The data/arguments of the metaflow module object() {} false
module The name of the metaflow module to execute at this node string(1..64) true

metaflows

Actions applied to a call outside of the normal callflow, initiated by the caller(s)

Key Description Type Default Required Support Level
binding_digit What DTMF will trigger the collection and analysis of the subsequent DTMF sequence `string('1' '2' '3' '4'
digit_timeout How long to wait between DTMF presses before processing the collected sequence (milliseconds) integer() false
listen_on Which leg(s) of the call to listen for DTMF `string('both' 'self' 'peer')`
numbers./^[0-9]+$/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
numbers A list of static numbers with their flows object() false
patterns./.+/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
patterns A list of patterns with their flows object() false

Call forwarding

Currently the call_forward object allows you to define call forwarding or failover but not both. If call_forward.enabled is true it will take precedence and settings will be used only for call forwarding. If call_forward.enabled is false and call_forward.failover is true, failover settings will be used.

Fetch

GET /v2/accounts/{ACCOUNT_ID}/devices

Sample Request:

curl -v -X GET \
    -X "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices
import axios from 'axios';

const response = await axios('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices', {
  method: 'x-auth-token: {auth_token}'
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "device_type": "sip_device",
            "enabled": false,
            "id": "{DEVICE_ID}",
            "mac_address": "00:04:f2:ab:7e:fd",
            "name": "MyPolycom"
        }
    ],
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Create a new device

See the schema for available fields to include in the data portion

PUT /v2/accounts/{ACCOUNT_ID}/devices

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"name":"New Device"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices',
  {
    'data': {
      'name': 'New Device'
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "call_restriction": {},
        "caller_id": {},
        "contact_list": {},
        "dial_plan": {},
        "enabled": true,
        "exclude_from_queues": false,
        "id": "{DEVICE_ID}",
        "media": {
            "audio": {
                "codecs": [
                    "PCMU"
                ]
            },
            "encryption": {
                "enforce_security": false,
                "methods": []
            },
            "video": {
                "codecs": []
            }
        },
        "music_on_hold": {},
        "mwi_unsolicited_updates": true,
        "name": "New Device",
        "register_overwrite_notify": false,
        "ringtones": {},
        "sip": {
            "invite_format": "username",
            "method": "password",
            "registration_expiration": 300
        },
        "suppress_unregister_notifications": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Remove a device

DELETE /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "call_restriction": {},
        "caller_id": {},
        "contact_list": {},
        "dial_plan": {},
        "enabled": true,
        "exclude_from_queues": false,
        "id": "{DEVICE_ID}",
        "media": {
            "audio": {
                "codecs": [
                    "PCMU"
                ]
            },
            "encryption": {
                "enforce_security": false,
                "methods": []
            },
            "video": {
                "codecs": []
            }
        },
        "music_on_hold": {},
        "mwi_unsolicited_updates": true,
        "name": "New Device",
        "register_overwrite_notify": false,
        "ringtones": {},
        "sip": {
            "invite_format": "username",
            "method": "password",
            "registration_expiration": 300
        },
        "suppress_unregister_notifications": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch a device

GET /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "call_restriction": {},
        "caller_id": {},
        "contact_list": {},
        "dial_plan": {},
        "enabled": true,
        "exclude_from_queues": false,
        "id": "{DEVICE_ID}",
        "media": {
            "audio": {
                "codecs": [
                    "PCMU"
                ]
            },
            "encryption": {
                "enforce_security": false,
                "methods": []
            },
            "video": {
                "codecs": []
            }
        },
        "music_on_hold": {},
        "mwi_unsolicited_updates": true,
        "name": "New Device",
        "register_overwrite_notify": false,
        "ringtones": {},
        "sip": {
            "invite_format": "username",
            "method": "password",
            "registration_expiration": 300
        },
        "suppress_unregister_notifications": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Change a device doc

Including "sync":true in the "data" will attempt to reboot the phone. See the sync section below.

POST /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{
        "name": "new device",
        "call_restriction": {},
        "caller_id": {},
        "contact_list": {},
        "dial_plan": {},
        "enabled": true,
        "exclude_from_queues": false,
        "media": {
            "audio": {"codecs": ["PCMU"]},
            "encryption": {"enforce_security": false, "methods": []},
            "video": {"codecs": []}
        },
        "music_on_hold": {},
        "mwi_unsolicited_updates": true,
        "register_overwrite_notify": false,
        "ringtones": {},
        "sip": {
            "invite_format": "username",
            "method": "password",
            "registration_expiration": 300
        },
        "suppress_unregister_notifications": false,
        "id": "4f3330e78e664bb57f8fb23fbaac2429"
        }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}',
  // '{"data":{\n        "name": "new device",\n        "call_restriction": {},\n        "caller_id": {},\n        "contact_list": {},\n        "dial_plan": {},\n        "enabled": true,\n        "exclude_from_queues": false,\n        "media": {\n            "audio": {"codecs": ["PCMU"]},\n            "encryption": {"enforce_security": false, "methods": []},\n            "video": {"codecs": []}\n        },\n        "music_on_hold": {},\n        "mwi_unsolicited_updates": true,\n        "register_overwrite_notify": false,\n        "ringtones": {},\n        "sip": {\n            "invite_format": "username",\n            "method": "password",\n            "registration_expiration": 300\n        },\n        "suppress_unregister_notifications": false,\n        "id": "4f3330e78e664bb57f8fb23fbaac2429"\n        }}',
  {
    'data': {
      'name': 'new device',
      'call_restriction': {},
      'caller_id': {},
      'contact_list': {},
      'dial_plan': {},
      'enabled': true,
      'exclude_from_queues': false,
      'media': {
        'audio': {
          'codecs': [
            'PCMU'
          ]
        },
        'encryption': {
          'enforce_security': false,
          'methods': []
        },
        'video': {
          'codecs': []
        }
      },
      'music_on_hold': {},
      'mwi_unsolicited_updates': true,
      'register_overwrite_notify': false,
      'ringtones': {},
      'sip': {
        'invite_format': 'username',
        'method': 'password',
        'registration_expiration': 300
      },
      'suppress_unregister_notifications': false,
      'id': '4f3330e78e664bb57f8fb23fbaac2429'
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "call_restriction": {},
        "caller_id": {},
        "contact_list": {},
        "dial_plan": {},
        "enabled": true,
        "exclude_from_queues": false,
        "id": "{DEVICE_ID}",
        "media": {
            "audio": {
                "codecs": [
                    "PCMU"
                ]
            },
            "encryption": {
                "enforce_security": false,
                "methods": []
            },
            "video": {
                "codecs": []
            }
        },
        "music_on_hold": {},
        "mwi_unsolicited_updates": true,
        "name": "new device",
        "register_overwrite_notify": false,
        "ringtones": {},
        "sip": {
            "invite_format": "username",
            "method": "password",
            "registration_expiration": 300
        },
        "suppress_unregister_notifications": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Patch a device

PATCH /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"presence_id":"dis_my_device"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}',
  '{"data":{"presence_id":"dis_my_device"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "call_restriction": {},
        "caller_id": {},
        "contact_list": {},
        "dial_plan": {},
        "enabled": true,
        "exclude_from_queues": false,
        "id": "{DEVICE_ID}",
        "media": {
            "audio": {
                "codecs": [
                    "PCMU"
                ]
            },
            "encryption": {
                "enforce_security": false,
                "methods": []
            },
            "video": {
                "codecs": []
            }
        },
        "music_on_hold": {},
        "mwi_unsolicited_updates": true,
        "name": "new device",
        "presence_id":"dis_my_device",
        "register_overwrite_notify": false,
        "ringtones": {},
        "sip": {
            "invite_format": "username",
            "method": "password",
            "registration_expiration": 300
        },
        "suppress_unregister_notifications": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Send a SIP NOTIFY to a device

Lumian will generate the NOTIFY packet if the device is registered.

PUT body options:

Key Type Description
action 'notify' Perform the 'notify' action
data.event string() The value of the Event header in the NOTIFY packet
data object() Parameters for the action

PUT /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"action": "notify",
         "data": {
           "event": "event"
         }
        }' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/notify
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/notify',
  // '{"action": "notify",\n         "data": {\n           "event": "event"\n         }\n        }',
  {
    'action': 'notify',
    'data': {
      'event': 'event'
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Fetch registration statuses of all devices

This will fetch the current registrations of any devices. If no devices are registered, an empty list will be returned.

GET /v2/accounts/{ACCOUNT_ID}/devices/status

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/status
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/status', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "device_id": "{DEVICE_ID}",
            "registered": true
        }
    ],
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Reboot a device

Some devices support receiving SIP NOTIFY packets with event = check-sync. This is typically used to reboot the phone if the configuration has changed. Lumian will generate the NOTIFY packet if the device is registered.

POST /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/sync

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/sync
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/sync',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": "sync request sent",
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Reboot or not after syncing

If your device can support it, you can toggle whether to reboot the phone after it resyncs its configuration.

On the device document, set provision.check_sync_reboot to the suffix to add to the check-sync event when syncing the phone and rebooting.

On the device document, set provision.check_sync_reload to the suffix to add to the check-sync event when syncing the phone when not rebooting.

And of course you can override the check-sync prefix with provision.check_sync_event.

Rebooting example

The device settings:

Sample Response:

{"provision":{
   "check_sync_reboot":"reboot=true"
 }
}

API to force the reboot: POST /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/sync?reboot=true

Will generate Event: check-sync;reboot=true in the SIP NOTIFY.

Reloading example

The device settings:

Sample Response:

{"provision":{
   "check_sync_reload":"reboot=false"
 }
}

API to force the reboot: POST /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/sync?reboot=false

Will generate Event: check-sync;reboot=false in the SIP NOTIFY.

Check Sync Event

Should the device respond to a different value for the purposes of syncing, it can be overridden in the provision.check_sync_event.

There is also a system_config in crossbar.devices, check_sync_event, that can be set for defaults.

Quickcalls

See the quickcall docs for how to perform this action.

Adding Ringtones

You can setup internal and external ringtones by adding this:

Sample Response:

{
    "name": "Device with custom ringtones",
    "ringtones": {
        "internal": "alert info SIP header",
        "external": "alert info SIP header"
    }
}

See, for instance, the Polycom example

Load a user's devices

Often you'll want to see what devices belong to a user, or devices that a user has hot-desked into.

Notice that the first device, {DEVICE_ID_1} is owned by {USER_ID} but the second device, {DEVICE_ID_2}, is owned by {OWNER_ID} and is currently hotdesked to {USER_ID} (see the "hotdesked":true attribute).

GET /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/devices

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/devices
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/devices', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "device_type": "sip_device",
            "enabled": true,
            "hotdesked": false,
            "id": "{DEVICE_ID_1}",
            "mac_address": "",
            "name": "USER_ID_DEVICE",
            "owner_id": "{USER_ID}"
        },
        {
            "device_type": "sip_device",
            "enabled": true,
            "hotdesked": true,
            "id": "{DEVICE_ID_2}",
            "mac_address": "",
            "name": "OWNER_ID_DEVICE",
            "owner_id": "{OWNER_ID}"
        }
      ],
     "request_id": "{REQUEST_ID}",
     "revision": "{REVISION}",
     "status": "success"
}

Create an Authn-By-IP Device

Here is a minimal API request that creates a device that will authenticate by IP address instead of username/password

PUT /v2/accounts/{ACCOUNT_ID}/devices

Sample Request:

    curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"enabled":true,"name":"authn_by_ip","sip":{"invite_format":"e164", "ip":"{IP_ADDRESS}","method":"ip"}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices',
  // '{"data":{"enabled":true,"name":"authn_by_ip","sip":{"invite_format":"e164", "ip":"{IP_ADDRESS}","method":"ip"}}}',
  {
    'data': {
      'enabled': true,
      'name': 'authn_by_ip',
      'sip': {
        'invite_format': 'e164',
        'ip': '{IP_ADDRESS}',
        'method': 'ip'
      }
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "call_restriction": {},
        "caller_id": {},
        "contact_list": {},
        "dial_plan": {},
        "enabled": true,
        "exclude_from_queues": false,
        "id": "{DEVICE_ID}",
        "media": {
            "audio": {
                "codecs": [
                    "PCMU"
                ]
            },
            "encryption": {
                "enforce_security": false,
                "methods": []
            },
            "video": {
                "codecs": []
            }
        },
        "music_on_hold": {},
        "mwi_unsolicited_updates": true,
        "name": "authn_by_ip",
        "register_overwrite_notify": false,
        "ringtones": {},
        "sip": {
            "invite_format": "e164",
            "ip": "{IP_ADDRESS}",
            "method": "ip",
            "registration_expiration": 300
        },
        "suppress_unregister_notifications": false
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Dialplans

About Dialplans

Schema

Permit local dialing by converting the dialed number to a routable form

Key Description Type Default Required Support Level
system.[] string() false
system List of system dial plans array(string()) false

Fetch

GET /v2/accounts/{ACCOUNT_ID}/dialplans

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/dialplans
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/dialplans', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Directories

About Directories

Directories provide the ability to route a caller to a user by having the caller enter DTMF corresponding to the directory users' first or last names (versus having to know the user's extension).

Schema

Allow a caller to search for a user/device by name instead of extension/DID

Key Description Type Default Required Support Level
confirm_match When one match is found, require caller to confirm the match before connecting boolean() true false supported
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
max_dtmf Cap the number of DTMF characters collected from a caller, 0 for unlimited integer() 0 false supported
min_dtmf How many DTMF characters to collect from a caller before processing the directory integer() 3 false supported
name The name of the directory string(1..) true supported
sort_by What field to sort on in matching documents when a caller enters characters `string('first_name' 'last_name')` last_name false
users.[] string() false supported
users The list of users associated with this directory array(string()) [] false supported

Fetch

GET /v2/accounts/{ACCOUNT_ID}/directories

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/directories
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/directories', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "id": "77dfb38ff2353624e35bf4df91acda94",
            "name": "SmartPBX Directory"
        }
    ],
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Remove a directory

DELETE /v2/accounts/{ACCOUNT_ID}/directories/{DIRECTORY_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/directories/{DIRECTORY_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/directories/{DIRECTORY_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch a directory listing

GET /v2/accounts/{ACCOUNT_ID}/directories/{DIRECTORY_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/directories/77dfb38ff2353624e35bf4df91acda94
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/directories/77dfb38ff2353624e35bf4df91acda94', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "confirm_match": false,
        "id": "77dfb38ff2353624e35bf4df91acda94",
        "max_dtmf": 0,
        "min_dtmf": 3,
        "name": "SmartPBX Directory",
        "sort_by": "last_name",
        "ui_metadata": {
            "origin": "voip",
            "ui": "monster-ui",
            "version": "3.23"
        },
        "users": []
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

It is possible to fetch the directory as a PDF for download (such as a company directory, a sales department directory, etc).

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Accept: application/pdf" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/directories/{DIRECTORY_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/directories/{DIRECTORY_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Accept': 'application/pdf'
  }
});
Streams back a PDF document.

If your client does not support setting the Accept header, you can append ?accept=pdf to the URI and Lumian will pretend you sent the proper Accept header.

Faxboxes

About Faxboxes

Fax boxes are used to receive, send and store incoming or outgoing faxes, allowing for configuration of individual fax virtual machines.

Schema

Key Description Type Default Required Support Level
attempts The number of attempts made, this will be set by the system and reset automatically on put/post integer() 0 false
caller_id The Caller-ID-Number string() false supported
caller_name The Caller-ID-Name string() Lumian Fax Printer false supported
custom_smtp_email_address custom smtp address string() false supported
fax_header The name printed at the top of the fax string() Lumian Fax Printer false supported
fax_identity The number printed at the top of the fax string() false supported
fax_timezone The timezone announced string() false supported
media.fax_option Is T.38 Supported? boolean() false beta
media The faxbox media parameters object() {} false beta
name A friendly name for the faxbox string(1..128) true supported
notifications.inbound.callback.method The http method to use when sending the results `string('post' 'put')` false
notifications.inbound.callback.type The content-type to use when sending the results `string('json' 'www-url-form-encoded')` false
notifications.inbound.callback.url The URL to call back with the results string() false
notifications.inbound.callback A URL to send results to object() false beta
notifications.inbound.email.send_to.[] string() false
notifications.inbound.email.send_to A list or string of email recipient(s) `string() array(string())` false
notifications.inbound.email Inbound Email Notifications object() false supported
notifications.inbound.sms.send_to.[] string() false
notifications.inbound.sms.send_to A list or string of sms recipient(s) `string() array(string())` false
notifications.inbound.sms SMS notifications object() false beta
notifications.inbound Inbound Status notifications object() false supported
notifications.outbound.callback.method The http method to use when sending the results `string('post' 'put')` false
notifications.outbound.callback.type The content-type to use when sending the results `string('json' 'www-url-form-encoded')` false
notifications.outbound.callback.url The URL to call back with the results string() false
notifications.outbound.callback A URL to send results to object() false beta
notifications.outbound.email.send_to.[] string() false
notifications.outbound.email.send_to A list or string of email recipient(s) `string() array(string())` false
notifications.outbound.email Email notifications object() false supported
notifications.outbound.sms.send_to.[] string() false
notifications.outbound.sms.send_to A list or string of sms recipient(s) `string() array(string())` false
notifications.outbound.sms SMS notifications object() false beta
notifications.outbound Outbound Status notifications object() false supported
notifications Status notifications object() false supported
retries The number of times to retry integer() 1 false supported
smtp_permission_list.[] string() false supported
smtp_permission_list smtp permission list. accepts regular expressions array(string()) [] false supported

Fetch

GET /v2/accounts/{ACCOUNT_ID}/faxboxes

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxboxes
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxboxes', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/faxboxes

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxboxes
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxboxes',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/faxboxes/{FAXBOX_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxboxes/{FAXBOX_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxboxes/{FAXBOX_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/faxboxes/{FAXBOX_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxboxes/{FAXBOX_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxboxes/{FAXBOX_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Patch

PATCH /v2/accounts/{ACCOUNT_ID}/faxboxes/{FAXBOX_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxboxes/{FAXBOX_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxboxes/{FAXBOX_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/faxboxes/{FAXBOX_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxboxes/{FAXBOX_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxboxes/{FAXBOX_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Faxes

Fax Subsystem Overview

The Faxes API exposes lots of ways to send, receive, track and manage faxes.

As a general concept, faxes are either considered inbound or outbound faxes. In addition:

Schema

Faxes API allows you to update and access fax jobs for both sending and receiving

Key Description Type Default Required Support Level
attempts The number of attempts made, this will be set by the system and reset automatically on put/post integer() 0 false
document.content The content provided in the body when fetching for transmission as a post string(0..256) false
document.content_type The content type header to be used when fetching for transmission as a post string() false
document.host The host header to be used when fetching for transmission string() false
document.method The method that should be used to retrieve the document `string('get' 'post')` get false
document.referer The referer header to be used when fetching for transmission string() false
document.url The url of the fax document string() true
document Parameters related to the storage of a fax document object() false
from_name The sender name for the fax string() false
from_number The sender number for the fax string() true
notifications.email.send_to.[] string() false
notifications.email.send_to A list or string of email recipient(s) `string() array(string())` false
notifications.email Email notifications object() false
notifications.sms.send_to.[] string() false
notifications.sms.send_to A list or string of sms recipient(s) `string() array(string())` false
notifications.sms SMS notifications object() false
notifications Status notifications object() false
retries The number of times to retry integer() 1 false
subject The subject header in an email to fax message string() false
to_name The recipient name for the fax string() false
to_number The recipient number for the fax string() true
tx_result.error_message A description of any error that occurred string() "" false
tx_result.fax_bad_rows The number of bad rows integer() 0 false
tx_result.fax_error_correction True if fax error correction was used boolean() false false
tx_result.fax_receiver_id The receiver id reported by the remote fax device string() "" false
tx_result.fax_speed The speed (Baud-Rate) achieved during transmission integer() 0 false
tx_result.pages_sent The number of pages transmitted integer() 0 false
tx_result.success True if the fax transmission was successful boolean() false false
tx_result.time_elapsed The amount of time from submission to completion integer() 0 false
tx_result The result of a transmission attempt object() false

Send Outgoing Fax

There are two way to send outgoing fax. You can provide a URL to the document you wish or send or you can provide the the document with the same send request payload.

PUT /v2/accounts/{ACCOUNT_ID}/faxes

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"document":{"url":"http://myserver.com/fax.pdf","method":"get"},"retries":3,"from_name":"Test Fax","from_number":"18884732963","to_name":"To Name","to_number":"18884732963"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes',
  '{"data":{"document":{"url":"http://myserver.com/fax.pdf","method":"get"},"retries":3,"from_name":"Test Fax","from_number":"18884732963","to_name":"To Name","to_number":"18884732963"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data":{
        "document":{
            "url":"http://myserver.com/fax.pdf",
            "method":"get"
        },
        "retries":3,
        "from_name":"Test Fax",
        "from_number":"18884732963",
        "to_name":"To Name",
        "to_number":"18884732963",
        "attempts":0,
        "tx_result":{
            "error_message":"",
            "fax_bad_rows":0,
            "fax_error_correction":false,
            "fax_receiver_id":""
            ,"fax_speed":0,
            "pages_sent":0,
            "success":false,
            "time_elapsed":0
        },
        "fax_timezone":"undefined",
        "id":"{FAX_JOB_ID}"
    },
    "revision":"{REVISION}",
    "request_id":"{REQUEST_ID}",
    "status":"success",
    "auth_token":"{AUTH_TOKEN}"
}

In the second method, you can use a single PUT request and send a multi-part content-type to attach both the JSON metadata about the fax transmission and the document itself, in a single request. This avoids needing to have an external storage location for storing fax attachments prior to processing. This is a good solution for portals that upload documents.

Sample Request:

curl -v -X PUT \
     -H "Content-Type: multipart/mixed" \
     -F "content=@fax.json; type=application/json" \
     -F "content=@fax.pdf; type=application/pdf" \
     -H 'X-Auth-Token: {AUTH_TOKEN}' \
     http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes
import axios from 'axios';
import FormData from 'form-data';
import * as fs from 'fs';

const form = new FormData();
form.append('content', fs.readFileSync('fax.json'), 'fax.json');
form.append('content', fs.readFileSync('fax.pdf'), 'fax.pdf');

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes',
  form,
  {
    headers: {
      ...form.getHeaders(),
      'Content-Type': 'multipart/mixed',
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Create an outgoing fax (Alias)

This is identical to the PUT /faxes above.

PUT /v2/accounts/{ACCOUNT_ID}/faxes/outgoing

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outgoing
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outgoing',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch outgoing faxes and their statuses

This API retrieves a listing of all outgoing faxes. Use the "id" to fetch details about a particular job. Results will contain a listing of both API- and SMTP (email) - initiated outbound faxes.

GET /v2/accounts/{ACCOUNT_ID}/faxes/outgoing

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outgoing
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outgoing', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "created": 63626410973,
            "from": "18884732963",
            "id": "{FAX_JOB_ID}",
            "status": "pending",
            "to": "18884732963"
        }
    ],
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "start_key": [
        "{START_KEY}"
    ],
    "status": "success"
}

Fetch details of a queued outgoing fax job

Get all the details about a fax that is in the outgoing queue.

GET /v2/accounts/{ACCOUNT_ID}/faxes/outgoing/{FAX_JOB_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outgoing/{FAX_JOB_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outgoing/{FAX_JOB_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "attempts": 0,
        "created": 63626410973,
        "delivered": "undefined",
        "document": {
            "method": "get",
            "url": "http://myserver.com/fax.pdf"
        },
        "fax_timezone": "undefined",
        "from_name": "Test Fax",
        "from_number": "18884732963",
        "id": "{FAX_JOB_ID}",
        "retries": 3,
        "status": "pending",
        "to_name": "To Name",
        "to_number": "18884732963",
        "tx_result": {
            "error_message": "",
            "fax_bad_rows": 0,
            "fax_error_correction": false,
            "fax_receiver_id": "",
            "fax_speed": 0,
            "pages_sent": 0,
            "success": false,
            "time_elapsed": 0
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Managing Past Outbound Faxes

Fetch All Previously Sent Faxes In The Outbox Folder

This API retrieves a listing of all outgoing faxes which have already been sent or attempted and are no longer in queue. Results will contain a listing of both API- and SMTP (email) - initiated outbound faxes.

GET /v2/accounts/{ACCOUNT_ID}/faxes/outbox

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outbox
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outbox', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch A Fax From The Outbox Folder

GET /v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Resubmit A Fax From The Outbox Folder

PUT /v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"action": "resubmit", "data": {}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}',
  '{"action": "resubmit", "data": {}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Fetch The Fax Payload

GET /v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}/attachment

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}/attachment
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}/attachment', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

If a fax job was queued or attempted to be queued as the result of an inbound email, the SMTP log for that fax can be retrieved via this API. This is also useful for helping debug problems with inbound faxes, such as when the domain matched an account for an inbound fax, but not a specific faxbox, and thus failed to process.

GET /v2/accounts/{ACCOUNT_ID}/faxes/smtplog

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/smtplog
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/smtplog', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

GET /v2/accounts/{ACCOUNT_ID}/faxes/smtplog/{ATTEMPT_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/smtplog/{ATTEMPT_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/smtplog/{ATTEMPT_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Remove A Fax From The Outbox Folder

This API allows you to delete an old fax message. For privacy reasons, this may be useful if you wish to remove all evidence of a previously sent outbound fax.

DELETE /v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Remove The Fax Payload

In some cases, you may wish to remove the document from a fax (usually for privacy reasons) but keep evidence that the fax transmission occurred. This will remove attachments but not the metadata from a sent fax.

DELETE /v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}/attachment

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}/attachment
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/outbox/{FAX_ID}/attachment', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Managing Past Inbound Faxes

Fetch All Faxes In The Inbox Folder

Retrieve a list of faxes that have previously been received.

GET /v2/accounts/{ACCOUNT_ID}/faxes/inbox

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/inbox
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/inbox', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch A Fax From The Inbox Folder

Retrieve all metadata about a particular fax for which you have the fax ID.

GET /v2/accounts/{ACCOUNT_ID}/faxes/inbox/{FAX_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/inbox/{FAX_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/inbox/{FAX_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch The Fax Payload

Retrieve the fax document / attachments for a particular inbound fax for which you have the fax ID.

GET /v2/accounts/{ACCOUNT_ID}/faxes/inbox/{FAX_ID}/attachment

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/inbox/{FAX_ID}/attachment
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/inbox/{FAX_ID}/attachment', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Remove A Fax From The Inbox Folder

Delete an old fax message. For privacy reasons, this may be useful if you wish to remove all evidence of a previously received inbound fax.

DELETE /v2/accounts/{ACCOUNT_ID}/faxes/inbox/{FAX_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/inbox/{FAX_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/inbox/{FAX_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Remove The Fax Payload

In some cases, you may wish to remove the document from a fax (usually for privacy reasons) but keep evidence that the fax receipt occurred. This will remove attachments but not the metadata from a received fax. Useful after you've done post-processing on a fax externally.

DELETE /v2/accounts/{ACCOUNT_ID}/faxes/inbox/{FAX_ID}/attachment

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/inbox/{FAX_ID}/attachment
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/inbox/{FAX_ID}/attachment', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

APIs under active development

Receiving Inbound Faxes

List Incoming Fax Jobs

Retrieve a list of faxes that are currently being received or attempted to be received. NOTE: THIS FUNCTION DOES NOT WORK YET AS OF THE WRITING OF THIS DOCUMENT. We'll update this doc once this function is complete.

GET /v2/accounts/{ACCOUNT_ID}/faxes/incoming

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/incoming
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/incoming', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch An Incoming Fax Job

GET /v2/accounts/{ACCOUNT_ID}/faxes/incoming/{FAX_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/incoming/{FAX_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/faxes/incoming/{FAX_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Query String Filters

Overview

Query string filters allow the API results to be filtered by additional criteria to limit the result set. This is especially useful when querying a collection that could be massive (like CDRs) but you're only interested in results that match certain criteria.

Available Filters

Filter Operates On Description
filter_not_{KEY} {VALUE} Doc include if {KEY} is not {VALUE}
filter_{KEY} {VALUE} Doc included if {KEY} is {VALUE}
has_key {KEY} Doc included if {KEY} is present on the doc
key_missing {KEY} Doc included if {KEY} is not present on the doc
has_value {KEY} Doc included if {KEY} exists and the {VALUE} is non-empty
missing_value {KEY} Doc included if {KEY} is not present or the {VALUE} is empty
created_from {VALUE} Doc included if the created time is greater than or equal to {VALUE}
created_to {VALUE} Doc included if the created time is less than or equal to {VALUE}
modified_from {VALUE} Doc included if the last-modified time is greater than or equal to {VALUE}
modified_to {VALUE} Doc included if the last-modified time is less than or equal to {VALUE}

Keys

Filters can be used on validated keys (those appearing in the schema) and on custom keys (those included by the caller).

{KEY} can be a dot-delimited string representing a JSON key path. So filter_foo.bar.baz=1 would match a doc that had {"foo":{"bar":{"baz":1}}} in it.

Multiple Filters

Filters can be chained together on a query string and will be applied as a boolean AND operation. For example, ?filter_foo=1&has_key=bar will look for docs where foo=1 and the key bar exists on the doc.

Freeswitch

About Freeswitch

Schema

Fetch

Global Provisioner Templates

About Global Provisioner Templates

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/global_provisioner_templates

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/global_provisioner_templates

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

GET /v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}/image

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}/image
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}/image', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}/image

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}/image
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}/image',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}/image

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}/image
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/global_provisioner_templates/{TEMPLATE_ID}/image', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Groups

About Groups

Schema

Validator for the group

Key Description Type Default Required Support Level
endpoints Endpoints included into group object() {} true supported
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
music_on_hold.media_id The ID of a media object that should be used as music on hold string(0..128) false
music_on_hold The music on hold parameters object() {} false beta
name A friendly name for the group string(1..128) true supported

Fetch

GET /v2/accounts/{ACCOUNT_ID}/groups

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/groups
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/groups', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": [
        {
            "id": "18ccfd6cea456cbdd38133e5aa726ec4",
            "name": "Group Name",
            "features": [],
            "endpoints": 2
        }
    ],
    "status": "success"
}

Fetch all groups containing a user

GET /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/groups

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/groups
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/groups', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": [
        {
            "id": "18ccfd6cea456cbdd38133e5aa726ec4",
            "name": "Group Name",
            "features": [],
            "endpoints": 2
        }
    ],
    "status": "success"
}

Create a group for a given account

PUT /v2/accounts/{ACCOUNT_ID}/groups

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{
            "data": {
                "music_on_hold": {},
                "name": "Test group",
                "endpoints": {
                    "df9274b450ea6795cdb381055c3f9b45": {
                        "type": "user",
                        "weight": 1
                    },
                    "dd03d7442a4bec5c092ea6a0e6d579ef": {
                        "type": "device",
                        "weight": 2
                    }
                }
            },
            "verb": "PUT"
        }' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/groups
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/groups',
  '{\n            "data": {\n                "music_on_hold": {},\n                "name": "Test group",\n                "endpoints": {\n                    "df9274b450ea6795cdb381055c3f9b45": {\n                        "type": "user",\n                        "weight": 1\n                    },\n                    "dd03d7442a4bec5c092ea6a0e6d579ef": {\n                        "type": "device",\n                        "weight": 2\n                    }\n                }\n            },\n            "verb": "PUT"\n        }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {
        "music_on_hold": {},
        "name": "Test group",
        "endpoints": {
            "df9274b450ea6795cdb381055c3f9b45": {
                "type": "user",
                "weight": 1
            },
            "dd03d7442a4bec5c092ea6a0e6d579ef": {
                "type": "device",
                "weight": 2
            }
        },
        "id": "1743724cd775bf6994380dbc79c1af09"
    },
    "status": "success"
}

Remove a group

DELETE /v2/accounts/{ACCOUNT_ID}/groups/{GROUP_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/groups/{GROUP_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/groups/{GROUP_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "music_on_hold": {},
        "name": "Test group 2",
        "id": "1743724cd775bf6994380dbc79c1af09",
        "endpoints": {
            "df9274b450ea6795cdb381055c3f9b45": {
                "type": "user",
                "weight": 1
            },
            "dd03d7442a4bec5c092ea6a0e6d579ef": {
                "type": "device",
                "weight": 2
            }
        }
    },
    "status": "success"
}

Get a group for a given account

GET /v2/accounts/{ACCOUNT_ID}/groups/{GROUP_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/groups/{GROUP_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/groups/{GROUP_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "music_on_hold": {},
        "name": "Test group",
        "endpoints": {
            "df9274b450ea6795cdb381055c3f9b45": {
                "type": "user",
                "weight": 1
            },
            "dd03d7442a4bec5c092ea6a0e6d579ef": {
                "type": "device",
                "weight": 2
            }
        },
        "ui_metadata": {
            "ui": "lumian-ui"
        },
        "id": "1743724cd775bf6994380dbc79c1af09"
    },
    "status": "success"
}

Update a group for a given account

POST /v2/accounts/{ACCOUNT_ID}/groups/{GROUP_ID}

PATCH /v2/accounts/{ACCOUNT_ID}/groups/{GROUP_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{
        "data": {
            "music_on_hold": {},
            "name": "Test group 2",
            "id": "1743724cd775bf6994380dbc79c1af09",
            "endpoints": {
                "df9274b450ea6795cdb381055c3f9b45": {
                    "type": "user",
                    "weight": 1
                },
                "dd03d7442a4bec5c092ea6a0e6d579ef": {
                    "type": "device",
                    "weight": 2
                }
            }
        },
        "verb": "POST"
    }' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/groups/{GROUP_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/groups/{GROUP_ID}',
  '{\n        "data": {\n            "music_on_hold": {},\n            "name": "Test group 2",\n            "id": "1743724cd775bf6994380dbc79c1af09",\n            "endpoints": {\n                "df9274b450ea6795cdb381055c3f9b45": {\n                    "type": "user",\n                    "weight": 1\n                },\n                "dd03d7442a4bec5c092ea6a0e6d579ef": {\n                    "type": "device",\n                    "weight": 2\n                }\n            }\n        },\n        "verb": "POST"\n    }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {
        "music_on_hold": {},
        "name": "Test group 2",
        "endpoints": {
            "df9274b450ea6795cdb381055c3f9b45": {
                "type": "user",
                "weight": 1
            },
            "dd03d7442a4bec5c092ea6a0e6d579ef": {
                "type": "device",
                "weight": 2
            }
        },
        "ui_metadata": {
            "ui": "lumian-ui"
        },
        "id": "1743724cd775bf6994380dbc79c1af09"
    },
    "status": "success"
}

Hotdesks

About Hotdesks

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/hotdesks

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/hotdesks
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/hotdesks', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Authenticating REST request

Almost every request to Crossbar must be supplying with authentication credentials.

Failing to do so will result in in 401 Unauthorized HTTP response status with response payload like this:

Sample Response:

{
  "data": {
    "message": "invalid credentials"
  },
  "error": "401",
  "message": "invalid_credentials",
  "status": "error",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{API_NODE}",
  "request_id": "{REQUEST_ID}",
  "auth_token": "{AUTH_TOKEN}"
}

Crossbar provide a number of ways of authenticating request. Most common way is authenticated as user and received a token usable on subsequent requests.

Basic Authentication

This is very simple and quick way of authentication. In this method you have to add Authorization HTTP header to request. The authorization method is Basic and the value is concatenation of your account ID and md5 hash of username and password. Refer to Basic Authentication for more information on how to generate the value for Authorization header.

Basic authentication has the disadvantage you have to provide the username and password in unencrypted text in every request.

Token based Authentication

This is the prefer and more secure way to do authentication with Crossbar API.

In Token based authentication you have to login first to get a token which you can then use in other requests. The token should be set in X-Auth-Token HTTP header in subsequent requests.

The are different ways to do login authentication.

Authenticate as a User

The best way to get authenticated and get the token for UI applications and making manual requests.

In this method, you provide the credentials of your user just for login and crossbar will generate an authentication token in response.

User credentials is MD5 hash of USERNAME:PASSWORD. So for example if your the username is john@example.com (usernames are always in lower case) and the password is m32c6NfqYEt MD5 hash of john@example.com:m32c6NfqYEt (note the colon : which separates the username and password) is 82a2dc91686ec828a67152d45a5c5ef7.

For generating MD5 of a text in terminal you can use md5sum (in Linux) or md5 (in macOS) as follow:

Sample Request:

$ echo -n 'john@example.com:m32c6NfqYEt' | md5sum
82a2dc91686ec828a67152d45a5c5ef7  -

NOTE: You can also use more secure SHA1 as your hash function. For generating SHA1 hash in terminal use shasum command.

If you are using a programming language, refer to its documentation on how to generate MD5 hash.

You also need another field of data to identify the user: account's name or account's realm or phone number assigned to this user.

After the required data is ready, you can use user_auth API to get an authentication token:

Sample Request:

$ curl -v -X PUT \
    -H "Content-Type: application/json" \
    -d '{"data":{"credentials":"82a2dc91686ec828a67152d45a5c5ef7", "account_name":"{ACCOUNT_NAME"}, "method":[md5|sha]}' \
    https://{SERVER}:8000/v2/user_auth
import axios from 'axios';

const response = await axios.put(
  'http://

And a successful response:

> **Sample Response:**

```json
{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "account_id": "{ACCOUNT_ID}",
        "apps": [],
        "is_reseller": true,
        "language": "en-US",
        "owner_id": "{OWNER_ID}",
        "reseller_id": "{RESELLER_ID}"
    },
    "node": "{API_NODE}",
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "{VERSION}",
}

Here {AUTH_TOKEN}, the authentication token, is a long list of characters which you can use in future requests.

Sample Request:

curl -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    https://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}
import axios from 'axios';

const response = await axios.get('https://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

See User Authentication to learn more about this API resource.

Account API Authentication

Uses your account's API token to generate authentication token. If you're building a server applications, this is the best way to authenticate your application.

This is the same as authenticating as user, except you supplying the API key as data.

Sample Request:

curl -X PUT \
    -d '{"data": {"api_key":"{API_KEY}"} }' \
    http://{SERVER}:8000/v2/api_auth
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/api_auth',
  '{"data": {"api_key":"{API_KEY}"} }',
  {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

You can find the API key in Authentication application in UI, or you can get it using Accounts API.

Next Steps

There are more way to authenticate your request, learn more about them in Authentication APIs section.

And a successful response:

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "account_id": "{ACCOUNT_ID}",
        "apps": [],
        "is_reseller": true,
        "language": "en-US",
        "owner_id": "{OWNER_ID}",
        "reseller_id": "{RESELLER_ID}"
    },
    "node": "{API_NODE}",
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "{VERSION}",
}

Here {AUTH_TOKEN}, the authentication token, is a long list of characters which you can use in future requests.

Sample Request:

curl -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    https://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}

See User Authentication to learn more about this API resource.

Account API Authentication

Uses your account's API token to generate authentication token. If you're building a server applications, this is the best way to authenticate your application.

This is the same as authenticating as user, except you supplying the API key as data.

Sample Request:

curl -X PUT \
    -d '{"data": {"api_key":"{API_KEY}"} }' \
    http://{SERVER}:8000/v2/api_auth

You can find the API key in Authentication application in UI, or you can get it using Accounts API.

Next Steps

There are more way to authenticate your request, learn more about them in Authentication APIs section.

IP Authentication

About IP Authentication

Schema

Create

PUT /v2/ip_auth

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/ip_auth
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/ip_auth',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Ledgers

About Ledgers

The Ledgers API provides an easy way to see usage like per-minutes or flat-rate and manage your account's credit/debit values.

Ledgers Schema

ledgers document

Key Description Type Default Required
account.id Account ID string() false
account.name Account name string() false
account Account info object() false
amount Ledger amount, in currency amount number() false
description Useful description for ledger string() false
metadata Metadata for ledger document object() false
period.end Period end integer() false
period.start Period start integer() false
period Period of ledger object() false
source.id Source ID string() true
source.service Source service string() true
source Origin of ledger object() true
usage.quantity Usage quantity integer() true
usage.type Usage type string() true
usage.unit Usage unit string() true
usage Usage for ledger object() true

Get Available Ledgers

List available ledger sources from the account's reseller.

GET /v2/accounts/{ACCOUNT_ID}/ledgers/available

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/available
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/available', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "name": "per-minute-voip",
            "friendly_name": "Per Minute VoIP",
            "markup_type": [
                "percentage"
            ]
        }
    ],
    "node": "{NODE}",
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "{VERSION}"
}

Fetch

GET /v2/accounts/{ACCOUNT_ID}/ledgers

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "mobile_data": {
            "amount": -10.5,
            "usage": {
                "quantity": 1000,
                "type": "debit",
                "unit": "MB"
            }
        },
        "per-minute-voip": {
            "amount": -54.7404,
            "usage": {
                "quantity": 14520,
                "type": "voice",
                "unit": "sec"
            }
        }
    },
    "node": "{NODE}",
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "{VERSION}"
}

Fetch Ledgers by source

GET /v2/accounts/{ACCOUNT_ID}/ledgers/{SOURCE_SERVICE}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/{SOURCE_SERVICE}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/{SOURCE_SERVICE}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get Ledger values

List ledger values for an account with paging and filtering support

GET /v2/accounts/{ACCOUNT_ID}/ledgers/{LEDGER_ID}?created_from=11111111&created_to=22222222

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/{LEDGER_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/{LEDGER_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "page_size": 30,
    "data": [
        {
            "source": {
                "service": "per-minute-voip",
                "id": "{CALL_ID}"
            },
            "account": {
                "id": "{ACCOUNT_ID}",
                "name": "{ACCOUNT_NAME}"
            },
            "usage": {
                "type": "voice",
                "quantity": 3,
                "unit": "sec"
            },
            "amount": 6,
            "description": "US Hollywood",
            "period": {
                "start": 63630348840
            },
            "id": "{DOC_ID}"
        }
    ],
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

Get Ledger document

GET /v2/accounts/{ACCOUNT_ID}/ledgers/{LEDGER_ID}/{LEDGER_ENTRY_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/{LEDGER_ID}/{LEDGER_ENTRY_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/{LEDGER_ID}/{LEDGER_ENTRY_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "source": {
            "service": "per-minute-voip",
            "id": "{CALL_ID}"
        },
        "account": {
            "id": "{ACCOUNT_ID}",
            "name": "{ACCOUNT_NAME}"
        },
        "usage": {
            "type": "voice",
            "quantity": 3,
            "unit": "sec"
        },
        "amount": 6,
        "description": "US Hollywood",
        "period": {
            "start": 63630348840
        },
        "id": "{DOC_ID}"
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

Credit / Debit

Credit or Debit a specific ledger. the account_id for AUTH_TOKEN must be reseller of target account.

Parameter "impact_reseller" (boolean not required) when true will also create the document in the reseller

PUT /v2/accounts/{ACCOUNT_ID}/ledgers/debit

PUT /v2/accounts/{ACCOUNT_ID}/ledgers/credit

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/debit
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/debit',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Sample Response:

{
    "data": {
        "amount": 100,
        "description": "blablabla",
        "source": {
            "service": "tower/support/...",
            "id": "mac/mdn/..."
        },
        "usage": {
            "type": "data",
            "quantity": 5,
            "unit": "MB"
        },
        "period": {
            "start": 10938710938,
            "end": 214109238023899
        }
    },
    "impact_reseller": true
}

Fetch

GET /v2/accounts/{ACCOUNT_ID}/ledgers/{SOURCE_SERVICE}/{LEDGER_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/{SOURCE_SERVICE}/{LEDGER_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/{SOURCE_SERVICE}/{LEDGER_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch Ledger and Account Summary Breakdown

Fetches the account ledger summary as well as a breakdown of ledgers per account for a given YYYYMM.

GET /v2/accounts/{ACCOUNT_ID}/ledgers/summary/{MODB_SUFFIX}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/summary/{MODB_SUFFIX}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/summary/{MODB_SUFFIX}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Example

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/summary/201905
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/ledgers/summary/201905', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

"data": {
    "summary": {
      "per-minute-voip": {
        "amount": -880.0,
        "usage": {
          "type": "voice",
          "quantity": 232320,
          "unit": "sec"
        }
      },
      "payments": {
        "amount": 251970.82,
        "usage": {
          "type": "debit",
          "quantity": 0,
          "unit": "dollars"
        }
      },
      "prorations": {
        "amount": -1.9258,
        "usage": {
          "quantity": 0
        }
      },
      "rollovers": {
        "amount": 36.102,
        "usage": {
          "quantity": 0
        }
      }
    },
    "breakdown": [
      {
        "account": {
          "id": "de6fd29a54407cfe46e6c9d3828ab0d8",
          "name": "Account A"
        },
        "ledgers": {
          "payments": {
            "amount": -1250000.0,
            "usage": {
              "type": "debit",
              "quantity": 0,
              "unit": "dollars"
            }
          },
          "per-minute-voip": {
            "amount": -385.0,
            "usage": {
              "type": "voice",
              "quantity": 101640,
              "unit": "sec"
            }
          }
        },
        "total": -1250385.0
      },
      {
        "account": {
          "id": "cc580f94d7da53816a94b87b2a1d25f8",
          "name": "Account 1"
        },
        "ledgers": {
          "payments": {
            "amount": 1501970.82,
            "usage": {
              "type": "credit",
              "quantity": 0,
              "unit": "dollars"
            }
          },
          "prorations": {
            "amount": -1.9258,
            "usage": {
              "quantity": 0
            }
          },
          "rollovers": {
            "amount": 36.102,
            "usage": {
              "quantity": 0
            }
          }
        },
        "total": 1500004.9962
      },
      {
        "account": {
          "id": "a71a670531d7dc92d2a4f9fc6774df36",
          "name": "Account B"
        },
        "ledgers": {
          "per-minute-voip": {
            "amount": -495.0,
            "usage": {
              "type": "voice",
              "quantity": 130680,
              "unit": "sec"
            }
          }
        },
        "total": -495.0
      }
    ]
  }

Limits

Configures limit on call consumed for your account.

About Limits

Schema

Limit an account's ability to place concurrent calls using flat rate trunks

Key Description Type Default Required Support Level
allow_prepay Determines if the account would like to allow per-minute calls if they have no available credit boolean() true false supported
burst_trunks The number of two-way, flat-rate trunks used only if no other trunks are available integer() false beta
calls A hard limit for the total number calls integer() false beta
inbound_trunks The number of inbound, flat-rate trunks integer() false supported
outbound_trunks The number of outbound, flat-rate trunks integer() false supported
resource_consuming_calls A hard limit for the number of resource consuming calls integer() false beta
twoway_trunks The number of two-way, flat-rate trunks integer() false beta

Fetch

GET /v2/accounts/{ACCOUNT_ID}/limits

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/limits
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/limits', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "twoway_trunks": 0,
        "inbound_trunks": 0,
        "id": "limits",
        "allow_prepay": true,
        "outbound_trunks": 5
    },
    "status": "success"
}

Update limits for a given account

POST /v2/accounts/{ACCOUNT_ID}/limits

First using API v1 (simplest):

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {
        "twoway_trunks": 0,
        "inbound_trunks": 11,
        "id": "limits",
        "allow_prepay": true,
        "outbound_trunks": 5
    }}' \
    http://{SERVER}:8000/v1/accounts/{ACCOUNT_ID}/limits
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v1/accounts/{ACCOUNT_ID}/limits',
  '{"data": {\n        "twoway_trunks": 0,\n        "inbound_trunks": 11,\n        "id": "limits",\n        "allow_prepay": true,\n        "outbound_trunks": 5\n    }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {
        "twoway_trunks": 0,
        "inbound_trunks": 11,
        "id": "limits",
        "allow_prepay": true,
        "outbound_trunks": 5
    },
    "status": "success",
}

Now with API v2:

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {
        "twoway_trunks": 0,
        "inbound_trunks": 11,
        "id": "limits",
        "allow_prepay": true,
        "outbound_trunks": 5
    }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/limits
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/limits',
  '{"data": {\n        "twoway_trunks": 0,\n        "inbound_trunks": 11,\n        "id": "limits",\n        "allow_prepay": true,\n        "outbound_trunks": 5\n    }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Reply warns that charges have to be accepted (402):

{
    "data": {
        "limits": {
            "inbound_trunks": {
                "category": "limits",
                "item": "inbound_trunks",
                "quantity": 11,
                "rate": 6.9900000000000002132,
                "single_discount": true,
                "single_discount_rate": 0.0,
                "cumulative_discount": 0,
                "cumulative_discount_rate": 0.0
            }
        }
    },
    "error": "402",
    "message": "accept charges",
    "status": "error",
}

Re-do the same request, setting accept_charges to true.

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {
        "twoway_trunks": 0,
        "inbound_trunks": 11,
        "id": "limits",
        "allow_prepay": true,
        "outbound_trunks": 5,
        "accept_charges": true
    }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/limits
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/limits',
  '{"data": {\n        "twoway_trunks": 0,\n        "inbound_trunks": 11,\n        "id": "limits",\n        "allow_prepay": true,\n        "outbound_trunks": 5,\n        "accept_charges": true\n    }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {
        "twoway_trunks": 0,
        "inbound_trunks": 11,
        "id": "limits",
        "allow_prepay": true,
        "outbound_trunks": 5
    },
    "status": "success",
}

Lists

Lists API provide to create list of numbers ore prefix numbers to match and route calls.

In the new behavior of List API, list is a collection of list entries (number, user, prefix numbers) allows you more flexibility to create and assign multi entries to different match list.

Schema

Schema for a match list

Key Description Type Default Required Support Level
description A friendly list description string(1..128) false
name A friendly match list name string(1..128) true
org Full legal name of the organization string() false

Fetch

GET /v2/accounts/{ACCOUNT_ID}/lists

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Add new list (beware: no entries)

PUT /v2/accounts/{ACCOUNT_ID}/lists

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"name": "list name"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists',
  '{"data": {"name": "list name"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Delete list and its entries

DELETE /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get list properties (doesn't return entries)

GET /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Updating list (without entries)

PATCH /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"description": "desc"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}',
  '{"data": {"description": "desc"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Rewrite list

POST /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"name": "New List name"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}',
  '{"data": {"name": "New List name"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Delete all entries from list

DELETE /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get list entries

GET /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Add an entry to a list

PUT /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"number": "0123", "displayname" : "List Entry"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries',
  '{"data": {"number": "0123", "displayname" : "List Entry"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Delete entry from the list

DELETE /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

List entry properties

GET /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Update list entry

PATCH /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"firstname" : "First name"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}',
  '{"data": {"firstname" : "First name"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Replace list entry

POST /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"number": "0123", "displayname" : "New List Entry"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}',
  '{"data": {"number": "0123", "displayname" : "New List Entry"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Add photo to List entry

POST /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}/photo

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}/photo
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}/photo',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

List entry vcard

GET /v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}/vcard

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}/vcard
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/entries/{LIST_ENTRY_ID}/vcard', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

v1 examples.

Get lists and their entries

curl -v -X GET -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v1/accounts/{ACCOUNT_ID}/lists

Create new list

curl -v -X PUT -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v1/accounts/{ACCOUNT_ID}/lists -d '{"data": {"name": "List name"}}'

Get list with LIST_ID

curl -v -X GET -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v1/accounts/{ACCOUNT_ID}/lists/{LIST_ID}

Add new entry to list

curl -v -X PUT -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v1/accounts/{ACCOUNT_ID}/lists/{LIST_ID} -d '{"data": {"pattern": "345"}}'

Delete list

curl -v -X DELETE -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v1/accounts/{ACCOUNT_ID}/lists/{LIST_ID}

Get entry {LIST_ENTRY_ID} from list {LIST_ID}

curl -v -X GET -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v1/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/{LIST_ENTRY_ID}

Rewrite entry {LIST_ENTRY_ID} in list {LIST_ID}

curl -v -X POST -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v1/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/{LIST_ENTRY_ID} -d "{"data": {"132", "321"}}"

Delete entry from list

curl -v -X DELETE -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v1/accounts/{ACCOUNT_ID}/lists/{LIST_ID}/{LIST_ENTRY_ID}

Local Provisioner Templates

About Local Provisioner Templates

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/local_provisioner_templates

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/local_provisioner_templates

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

GET /v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}/image

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}/image
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}/image', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}/image

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}/image
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}/image',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}/image

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}/image
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/local_provisioner_templates/{TEMPLATE_ID}/image', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Crossbar Maintenance Functions

Migrate Ring Group Callflow

The migrate_ring_group_callflow function will migrate callflow create with a ring group by Monster UI before v3.19.

It will extract the ring group and create a new callflow and then reference this new callflow in the old one. This function will not impact any other callflows or ring group callflows create by Lumian UI.

Update UI apps metadata

List all apps

Sample Request:

sup crossbar_maintenance apps

Get one app's metadata

Sample Request:

sup crossbar_maintenance app AppName_Or_AppId

Update an app's en-US fields

Sample Request:

sup crossbar_maintenance set_app_label AppId NewValue

Sample Request:

sup crossbar_maintenance set_app_description AppId NewValue

Sample Request:

sup crossbar_maintenance set_app_extended_description AppId NewValue

Note: features must be @-separated if more than one is supplied.

Sample Request:

sup crossbar_maintenance set_app_features AppId 'my feature'
sup crossbar_maintenance set_app_features AppId feature1@featureB@feat3

Update an app's icon

Sample Request:

sup crossbar_maintenance set_app_icon AppId PathToPNGIcon

Update an app's screenshots

Sample Request:

sup crossbar_maintenance set_app_screenshots AppId PathToScreenshotsFolder

Media

About Media

Uploading media for custom music on hold, IVR prompts, or TTS (if a proper TTS engine is enabled).

Lumian provides some default system media files for common things like voicemail prompts. These are accessible via the media Crossbar endpoint as well, if your user has super duper admin privileges. To manipulate those resources, simply omit the /accounts/{ACCOUNT_ID} from the URI.

For example, to get a listing of all system media files:

Sample Request:

curl -v -X GET -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v2/media

You can then get the id of the media file and manipulate it in a similar fashion as regular account media (including TTS if you have a TTS engine like iSpeech configured).

Media Languages

Part of the schema of media files is a language attribute. It defaults to a system_config/media value for the default_language key (and is "en-us" by default). Properly defined media files can be searched for based on language using the basic filters provided by Crossbar:

Sample Request:

curl -v -X GET -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v2/media?filter_language=en
curl -v -X GET -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v2/media?filter_language=en-US
curl -v -X GET -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v2/media?filter_language=fr-FR

The comparison is case-insensitive, but en and en-US are treated separately. If a media metadata object is missing a language attribute (on an older installation when system media was imported with no language field, say), use key_missing=language in the request.

Once you've assigned languages, you can use the language callflow action to set the language for that call.

Normalize Media Files

Lumian can be configured to normalize uploaded media files. This can fix things like:

By default, if enabled, normalization will convert all media to MP3 (retaining the original upload as well) using the sox utility to accomplish the conversion.

Enable Normalization Via SUP

Enable Normalization Via DB

  1. Open system_config/crossbar.media document, create or update the key normalize_media to true.
  2. Flush the kapps_config cache, sup kapps_config flush crossbar.media, on all servers running Crossbar.

Set Target Format Via SUP

Set Target Format Via DB

In the system_config/crossbar.media document, create or update the key normalization_format to your desired format (mp3, wav, etc). Flush the kapps_config cache on all servers running Crossbar. All new uploads will be normalized (if possible) to the new format.

Normalization parameters

The default sox command is sox -t <input_format> - -r 8000 -t <output_format> - but this is configurable via the system_config/media document (or similar SUP command).

You can fine-tune the source and destination arguments using the normalize_source_args and normalize_destination_args keys respectively. By default, the source args are "" and the destination args are "-r 8000" (as can be seen from the default sox command above.

The normalizer code uses stdin to send the binary data to sox and reads from stdout to get the normalized binary data back (the " - " (there are 2) in command above).

You can also set the specific path for sox in the normalize_executable key, in case you've installed it to a non-standard path.

Be sure to install sox with mp3 support! Conversion will not happen (assuming you're targeting mp3) if sox can't write the mp3. You can check the media meta document for the key normalization_error if sox failed for some reason.

Schema

Schema for media

Key Description Type Default Required Support Level
content_length Length, in bytes, of the file integer() false supported
content_type Used to override the automatic upload type `string('audio/mp3' 'audio/mpeg' 'audio/mpeg3' 'audio/x-wav'
description A brief description of the media update, usually the original file name string(1..128) false supported
language The language of the media file or text string() en-us false supported
media_source Defines the source of the media `string('recording' 'upload' 'tts')` upload
name A friendly name for the media string(1..128) true supported
prompt_id The prompt this media file represents string() false
source_id If the media was generated from a callflow module, this is ID of the properties string(32) false beta
source_type If the media was generated from a callflow module, this is the module name string() false beta
streamable Determines if the media can be streamed boolean() true false supported
tts.text The text to be converted into audio string(1..) false supported
tts.voice The voice to be used during the conversion `string('female/en-US' 'male/en-US' 'female/en-CA' 'female/en-AU'
tts Text-to-speech options used to create audio files from text object() {} false supported

Fetch

GET /v2/accounts/{ACCOUNT_ID}/media

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "id": "0c347bba3754056caad610bb8a337342",
            "is_prompt": false,
            "language": "en-us",
            "media_source": "tts",
            "name": "Main AA BG"
        }
    ],
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Create a new media object (required before uploading the actual media data)

PUT /v2/accounts/{ACCOUNT_ID}/media

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{
        "streamable":true,
        "name": "File",
        "description": "My Test Media File",
        }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{
        "streamable": true,
        "name": "FR-vm-enter_pass",
        "description": "FR - Enter Password prompt",
        "prompt_id": "vm-enter_pass",
        "language":"fr"
        }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{
        "name": "TestTTS",
        "media_source": "tts",
        "tts": {"text": "Testing TTS", "voice": "female/en-US"}
        }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media

A response:

Sample Response:

{
    "data":
    {
        "streamable": true,
        "name": "vm-enter_pass",
        "description": "FR - Enter Password prompt",
        "prompt_id": "vm-enter_pass",
        "language": "fr-fr",
        "tts": {
            "voice": "female/en-US"
        },
        "media_source": "upload",
        "id": "fr-fr%2Fvm-enter_pass"
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

Remove metadata

Optional Parameter: "hard_delete": true - will perform a hard delete of the document (default is soft delete)

DELETE /v2/accounts/{ACCOUNT_ID}/media/{MEDIA_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media/{MEDIA_ID}

Get metadata about a media file

GET /v2/accounts/{ACCOUNT_ID}/media/{MEDIA_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media/0c347bba3754056caad610bb8a337342

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "description": "tts file",
        "id": "0c347bba3754056caad610bb8a337342",
        "language": "en-us",
        "media_source": "tts",
        "name": "Main AA BG",
        "streamable": true,
        "tts": {
            "text": "Thank you for calling My Amazing Company where we do amazing things. You may dial any extension at any time. To schedule an appointment, press 1. For billing questions about your account, press 2. For all other inquiries, press 0.  To hear this menu again, please stay on the line.",
            "voice": "female/en-US"
        },
        "ui_metadata": {
            "origin": "callflows",
            "ui": "monster-ui",
            "version": "4.0-7"
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Update metadata

POST /v2/accounts/{ACCOUNT_ID}/media/{MEDIA_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media/{MEDIA_ID}

List all prompts and the number of translations existing

GET /v2/accounts/{ACCOUNT_ID}/media/prompts

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media/prompts

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "agent-already_logged_in": 1,
            "agent-enter_pin": 1,
            "agent-invalid_choice": 1,
            "agent-logged_in": 1,
            "agent-logged_out": 1,
            "agent-not_call_center_agent": 1,
            "agent-pause": 1,
            "agent-resume": 1,
            "agent_enter_pin": 1,
            "agent_logged_already_in": 1,
            "agent_logged_in": 1,
            "agent_logged_out": 1,
            "cf-disabled": 1,
            "cf-disabled_menu": 1,
            "cf-enabled_menu": 1,
            "cf-enter_number": 1,
            "cf-move-no_channel": 1,
            "cf-move-no_owner": 1,
            "cf-move-too_many_channels": 1,
            "cf-not_available": 1,
            "cf-now_forwarded_to": 1,
            "cf-unauthorized_call": 1,
            "conf-alone": 1,
            "conf-bad_conf": 1,
            "conf-bad_pin": 1
        }
    ],
    "next_start_key": "conf-deaf",
    "page_size": 25,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

List languages available

GET /v2/accounts/{ACCOUNT_ID}/media/languages

This request will return a list of languages found, as well as the counts of how many media files have that language defined:

Note, the "missing" key indicates how many media files have no associated language.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media/languages

Sample Response:

{
    "data": [{ "en": 3
               ,"missing": 1
             }
            ],
}

Get the raw media file

GET /v2/accounts/{ACCOUNT_ID}/media/{MEDIA_ID}/raw

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H 'Accept: audio/mp3' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media/{MEDIA_ID}/raw

Streams back an MP3-encoded media.

Add the media binary file to the media meta data

POST /v2/accounts/{ACCOUNT_ID}/media/{MEDIA_ID}/raw

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H 'Content-Type: audio/mp3' \
    --data-binary @/path/to/file.mp3 \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media/{MEDIA_ID}/raw

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H 'Content-Type: audio/x-wav \
    --data-binary @/path/to/file.wav \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media/{MEDIA_ID}/raw

Only one of the above; any subsequent POSTs will overwrite the existing binary data.

List all translations of a given prompt

GET /v2/accounts/{ACCOUNT_ID}/media/prompts/{PROMPT_ID}

You can use that list to fetch the specific media files associated with that prompt.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media/prompts/{PROMPT_ID}

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        "fr-fr%2Fvm-enter_pass",
        "en-us%2Fvm-enter_pass"
    ],
    "page_size": 2,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "start_key": "vm-enter_pass",
    "status": "success"
}

List media files with specific language

GET /v2/accounts/{ACCOUNT_ID}/media/languages/{LANGUAGE}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/media/languages/{LANGUAGE}

Sample Response:

{
    "data":["media_id_1", "media_id_2",...]
}

To get the IDs of the media docs missing a language:

Sample Request:

curl -v -X GET -H "X-Auth-Token: {AUTH_TOKEN}" http://server.com:8000/v2/accounts/{ACCOUNT_ID}/media/languages/missing
...
"data":["media_id_1", "media_id_2",...]
...

Menus

Menus, IVRs, what ever you call them, allow you to create branches in the callflow based on the caller's input.

About Menus

The DTMF entered is matched against the "children" keys and that branch is taken.

Additionally, you can branch based on a timeout (no DTMF entered) by using "timeout" in the "children" keys":

Sample Response:

{
    "module":"menu",
    "data": {...},
    "children": {
        "1": {"module":"...",...},
        "2": {"module":"...",...},
        "timeout": {"module":"...",...}
    }
}

If no timeout child is specified, the menu is retried (until retries are exceeded).

Schema

Schema for a menus

Key Description Type Default Required Support Level
allow_record_from_offnet Determines if the record pin can be used by external calls boolean() false false supported
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
hunt Determines if the callers can dial internal extensions directly boolean() true false supported
hunt_allow A regular expression that an extension the caller dialed must match to be allowed to continue string(1..256) false supported
hunt_deny A regular expression that if matched does not allow the caller to dial directly string(1..256) false supported
interdigit_timeout The amount of time (in milliseconds) to wait for the caller to press the next digit after pressing a digit integer() false supported
max_extension_length The maximum number of digits that can be collected integer() 4 false supported
media.exit_media When a call is transferred from the menu after all retries exhausted this media can be played (prior to transfer if enabled) `boolean() string(3..2048)` false
media.greeting The ID of a media object that should be used as the menu greeting string(3..2048) false supported
media.invalid_media When the collected digits don't result in a match or hunt this media can be played `boolean() string(3..2048)` false
media.transfer_media When a call is transferred from the menu, either after all retries exhausted or a successful hunt, this media can be played `boolean() string(3..2048)` false
media The media (prompt) parameters object() {} false supported
name A friendly name for the menu string(1..128) true supported
record_pin The pin number used to record the menu prompt string(3..6) false supported
retries The number of times a menu should be played until a valid entry is collected integer() 3 false supported
suppress_media Determines if the playing of 'Invalid Entry' is suppressed. boolean() false false supported
timeout The amount of time (in milliseconds) to wait for the caller to begin entering digits integer() false supported

Fetch

GET /v2/accounts/{ACCOUNT_ID}/menus

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/menus
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/menus', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/menus

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/menus
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/menus',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/menus/{MENU_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/menus/{MENU_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/menus/{MENU_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/menus/{MENU_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/menus/{MENU_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/menus/{MENU_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Patch

PATCH /v2/accounts/{ACCOUNT_ID}/menus/{MENU_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/menus/{MENU_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/menus/{MENU_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/menus/{MENU_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/menus/{MENU_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/menus/{MENU_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Metaflows

About Metaflows

Metaflows allow functionality to be executed on an in-progress call, triggered by DTMFs from the caller/callee or an API call.

For instance, a callee could setup a metaflow on their user doc such that when they receive a call, they can press *9 to initiate a recording of the call.

Schema

Actions applied to a call outside of the normal callflow, initiated by the caller(s)

Key Description Type Default Required Support Level
binding_digit What DTMF will trigger the collection and analysis of the subsequent DTMF sequence `string('1' '2' '3' '4'
digit_timeout How long to wait between DTMF presses before processing the collected sequence (milliseconds) integer() false
listen_on Which leg(s) of the call to listen for DTMF `string('both' 'self' 'peer')`
numbers./^[0-9]+$/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
numbers A list of static numbers with their flows object() false
patterns./.+/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
patterns A list of patterns with their flows object() false

metaflow

A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to

Key Description Type Default Required Support Level
children./.+/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
children Children metaflows object() false
data The data/arguments of the metaflow module object() {} false
module The name of the metaflow module to execute at this node string(1..64) true

Fetch account-level metaflows

These are available to all users and devices within the account

GET /v2/accounts/{ACCOUNT_ID}/metaflows

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/metaflows
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/metaflows', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

No metaflows assigned

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {},
    "node": "{NODE_HASH}",
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "4.2.42"
}

Set metaflows on the account

POST /v2/accounts/{ACCOUNT_ID}/metaflows

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: $AUTH_TOKEN" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/metaflows \
    -d '{"data":{"binding_digit":"*", "patterns":{"2(\\d+)":{"module":"transfer", "data":{"takeback_dtmf":"*1"}}}}}'
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/metaflows',
  '{"data":{"binding_digit":"*", "patterns":{"2(\\\\d+)":{"module":"transfer", "data":{"takeback_dtmf":"*1"}}}}}',
  {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "binding_digit": "*",
        "patterns": {
            "2(\\d+)": {
                "data": {
                    "captures": [
                        "no_match"
                    ],
                    "takeback_dtmf": "*1",
                    "transfer_type": "blind"
                },
                "module": "transfer"
            }
        }
    },
    "node": "{NODE_HASH}",
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "4.2.42"
}

In the above example, the binding_digit is * which, when pressed on the phone's keypad, tells Lumian that a metaflow is starting. 2(\\d+) tells Lumian to look for a 2 and then capture one or more digits.

Thus *21234 would instruct Lumian to blind-transfer the caller to extension 1234.

Remove metaflows from the account

DELETE /v2/accounts/{ACCOUNT_ID}/metaflows

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/metaflows
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/metaflows', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {},
    "node": "{NODE_HASH}",
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "4.2.42"
}

Users and devices

If you want to assign metaflows to a particular user or device, the API commands are the same except you need to include the user or device in the URI:

Metaflow actions

To see what actions can be set in a metaflow, look at the relevant documentation in Konami

Migrations

About Migrations

The migrations API allows you to perform various account based migrations on an account, or an account and its sub-accounts.

List

Lists all available migrations.

GET /v2/accounts/{ACCOUNT_ID}/migrations

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/migrations
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/migrations', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Summary

Provides a summary of a migration, including information on if it was performed and by whom and when.

GET /v2/accounts/{ACCOUNT_ID}/migrations/{MIGRATION_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/migrations/{MIGRATION_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/migrations/{MIGRATION_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Perform Migration

Allows you to perform a migration, with the following potential parameters:

Name Values Description
perform_migration now Performs the migration now (now is the only currently supported option)
include_descendants true / false (default: false) If true the migration is performed on the account and sub-accounts
include_resellers true / false (default: false) If true the migration is performed on reseller sub-accounts

POST /v2/accounts/{ACCOUNT_ID}/migrations/{MIGRATION_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/migrations/{MIGRATION_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/migrations/{MIGRATION_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

MMS

About MMS

Lumian offers API-based message services for inbound and outbound messages. Outbound messages sent through the Lumian API must be sent from a telephone number assigned to your Lumian account with an active messaging feature to be eligible for use.

Schema

MMS Document

Key Description Type Default Required
body mime encoded mms message string(1..) false
from caller-id-number, taken from user if absent string() false
to callee-id-number string() true
media media-url string() true

Fetch

GET /v2/accounts/{ACCOUNT_ID}/mms

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/mms
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/mms', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/mms

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{ \
    "data": { \
        "from": "15555555555", \
        "to": "14444444444", \
        "body": "Hello", \
        "media": ["https://www.lumian.net/lmn/media/LM/Theme/images/logo.svg"] \
        } \
    }' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/mms
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/mms',
  '{"data": { 
        "from": "15555555555", 
        "to": "14444444444", 
        "body": "Hello", 
        "media": ["https://www.lumian.net/lmn/media/LM/Theme/images/logo.svg"] 
        } 
    }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
  "success": true,
  "reason": "SUCCESS",
  "detail": "SUCCESS",
  "result": {
    "referenceId": "d88231b5-13b3-4fb2-8528-bc7164309389",
    "from": "15555555555",
    "text": "Hello",
    "media": [
      "https://www.lumian.net/lmn/media/LM/Theme/images/logo.svg"
    ],
    "messageType": "MMS",
    "resultResponses": [
      {
        "to": "14444444444",
        "status": "SUCCESS"
      }
    ]
  }
}

Fetch

GET /v2/accounts/{ACCOUNT_ID}/mms/{MMS_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/mms/{MMS_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/mms/{MMS_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/mms/{MMS_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/mms/{MMS_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/mms/{MMS_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Multi Factor Authentication Configuration

About Multi Factor

API endpoint to configure Crossbar Multi Factor Authentication (MFA) providers.

Enable MFA for a Crossbar auth module

If you want to use multi factor authentication for a module, set the multi_factor.enabled to true for that authentication module. You can control if the multi factor settings can be applied to the account's children by multi_factor.include_subaccounts. See Crossbar Security API documentation.

!!! note You can specify the id of multi factor provider settings. If you miss this value, system's default MFA provider will be used!

Multi Factor Authentication (MFA) flow summary

The MFA process in Lumian is straight forward. You configured the Lumian integrated MFA service provider, and enabling the multi factor for an authentication endpoint. User will authenticate as usual by its own Lumian credential. If the first factor authentication passed, second-factor provider information (usually a signed token) would be returned to client with HTTP 401 Unauthorized status.

User's client performs the second-factor authentication with the provider and sends provider response to Lumian. If the provider validates user will be authenticated successful and a Lumian token will be generated as usual otherwise if the second-factor provider response is not validated a HTTP 401 Unauthorized will be returned.

Provider Configuration Schema

Key Description Type Default Required
enabled whether or not this configuration is enabled or not boolean true
name A friendly name for the configuration string true
provider_name multi factor provider name string true
settings provider configuration object false

List Account Configuration and Available System Providers

List configured multi factor providers and available system multi factor provider.

GET /v2/accounts/{ACCOUNT_ID}/multi_factor

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": {
    "configured": [
      {
        "id": "c757665dca55edba2395df3ca6423f4f",
        "enabled": true,
        "name": "a nice day",
        "provider_name": "duo",
        "provider_type": "multi_factor"
      }
    ],
    "multi_factor_providers": [
      {
        "id": "duo",
        "enabled": false,
        "name": "System Default Provider",
        "provider_name": "duo",
        "provider_type": "multi_factor"
      }
    ]
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Create a Provider Configuration for an Account

Create configuration for a MFA provider. Provider config should be in "settings". See Lumian Auth Multi-Factor to find out required configuration for each provider.

PUT /v2/accounts/{ACCOUNT_ID}/multi_factor

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    --data '{"data": {"name": "another nice day", "enabled": true, "provider_name": "duo", "settings": {"integration_key": "{DUO_IKEY}", "secret_key": "{DUO_SKEY}", "application_secret_key": "{DUO_AKEY}", "api_hostname": "{DUO_HOST_NAME}", "duo_expire": 300,"app_expire": 3600}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor',
  '{"data": {"name": "another nice day", "enabled": true, "provider_name": "duo", "settings": {"integration_key": "{DUO_IKEY}", "secret_key": "{DUO_SKEY}", "application_secret_key": "{DUO_AKEY}", "api_hostname": "{DUO_HOST_NAME}", "duo_expire": 300,"app_expire": 3600}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Sample Response:

{
  "data": {
    "settings": {
      "secret_key": "{DUO_SKEY}",
      "integration_key": "{DUO_IKEY}",
      "duo_expire": 300,
      "application_secret_key": "{DUO_AKEY}",
      "app_expire": 3600,
      "api_hostname": "{DUO_HOST_NAME}"
    },
    "provider_name": "duo",
    "name": "another nice day",
    "enabled": true,
    "id": "c757665dca55edba2395df3ca6423f4f"
  },
  "revision": "{REVERSION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Fetch an Account's Provider Configuration

Get account's configuration of a provider.

GET /v2/accounts/{ACCOUNT_ID}/multi_factor/{CONFIG_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor/c757665dca55edba2395df3ca6423f4f
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor/c757665dca55edba2395df3ca6423f4f', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": {
    "settings": {
      "secret_key": "{DUO_SKEY}",
      "integration_key": "{DUO_IKEY}",
      "duo_expire": 300,
      "application_secret_key": "{DUO_AKEY}",
      "app_expire": 3600,
      "api_hostname": "{DUO_HOST_NAME}"
    },
    "provider_name": "duo",
    "name": "another nice day",
    "enabled": true,
    "id": "c757665dca55edba2395df3ca6423f4f"
  },
  "revision": "{REVERSION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Change an Account's Provider Configuration

POST /v2/accounts/{ACCOUNT_ID}/multi_factor/{CONFIG_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    --data '{"data": {"name": "another nice day with a change", "enabled": true, "provider_name": "duo", "settings": {"integration_key": "{DUO_IKEY}", "secret_key": "{DUO_SKEY}", "application_secret_key": "{DUO_AKEY}", "api_hostname": "{DUO_HOST_NAME}", "duo_expire": 300,"app_expire": 3600}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor/c757665dca55edba2395df3ca6423f4f
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor/c757665dca55edba2395df3ca6423f4f',
  '{"data": {"name": "another nice day with a change", "enabled": true, "provider_name": "duo", "settings": {"integration_key": "{DUO_IKEY}", "secret_key": "{DUO_SKEY}", "application_secret_key": "{DUO_AKEY}", "api_hostname": "{DUO_HOST_NAME}", "duo_expire": 300,"app_expire": 3600}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Sample Response:

{
  "data": {
    "settings": {
      "secret_key": "{DUO_SKEY}",
      "integration_key": "{DUO_IKEY}",
      "duo_expire": 300,
      "application_secret_key": "{DUO_AKEY}",
      "app_expire": 3600,
      "api_hostname": "{DUO_HOST_NAME}"
    },
    "provider_name": "duo",
    "name": "another nice day with a change",
    "enabled": true,
    "id": "c757665dca55edba2395df3ca6423f4f"
  },
  "revision": "{REVERSION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Patch Fields in an Account's Provider Configuration

PATCH /v2/accounts/{ACCOUNT_ID}/multi_factor/{CONFIG_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    --data '{"data": {"enabled": false}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor/c757665dca55edba2395df3ca6423f4f
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor/c757665dca55edba2395df3ca6423f4f',
  '{"data": {"enabled": false}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Sample Response:

{
  "data": {
    "settings": {
      "secret_key": "{DUO_SKEY}",
      "integration_key": "{DUO_IKEY}",
      "duo_expire": 300,
      "application_secret_key": "{DUO_AKEY}",
      "app_expire": 3600,
      "api_hostname": "{DUO_HOST_NAME}"
    },
    "provider_name": "duo",
    "name": "another nice day with a change",
    "enabled": false,
    "id": "c757665dca55edba2395df3ca6423f4f"
  },
  "revision": "{REVERSION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Remove an Account's Provider Configuration

DELETE /v2/accounts/{ACCOUNT_ID}/multi_factor/{CONFIG_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor/c757665dca55edba2395df3ca6423f4f
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor/c757665dca55edba2395df3ca6423f4f', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get a Summary of Multi Factor Login Attempts

GET /v2/accounts/{ACCOUNT_ID}/multi_factor/attempts

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor/attempts
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor/attempts', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": [
    {
      "id": "201702-09a979346eff06746e445a8cc1e574c4",
      "auth_type": "multi_factor",
      "auth_module": "cb_user_auth",
      "status": "failed",
      "message": "no multi factor authentication provider is configured",
      "timestamp": 63655033238,
      "client_ip": "10.1.0.2:8000",
    }
  ],
  "revision": "{REVERSION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Fetch Details of a Multi Factor Login Attempts

GET /v2/accounts/{ACCOUNT_ID}/multi_factor/attempts/{ATTEMPT_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor/attempts/201702-09a979346eff06746e445a8cc1e574c4
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/multi_factor/attempts/201702-09a979346eff06746e445a8cc1e574c4', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": {
    "auth_type": "multi_factor",
    "status": "failed",
    "auth_module": "cb_user_auth",
    "message": "no multi factor authentication provider is configured",
    "client_headers": {
      "host": "10.1.0.2:8000",
      "connection": "keep-alive",
      "content-length": "83",
      "accept": "application/json, text/javascript, */*; q=0.01",
      "x-auth-token": "undefined",
      "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.39 Safari/537.36",
      "origin": "http://127.0.0.1:3000",
      "content-type": "application/json",
      "dnt": "1",
      "referer": "http://127.0.0.1:3000/",
      "accept-encoding": "gzip, deflate",
      "accept-language": "en-US,en;q=0.8"
    },
    "client_ip": "10.1.0.2:8000",
    "crossbar_request_id": "5dd9a7b69f74b3c09ca065316096b83e",
    "timestamp": 63655033238,
    "metadata": {
        "owner_id": "b6205d9a4a62d8e971c2d8f177676130",
        "account_id": "a391d64a083b99232f6d2633c47432e3"
    },
    "id": "201702-09a979346eff06746e445a8cc1e574c4"
  },
  "revision": "{REVERSION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

List System Multi Factor Providers

List system multi factor providers

GET /v2/multi_factor

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/multi_factor
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/multi_factor', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "page_size": 1,
  "start_key": [
    "multi_factor"
  ],
  "data": [
    {
      "id": "duo",
      "enabled": false,
      "name": "System Default Provider",
      "provider_name": "duo",
      "provider_type": "multi_factor"
    }
  ],
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Create a System Provider Configuration

Provider config should be in "settings". See Lumian Auth Multi-Factor to find out required configuration for each provider.

!!! note Only super duper admin can create system providers configuration!

PUT /v2/multi_factor

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    --data '{"data": {"name": "have a nice day", "enabled": true, "provider_name": "duo", "settings": {"integration_key": "{DUO_IKEY}", "secret_key": "{DUO_SKEY}", "application_secret_key": "{DUO_AKEY}", "api_hostname": "{DUO_HOST_NAME}", "duo_expire": 300,"app_expire": 3600}}}' \
    http://{SERVER}:8000/v2/multi_factor
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/multi_factor',
  '{"data": {"name": "have a nice day", "enabled": true, "provider_name": "duo", "settings": {"integration_key": "{DUO_IKEY}", "secret_key": "{DUO_SKEY}", "application_secret_key": "{DUO_AKEY}", "api_hostname": "{DUO_HOST_NAME}", "duo_expire": 300,"app_expire": 3600}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Sample Response:

{
  "data": {
    "settings": {
      "secret_key": "{DUO_SKEY}",
      "integration_key": "{DUO_IKEY}",
      "duo_expire": 300,
      "application_secret_key": "{DUO_AKEY}",
      "app_expire": 3600,
      "api_hostname": "{DUO_HOST_NAME}"
    },
    "provider_name": "duo",
    "name": "have a nice day",
    "enabled": true,
    "id": "5c61dd2098466017f716417792f769cc"
  },
  "revision": "{REVERSION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Fetch a System Provider Configuration

!!! note Only super duper admin can get system providers configuration!

GET /v2/multi_factor/{CONFIG_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/multi_factor/c757665dca55edba2395df3ca6423f4f
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/multi_factor/c757665dca55edba2395df3ca6423f4f', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": {
    "settings": {},
    "provider_name": "duo",
    "name": "System Default Provider",
    "enabled": false,
    "id": "duo"
  },
  "revision": "{REVERSION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Change a System Provider Configuration

!!! note Only super duper admin can change system providers configuration!

POST /v2/multi_factor/{CONFIG_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    --data '{"data": {"name": "System Default Provider", "enabled": true, "provider_name": "duo", "settings": {"integration_key": "{DUO_IKEY}", "secret_key": "{DUO_SKEY}", "application_secret_key": "{DUO_AKEY}", "api_hostname": "{DUO_HOST_NAME}", "duo_expire": 300,"app_expire": 3600}}}' \
    http://{SERVER}:8000/v2/multi_factor/duo
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/multi_factor/duo',
  '{"data": {"name": "System Default Provider", "enabled": true, "provider_name": "duo", "settings": {"integration_key": "{DUO_IKEY}", "secret_key": "{DUO_SKEY}", "application_secret_key": "{DUO_AKEY}", "api_hostname": "{DUO_HOST_NAME}", "duo_expire": 300,"app_expire": 3600}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Sample Response:

{
  "data": {
    "settings": {
      "secret_key": "{DUO_SKEY}",
      "integration_key": "{DUO_IKEY}",
      "duo_expire": 300,
      "application_secret_key": "{DUO_AKEY}",
      "app_expire": 3600,
      "api_hostname": "{DUO_HOST_NAME}"
    },
    "provider_name": "duo",
    "name": "System Default Provider",
    "enabled": true,
    "id": "duo"
  },
  "revision": "{REVERSION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Patch fields in a System provider configuration

!!! note Only super duper admin can change system providers configuration!

PATCH /v2/multi_factor/{CONFIG_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    --data '{"data": {"enabled": false}}' \
    http://{SERVER}:8000/v2/multi_factor/duo
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/multi_factor/duo',
  '{"data": {"enabled": false}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Sample Response:

{
  "data": {
    "settings": {
      "secret_key": "{DUO_SKEY}",
      "integration_key": "{DUO_IKEY}",
      "duo_expire": 300,
      "application_secret_key": "{DUO_AKEY}",
      "app_expire": 3600,
      "api_hostname": "{DUO_HOST_NAME}"
    },
    "provider_name": "duo",
    "name": "System Default Provider",
    "enabled": false,
    "id": "duo"
  },
  "revision": "{REVERSION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Multi-part Request

Some APIs support sending a multi part request, such as outgoing faxes.

Sending Multi-part Outgoing Faxes Request

With multi part you can create an outgoing fax request and upload the document to fax (e.g.: a PDF file) at the same time.

Create a JSON file for the outgoing fax options

Sample Response:

{
    "data": {
        "retries": 3,
        "from_name": "Fax Sender",
        "from_number": "{FROM_NUMBER}",
        "to_name": "Fax Recipient",
        "to_number": "{TO_NUMBER}",
        "fax_identity_number": "{ID_NUMBER}",
        "fax_identity_name": "Fax Header"
    }
}

Execute the cURL request

Sample Request:

curl -v -X PUT -i \
    -H 'X-Auth-Token: {AUTH_TOKEN}' \
    -H "Content-Type: multipart/mixed" \
    -F "content=@{FILE.json}; type=application/json" \
    -F "content=@{FILE.pdf}; type=application/pdf" \
    http://{SERVER}/v2/accounts/{ACCOUNT_ID}/faxes/outgoing
import axios from 'axios';
import FormData from 'form-data';
import * as fs from 'fs';

const form = new FormData();
form.append('content', fs.readFileSync('{FILE.json}'), '{FILE.json}');
form.append('content', fs.readFileSync('{FILE.pdf}'), '{FILE.pdf}');

const response = await axios.put(
  'http://{SERVER}/v2/accounts/{ACCOUNT_ID}/faxes/outgoing',
  form,
  {
    headers: {
      ...form.getHeaders(),
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'multipart/mixed'
    }
  }
);

Email Notifications Template

Allow managing templates for notification emails.

About Notifications

Flowing is a basic structure of a notification object (here it is a digest of voicemail_to_email notification).

Sample Response:

{
  "enabled": true,
  "macros": {
    "voicemail.vmbox_id": {
      "i18n_label": "voicemail_vmbox_id",
      "friendly_name": "Voicemail Box Id",
      "description": "Which voicemail box was the message left in"
    }
  },
  "subject": "New voicemail from {{caller_id.name}} ({{caller_id.number}})",
  "category": "voicemail",
  "friendly_name": "Voicemail To Email",
  "to": {
    "type": "original"
  },
  "from": "no_reply@localhost.me",
  "cc": {
    "type": "specified",
    "email_addresses": []
  },
  "bcc": {
    "type": "specified",
    "email_addresses": []
  },
  "id": "voicemail_to_email",
  "account_overridden": true,
  "templates": {
    "text/plain": {
      "length": 971
    },
    "text/html": {
      "length": 13350
    }
  }
}

By looking at above structure, there are some points of interest to say about notification parameters:

In addition to the JSON data describing configuration of a notification, notification templates can be represented in various formats and can be modified by uploading the representation document (such as using a WYSIWYG tool). Currently supported formats are plain text and HTML documents.

Template Formats

Creating the configuration documents is all well and good, but it is necessary to be able to attach the templates in their various forms as well. Currently supported formats are text/html and text/plain.

Operation considerations

In Lumian versions prior to 3.19, notification templates were managed and processed by the notify application. In the newer Lumian versions this has been replace by a robust and more featureful teletype application.

All accounts will continue to be processed by the notify application until the Crossbar notification APIs are accessed for the first time (for instance, when using the Branding application in Monster). Once a client has accessed the APIs, a flag is set on the account telling the notify application to ignore processing and instructs the teletype application to process it instead. This allows administrators to run both notify and teletype concurrently without sending multiple copies of each notification.

Notifications Schema

Key Description Type Default Required
bcc.email_addresses.[] string() false
bcc.email_addresses BCC Email Addresses array(string()) false
bcc.type `string('original' 'specified' 'admins')`
bcc Bcc email field object() false
category Category of the template, for grouping purposes string(1..) false
cc CC email field object() false
cc.email_addresses.[] string() false
cc.email_addresses CC Email Addresses array(string()) false
cc.type string('original' or 'specified' or 'admins') false
enabled Enable notification boolean() true false
friendly_name Friendly name of the template string(1..) false
from From: email address string() true
macros object() {} false
reply_to Reply-To: email address string() false
subject Email subject string(1..200) true
template_charset string(1..) utf-8 false
to To email field object() true
to.email_addresses.[] string() false
to.email_addresses array(string()) false
to.type string('original' or 'specified' or 'admins') false

Summary of Available System Notifications

Request to see what templates exist on the system to override.

GET /v2/notifications

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "auth_token": "{AUTH_TOKEN}",
  "data": [
    {
      "id": "voicemail_to_email",
      "macros": {
        "call_id": {
          "description": "Call ID of the caller",
          "friendly_name": "Call ID",
          "i18n_label": "call_id"
        },
        "caller_id.name": {
          "description": "Caller ID Name",
          "friendly_name": "Caller ID Name",
          "i18n_label": "caller_id_name"
        }
        "..."
      }
    }
    "..."
  ],
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Summary of Account Overridden Notifications

To see what notification templates an account overrides. The key account_overridden will exist on any templates that are account-specific.

GET /v2/accounts/{ACCOUNT_ID}/notifications

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications

Response

Sample Response:

{
  "auth_token": "{AUTH_TOKEN}",
  "data": [
    {
      "id": "voicemail_to_email",
      "macros": {
        "call_id": {
          "description": "Call ID of the caller",
          "friendly_name": "Call ID",
          "i18n_label": "call_id"
        },
        "caller_id.name": {
          "description": "Caller ID Name",
          "friendly_name": "Caller ID Name",
          "i18n_label": "caller_id_name"
        }
        "..."
      },
      "account_overridden": true
    }
    "..."
  ],
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Create a New System Notification

Creates a new system notification template.

!!! note Only a super duper admin can create/modify/delete system notifications!

PUT /v2/notifications

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ \
    "data": { \
      "id":"voicemail_to_email", \
      "to": { \
        "type": "users", \
        "email_addresses": ["user@account.com"] \
      }, \
      "from": "reseller@resellerdomain.com", \
      "subject": "Hello {{user.first_name}}, you received a new voicemail!", \
      "enabled": true, \
      "template_charset": "utf-8" \
    }}' \
    http://{SERVER}:8000/v2/notifications
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/notifications',
  '{ \\\n    "data": { \\\n      "id":"voicemail_to_email", \\\n      "to": { \\\n        "type": "users", \\\n        "email_addresses": ["user@account.com"] \\\n      }, \\\n      "from": "reseller@resellerdomain.com", \\\n      "subject": "Hello {{user.first_name}}, you received a new voicemail!", \\\n      "enabled": true, \\\n      "template_charset": "utf-8" \\\n    }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Sample Response:

{
  "auth_token": "{AUTH_TOKEN}",
  "data": {
    "enabled": true,
    "id": "voicemail_to_email",
    "from": "reseller@resellerdomain.com",
    "macros": { "..." },
    "subject": "Hello {{user.first_name}}, you received a new voicemail!",
    "template_charset": "utf-8",
    "to": {
      "email_addresses": [
        "user@account.com"
      ],
      "type": "users"
    }
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Create a Notification as Account Override

Now that you've fetched the system default template, modify the template and PUT it back to the account.

!!! note This request will fail if id does not already exist in the system defaults.

PUT /v2/accounts/{ACCOUNT_ID}/notifications

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ \ 
    "data": { \
      "id":"voicemail_to_email", \
      "to": { \
        "type": "users", \
        "email_addresses": ["user@account.com"] \
      }, \
      "from": "reseller@resellerdomain.com", \
      "subject": "Hello {{user.first_name}}, you received a new voicemail!", \
      "enabled": true, \
      "template_charset": "utf-8" \ 
    }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications',
  '{ \\ \n    "data": { \\\n      "id":"voicemail_to_email", \\\n      "to": { \\\n        "type": "users", \\\n        "email_addresses": ["user@account.com"] \\\n      }, \\\n      "from": "reseller@resellerdomain.com", \\\n      "subject": "Hello {{user.first_name}}, you received a new voicemail!", \\\n      "enabled": true, \\\n      "template_charset": "utf-8" \\ \n    }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Same as PUT for system level, but with the "account_overridden": true flag added.

Details of a System Notification

Using the ID from the system listing above, get the template object. This document allows you to set some "static" properties (things not derived from the event causing the notification, e.g. call data, system alert, etc).

GET /v2/notifications/{NOTIFICATION_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/notifications/{NOTIFICATION_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/notifications/{NOTIFICATION_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "auth_token": "{AUTH_TOKEN}",
  "data": [
    {
      "id": "{NOTIFICATION_ID}",
      "macros": { "..." },
      "templates": {
        "text/html": {
          "length": 600
        }
        ,"text/plain": {
          "length": 408
        }
      }
    }
    "..."
  ],
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Details of an Account Overridden Notification

Performing a GET with an account ID will return the notification object, again with the account_overridden flag added.

GET /v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Same as above with the account_overridden flag added.

Update a Notification

Similar to the PUT, POST will update an existing configuration:

POST /v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}

!!! note Omit /accounts/{ACCOUNT_ID} to update the system's version. Only a super duper admin can create/modify/delete system notifications!

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ \ 
    "data": { \
        "id":"{NOTIFICATION_ID}", \
        "to": { \
            "type": "users", \
            "email_addresses": ["user@account.com"] \
        }, \
        "from": "reseller@resellerdomain.com", \
        "subject": "Hello {{user.first_name}}, you received a new voicemail!", \
        "enabled": true, \
        "template_charset": "utf-8", \
        "macros": { \
            "user.first_name": { \
                "i18n_label": "first_name", \
                "friendly_name": "First Name", \
                "description": "If the voicemail box has an owner id, this is the first name of that user.  Not always present" \
            }, \
            "user.last_name": { \
                "i18n_label": "last_name", \
                "friendly_name": "Last Name", \
                "description": "If the voicemail box has an owner id, this is the last name of that user.  Not always present" \
            } \
        } \
    }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}',
  '{ \\ \n    "data": { \\\n        "id":"{NOTIFICATION_ID}", \\\n        "to": { \\\n            "type": "users", \\\n            "email_addresses": ["user@account.com"] \\\n        }, \\\n        "from": "reseller@resellerdomain.com", \\\n        "subject": "Hello {{user.first_name}}, you received a new voicemail!", \\\n        "enabled": true, \\\n        "template_charset": "utf-8", \\\n        "macros": { \\\n            "user.first_name": { \\\n                "i18n_label": "first_name", \\\n                "friendly_name": "First Name", \\\n                "description": "If the voicemail box has an owner id, this is the first name of that user.  Not always present" \\\n            }, \\\n            "user.last_name": { \\\n                "i18n_label": "last_name", \\\n                "friendly_name": "Last Name", \\\n                "description": "If the voicemail box has an owner id, this is the last name of that user.  Not always present" \\\n            } \\\n        } \\\n    }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Remove a Notification

DELETE /v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}

Note: Omit the /accounts/{ACCOUNT_ID} to remove the system default. Only a super duper admin can create/modify/delete system notifications!

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get Notification Template:

GET /v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}

When you perform a GET request to fetch a notification configuration (using Accept: application/json header or not setting Accept header at all), there is a Content-Type HTTP header in the response headers. Use those content types to fetch a specific template by setting your request Accept header:

Get as text/html

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H 'Accept: text/html' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Accept': 'text/html'
  }
});

Get as text/plain

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H 'Accept: text/plain' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Accept': 'text/plain'
  }
});

Note that the only difference is the Accept attribute. This will determine which attachment is returned in the payload. If you specify a nonexistent Accept MIME type, expect to receive a 406 Not Acceptable error.

For clients that do not support setting the Accept header, a query string parameter can be included (e.g. /v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}?accept=text/html to get the HTML template).

Update Notification Template:

POST /v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}

Update text/plain

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H 'Content-Type: text/plain' \
    -d 'some plain text template code' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}',
  'some plain text template code',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'text/plain'
    }
  }
);

Update text/html

An Example using Curl Upload File:

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H 'Content-Type: text/html' \
    -F content=@file.html \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}
import axios from 'axios';
import FormData from 'form-data';
import * as fs from 'fs';

const form = new FormData();
form.append('content', fs.readFileSync('file.html'), 'file.html');

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}',
  form,
  {
    headers: {
      ...form.getHeaders(),
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'text/html'
    }
  }
);

!!! note Omit /accounts/{ACCOUNT_ID} to update the system's version. Only a super duper admin can create/modify/delete system notifications!

Preview a new template

It can be helpful to preview the resulting email when modifying templates, but before actually saving the template.

POST /v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}/preview

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H 'Content-Type: application/json' \
    -d '{"data": { \
        "to": {"email_addresses": ["me@lumian.net"]}, \
        "from": "lumian@lumian.net", \
        "subject": "Testing NOTIFICATION", \
        "html": "SSUyNTIwJTI1dTI2NjElMjUyMFVuaWNvZGUlMjUyMQ==", \
        "plain": "You just received an email! It was sent to {{user.email}}", \
        "enabled": true \
        }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}/preview
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/{NOTIFICATION_ID}/preview',
  '{"data": { \\\n        "to": {"email_addresses": ["me@lumian.net"]}, \\\n        "from": "lumian@lumian.net", \\\n        "subject": "Testing NOTIFICATION", \\\n        "html": "SSUyNTIwJTI1dTI2NjElMjUyMFVuaWNvZGUlMjUyMQ==", \\\n        "plain": "You just received an email! It was sent to {{user.email}}", \\\n        "enabled": true \\\n        }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Remove All Account's Notification Customizations

To remove all notification customizations made on the account use a DELETE method with action remove_customizations.

DELETE /v2/accounts/{ACCOUNT_ID}/notifications/

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"action": "remove_customizations", "data": {}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  data: '{"action": "remove_customizations", "data": {}}'
});

Response

Sample Response:

{
  "auth_token": "{AUTH_TOKEN}",
  "data": {
    "account_zone_change": "deleted",
    "cnam_request": "deleted",
    "customer_update": "deleted",
    "denied_emergency_bridge": "deleted",
    "deregister": "deleted",
    "fax_inbound_error_to_email": "deleted",
    "fax_inbound_error_to_email_filtered": "deleted",
    "fax_inbound_to_email": "deleted",
    "fax_outbound_error_to_email": "deleted",
    "fax_outbound_smtp_error_to_email": "deleted",
    "fax_outbound_to_email": "deleted",
    "first_occurrence": "deleted",
    "low_balance": "deleted",
    "missed_call": "deleted",
    "new_account": "deleted",
    "new_user": "deleted",
    "password_recovery": "deleted",
    "port_cancel": "deleted",
    "port_comment": "deleted",
    "port_pending": "deleted",
    "port_rejected": "deleted",
    "port_request": "deleted",
    "port_request_admin": "deleted",
    "port_scheduled": "deleted",
    "port_unconfirmed": "deleted",
    "ported": "deleted",
    "service_added": "deleted",
    "skel": "deleted",
    "system_alert": "deleted",
    "topup": "deleted",
    "transaction": "deleted",
    "transaction_failed": "deleted",
    "voicemail_full": "deleted",
    "voicemail_to_email": "deleted",
    "webhook_disabled": "deleted"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Force All Account's Notifications to System Default

To remove all notification customizations made on the account and use the notification from system use a PUT method with action force_system.

PUT /v2/accounts/{ACCOUNT_ID}/notifications/

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"action": "force_system", "data": {}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications',
  '{"action": "force_system", "data": {}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Sample Response:

{
  "auth_token": "{AUTH_TOKEN}",
  "data": {
    "account_zone_change": "replaced",
    "cnam_request": "replaced",
    "customer_update": "replaced",
    "denied_emergency_bridge": "replaced",
    "deregister": "replaced",
    "fax_inbound_error_to_email": "replaced",
    "fax_inbound_error_to_email_filtered": "replaced",
    "fax_inbound_to_email": "replaced",
    "fax_outbound_error_to_email": "replaced",
    "fax_outbound_smtp_error_to_email": "replaced",
    "fax_outbound_to_email": "replaced",
    "first_occurrence": "replaced",
    "low_balance": "replaced",
    "missed_call": "replaced",
    "new_account": "replaced",
    "new_user": "replaced",
    "password_recovery": "replaced",
    "port_cancel": "replaced",
    "port_comment": "replaced",
    "port_pending": "replaced",
    "port_rejected": "replaced",
    "port_request": "replaced",
    "port_request_admin": "replaced",
    "port_scheduled": "replaced",
    "port_unconfirmed": "replaced",
    "ported": "replaced",
    "service_added": "replaced",
    "skel": "replaced",
    "system_alert": "replaced",
    "topup": "replaced",
    "transaction": "replaced",
    "transaction_failed": "replaced",
    "voicemail_full": "replaced",
    "voicemail_to_email": "replaced",
    "webhook_disabled": "replaced"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Get the Notifications SMTP Logs

GET /v2/accounts/{ACCOUNT_ID}/notifications/smtplog

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H 'Content-Type: text/plain' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/smtplog
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/smtplog', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'text/plain'
  }
});

Get a notification's SMTP log

GET /v2/accounts/{ACCOUNT_ID}/notifications/smtplog/{SMTP_LOG_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/smtplog/{SMTP_LOG_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/notifications/smtplog/{SMTP_LOG_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "data": {
    "rendered_templates": {
      "text/plain": "Expired registration in account \"teste\".\nNotifications are enabled for loss of registration on the device 995582142@teste.sip.90e9.com\n\nLast Registration:\nDevice ID: 4e411cf70ad352a222e24fbacf467c18\nAccount ID: 85ea6075c6c1e266f8512e2233541bdb\nUser Agent: Grandstream GXP2130 1.0.7.25\nContact: sip:995582142@192.168.26.13:5060;reg-id=1;+sip.instance=&quot;urn:uuid:00000000-0000-1000-8000-000B826C4283&quot;\n\nThis may be due to a network connectivity issue, power outage, or misconfiguration. Please check the device.",
      "text/html": "<h2>Expired registration in account \"teste\"</h2><p>Notifications are enabled for loss of registration on the device 995582142@teste.sip.90e9.com</p><h3>Last Registration</h3><table><tbody><tr><td>Device ID</td><td>4e411cf70ad352a222e24fbacf467c18</td></tr><tr><td>Account ID</td><td>85ea6075c6c1e266f8512e2233541bdb</td></tr><tr><td>User Agent</td><td>Grandstream GXP2130 1.0.7.25</td></tr><tr><td>Contact</td><td>sip:995582142@192.168.26.13:5060;reg-id=1;+sip.instance=&quot;urn:uuid:00000000-0000-1000-8000-000B826C4283&quot;</td></tr></tbody></table><p>This may be due to a network connectivity issue, power outage, or misconfiguration. Please check the device.</p>"
    },
    "subject": "Loss of Registration for 995582142@teste.sip.90e9.com",
    "emails": {
      "from": "no_reply@dev-01.90e9.com",
      "to": [
        "teste@factorlusitano.com"
      ]
    },
    "receipt": "2.0.0 Ok: queued as B60E22044B",
    "account_id": "{ACCOUNT_ID}",
    "account_db": "{ACCOUNT_DB}",
    "template_id": "deregister",
    "template_account_id": "5ba01ad7ad1611d436b1860d8c552897",
    "id": "{SMTP_LOG_ID}"
  },
  "revision": "{REVISION}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Customer update

You can use the special Customer Update notification to send a message to all reseller's children users or to a particular account's users.

Send Message to All Reseller's Accounts:

POST /v2/accounts/{ACCOUNT_ID}/notifications/customer_update/message

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H 'Content-Type: text/html' \
    -F content=@file.html \
    http://{SERVER}:8000/v2/accounts/{RESELLER_ACCOUNT_ID}/notifications/customer_update/message
import axios from 'axios';
import FormData from 'form-data';
import * as fs from 'fs';

const form = new FormData();
form.append('content', fs.readFileSync('file.html'), 'file.html');

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{RESELLER_ACCOUNT_ID}/notifications/customer_update/message',
  form,
  {
    headers: {
      ...form.getHeaders(),
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'text/html'
    }
  }
);

Send Message to a Particular Account:

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H 'Content-Type: text/html' \
    -d '{"data": {"recipient_id": "33ca3929ed585e0e423eb39e4ffe1452"}}' \
    http://{SERVER}:8000/v2/accounts/{PARTICULAR_ACCOUNT_ID}/notifications/customer_update/message
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{PARTICULAR_ACCOUNT_ID}/notifications/customer_update/message',
  '{"data": {"recipient_id": "33ca3929ed585e0e423eb39e4ffe1452"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'text/html'
    }
  }
);

Send Message to Particular Users

You can send a message to all users, administrators only or a particular user within an account. Just add a user_type field to your payload:

Sample Response:

{
  "data": {"user_type": "all_users"}
}

Note: Applicable to send message to a particular account only.

Sample Response:

{
  "data": {"user_type": "{USER_ID}"}
}

Note: This is the default behavior, so user_type could be omitted.

Sample Response:

{
  "data": {"user_type": "admins_only"}
}

Specifying the Message

For specifying the actual message (and the email subject) you want to send, provide HTML and plain text templates by uploading document:

Sample Response:

{
  "data": {
    "recipient_id": "33ca3929ed585e0e423eb39e4ffe1452",
    "user_type": "3d9b564d5c95d52d81a2e49ea0c57941",
    "id": "customer_update",
    "account_overridden": true,
    "enabled": true,
    "category": "user",
    "friendly_name": "Customer update",
    "from": "info@localhost.me",
    "subject": "Test Reseller customer update",
    "bcc": {
      "email_addresses": [],
      "type": ""
    },
    "cc": {
      "email_addresses": [],
      "type": ""
    },
    "macros": {
      "user.email": {
        "description": "Email of the user",
        "friendly_name": "Email",
        "i18n_label": "user_email"
      },
      "user.first_name": {
        "description": "First Name",
        "friendly_name": "First Name",
        "i18n_label": "first_name"
      },
      "user.last_name": {
        "description": "Last Name",
        "friendly_name": "Last Name",
        "i18n_label": "last_name"
      },
      "user.timezone": {
        "description": "Timezone of the user",
        "friendly_name": "Timezone",
        "i18n_label": "user_timezone"
      },
      "user.username": {
        "description": "Username",
        "friendly_name": "Username",
        "i18n_label": "username"
      }
    },
    "template_charset": "utf-8",
    "html": "PHA+RGVhciB7e3VzZXIuZmlyc3RfbmFtZX19IHt7dXNlci5sYXN0X25hbWV9fS48L3A+CjxwPkhlcmUgYXJlIHNvbWUgbmV3cyB0aGF0IHdlIGhhdmUgc2VsZWN0ZWQgZm9yIHlvdTwvcD4KPHA+QmVzdCByZWdhcmRzLDwvcD4KPHA+T25OZXQgSW5ub3ZhdGlvbnMgTGltaXRlZC48L3A+",
    "plain": "Dear {{user.first_name}} {{user.last_name}}.\n\nHere are some more news that we have selected for you.\n\nBest regards,\nYour Imagination, Corp",
    "templates": {
      "text/html": {
        "length": 161
      },
      "text/plain": {
        "length": 136
      }
    },
  }
}

Send Message from your Lumian Application

To send an update to a customer from your Lumian Application, you can build payload include your application data (<<"DataBag">> field) and send it over AMQP using predefined particular template (<<"Template-ID">> field) or your own hard coded templates (<<"HTML">> and <<"Text">> fields):

-spec send_account_update(ne_binary()) -> 'ok'.
send_account_update(AccountId) ->
    case kz_amqp_worker:call(build_customer_update_payload(AccountId)
                            ,fun kapi_notifications:publish_customer_update/1
                            ,fun kapi_notifications:customer_update_v/1
                            )
    of
        {'ok', _Resp} ->
            lager:debug("published customer_update notification");
        {'error', _E} ->
            lager:debug("failed to publish_customer update notification: ~p", [_E])
    end.

-spec build_customer_update_payload(ne_binary()) -> kz_proplist().
build_customer_update_payload(AccountId) ->
    props:filter_empty(
      [{<<"Account-ID">>, kz_services_reseller:get_id(AccountId)}
      ,{<<"Recipient-ID">>, AccountId}
      %% DataBag is useful if you have a customized template and wants to pass some your info from your app
      ,{<<"DataBag">>, kz_json:from_list([{<<"field1">>,<<"value1">>},{<<"field2">>,{[{<<"subfield1">>,<<"subvalue1">>},{<<"subfield2">>,<<"subvalue2">>}]}}])}
      %% set below prop if you have a customized template with this ID in your account's DB
      ,{<<"Template-ID">>, <<"customer_update_billing_period">>}
      %% otherwise set your customized message as below:
      ,{<<"HTML">>, base64:encode(<<"Dear {{user.first_name}} {{user.last_name}}. <br /> DataBag test: {{databag.field2.subfield1}} <br /> Kind regards,">>)}
      ,{<<"Text">>, <<"Oh Dear {{user.first_name}} {{user.last_name}}.\n\nDataBag test: {{databag.field2.subfield2}}\n\nBest regards,">>}
       | kz_api:default_headers(?APP_NAME, ?APP_VERSION)
      ]).

Onboard

About Onboard

Schema

Create

Onboarding

Used to create new accounts without being signed in.

NOTICE

This API has been deprecated and is no longer maintained by the Lumian core team. The preferred method would be to code your own middleware to create accounts using an appropriate parent account API key or auth-token. The PHP SDK is an excellent starting point or you can roll your own!

An example request

PUT to /v1/onboard

{
    "data": {
        "account": {
            "available_apps": [
                "voip",
                "demo_done",
                "cluster",
                "userportal",
                "accounts",
                "developer",
                "numbers",
                "pbxs"
            ],
            "caller_id": {
                "default": {
                    "number": "{COMPANY_PRIMARY_CALLERID}"
                },
                "emergency": {
                    "number": "{COMPANY_EMERGENCY_CALLERID}"
                }
            },
            "default_api_url": "http://{SERVER}/v1",
            "name": "{COMPANY_NAME}",
            "role": "{ACCOUNT_TYPE}"
        },
        "braintree": {
            "company": "{COMPANY_NAME}",
            "credit_card": {
                "billing_address": {
                    "country": "{COMPANY_COUNTRY}",
                    "extended_address": "{COMPANY_EXTENDED_ADDRESS}",
                    "first_name": "{BILLING_CONTACT_FIRST_NAME}",
                    "last_name": "{BILLING_CONTACT_LAST_NAME}",
                    "locality": "{COMPANY_CITY}",
                    "postal_code": "{COMPANY_ZIP_CODE}",
                    "region": "{COMPANY_STATE}",
                    "street_address": "{COMPANY_ADDRESS}"
                },
                "cardholder_name": "{CREDIT_CARD_HOLDER_NAME}",
                "cvv": "{CREDIT_CARD_SECURITY_CODE}",
                "expiration_date": "{CREDIT_CARD_EXPIRATION}",
                "make_default": true,
                "number": "{CREDIT_CARD_NUMBER}"
            },
            "email": "{ADMIN_EMAIL}",
            "first_name": "{ADMIN_FIRST_NAME}",
            "last_name": "{ADMIN_LAST_NAME}"
        },
        "extensions": [
            {
                "callflow": {
                    "numbers": [
                        "{ADMIN_DID}",
                        "{ADMIN_EXTENSION_NUMBER}"
                    ]
                },
                "user": {
                    "apps": {
                        "accounts": {
                            "api_url": "http://{SERVER}:8000/v1",
                            "icon": "account",
                            "label": "Accounts"
                        },
                        "numbers": {
                            "api_url": "http://{SERVER}:8000/v1",
                            "icon": "menu",
                            "label": "Number Manager"
                        },
                        "voip": {
                            "api_url": "http://{SERVER}:8000/v1",
                            "icon": "phone",
                            "label": "Hosted PBX"
                        },
                        "Demo": {
                            "api_url": "http://{SERVER}:8000/v1",
                            "icon": "phone",
                            "label": "Test App"
                        }
                    },
                    "credentials": "a9e4685a973f0ed2844ee9f36e211736",
                    "email": "{ADMIN_EMAIL}",
                    "first_name": "{ADMIN_FIRST_NAME}",
                    "last_name": "{ADMIN_LAST_NAME}",
                    "priv_level": "admin"
                }
            },
            {
                "callflow": {
                    "numbers": [
                        "{USER1_EXTENSION_NUMBER}"
                    ]
                },
                "user": {
                    "first_name": "{USER1_FIRST_NAME}",
                    "last_name": "{USER1_LAST_NAME}",
                    "priv_level": "user"
                }
            },
            {
                "callflow": {
                    "numbers": [
                        "{USER2_EXTENSION_NUMBER}"
                    ]
                },
                "user": {
                    "first_name": "{USER2_FIRST_NAME}",
                    "last_name": "{USER2_LAST_NAME}",
                    "priv_level": "user"
                }
            },
            {
                "callflow": {
                    "numbers": [
                        "{USER3_EXTENSION_NUMBER}"
                    ]
                },
                "user": {
                    "first_name": "{USER3_FIRST_NAME}",
                    "last_name": "{USER4_LAST_NAME}",
                    "priv_level": "user"
                }
            },
            {
                "callflow": {
                    "numbers": [
                        "{USER4_EXTENSION_NUMBER}"
                    ]
                },
                "user": {
                    "first_name": "{USER4_FIRST_NAME}",
                    "last_name": "{USER4_LAST_NAME}",
                    "priv_level": "user"
                }
            }
        ],
        "invite_code": "9351b14aa94b9d580dea57b8deefff0c",
        "phone_numbers": {
            "{ADMIN_EXTENSION_NUMBER}": {
                "e911": {
                    "extended_address": "{ADMIN_EXTENDED_ADDRESS}",
                    "locality": "{ADMIN_CITY}",
                    "postal_code": "{ADMIN_ZIP_CODE}",
                    "region": "{ADMIN_STATE}",
                    "street_address": "{ADMIN_ADDRESS}"
                }
            }
        }
    },
    "verb": "PUT"
}

Parked Calls Slots

Fetch

GET /v2/accounts/{ACCOUNT_ID}/parked_calls

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/parked_calls
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/parked_calls', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Phone Numbers API

About Phone Numbers

The Lumian API for managing numbers.

Per-number CRUD operations

Schema

Schema for a number

Key Description Type Default Required Support Level
carrier_name string(1..30) false
cnam.display_name string(1..15) false
cnam.inbound_lookup boolean() false
cnam object() false
create_with_state The state to create numbers in `string('aging' 'available' 'deleted' 'discovery'
e911.activated_time The time stamp e911 was provisioned string() false
e911.caller_name The name that will show to emergency services string(3..) false
e911.extended_address The suit/floor/apt. address where the number is in service string() false
e911.latitude The e911 provisioning system calculated service address latitude string() false
e911.legacy_data.house_number The name that will show to emergency services string() false
e911.legacy_data.predirectional The name that will show to emergency services string() false
e911.legacy_data.streetname The name that will show to emergency services string() false
e911.legacy_data.suite The name that will show to emergency services string() false
e911.legacy_data Legacy E911 information object() false
e911.locality The locality (city) where the number is in service string() true
e911.location_id The e911 provisioning system internal id for this service address string() false
e911.longitude The e911 provisioning system calculated service address longitude string() false
e911.notification_contact_emails.[] string() false
e911.notification_contact_emails A list of email addresses to receive notification when this number places an emergency call array(string()) [] false
e911.plus_four The extended zip/postal code where the number is in service string() false
e911.postal_code The zip/postal code where the number is in service string() true
e911.region The region (state) where the number is in service string(2) true
e911.status The e911 provisioning system status for this service address `string('INVALID' 'GEOCODED' 'PROVISIONED' 'REMOVED'
e911.street_address The street address where the number is in service string() true
e911 object() false
porting.billing_account_id The account id the losing carrier has on file string() false
porting.billing_extended_address The suit/floor/apt. address the losing carrier has on file string() false
porting.billing_locality The locality (city) the losing carrier has on file string() false
porting.billing_name The name or company name the losing carrier has on file string() false
porting.billing_postal_code The zip/postal code the losing carrier has on file string() false
porting.billing_region The region (state) the losing carrier has on file string() false
porting.billing_street_address The street address the losing carrier has on file string() false
porting.billing_telephone_number The BTN of the account the number belongs to string() false
porting.comments.[] string() false
porting.comments An array of comments array(string()) false
porting.customer_contact The phone number that can be used to contact the owner of the number string() false
porting.port_id The id of the port request string() false
porting.requested_port_date The requested port date string() false
porting.service_provider The name of the losing carrier string() false
porting Porting (in) information for the phone number object() false

Search For Available Numbers On System

GET /v2/phone_numbers?prefix={PREFIX}&quantity={QUANTITY}&offset={OFFSET}&country={COUNTRY}

Sample Request:

curl -v -X GET \
    http://{SERVER}:8000/v2/phone_numbers?prefix=415&quantity=2
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/phone_numbers', {
  params: {
    'prefix': '415'
  }
});

Sample Response:

{
    "auth_token": "",
    "data": [
        {
            "e164": "+14152338397",
            "formatted_number": "1-415-233-8397",
            "npa_nxx": "415233",
            "number": "+14152338397",
            "number_id": "4AA418FB-3409-4340-8210-E7EAFE2AB118",
            "rate_center": {
                "lata": "722",
                "name": "SAN RAFAEL",
                "state": "CA"
            },
            "status": "Available",
            "ten_digit": "4152338397"
        },
        {
            "e164": "+14152338421",
            "formatted_number": "1-415-233-8421",
            "npa_nxx": "415233",
            "number": "+14152338421",
            "number_id": "0CD68E85-F149-477F-9C13-1E720ACCC3EE",
            "rate_center": {
                "lata": "722",
                "name": "SAN RAFAEL",
                "state": "CA"
            },
            "status": "Available",
            "ten_digit": "4152338421"
        }
    ],
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Search for available numbers you own

GET /v2/accounts/{ACCOUNT_ID}/phone_numbers?prefix={PREFIX}&quantity={QUANTITY}&offset={OFFSET}

Sample Request:

curl -v -X GET \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers?prefix=555&quantity=3&offset=6
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers', {
  params: {
    'prefix': '555'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "number": "+15552225562",
            "state": "available"
        },
        {
            "number": "+15554445558",
            "state": "discovery"
        },
        {
            "number": "+15552225562",
            "state": "available"
        }
    ],
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

See How Many Digits A {PREFIX} Can Take

GET /v2/accounts/{ACCOUNT_ID}/phone_numbers/carriers_info

Depending on your carriers configuration you may be allowed to query numbers by NPA-NXX instead of just NPA.

Sample Request:

curl -v -X GET \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/carriers_info
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/carriers_info');

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "maximal_prefix_length": 3,
        "usable_carriers": [
            "bandwidth2",
            "bandwidth",
            "inum",
            "local",
            "inventory",
            "managed",
            "mdn",
            "other",
            "simwood",
            "telnyx",
            "vitelity",
            "voip_innovations"
        ],
        "usable_creation_states": [
            "aging",
            "available",
            "in_service",
            "port_in",
            "reserved"
        ]
    },
    "node": "{NODE}",
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "timestamp": "2017-05-01T20:31:35",
    "version": "4.0.0"
}

List Account's Phone Numbers

This lists the numbers an account owns, along with their properties.

!!! note one can apply filters such as ?filter_state=in_service or ?created_from=63627345744

GET /v2/accounts/{ACCOUNT_ID}/phone_numbers

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers?page_size=3&start_key=%2B14152338421
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers', {
  params: {
    'page_size': '3'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:


    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cascade_quantity": 0,
        "numbers": {
            "+14152338421": {
                "assigned_to": "{ACCOUNT_ID}",
                "created": 63628550806,
                "features": [],
                "state": "in_service",
                "updated": 63628550806
            },
            "+14155234712": {
                "assigned_to": "{ACCOUNT_ID}",
                "created": 63636963275,
                "features": [
                    "local"
                ],
                "state": "in_service",
                "updated": 63636963275
            },
            "+14155558920": {
                "assigned_to": "{ACCOUNT_ID}",
                "created": 63633211146,
                "features": [
                    "local"
                ],
                "state": "reserved",
                "updated": 63633211146
            }
        }
    },
    "next_start_key": "+14155558921",
    "page_size": 3,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "start_key": "+14152338421",
    "status": "success"
}

Remove a number from the account owning it

DELETE /v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response

Success

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "created": 63627848588,
            "modified": 63627848588,
            "state": "available"
        },
        "id": "{PHONE_NUMBER}",
        "state": "available"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Number not in account

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "message": "bad identifier",
        "not_found": "The number could not be found"
    },
    "error": "404",
    "message": "bad_identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Remove A Number From Account (admin Only)

DELETE /v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}?hard=true
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}', {
  params: {
    'hard': 'true'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "created": 63627848588,
            "modified": 63627848588,
            "state": "deleted"
        },
        "id": "{PHONE_NUMBER}",
        "state": "deleted"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

List An Account's Specific Phone Number

Show the number's properties along with user-defined properties.

GET /v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Success Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "created": 63627848989,
            "features": [
                "local"
            ],
            "modified": 63627848989,
            "state": "reserved"
        },
        "features": [
            "local"
        ],
        "id": "{PHONE_NUMBER}",
        "state": "reserved",
        "my_own_field": {}
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Failure Response

Possible reasons for failure:

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "message": "bad identifier",
        "not_found": "The number could not be found"
    },
    "error": "404",
    "message": "bad_identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Update Public Fields Of A Number

!!! note some public fields are used to configure number features.

POST /v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"my_own_field":"some other value", "cnam":{"display_name":"My caller ID", "inbound_lookup":true}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}',
  '{"data":{"my_own_field":"some other value", "cnam":{"display_name":"My caller ID", "inbound_lookup":true}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "created": 63635220353,
            "features": [
                "outbound_cnam",
                "inbound_cnam"
            ],
            "modified": 63635220353,
            "state": "in_service",
            "used_by": "callflow"
        },
        "cnam": {
            "display_name": "My caller ID",
            "inbound_lookup": true
        },
        "features": [
            "outbound_cnam",
            "inbound_cnam"
        ],
        "id": "{PHONE_NUMBER}",
        "my_own_field": "some other value",
        "state": "in_service",
        "used_by": "callflow"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Patch Public Fields Of A Number

PATCH /v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"my_own_field":42}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}',
  '{"data":{"my_own_field":42}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "created": 63635220353,
            "features": [
                "outbound_cnam",
                "inbound_cnam"
            ],
            "modified": 63635220353,
            "state": "in_service",
            "used_by": "callflow"
        },
        "cnam": {
            "display_name": "My caller ID",
            "inbound_lookup": true
        },
        "features": [
            "outbound_cnam",
            "inbound_cnam"
        ],
        "id": "{PHONE_NUMBER}",
        "my_own_field": 42,
        "state": "in_service",
        "used_by": "callflow"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Add a number to the database

Adds a number to the database, returning its properties.

!!! note Set field "create_with_state" in payload to your desired number state (defaults to "reserved").

!!! note Payload is optional.

PUT /v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"my_own_field": {}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}',
  '{"data": {"my_own_field": {}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Success Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "created": 63627848989,
            "modified": 63627848989,
            "state": "reserved"
        },
        "id": "{PHONE_NUMBER}",
        "state": "reserved",
        "my_own_field": {}
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Failure Responses

Number already exists

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cause": "{PHONE_NUMBER}",
        "code": 409,
        "error": "number_exists",
        "message": "number {PHONE_NUMBER} already exists"
    },
    "error": "409",
    "message": "number_exists",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Number does not conform to E.164 format

A non-conforming {PHONE_NUMBER}: "+141510010+15".

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cause": "{PHONE_NUMBER}",
        "code": 404,
        "error": "not_reconcilable",
        "message": "number {PHONE_NUMBER} is not reconcilable"
    },
    "error": "404",
    "message": "not_reconcilable",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Account unauthorized

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "message": "unknown failure",
        "unauthorized": "Not authorized to perform requested number operation"
    },
    "error": "500",
    "message": "unauthorized",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Check Availability Of Phone Numbers

POST /v2/accounts/{ACCOUNT_ID}/phone_numbers/check

This API checks if the numbers are still available for purchase.

A status of "error" may be due to:

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"numbers": [{PHONE_NUMBER1}, {PHONE_NUMBER2}]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/check
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/check',
  '{"data": {"numbers": [{PHONE_NUMBER1}, {PHONE_NUMBER2}]}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "{PHONE_NUMBER1}": "success",
        "{PHONE_NUMBER2}": "error"
    }
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Get Locality Information For A Collection Of Numbers

POST /v2/accounts/{ACCOUNT_ID}/phone_numbers/locality

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}"]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/locality
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/locality',
  '{"data":{"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}"]}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Success Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "{PHONE_NUMBER1}": {
            "carrier": {
                "company": "T-Mobile USA Inc",
                "dba": "T-Mobile USA Inc",
                "id": "6529",
                "type": "WIRELESS"
            },
            "country": "US",
            "e164_number": "{PHONE_NUMBER1}",
            "geocode": {
                "locality": "California"
            },
            "locality": {
                "alt_postal_codes": [
                    "94965",
                    "94941"
                ],
                "extended_postal_code": null,
                "latitude": "37.8725359094361",
                "locality": "Belvedere",
                "longitude": "-122.465900466078",
                "postal_code": "94920",
                "province": "CA",
                "switch": "OKLECAZVGT0",
                "type": "WIRELESS"
            },
            "number": "{PHONE_NUMBER1}",
            "status": "success"
        },
        "{PHONE_NUMBER2}": {
            "carrier": {
                "company": "Bandwidth.com CLEC LLC - CA",
                "dba": "Bandwidth.com CLEC LLC",
                "id": "981E",
                "type": "CLEC"
            },
            "country": "US",
            "e164_number": "{PHONE_NUMBER2}",
            "geocode": {
                "locality": "California"
            },
            "locality": {
                "alt_postal_codes": [
                    "94939",
                    "94976"
                ],
                "extended_postal_code": null,
                "latitude": "37.9267845442655",
                "locality": "Corte Madera",
                "longitude": "-122.527924297914",
                "postal_code": "94904",
                "province": "CA",
                "switch": "SNFCCA21XUY",
                "type": "LANDLINE"
            },
            "number": "{PHONE_NUMBER2}",
            "status": "success"
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Backend to PhoneBook not set up

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": "Unable to acquire numbers missing carrier url",
    "error": "500",
    "message": "init failed",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

List Available Numbers Of A Given US City

GET /v2/accounts/{ACCOUNT_ID}/phone_numbers/prefix

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/prefix?city={CITY}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/prefix?city={CITY}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Success Response

Sample Response:


Country or city not found

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "data": {},
        "error": 404,
        "message": "Not Found",
        "status": "error"
    },
    "error": "500",
    "message": "init failed",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Backend to PhoneBook not set up

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": "Unable to acquire numbers missing carrier url",
    "error": "500",
    "message": "init failed",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Remove a list of numbers from the database

DELETE /v2/accounts/{ACCOUNT_ID}/phone_numbers/collection

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}", "{PHONE_NUMBER3}"]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/collection
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/collection', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  data: '{"data":{"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}", "{PHONE_NUMBER3}"]}}'
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "success": {
            "{PHONE_NUMBER1": {
                "_read_only": {
                    "created": 63628473168,
                    "modified": 63628473168,
                    "state": "available"
                },
                "id": "{PHONE_NUMBER1}",
                "state": "available"
            },
            "{PHONE_NUMBER2}": {
                "_read_only": {
                    "created": 63628473168,
                    "modified": 63628473168,
                    "state": "available"
                },
                "id": "{PHONE_NUMBER2}",
                "state": "available"
            },
            "{PHONE_NUMBER3}": {
                "_read_only": {
                    "created": 63628473168,
                    "modified": 63628473168,
                    "state": "available"
                },
                "id": "{PHONE_NUMBER3}",
                "state": "available"
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Remove a list of numbers from account (admin only)

DELETE /v2/accounts/{ACCOUNT_ID}/phone_numbers/collection

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}", "{PHONE_NUMBER3}"]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/collection?hard=true
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/collection', {
  params: {
    'hard': 'true'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  data: '{"data":{"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}", "{PHONE_NUMBER3}"]}}'
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "success": {
            "{PHONE_NUMBER1": {
                "_read_only": {
                    "created": 63628473168,
                    "modified": 63628473168,
                    "state": "deleted"
                },
                "id": "{PHONE_NUMBER1}",
                "state": "deleted"
            },
            "{PHONE_NUMBER2}": {
                "_read_only": {
                    "created": 63628473168,
                    "modified": 63628473168,
                    "state": "deleted"
                },
                "id": "{PHONE_NUMBER2}",
                "state": "deleted"
            },
            "{PHONE_NUMBER3}": {
                "_read_only": {
                    "created": 63628473168,
                    "modified": 63628473168,
                    "state": "deleted"
                },
                "id": "{PHONE_NUMBER3}",
                "state": "deleted"
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Update public fields of a list of numbers

POST /v2/accounts/{ACCOUNT_ID}/phone_numbers/collection

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}"], "myfield": 1337}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/collection
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/collection',
  '{"data": {"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}"], "myfield": 1337}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "success": {
            "{PHONE_NUMBER1}": {
                "_read_only": {
                    "created": 63628454912,
                    "modified": 63628454912,
                    "state": "reserved"
                },
                "id": "{PHONE_NUMBER1}",
                "myfield": 1337,
                "state": "reserved"
            },
            "{PHONE_NUMBER2}": {
                "_read_only": {
                    "created": 63628454912,
                    "modified": 63628454912,
                    "state": "reserved"
                },
                "id": "{PHONE_NUMBER2}",
                "myfield": 1337,
                "state": "reserved"
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Patch public fields of a list of numbers

PATCH /v2/accounts/{ACCOUNT_ID}/phone_numbers/collection

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}"], "myfield": 2337}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/collection
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/collection',
  '{"data": {"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}"], "myfield": 2337}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "success": {
            "{PHONE_NUMBER1}": {
                "_read_only": {
                    "created": 63628454912,
                    "modified": 63628454912,
                    "state": "reserved"
                },
                "id": "{PHONE_NUMBER1}",
                "myfield": 2337,
                "state": "reserved"
            },
            "{PHONE_NUMBER2}": {
                "_read_only": {
                    "created": 63628454912,
                    "modified": 63628454912,
                    "state": "reserved"
                },
                "id": "{PHONE_NUMBER2}",
                "myfield": 2337,
                "state": "reserved"
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Add a list of numbers to the database

Note: set field "create_with_state" in payload to your desired number state (defaults to "reserved").

PUT /v2/accounts/{ACCOUNT_ID}/phone_numbers/collection

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}", "{PHONE_NUMBER3}"]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/collection
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/collection',
  '{"data":{"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}", "{PHONE_NUMBER3}"]}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Success Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "success": {
            "{PHONE_NUMBER1}": {
                "_read_only": {
                    "created": 63628454912,
                    "modified": 63628454912,
                    "state": "reserved"
                },
                "id": "{PHONE_NUMBER1}",
                "state": "reserved"
            },
            "{PHONE_NUMBER2}": {
                "_read_only": {
                    "created": 63628454912,
                    "modified": 63628454912,
                    "state": "reserved"
                },
                "id": "{PHONE_NUMBER2}",
                "state": "reserved"
            },
            "{PHONE_NUMBER3}": {
                "_read_only": {
                    "created": 63628454912,
                    "modified": 63628454912,
                    "state": "reserved"
                },
                "id": "{PHONE_NUMBER3}",
                "state": "reserved"
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Failure

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "{PHONE_NUMBER2}": {
            "cause": "{PHONE_NUMBER2}",
            "code": 409,
            "error": "number_exists",
            "message": "number {PHONE_NUMBER2} already exists"
        },
        "{PHONE_NUMBER3}": {
            "cause": "{PHONE_NUMBER3}",
            "code": 409,
            "error": "number_exists",
            "message": "number {PHONE_NUMBER3} already exists"
        }
    },
    "error": "400",
    "message": "client error",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

List classifiers

GET /v2/accounts/{ACCOUNT_ID}/phone_numbers/classifiers

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/classifiers
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/classifiers', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "caribbean": {
            "friendly_name": "Caribbean",
            "pretty_print": "SS(###) ### - ###",
            "regex": "^\\+?1((?:684|264|268|242|246|441|284|345|767|809|829|849|473|671|876|664|670|787|939|869|758|784|721|868|649|340)\\d{7})$"
        },
        "did_us": {
            "friendly_name": "US DID",
            "pretty_print": "SS(###) ### - ###",
            "regex": "^\\+?1?([2-9][0-9]{2}[2-9][0-9]{6})$"
        },
        "emergency": {
            "friendly_name": "Emergency Dispatcher",
            "regex": "^(911)$"
        },
        "international": {
            "friendly_name": "International",
            "regex": "^(011\\d*)$|^(00\\d*)$"
        },
        "toll_us": {
            "friendly_name": "US Toll",
            "pretty_print": "SS(###) ### - ###",
            "regex": "^\\+1(900\\d{7})$"
        },
        "tollfree_us": {
            "friendly_name": "US TollFree",
            "pretty_print": "SS(###) ### - ###",
            "regex": "^\\+1((?:800|888|877|866|855)\\d{7})$"
        },
        "unknown": {
            "friendly_name": "Unknown",
            "regex": "^(.*)$"
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fix issues

POST /v2/accounts/{ACCOUNT_ID}/phone_numbers/fix

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/fix
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/fix',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cascade_quantity": 0,
        "numbers": {
            "+14152338421": {
                "assigned_to": "{ACCOUNT_ID}",
                "created": 63627334163,
                "state": "in_service",
                "updated": 63627447350
            },
            "+14155555555": {
                "assigned_to": "{ACCOUNT_ID}",
                "created": 63602230185,
                "state": "in_service",
                "updated": 63602230212,
                "used_by": "callflow"
            },
            "+14158865100": {
                "assigned_to": "{ACCOUNT_ID}",
                "created": 63624719324,
                "state": "in_service",
                "updated": 63624719325
            }
        }
    },
    "page_size": 2,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fix used_by field (and others) of a specific number

POST /v2/accounts/{ACCOUNT_ID}/phone_numbers/fix/{PHONE_NUMBER}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/fix/%2B15554445563
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/fix/%2B15554445563',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "created": 63635220353,
            "features": [
                "inbound_cnam",
                "outbound_cnam"
            ],
            "features_available": [
                "cnam",
                "e911",
                "port",
                "prepend"
            ],
            "modified": 63635220353,
            "state": "in_service",
            "used_by": "callflow"
        },
        "cnam": {
            "display_name": "My Main Num2",
            "inbound_lookup": true
        },
        "features": [
            "inbound_cnam",
            "outbound_cnam"
        ],
        "id": "+15554445563",
        "state": "in_service",
        "ui_metadata": {
            "origin": "common",
            "ui": "monster-ui",
            "version": "3.23"
        },
        "used_by": "callflow"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Return which account a number belongs to

GET /v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}/identify

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}/identify
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}/identify', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Success Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "account_id": "009afc511c97b2ae693c6cc4920988e8",
        "number": "{PHONE_NUMBER}"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Number not in service or account disabled

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "account_id": "009deaaadc97b2ae693c6cc4920988e8",
        "cause": "not_in_service"
    },
    "error": "400",
    "message": "client error",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Number not found or not enough privileges

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "message": "bad identifier",
        "not_found": "The number could not be found"
    },
    "error": "404",
    "message": "bad_identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Create a number in the port_in state

PUT /v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}/port

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
     -d '{"data": {"blip": 432}}' \
     http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/%2B14145137345/port
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/%2B14145137345/port',
  '{"data": {"blip": 432}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "created": 63644564835,
            "features": [
                "local"
            ],
            "features_available": [
                "cnam",
                "e911",
                "failover",
                "port",
                "prepend"
            ],
            "modified": 63644564835,
            "state": "port_in"
        },
        "blip": 432,
        "features": [
            "local"
        ],
        "id": "+14145137345",
        "state": "port_in"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Move a number to the reserved state

PUT /v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}/reserve

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}/reserve
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}/reserve',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Success Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "created": 63628556896,
            "modified": 63628556896,
            "state": "reserved"
        },
        "id": "{PHONE_NUMBER}",
        "state": "reserved"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Number already in reserved state

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "code": 400,
        "error": "no_change_required",
        "message": "no change required"
    },
    "error": "400",
    "message": "no_change_required",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Number does not exist

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "message": "bad identifier",
        "not_found": "The number could not be found"
    },
    "error": "404",
    "message": "bad_identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Buy a number once searched for

Note: one is not charged if number is already in service.

PUT /v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}/activate

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}/activate
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{PHONE_NUMBER}/activate',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Success

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "created": 63628027112,
            "modified": 63628027112,
            "state": "in_service"
        },
        "id": "{PHONE_NUMBER}",
        "state": "in_service"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Number was not returned in previous search results or other error

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "code": 500,
        "error": "unspecified_fault",
        "message": "missing_provider_url"
    },
    "error": "500",
    "message": "unspecified_fault",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Carrier fault

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cause": "{PHONE_NUMBER}",
        "code": 500,
        "error": "unspecified_fault",
        "message": "fault by carrier"
    },
    "error": "500",
    "message": "unspecified_fault",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Classify a number

GET /v2/accounts/{ACCOUNT_ID}/phone_numbers/classifiers/{PHONE_NUMBER}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/classifiers/{PHONE_NUMBER}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/classifiers/{PHONE_NUMBER}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "e164": "+1{PHONE_NUMBER}",
        "friendly_name": "US DID",
        "name": "did_us",
        "number": "{PHONE_NUMBER}",
        "pretty_print": "SS(###) ### - ###",
        "regex": "^\\+?1?([2-9][0-9]{2}[2-9][0-9]{6})$"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Buy a list of numbers

Note: numbers must have appeared as part of the results of a numbers search.

PUT /v2/accounts/{ACCOUNT_ID}/phone_numbers/collection/activate

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}"]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/collection/activate
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/collection/activate',
  '{"data":{"numbers": ["{PHONE_NUMBER1}", "{PHONE_NUMBER2}"]}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Success Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "success": {
            "{PHONE_NUMBER1}": {
                "_read_only": {
                    "created": 63628542222,
                    "modified": 63628542222,
                    "state": "in_service"
                },
                "id": "{PHONE_NUMBER1}",
                "state": "in_service"
            },
            "{PHONE_NUMBER2}": {
                "_read_only": {
                    "created": 63628542222,
                    "modified": 63628542222,
                    "state": "in_service"
                },
                "id": "{PHONE_NUMBER2}",
                "state": "in_service"
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Number not found or other error

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "{PHONE_NUMBER2}": {
            "code": 500,
            "error": "unspecified_fault",
            "message": "missing_provider_url"
        }
    },
    "error": "400",
    "message": "client error",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

E911

POST /v2/accounts/{ACCOUNT_ID}/phone_numbers/collection/activate

With a sample payload like below:

Sample Response:

{
    "data": {
        "used_by": "callflow",
        "id": "{NUMBER}",
        "e911": {
            "caller_name": "{NAME}",
            "postal_code": "{ZIP_CODE}",
            "street_address": "{ADDRESS}",
            "extended_address": "{EXTENDED}",
            "locality": "{CITY}",
            "region": "{STATE}"
        }
    }
}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{_ABOVE_SAMPLE_}' \
    'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{NUMBER}'
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/phone_numbers/{NUMBER}',
  '{_ABOVE_SAMPLE_}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Invalid address

Sample Response:

{
    "data": {
        "address": {
            "invalid": {
                "cause": {
                    "caller_name": "{NAME}",
                    "postal_code": "{ZIP_CODE}",
                    "street_address": "{ADDRESS}",
                    "extended_address": "{EXTENDED}",
                    "locality": "{CITY}",
                    "region": "{STATE}"
                },
                "message": "Location is not geocoded"
            }
        }
    },
    "error": "400",
    "message": "invalid data",
    "status": "error"
}

Multiple choice

Sample Response:

{
    "data": {
        "multiple_choice": {
            "e911": {
                "cause": {
                    "postal_code": "{ZIP_CODE}",
                    "street_address": "{ADDRESS}",
                    "extended_address": "{EXTENDED}",
                    "locality": "{CITY}",
                    "region": "{STATE}"
                },
                "details": [{
                    "postal_code": "{ZIP_CODE}",
                    "street_address": "{ADDRESS}",
                    "extended_address": "{EXTENDED}",
                    "locality": "{CITY}",
                    "region": "{STATE}"
                }, {
                    "postal_code": "{ZIP_CODE}",
                    "street_address": "{ADDRESS}",
                    "extended_address": "{EXTENDED}",
                    "locality": "{CITY}",
                    "region": "{STATE}"
                }],
                "message": "more than one address found"
            }
        }
    },
    "error": "400",
    "message": "multiple_choice",
    "status": "error"
}

Success

Sample Response:

{
    "data": {
        "used_by": "callflow",
        "id": "{NUMBER}",
        "e911": {
            "street_address": "116 NATOMA ST",
            "extended_address": "APT 116",
            "caller_name": "Michel Mabel",
            "locality": "SAN FRANCISCO",
            "latitude": "37.786861",
            "longitude": "-122.399484",
            "location_id": "27578725",
            "plus_four": "3745",
            "postal_code": "94105",
            "region": "CA",
            "status": "PROVISIONED",
            "legacy_data": {
                "house_number": "116",
                "streetname": "NATOMA ST",
                "suite": "APT 116"
            }
        }
    },
    "status": "success"
}

Pivot API

About Pivot

The Pivot resource allows the client to query and inspect data related to the Pivot application (real-time call control).

Enabling in Crossbar

The Pivot endpoint is not loaded on start in a default Lumian installation.

!!! note Adding cb_pivot to the crossbar system_config doc will not start the endpoint; only on restarting Crossbar will cb_pivot be loaded. Use the sup command above to start the endpoint at runtime.

Callflow Schema

See the Pivot callflow schema for details.

Debugging pivot attempts

You will need to edit the data object in the pivot callflow element to include a debug flag:

Sample Response:

{
    "flow": {
      "data": {
        "method": "GET",
        "req_format": "lumian",
        "voice_url": "http://your.pivot.server/path/to/callflow.php",
        "debug": true
        },
      "module": "pivot",
      "children": {
      }
    }
}

All calls to this callflow will now store debug logs to the account's current MODb database.

Fetch a List of Debug UUIDs

GET /v2/accounts/{ACCOUNT_ID}/pivot/debug

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/pivot/debug
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/pivot/debug', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "call_id": "{UUID_1}",
            "created": 63635231906,
            "iteration": 1,
            "node": "{PIVOT_SERVER}",
            "status_code": "404",
            "has_schema_errors": false,
            "uri": "http://127.0.0.1/pivot/lumian_486.php?Language=en-us&Caller-ID-Number=user_suyt9r93ng&Caller-ID-Name=user_suyt9r93ng&Direction=inbound&Api-Version=2015-03-01&To-Realm={SIP_REALM}&To=4786&From-Realm={SIP_REALM}&From=user_suyt9r93ng&Account-ID={ACCOUNT_ID}&Call-ID={UUID_1}"
        },
        {
            "call_id": "{UUID_2}",
            "created": 63635230409,
            "iteration": 1,
            "node": "{PIVOT_SERVER}",
            "has_schema_errors": true
        }
      ],
     "page_size": 3,
     "request_id": "{REQUEST_ID}",
     "revision": "{REVISION}",
     "status": "success",
}

Fetch Debug Logs for a UUID

GET /v2/accounts/{ACCOUNT_ID}/pivot/debug/{UUID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/pivot/debug/{UUID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/pivot/debug/{UUID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}"
     ,"data": [{"call_id": "829597750@10.26.0.158"
                ,"id": "b791e38c9641652a69e297dc9c3a8d66"
                ,"method": "get"
                ,"req_body": ""
                ,"req_headers": {}
                ,"uri": "http://{PIVOT_SERVER}/path/to/callflow.php?CallerNumber={CALLER_ID_NUMBER}&CallerName={CALLER_ID_NAME}&Direction=inbound&ApiVersion=2010-04-01&ToRealm={TO_SIP_REALM}&To={DIALED_NUMBER}&FromRealm={FROM_SIP_REALM}&From={SIP_FROM_USER}&AccountSid={ACCOUNT_ID}&CallSid=829597750%4010.26.0.158"
               }
               ,{"call_id": "829597750@10.26.0.158"
                 ,"id": "f071ae42d9bcebd158f263258e73b001"
                 ,"resp_headers": {
                   "content-length": "303"
                   ,"content-type": "text/html"
                   ,"date": "fri, 30 may 2014 20:42:53 gmt"
                   ,"server": "apache/2.4.7 (ubuntu)"
                 }
                 ,"resp_status_code": "404"
               }
               ,{"call_id": "829597750@10.26.0.158"
                 ,"id": "79604993e4dbe962872a71fe6cbc9717"
                 ,"resp_body": "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>404 Not Found</title>\n</head><body>\n<h1>Not Found</h1>\n<p>The requested URL /path/to/callflow.php was not found on this server.</p>\n<hr>\n<address>Apache/2.4.7 (Ubuntu) Server at {PIVOT_SERVER} Port 80</address>\n</body></html>\n"
                 }
               ]
      ,"request_id": "{REQUEST_ID}"
      ,"revision": "{REVISION}"
      ,"status": "success"
     }

!!! note You must URL-encode the call-id in the URL. Typically this would just mean converting @ to %40, but you'll need to take care depending on how your call-ids are constructed.

Number Port Requests

About Port Requests

Manage and track number port requests through the Port Requests API.

Table of Content

Port request stats

A port request can be in one of seven states:

Port states diagram

porting state flow

Port authority (Port Agent)

Port authority (sometimes called port agent or simply agent) is an account in Lumian system which is responsible for managing their sub account port requests. They are responsible to submitted and oversees port request to losing/winner carrier.

Master account is default port authority.

If you want to manage port request for your sub account, you have to white label your account and set "I will manage port request from my account" in Branding application, "Advanced" tab. If you select that option, you have to set an Email address for port "Support Contact" in the same section.

Schema

Schema for a port request

Key Description Type Default Required Support Level
bill.account_number Account Number to identify account string() false
bill.btn Billing Telephone Number (BTN) to identify account string() false
bill.carrier The name of the losing carrier string() false
bill.locality The locality (city) of the billing address the losing carrier has on record string() false
bill.name The losing carrier billing/account name string() false
bill.pin Personal Identification Number (PIN) to identify account string() false
bill.postal_code The zip/postal code of the billing address the losing carrier has on record string() false
bill.region The region (state) of the billing address the losing carrier has on record string() false
bill.street_address The street name of the billing address the losing carrier has on record string() false
bill.street_number The street number of the billing address the losing carrier has on record string() false
bill.street_post_dir Street Post-Direction string() false
bill.street_pre_dir Street Pre-Direction string() false
bill.street_type The street type of the billing address the losing carrier has on record string() false
bill Billing information of the losing carrier object() false
comments The history of comments made on a port request ["array(", "[#/definitions/comment](#comment)", ")"] false
name A friendly name for the port request string(1..128) true
notifications.email.send_to.[] string() false
notifications.email.send_to A list or string of email recipient(s) `string() array(string())` false
notifications.email Inbound Email Notifications object() false
notifications Status notifications object() false
numbers./\+?[0-9]+/ object() false
numbers The numbers to port in object() true
reference_number Winning carrier reference number or order ID string() false
signee_name The name of the person authorizing the release of numbers from the losing carrier string() false
signing_date The date in Gregorian timestamp on which the document releasing the numbers from the losing carrier was signed integer() false
transfer_date Requested transfer date in Gregorian timestamp integer() false
winning_carrier The name of winning carrier string() false

List port requests

Port request API URL path has specific meaning:

List port requests by type

You can issue GET requests to find all ports in a particular state.

All requests are not paginated, with the exception of the completed state. Use pagination toggles for date range as desired.

To do this add by_types query string to the request. If you don't set by_types, by default active port request will be shown. Possible values are:

Example usage of by_type

Here an example of setting by_types:

/v2/port_requests?by_types=prgressing
/v2/accounts/{ACCOUNT_ID}/port_requests?by_types=prgressing

You can also specify multiple types by separating them by comma like example below:

/v2/port_requests?by_types=pending,scheduled,rejected
/v2/accounts/{ACCOUNT_ID}/port_requests?by_types=pending,scheduled,rejected

Search/list a port request by phone number

For finding a port request by a phone number set by_number in query string. Including an account ID in the URL will change how the port requests are searched:

Example request/response

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/port_requests?by_number={PHONE_NUMBER}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/port_requests?by_number={PHONE_NUMBER}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "account_id": "{ACCOUNT_ID}",
            "created": 63630107130,
            "id": "0684aa1e2785d62c76ce27d2451a1c26",
            "name": "Porting 202.555.9000",
            "numbers": {
                "{PHONE_NUMBER}": {}
            },
            "port_state": "canceled",
            "sent": false,
            "updated": 63630120578,
            "uploads": {
                "file.pdf": {
                    "content_type": "application/pdf",
                    "length": 90931
                }
            }
        }
    ],
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

List port requests that are managed by you

This lists all port requests that you (authenticated account) are managing and port authority for. See Port Authority for more details.

GET /v2/port_requests

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/port_requests
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/port_requests', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "account_id": "{ACCOUNT_ID}",
            "account_name": "{ACCOUNT_NAME}",
            "port_requests": [
                {
                    "account_id": "{ACCOUNT_ID}",
                    "created": 63630097779,
                    "id": "462da37f8be11e46161fb40bc71173a9",
                    "name": "Porting 202.555.9000",
                    "numbers": {
                        "+12025559000": {}
                    },
                    "port_state": "unconfirmed",
                    "sent": false,
                    "updated": 63630097779,
                    "uploads": {}
                }
            ]
        }
    ],
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

List port requests for an account

This only lists port requests of a single account.

GET /v2/accounts/{ACCOUNT_ID}/port_requests

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response is same as listing for agent.

List port requests of sub-accounts

GET /v2/accounts/{ACCOUNT_ID}/descendants/port_requests

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/descendants/port_requests
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/descendants/port_requests', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response is same as listing for agent.

Listing all port requests by their last transition to the submitted state

GET /v2/accounts/{ACCOUNT_ID}/port_requests/last_submitted

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/last_submitted
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/last_submitted', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "id": "{PORT_REQUEST_ID}",
            "transition": {
                "authorization": {
                    "account": {
                        "id": "{AUTH_ACCOUNT_ID}",
                        "name": "{AUTH_ACCOUNT_NAME}"
                    },
                    "user": {
                        "id": "0d46906ff1eb36bff4d09b5b32fc14be",
                        "first_name": "John",
                        "last_name": "Doe"
                    }
                },
                "reason": "this was approved by Jane Doe",
                "timestamp": 63664096014,
                "transition": {
                    "new": "submitted",
                    "previous": "unconfirmed"
                },
                "type": "transition"
            }
        }
    ],
    "node": "{NODE}",
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success",
    "timestamp": "2017-06-07T23:07:09",
    "version": "4.1.12"
}

Listing transitions and comments

GET /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/timeline

This shows the port request's timeline as a sorted list of transitions and comments.

Admins are able to list every transitions and comments regardless of their privacy setting. Non admins only see transitions and public comments.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/timeline
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/timeline', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "authorization": {
                "account": {
                    "id": "{AUTH_ACCOUNT_ID}",
                    "name": "{AUTH_ACCOUNT_NAME}"
                },
                "user": {
                    "id": "0d46906ff1eb36bff4d09b5b32fc14be",
                    "first_name": "John",
                    "last_name": "Doe"
                }
            },
            "timestamp": 63663993575,
            "transition": {
                "new": "unconfirmed"
            },
            "type": "transition"
        },
        {
            "account_id": "0d46906ff1eb36bff4d09b5b32fc14be",
            "action_required": true,
            "content": "the comment is private, and user is required to make an action port request",
            "is_private": true,
            "timestamp": 63664000760,
            "user_id": "0d46906ff1eb36bff4d09b5b32fc14be"
        },
        {
            "account_id": "0d46906ff1eb36bff4d09b5b32fc14be",
            "content": "this is not private",
            "action_required": false,
            "is_private": false,
            "timestamp": 63664000768,
            "user_id": "0d46906ff1eb36bff4d09b5b32fc14be"
        },
        {
            "authorization": {
                "account": {
                    "id": "{AUTH_ACCOUNT_ID}",
                    "name": "{AUTH_ACCOUNT_NAME}"
                },
                "user": {
                    "id": "0d46906ff1eb36bff4d09b5b32fc14be",
                    "first_name": "John",
                    "last_name": "Doe"
                }
            },
            "reason": "this was approved by Jane Doe",
            "timestamp": 63664096014,
            "transition": {
                "new": "submitted",
                "previous": "unconfirmed"
            },
            "type": "transition"
        }
    ],
    "node": "{NODE}",
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success",
    "timestamp": "2017-06-07T23:07:09",
    "version": "4.1.12"
}

Port request management

Create a new port request

PUT /v2/accounts/{ACCOUNT_ID}/port_requests

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"numbers":{"{PHONE_NUMBER}":{}}, "name":"{PORT_REQUEST_NAME}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests',
  // '{"data":{"numbers":{"{PHONE_NUMBER}":{}}, "name":"{PORT_REQUEST_NAME}"}}',
  {
    'data': {
      'numbers': {
        '{PHONE_NUMBER}': {}
      },
      'name': '{PORT_REQUEST_NAME}'
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Responses

Success

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "id": "462da37f8be11e46161fb40bc71173a9",
        "name": "{PORT_REQUEST_NAME}",
        "numbers": {
            "{PHONE_NUMBER}": {}
        },
        "port_state": "unconfirmed",
        "uploads": {},
        "_read_only": {
            "port_authority": "{PORT_AUTHORITY_ACCOUNT_ID}",
            "port_authority_name": "{PORT_AUTHORITY_ACCOUNT_NAME}"
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Failure: a port already exists for this number

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "{PHONE_NUMBER}": {
            "type": {
                "cause": "{PHONE_NUMBER}",
                "message": "Number is on a port request already: 41ed5722d24bfc69bc479208b274be6b"
            }
        }
    },
    "error": "500",
    "message": "invalid request",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Failure: an account already owns this number

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "{PHONE_NUMBER}": {
            "type": {
                "cause": "{PHONE_NUMBER}",
                "message": "Number exists on the system already"
            }
        }
    },
    "error": "500",
    "message": "invalid request",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Get port request details

GET /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "created": 63630097779,
        "id": "{PORT_REQUEST_ID}",
        "name": "Porting 202.555.9000",
        "numbers": {
            "+12025559000": {}
        },
        "port_state": "unconfirmed",
        "sent": false,
        "updated": 63630097779,
        "uploads": {},
        "_read_only": {
            "port_authority": "{PORT_AUTHORITY_ACCOUNT_ID}",
            "port_authority_name": "{PORT_AUTHORITY_ACCOUNT_NAME}"
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Edit a port request

POST /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"numbers":{"{PHONE_NUMBER}":{"state":"NY"}}, "name": "{PORT_REQUEST_NAME}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}',
  // '{"data":{"numbers":{"{PHONE_NUMBER}":{"state":"NY"}}, "name": "{PORT_REQUEST_NAME}"}}',
  {
    'data': {
      'numbers': {
        '{PHONE_NUMBER}': {
          'state': 'NY'
        }
      },
      'name': '{PORT_REQUEST_NAME}'
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "created": 63630097779,
        "id": "{PORT_REQUEST_ID}",
        "name": "{PORT_REQUEST_NAME}",
        "numbers": {
            "{PHONE_NUMBER}": {
                "state": "NY"
            }
        },
        "port_state": "unconfirmed",
        "sent": false,
        "updated": 63630104652,
        "uploads": {},
        "_read_only": {
            "port_authority": "{PORT_AUTHORITY_ACCOUNT_ID}",
            "port_authority_name": "{PORT_AUTHORITY_ACCOUNT_NAME}"
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Patch a port request

PATCH /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"your_custom_info":{"carrier_port_id": "apc-8535937-gtk123", "carrier_name": "ace phone co"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}',
  '{"data":{"your_custom_info":{"carrier_port_id": "apc-8535937-gtk123", "carrier_name": "ace phone co"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "created": 63630097779,
        "id": "{PORT_REQUEST_ID}",
        "name": "{PORT_REQUEST_NAME}",
        "numbers": {
            "{PHONE_NUMBER}": {
                "state": "NY"
            }
        },
        "port_state": "unconfirmed",
        "sent": false,
        "updated": 63630104652,
        "uploads": {},
        "your_custom_info": {
            "carrier_port_id": "apc-8535937-gtk123",
            "carrier_name": "ace phone co"
        },
        "_read_only": {
            "port_authority": "{PORT_AUTHORITY_ACCOUNT_ID}",
            "port_authority_name": "{PORT_AUTHORITY_ACCOUNT_NAME}"
        }
    }
}

DELETE a port request

DELETE /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "id": "{PORT_REQUEST_ID}",
        "name": "Porting 202.555.9000",
        "numbers": {
            "+12025559000": {
                "state": "NY"
            }
        },
        "port_state": "unconfirmed",
        "_read_only": {
            "port_authority": "{PORT_AUTHORITY_ACCOUNT_ID}",
            "port_authority_name": "{PORT_AUTHORITY_ACCOUNT_NAME}"
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Attachment Management

List attachments on a port request

GET /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "file.pdf": {
            "content_type": "application/pdf",
            "length": 90931
        },
        "otherfile.pdf": {
            "content_type": "application/pdf",
            "length": 767684
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Add an attachment to a port request

Note: if ATTACHMENT_ID does not end with .pdf the extension will be appended.

PUT /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/pdf" \
    --data-binary @/path/to/file.pdf \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments?filename={ATTACHMENT_ID}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments?filename={ATTACHMENT_ID}',
  '@/path/to/file.pdf',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/pdf'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {},
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Get an attachment from a port request

GET /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments/{ATTACHMENT_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Accept: application/pdf" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments/{ATTACHMENT_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments/{ATTACHMENT_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Accept': 'application/pdf'
  }
});

Streams back the contents of the PDF file {ATTACHMENT_ID}.

Replace an attachment on a port request

POST /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments/{ATTACHMENT_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/pdf" \
    --data-binary @/path/to/file.pdf \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments/{ATTACHMENT_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments/{ATTACHMENT_ID}',
  '@/path/to/file.pdf',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/pdf'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {},
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Delete an attachment on a port request

DELETE /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments/{ATTACHMENT_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments/{ATTACHMENT_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/attachments/{ATTACHMENT_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {},
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Updating a port request status

When PATCHing a port request a reason can be added to the transition with the following request value: * reason: an optional string that can be used to describe the reason for the transition

This information will then be available in the timeline.

Note: request values can be set either in the query string or in the data payload.

Indicate a port is ready to be processed

PATCH /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/submitted

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/submitted?reason=this+was+approved+by+Jane+Doe
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/submitted',
  '',
  {
    params: {
      'reason': 'this was approved by Jane Doe'
    },
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Success

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "created": 63630107130,
        "id": "{PORT_REQUEST_ID}",
        "name": "Porting 202.555.9000",
        "numbers": {
            "+12025559000": {}
        },
        "port_state": "submitted",
        "sent": false,
        "updated": 63630120443,
        "uploads": {
            "file.pdf": {
                "content_type": "application/pdf",
                "length": 90931
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Failure: charges have to be accepted

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "activation_charges": 10.0,
        "number_services": {
            "port": {
                "activation_charges": 10.0
            }
        }
    },
    "error": "402",
    "message": "accept charges",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Put port in pending

PATCH /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/pending

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/pending
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/pending',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Success

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "created": 63630107130,
        "id": "{PORT_REQUEST_ID}",
        "name": "Porting 202.555.9000",
        "numbers": {
            "+12025559000": {}
        },
        "port_state": "pending",
        "sent": false,
        "updated": 63630120502,
        "uploads": {
            "file.pdf": {
                "content_type": "application/pdf",
                "length": 90931
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Failure: target state illegal given current state

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "port_state": {
            "enum": {
                "cause": "pending",
                "message": "Cannot move to new state from current state"
            }
        }
    },
    "error": "500",
    "message": "invalid request",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Put port in progress (sent to losing carrier)

PATCH /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/scheduled

!!! note - schedule_on is a required field for this state transition.

!!! note - scheduled_date is an automatically added timestamp computed from the value of the schedule_on object.

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"schedule_on": {"timezone":"America/Los_Angeles", "date_time":"2017-06-24 12:00"}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/scheduled
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/scheduled',
  '{"data": {"schedule_on": {"timezone":"America/Los_Angeles", "date_time":"2017-06-24 12:00"}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "created": 63630107130,
        "id": "{PORT_REQUEST_ID}",
        "name": "Porting 202.555.9000",
        "numbers": {
            "+12025559000": {}
        },
        "port_state": "scheduled",
        "schedule_on": {
            "date_time": "2017-06-24 12:00",
            "timezone": "America/Los_Angeles"
        },
        "scheduled_date": 63658292400,
        "sent": false,
        "updated": 63630120528,
        "uploads": {
            "file.pdf": {
                "content_type": "application/pdf",
                "length": 90931
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Complete port, numbers will activate in the system, account will be billed

PATCH /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/completed

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/completed
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/completed',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "created": 63630107130,
        "id": "{PORT_REQUEST_ID}",
        "name": "Porting 202.555.9000",
        "numbers": {
            "+12025559000": {}
        },
        "port_state": "completed",
        "sent": false,
        "updated": 63630120570,
        "uploads": {
            "file.pdf": {
                "content_type": "application/pdf",
                "length": 90931
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Reject a port

PATCH /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/rejected

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/rejected
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/rejected',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "created": 63630107130,
        "id": "{PORT_REQUEST_ID}",
        "name": "Porting 202.555.9000",
        "numbers": {
            "+12025559000": {}
        },
        "port_state": "rejected",
        "sent": false,
        "updated": 63630120570,
        "uploads": {
            "file.pdf": {
                "content_type": "application/pdf",
                "length": 90931
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Cancel a port

PATCH /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/canceled

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/canceled
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/canceled',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "created": 63630107130,
        "id": "{PORT_REQUEST_ID}",
        "name": "Porting 202.555.9000",
        "numbers": {
            "+12025559000": {}
        },
        "port_state": "canceled",
        "sent": false,
        "updated": 63630120578,
        "uploads": {
            "file.pdf": {
                "content_type": "application/pdf",
                "length": 90931
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Build an LOA PDF from a port request

Download the generated Letter Of Authorization PDF file.

GET /v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/loa

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Accept: application/x-pdf" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/loa
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/port_requests/{PORT_REQUEST_ID}/loa', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Accept': 'application/x-pdf'
  }
});

Presence

About Presence

Lumian tracks presence subscriptions and those states can be accessed/manipulated via this API.

There are three main ways to access presence information:

List Subscriptions

GET /v2/accounts/{ACCOUNT_ID}/presence

It is possible to search/list all subscriptions for an account:

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/presence
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/presence', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
     "auth_token": "{AUTH_TOKEN}",
     "data": {
         "subscriptions": {
             "{EXTENSION}": {
                 "dialog": {
                     "{CALL_ID}": {
                         "expires": 1820,
                         "from": "{SIP_USERNAME}@{ACCOUNT_REALM}",
                         "notify": {
                             "body": "undefined",
                             "reply": 0,
                             "sequence": 0
                         },
                         "stalker": "BLF-kamailio.lumian.net",
                         "timestamp": 63606201099,
                         "version": 1
                     }
                 }
             },
             "{SIP_USERNAME}": {
                 "dialog": {
                     "{CALL_ID}": {
                         "expires": 1820,
                         "from": "{SIP_USERNAME}@{ACCOUNT_REALM}",
                         "notify": {
                             "body": "undefined",
                             "reply": 0,
                             "sequence": 0
                         },
                         "stalker": "BLF-kamailio.lumian.net",
                         "timestamp": 63606201394,
                         "version": 1
                     }
                 }
             }
         }
     },
     "request_id": "{REQUEST_ID}",
     "revision": "{REVISION}",
     "status": "success"
}

Reset presence state

Sometimes folks subscribe for parking slots or other values that are not represented in the Lumian REST API.

POST /v2/accounts/{ACCOUNT_ID}/presence/{EXTENSION}

Where {EXTENSION} could be *3101, 110011, or whatever other extensions are allowed.

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"action": "reset"}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/presence/{EXTENSION}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/presence/{EXTENSION}',
  '{"data": {"action": "reset"}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Devices

This API will use the presence_id of the device, if present; otherwise it will use the SIP user name of the device.

Post To Reset Presence State

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"action":"reset"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/presence
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/presence',
  '{"data":{"action":"reset"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Post To Update Presence State

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"action":"set","state":"{PRESENCE_STATE}"}}' \
    'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/presence'
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/presence',
  '{"data":{"action":"set","state":"{PRESENCE_STATE}"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Users

This API will use the presence_id of the user is applicable; otherwise it will use all the user's devices' states.

Post To Reset Presence State

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"action":"reset"}}' \
    'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/presence'
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/presence',
  '{"data":{"action":"reset"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Post To Update Presence State

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"action":"reset","state":"{PRESENCE_STATE}"}}' \
    'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/presence'
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/presence',
  '{"data":{"action":"reset","state":"{PRESENCE_STATE}"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Queues

About Queues

When you have more callers than agents to handle those calls, you can create a call queue to put the callers on hold while agents process callers in the order they arrived in.

Schema

Call Queues - FIFO call queues for serializing callers connecting to agents

Key Description Type Default Required Support Level
agent_ring_timeout In seconds, how long to ring an agent before progressing to the next agent available integer() 15 false
agent_wrapup_time Pre-defined wait period applied after an agent handles a customer call integer() 0 false
announce Media ID (or appropriate media URI) of media to play when caller is about to be connected. string() false
announcements.interval Time between announcements integer() 30 false
announcements.media.in_the_queue Played after the numeric position string() true
announcements.media.increase_in_call_volume Played if the estimated wait time has increased since the previous wait time announcement string() true
announcements.media.the_estimated_wait_time_is Played before the estimated wait time media string() true
announcements.media.you_are_at_position Played before the numeric position string() true
announcements.media Custom prompts to be played for the announcements object() false
announcements.position_announcements_enabled Whether announcements of the caller's position in the queue should be played boolean() false
announcements.wait_time_announcements_enabled Whether announcements of the estimated wait time in the queue should be played boolean() false
announcements Configuration for periodic announcements to callers waiting in the queue object() false
caller_exit_key Key caller can press while on hold to exit the queue and continue in the callflow `string('1' '2' '3' '4'
cdr_url An optional HTTP URL to POST the CDR string() false
connection_timeout In seconds, how long to try to connect the caller before progressing past the queue callflow action integer() 3600 false
enter_when_empty Allows a caller to enter a queue and wait when no agents are available boolean() true false
max_priority Maximum possible priority level queue will support. Can not be redefined for existing queue. integer() false
max_queue_size How many callers are allowed to wait on hold in the queue (0 for no limit) integer() 0 false
moh Media ID (or appropriate media URI) of media to play while caller is on hold. string() false
name A friendly name for the queue string(1..128) true
record_caller When enabled, a caller's audio will be recorded boolean() false false
recording_url An optional HTTP URL to PUT the call recording after the call ends (and should respond to GET for retrieving the audio data) string() false
ring_simultaneously The number of agents to try in parallel when connecting a caller integer() 1 false
strategy The queue strategy for connecting agents to callers `string('round_robin' 'most_idle')` round_robin false

Fetch

GET /v2/accounts/{ACCOUNT_ID}/queues

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "id": "37139638ff5b68f155d8445178524df1",
            "name": "Support Queue"
        }
    ],
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Create a queue

PUT /v2/accounts/{ACCOUNT_ID}/queues

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"name":"Support Queue"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues',
  '{"data":{"name":"Support Queue"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "agent_ring_timeout": 15,
        "agent_wrapup_time": 0,
        "caller_exit_key": "#",
        "connection_timeout": 3600,
        "enter_when_empty": true,
        "id": "37139638ff5b68f155d8445178524df1",
        "max_queue_size": 0,
        "name": "Support Queue",
        "record_caller": false,
        "ring_simultaneously": 1,
        "strategy": "round_robin"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Remove a queue

DELETE /v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "agent_ring_timeout": 15,
        "agent_wrapup_time": 0,
        "caller_exit_key": "#",
        "connection_timeout": 3600,
        "enter_when_empty": true,
        "id": "{QUEUE_ID}",
        "max_queue_size": 0,
        "name": "Support Queue",
        "record_caller": false,
        "ring_simultaneously": 1,
        "strategy": "round_robin"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Details of a specific queue

GET /v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "agent_ring_timeout": 15,
        "agent_wrapup_time": 0,
        "agents": [],
        "caller_exit_key": "#",
        "connection_timeout": 3600,
        "enter_when_empty": true,
        "id": "{QUEUE_ID}",
        "max_queue_size": 0,
        "name": "Support Queue",
        "record_caller": false,
        "ring_simultaneously": 1,
        "strategy": "round_robin"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Update a queue's properties

POST /v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}

PATCH /v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"name":"Support Queue", "max_queue_size": 7}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}',
  '{"data": {"name":"Support Queue", "max_queue_size": 7}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "agent_ring_timeout": 15,
        "agent_wrapup_time": 0,
        "caller_exit_key": "#",
        "connection_timeout": 3600,
        "enter_when_empty": true,
        "id": "93d35ae9f91cf2d5ee4e1bfe59dda029",
        "max_queue_size": 7,
        "name": "Support Queue",
        "record_caller": false,
        "ring_simultaneously": 1,
        "strategy": "round_robin"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

List queues stats

GET /v2/accounts/{ACCOUNT_ID}/queues/stats

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues/stats
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues/stats', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "current_timestamp": 63642383800,
        "stats": []
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Clear a queue's roster

DELETE /v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}/roster

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}/roster
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}/roster', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

List queue roster (which agents are assigned to the queue)

GET /v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}/roster

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}/roster
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}/roster', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [],
    "page_size": 0,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Set the queue roster

curl -v -X POST -H "X-Auth-Token: {AUTH_TOKEN}" -H "Content-Type: application/json" http://server.com:8000/v1/accounts/{ACCOUNT_ID}/queues/{QUEUE_ID}/roster -d '{"data": ["f3ced8ea7bccc352a2124e8a34351e81", "e154a97ec2942599865a1591a477fd19"]}'

Quickcall

About Quickcall

Quickcall allows you to create legs between endpoints (user, device, ...).

Target Call ID

If you know ahead of time that this new quickcall leg will be interacting with an existing call leg, you can supply the existing call-id to the API call to ensure the new leg will be created on the same media server as the existing call leg.

Endpoints supported

Supported endpoints for quickcalls:

Endpoint Type Endpoint Id
users {USER_ID}
devices {DEVICE_ID}

Custom Application Vars

CAVs allow you to set custom data that will appear on subsequent call events (found in Webhook and Websocket payloads) as well as the final CDR.

You can specify CAVs in two way:

Schema

Request options (query string in a GET or POST body):

Key Type Description
auto_answer boolean() Tells the SIP phone to auto-answer the call, if supported
cid-name string() Set the caller ID name (defaults to "Device QuickCall")
cid-number string() Set the caller ID number (defaults to the {PHONE_NUMBER})
custom_application_vars object() Custom data to include on the call (and events related to the call)
ignore-early-media boolean() Toggle whether to ignore early media
media string('bypass', 'process') Toggle whether to go peer-to-peer(bypass with the RTP
number_filter boolean(), regex() If true, remove non-alphanumeric characters. If a regex, use the first capture group as the "number" to dial.
target_call_id string() An existing call-id used to determine what media server to create the quickcall leg on
timeout integer(3..) In seconds, how long to ring the device(s) (defaults to 30)

Non-blocking Quickcall

This returns a 202 immediately. The drawback is that if no endpoints are found to originate to, there will be no channel events generated to let an external application know the quickcall failed.

GET /v2/accounts/{ACCOUNT_ID}/{ENDPOINTS}/{ENDPOINT_ID}/quickcall/{NUMBER}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/{ENDPOINTS}/{ENDPOINT_ID}/quickcall/{NUMBER}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/{ENDPOINTS}/{ENDPOINT_ID}/quickcall/{NUMBER}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Blocking Quickcall

This will return a 202 if the quickcall successfully originates (meaning a channel is started). It will return errors if the originate fails to start a channel or if there aren't any endpoints available.

POST /v2/accounts/{ACCOUNT_ID}/{ENDPOINTS}/{ENDPOINT_ID}/quickcall/{NUMBER}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/{ENDPOINTS}/{ENDPOINT_ID}/quickcall/{NUMBER}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/{ENDPOINTS}/{ENDPOINT_ID}/quickcall/{NUMBER}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Execute a quick call for a device

Ring the device; once answered, connect to {PHONE_NUMBER}

In this scenario, the device is considered the callee while the {PHONE_NUMBER} side is considered the caller (helpful to know when debugging a call!).

GET /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/quickcall/{PHONE_NUMBER}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/quickcall/{PHONE_NUMBER}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/quickcall/{PHONE_NUMBER}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "auth_token": "{AUTH_TOKEN}",
  "data": {
    "export_custom_channel_vars": [
      "Account-ID",
      "Retain-CID",
      "Authorizing-ID",
      "Authorizing-Type"
    ],
    "custom_channel_vars": {
      "authorizing_id": "{DEVICE_ID}",
      "authorizing_type": "device",
      "inherit_codec": "false",
      "retain_cid": "true",
      "account_id": "{ACCOUNT_ID}"
    },
    "continue_on_fail": false,
    "dial_endpoint_method": "simultaneous",
    "outbound_callee_id_number": "{DEVICE_CALLER_ID_NUMBER}",
    "outbound_callee_id_name": "{DEVICE_CALLER_ID_NAME}",
    "outbound_caller_id_number": "{E164_NUMBER}",
    "outbound_caller_id_name": "Device QuickCall",
    "media": "process",
    "ignore_early_media": true,
    "timeout": 30,
    "endpoints": [
      {
        "outbound_call_id": "{CALL_ID}-quickcall",
        "custom_channel_vars": {
          "auto_answer": true,
          "authorizing_id": "{DEVICE_ID}",
          "owner_id": "{USER_ID}",
          "account_id": "{ACCOUNT_ID}",
          "media_encryption_enforce_security": false,
          "sip_invite_domain": "{ACCOUNT_REALM}"
        },
        "custom_sip_headers": {
          "x_kazoo_aor": "sip:{DEVICE_SIP_USER}@{ACCOUNT_REALM}"
        },
        "presence_id": "{PRESENCE_ID}",
        "codecs": [
          "PCMU",
          "PCMA"
        ],
        "endpoint_id": "{DEVICE_ID}",
        "to_did": "{E164_NUMBER}",
        "to_realm": "{ACCOUNT_REALM}",
        "to_username": "{DEVICE_SIP_USER}",
        "to_user": "{DEVICE_SIP_USER}",
        "invite_format": "username"
      }
    ],
    "application_data": {
      "route": "{PHONE_NUMBER}"
    },
    "application_name": "transfer"
  },
  "status": "success",
  "request_id": "{REQUEST_ID}",
  "revision": "{REVISION}"
}

Execute a quick call for a user

Ring user's devices; once answered, connect to {PHONE_NUMBER}

In this scenario, the user's devices are considered the callee while the {PHONE_NUMBER} side is considered the caller (helpful to know when debugging a call!).

GET /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/quickcall/{PHONE_NUMBER}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/quickcall/{PHONE_NUMBER}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/quickcall/{PHONE_NUMBER}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "auth_token": "{AUTH_TOKEN}",
  "data": {
    "export_custom_channel_vars": [
      "Account-ID",
      "Retain-CID",
      "Authorizing-ID",
      "Authorizing-Type"
    ],
    "custom_channel_vars": {
      "authorizing_id": "{USER_ID}",
      "authorizing_type": "user",
      "inherit_codec": "false",
      "retain_cid": "true",
      "account_id": "{ACCOUNT_ID}"
    },
    "continue_on_fail": false,
    "dial_endpoint_method": "simultaneous",
    "outbound_callee_id_number": "{DEVICE_CALLER_ID_NUMBER}",
    "outbound_callee_id_name": "{DEVICE_CALLER_ID_NAME}",
    "outbound_caller_id_number": "{E164_NUMBER}",
    "outbound_caller_id_name": "Device QuickCall",
    "media": "process",
    "ignore_early_media": true,
    "timeout": 30,
    "endpoints": [
      {
        "outbound_call_id": "{CALL_ID}-quickcall",
        "custom_channel_vars": {
          "auto_answer": true,
          "authorizing_id": "{USER_ID}",
          "owner_id": "{USER_ID}",
          "account_id": "{ACCOUNT_ID}",
          "media_encryption_enforce_security": false,
          "sip_invite_domain": "{ACCOUNT_REALM}"
        },
        "custom_sip_headers": {
          "x_kazoo_aor": "sip:{DEVICE_SIP_USER}@{ACCOUNT_REALM}"
        },
        "presence_id": "{PRESENCE_ID}",
        "codecs": [
          "PCMU",
          "PCMA"
        ],
        "endpoint_id": "{DEVICE_ID}",
        "to_did": "{E164_NUMBER}",
        "to_realm": "{ACCOUNT_REALM}",
        "to_username": "{DEVICE_SIP_USER}",
        "to_user": "{DEVICE_SIP_USER}",
        "invite_format": "username"
      }
    ],
    "application_data": {
      "route": "{PHONE_NUMBER}"
    },
    "application_name": "transfer"
  },
  "status": "success",
  "request_id": "{REQUEST_ID}",
  "revision": "{REVISION}"
}

Failing Requests

If issued with a GET to an unregistered device (or a user with no available devices), quickcall will return the call setup information immediately but no channel events will be generated (no events for webhooks/websockets). This can lead external apps to not know if the quickcall was originated properly or not.

Therefore, it is advisable to use POST which will block the API request until the channel starts or the quickcall fails. An example of a failing quickcall (which generated no call events):

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "error_message": "DESTINATION_OUT_OF_ORDER",
        "request": {
            "app_name": "crossbar",
            "app_version": "4.0.0",
            "application_data": {
                "route": "{EXTENSION}"
            },
            "application_name": "transfer",
            "continue_on_fail": false,
            "custom_channel_vars": {
                "account_id": "{ACCOUNT_ID}",
                "authorizing_id": "{DEVICE_ID}",
                "authorizing_type": "device",
                "inherit_codec": "false",
                "retain_cid": "true"
            },
            "dial_endpoint_method": "simultaneous",
            "endpoints": [
                {
                    "codecs": [
                        "PCMU",
                        "PCMA"
                    ],
                    "custom_channel_vars": {
                        "account_id": "{ACCOUNT_ID}",
                        "authorizing_id": "{DEVICE_ID}",
                        "authorizing_type": "device",
                        "auto_answer": true,
                        "media_encryption_enforce_security": false,
                        "sip_invite_domain": "{SIP_REALM}"
                    },
                    "custom_sip_headers": {
                        "x_kazoo_aor": "sip:{SIP_USERNAME}@{SIP_REALM}",
                        "x_kazoo_invite_format": "contact"
                    },
                    "ignore_completed_elsewhere": false,
                    "invite_format": "contact",
                    "outbound_call_id": "{CALL_ID}-quickcall",
                    "presence_id": "{SIP_USERNAME}@{SIP_REALM}",
                    "to_did": "{EXTENSION}",
                    "to_realm": "{SIP_REALM}",
                    "to_user": "{SIP_USERNAME}",
                    "to_username": "{SIP_USERNAME}"
                }
            ],
            "event_category": "resource",
            "event_name": "originate_req",
            "export_custom_channel_vars": [
                "Account-ID",
                "Retain-CID",
                "Authorizing-ID",
                "Authorizing-Type",
                "Outbound-Callee-ID-Number",
                "Outbound-Callee-ID-Name"
            ],
            "ignore_early_media": true,
            "media": "process",
            "msg_id": "{MSG_ID}",
            "node": "{NODE}",
            "outbound_callee_id_name": "{DEVICE_CALLER_ID_NAME}",
            "outbound_callee_id_number": "{DEVICE_CALLER_ID_NUMBER}",
            "outbound_caller_id_name": "Device QuickCall",
            "outbound_caller_id_number": "{EXTENSION}",
            "server_id": "{API_AMQP_QUEUE}",
            "system_log_id": "{LOG_ID}",
            "timeout": 30
        }
    },
    "error": "500",
    "message": "quickcall initiation failed",
    "node": "{NODE}",
    "request_id": "{REQUEST_ID}",
    "status": "error",
    "timestamp": "{TIMESTAMP}",
    "version": "4.2.2"
}

API abuse can occur either maliciously or on accident; either way, the API server needs to protect itself from clients consuming too many server resources simultaneously.

Token Buckets

Crossbar (and Lumian in general) has a token bucket facility for tracking how many tokens a particular bucket has left. A bucket will be created for each client based on the IP of the client and the account ID being manipulated. As a client makes requests against the APIs, tokens will be deducted from the bucket until the bucket is exhausted, at which point API requests will return an HTTP 429 error.

If you receive a 429, that means you're accessing the APIs too quickly and your bucket hasn't been replenished enough to permit the request to be processed.

Configuring API Costs

By default, every request costs 1 token (typically a bucket starts with 100 tokens which refill at 10 tokens per second). Administrators can optionally increase the cost of certain APIs (or methods on an API) to discourage heavy access patterns.

In the system_config/crossbar document, administrators can create a token_costs object to set the costs associated with a particular endpoint. Crossbar will check a series of keys, in order, to find the first one that returns a non-negative cost.

Concretely, if the request is GET /v2/accounts/{ACCOUNT_ID}/callflows, Crossbar will check:

  1. Check {ACCOUNT_ID}."callflows"."GET"
  2. Check {ACCOUNT_ID}."callflows"
  3. Check {ACCOUNT_ID}
  4. Check "callflows"."GET"
  5. Check "callflows"

Note: {HTTP_METHOD} is always in all-caps for the key.

Sample configuration

Using the request above, you can configure the costs in the crossbar config in a variety of ways:

  1. All requests to callflows cost the same

    {"_id":"crossbar" ,"default":{ "token_costs":{ "callflows":2 } } }

  2. callflows vary in cost depending on method:

    {"_id":"crossbar" ,"default":{ "token_costs":{ "callflows":{ "GET":1 ,"PUT":5 ,"POST":5 ,"DELETE":1 } } } }

  3. Deduct flat amount for an account:

    {"_id":"crossbar" ,"default":{ "token_costs":{ "{ACCOUNT_ID}":2 } } }

  4. Deduct flat amount for an account for a specific endpoint:

    {"_id":"crossbar" ,"default":{ "token_costs":{ "{ACCOUNT_ID}":{ "callflows":10 } } } }

Disable token costs

If an administrator prefers to not use rate-limiting, or wants to set a flat-rate for all requests, configure the token_costs with just a number (0 to disable):

  1. Disable token buckets

    {"_id":"crossbar" ,"default":{ "token_costs":0 } }

  2. Flat cost across APIs - any integer > 0. This is how the default Crossbar operates.

    {"_id":"crossbar" ,"default":{ "token_costs":1 } }

Configuring Token Bucket start parameters

To configure the token buckets themselves, look in the system_config/token_buckets document.

So the default bucket will have a maximum of 100 tokens, refilling at 10 tokens per second.

Per-Application configuration

An administrator can change the above parameters on a per-application basis. This would allow larger token limits for Crossbar-related buckets, and smaller limits for Callflow-related (for instance). Configure these per-application settings in the token_buckets document by creating an object with the application name as the key, and the parameters above as sub keys.

{"_id":"token_buckets" ,"default":{ "crossbar":{ "max_bucket_tokens":250 ,"tokens_fill_rate":10 ,"tokens_fill_time":"minute" } ,"callflow":{ "max_bucket_tokens":50 ,"tokens_fill_rate":5 ,"tokens_fill_time":"hour" } } }

Special Cases

There are some APIs that have extra rate limiting options for administrators to tweak.

Quickcall

Administrators can increase the cost of the quickcall API to keep call volume low from this endpoint.

Given a GET /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/quickcall/{NUMBER}

  1. Check {ACCOUNT_ID}."devices"."GET"."quickcall"
  2. Check {ACCOUNT_ID}."devices"."quickcall"
  3. Check {ACCOUNT_ID}."quickcall"
  4. Check "devices"."GET"."quickcall"
  5. Check "devices"."quickcall"

So a configuration to make all quickcall requests cost 20 tokens would look like:

{"_id":"crossbar" ,"default":{ "token_costs":{ "devices":{ "quickcall":20 } } } }

Rate Limits

About Rate Limits

The rate-limits API allows setting per-second and per-minute incoming SIP packets rate limits for devices and whole accounts (realms) that can be used at SBC level. The system level packet rate limits can also be set to protect the whole cluster.

Modify Rate Limits Using API

Using Crossbar to modify rate limits is very simple. There are only three actions:

JSON object has self-describing structure. The name of the root key is rate_limits.

The application-level system-wide configuration resides in system_config/frontier document and its syntax is equal to the account-level documents.

Account-Level Document

Account-level document contains two sections:

Doc Example

Sample Response:

{
    "data": {
        "account": {
            "per_minute": {
                "registrations": 100,
                "invites": 100,
                "total_packets": 1000
            },
            "per_second": {
                "registrations": 5,
                "invites": 5,
                "total_packets": 20
            }
        },
        "device": {
            "per_minute": {
                "registrations": 10,
                "invites": 10,
                "total_packets": 100
            },
            "per_second": {
                "registrations": 2,
                "invites": 4,
                "total_packets": 10
            }
        }
    }
}

Fetch Account's Rate Limits

GET /v2/accounts/{ACCOUNT_ID}/rate_limits

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/rate_limits
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/rate_limits', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Update Account's Rate Limits

POST /v2/accounts/{ACCOUNT_ID}/rate_limits

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"account": {"per_minute": {"total_packets": 3000},"per_second": {"total_packets": 50}},"device": {"per_minute": {"total_packets": 300},"per_second": {"total_packets": 5}}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/rate_limits
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/rate_limits',
  '{"data": {"account": {"per_minute": {"total_packets": 3000},"per_second": {"total_packets": 50}},"device": {"per_minute": {"total_packets": 300},"per_second": {"total_packets": 5}}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Remove Account's Rate Limits

DELETE /v2/accounts/{ACCOUNT_ID}/rate_limits

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/rate_limits
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/rate_limits', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Device-Level Document

Device-level document contains is one level "higher" and contains only the "device" part which contains registrations, invites and total_packets keys with integer values of possible number of packets within per_minute and per_second sections.

Device-Level Example

Sample Response:

{
    "data": {
        "per_minute": {
            "registrations": 60,
            "invites": 60,
            "total_packets": 180
        },
        "per_second": {
            "registrations": 2,
            "invites": 4,
            "total_packets": 7
        }
    }
}

Fetch Device's Rate Limits

GET /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/rate_limits

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/rate_limits
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/rate_limits', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Update Device's Rate Limits

POST /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/rate_limits

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"per_minute": {"registrations": 60,"invites": 60,"total_packets": 180},"per_second": {"registrations": 2,"invites": 4,"total_packets": 7}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/rate_limits
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/rate_limits',
  '{"data": {"per_minute": {"registrations": 60,"invites": 60,"total_packets": 180},"per_second": {"registrations": 2,"invites": 4,"total_packets": 7}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Remove Device's Rate Limits

DELETE /v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/rate_limits

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/rate_limits
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/rate_limits', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Call Rates

About Rates

System operators can create multiple ratedecks and assign those ratedecks to their accounts or sub-accounts via service plans.

If no ratedeck has been assigned, the system ratedeck will be used (backwards-compatible operation).

Flow is:

  1. System admin creates a ratedeck CSV and uploads it using the tasks API endpoint. a. Optionally assign a ratedeck_name to each row to add rates to different ratedeck databases
  2. Create a service plan for ratedecks a. Add the service plan to account(s)
  3. When {ACCOUNT_ID} has a rate-able call, Lumian hotornot application will lookup what ratedeck database to use a. If using the trie algorithm, hotornot will find the PID with that ratedeck's trie and query it b. Otherwise, use the view of the ratedeck database to query for rates

Schema

Defines a rate for a given prefix

Key Description Type Default Required Support Level
account_id Reseller's account ID string() false
caller_id_numbers String of caller id prefixes separated by ':' string() false
carrier Friendly name for the carrier providing this rate string() false
description Friendly description of the rate string() false
direction.[] `string('inbound' 'outbound')` false
direction Apply this rate based on the direction of the call (relative to FreeSWITCH) `array(string('inbound' 'outbound'))` false
internal_rate_cost The per-min rate charged by the upstream provider number() false
iso_country_code Country code this rate applies to string() false
options.[] string() false
options List of options this rate is good for, to be matched against a customer's options array(string()) false
prefix E.164 prefix (ignoring the +) integer() true
rate_cost The per-min rate charged to the downstream customer number() true
rate_increment The time slice, in seconds, to bill in. integer() false
rate_minimum The minimum time slice, in seconds to bill a call integer() false
rate_name Friendly name of the rate string() false
rate_nocharge_time If the call duration is shorter than this threshold (seconds), the call is not billed integer() false
rate_suffix Suffix applied to rate name string() false
rate_surcharge The upfront cost of connecting the call number() false
rate_version Rate version string() false
ratedeck_id ID of the ratedeck this rate belongs to string() false
routes.[] string() false
routes List of regexps that match valid DIDs for this rate array(string()) false
weight Ordering against other rates, 1 being most preferred, 100 being least preferred integer() false

List All Rates

GET /v2/rates

Sample Request:

curl -v -X GET \
    -H "Accept: application/json" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/rates
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/rates', {
  headers: {
    'Accept': 'application/json',
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "cost": 0.1,
            "description": "Default US Rate",
            "id":"{RATE_ID}",
            "prefix": "1",
            "surcharge": 0
        }
    ],
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Switch the Accept header to text/csv to get the page as a CSV.

Upload a Ratedeck CSV

Uploading CSVs has moved to using the 'tasks' API, which provides a more generic interface. See the rates task documentation for more details on uploading rates.

Deprecated version

POST /v2/rates

For bulk uploading. CSV rows can be formatted in the following ways:

A US-1 row might look like:

1, "US-1", "US default rate", 0.01

This API will return an HTTP 202 and process the CSV in a background process.

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: text/csv" \
    --data-binary @/path/to/rates.csv \
    http://{SERVER}:8000/v2/rates
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/rates',
  '@/path/to/rates.csv',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'text/csv'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data":"attempting to insert rates from the uploaded document",
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Create a new rate

PUT /v2/rates

The routes key will be populated for you, using the prefix, unless you specify the routes list here.

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{ \
        "prefix":"1", \
        "iso_country_code": "US", \
        "description": "Default US Rate", \
        "rate_cost": 0.1 \
        }}' \
    http://{SERVER}:8000/v2/rates
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/rates',
  '{"data":{ \\\n        "prefix":"1", \\\n        "iso_country_code": "US", \\\n        "description": "Default US Rate", \\\n        "rate_cost": 0.1 \\\n        }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "description": "Default US Rate",
        "id": "561d9c4c75950235d5565d138752452c",
        "iso_country_code": "US",
        "prefix": "1",
        "rate_cost": 0.1,
        "rate_increment": 60,
        "rate_minimum": 60,
        "rate_nocharge_time": 0,
        "rate_surcharge": 0,
        "routes": [
            "^\\+?1.+$"
        ]
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Remove a rate

DELETE /v2/rates/{RATE_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/rates/{RATE_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/rates/{RATE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token":"{AUTH_TOKEN}",
    "data": {
        "description": "Default US Rate",
        "id": "{RATE_ID}",
        "iso_country_code": "US",
        "prefix": "1",
        "rate_cost": 0.1,
        "rate_increment": 60,
        "rate_minimum": 60,
        "rate_nocharge_time": 0,
        "rate_surcharge": 0
    },
    "request_id":"{REQUEST_ID}",
    "revision":"{REVISION}",
    "status":"success"
}

Fetch a rate

GET /v2/rates/{RATE_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/rates/{RATE_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/rates/{RATE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "description": "Default US Rate",
        "id": "{RATE_ID}",
        "iso_country_code": "US",
        "prefix": "1",
        "rate_cost": 0.1,
        "rate_increment": 60,
        "rate_minimum": 60,
        "rate_nocharge_time": 0,
        "rate_surcharge": 0,
        "routes": [
            "^\\+?1.+$"
        ]
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Patch a rate's properties

PATCH /v2/rates/{RATE_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"description": "Default North America Rate"}}' \
    http://{SERVER}:8000/v2/rates/{RATE_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/rates/{RATE_ID}',
  '{"data": {"description": "Default North America Rate"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "description": "Default North America Rate",
        "id": "{RATE_ID}",
        "iso_country_code": "US",
        "prefix": "1",
        "rate_cost": 0.1,
        "rate_increment": 60,
        "rate_minimum": 60,
        "rate_nocharge_time": 0,
        "rate_surcharge": 0,
        "routes": [
            "^\\+?1.+$"
        ]
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Change a rate doc

POST /v2/rates/{RATE_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{ \
        "description": "Default North America Rate", \
        "iso_country_code": "US", \
        "prefix": "1", \
        "rate_cost": 0.1, \
        "rate_increment": 60, \
        "rate_minimum": 60, \
        "rate_nocharge_time": 0, \
        "rate_surcharge": 0, \
        "routes": ["^\\+?1.+$"] \
        }}' \
    http://{SERVER}:8000/v2/rates/{RATE_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/rates/{RATE_ID}',
  '{"data":{ \\\n        "description": "Default North America Rate", \\\n        "iso_country_code": "US", \\\n        "prefix": "1", \\\n        "rate_cost": 0.1, \\\n        "rate_increment": 60, \\\n        "rate_minimum": 60, \\\n        "rate_nocharge_time": 0, \\\n        "rate_surcharge": 0, \\\n        "routes": ["^\\\\+?1.+$"] \\\n        }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "description": "Default North America Rate",
        "id": "{RATE_ID}",
        "iso_country_code": "US",
        "prefix": "1",
        "rate_cost": 0.1,
        "rate_increment": 60,
        "rate_minimum": 60,
        "rate_nocharge_time": 0,
        "rate_surcharge": 0,
        "routes": [
            "^\\+?1.+$"
        ]
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Rate a phone number

This API requires that the backend app hotornot is running.

GET /v2/rates/number/{PHONE_NUMBER}

The {PHONE_NUMBER} must be reconcilable (see your reconcile_regex for that criteria).

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/rates/number/{PHONE_NUMBER}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/rates/number/{PHONE_NUMBER}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Success Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "Base-Cost": 0.1,
        "E164-Number": "+{PHONE_NUMBER}",
        "Prefix": "1",
        "Rate": 0.1,
        "Rate-Description": "Default US Rate",
        "Rate-Increment": "60",
        "Rate-Minimum": "60",
        "Surcharge": 0.0
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Error: unrateable phone number

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "message": "No rate found for this number"
    },
    "error": "500",
    "message": "No rate found for this number",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Call Recordings

About Recordings

Recordings endpoint provides a way to access call recordings.

Fetch recordings

GET /v2/accounts/{ACCOUNT_ID}/recordings GET /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/recordings

Lists the call recording with pagination and filtering.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/recordings
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/recordings', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch recording media or document

GET /v2/accounts/{ACCOUNT_ID}/recordings/{RECORDING_ID}

Gets a specific recording document.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/recordings/{RECORDING_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/recordings/{RECORDING_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Gets a specific recording document attachment if available. Mind the Accept header in example below. For clients that do not support setting the Accept header, a query string parameter can be included: ?accept=audio/mpeg.

Optional parameter inline can be either true or false.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Accept: audio/mpeg" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/recordings/{RECORDING_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/recordings/{RECORDING_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Accept': 'audio/mpeg'
  }
});

Remove a recording

DELETE /v2/accounts/{ACCOUNT_ID}/recordings/{RECORDING_ID}

This will delete the metadata document. If the binary data is stored on the metadata document (instead of on a storage provider), it will also be deleted. Recordings stored on storage providers will not be deleted.

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/recordings/{RECORDING_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/recordings/{RECORDING_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Registrations

About Registrations

The Registrations API provides an easy way to see and manage current registrations.

Flush All Account's Registrations

DELETE /v2/accounts/{ACCOUNT_ID}/registrations

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/registrations
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/registrations', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
     "auth_token": "{AUTH_TOKEN}",
     "data": "ok",
     "request_id": "{REQUEST_ID}",
     "revision": "{REVISION}",
     "status": "success"
}

List Account's Registrations

GET /v2/accounts/{ACCOUNT_ID}/registrations

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/registrations
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/registrations', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
     "auth_token": "{AUTH_TOKEN}",
     "data": [
         {"account_name": "{ACCOUNT_NAME}",
          "account_realm": "{ACCOUNT_REALM}",
          "authorizing_id": "{DEVICE_ID}",
          "authorizing_type": "device",
          "call_id": "792957271@10.26.0.158",
          "contact": "sip:{SIP_USERNAME}@{IP.AD.DR.ESS}:{PORT}...",
          "contact_ip": "{IP.AD.DR.ESS}",
          "contact_port": "{PORT}",
          "event_timestamp": 63581321366,
          "expires": 257,
          "from_host": "{ACCOUNT_REALM}",
          "from_user": "{SIP_USERNAME}",
          "network_ip": "undefined",
          "network_port": "undefined",
          "original_contact": "sip:{SIP_USERNAME}@{IP.AD.DR.ESS}:{PORT}...",
          "owner_id": "{USER_ID}",
          "proxy_ip": "{KAMAILIO_IP}",
          "proxy_port": "{KAMAILIO_PORT}",
          "realm": "{ACCOUNT_REALM}",
          "suppress_unregister_notify": true,
          "to_host": "{ACCOUNT_REALM}",
          "to_user": "{SIP_USERNAME}",
          "user_agent": "Yealink SIP-T38G 38.0.0.115",
          "username": "{SIP_USERNAME}"
          }
      ],
      "page_size": 1,
      "request_id": "{REQUEST_ID}",
      "revision": "{REVISION}",
      "status": "success"
}

Flush A Specific Device's Registration

DELETE /v2/accounts/{ACCOUNT_ID}/registrations/{USERNAME}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/registrations/{USERNAME}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/registrations/{USERNAME}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
     "auth_token": "{AUTH_TOKEN}",
     "data": "ok",
     "request_id": "{REQUEST_ID}",
     "revision": "{REVISION}",
     "status": "success"
}

Fetch Account Registration Count

GET /v2/accounts/{ACCOUNT_ID}/registrations/count

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/registrations/count
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/registrations/count', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
     "auth_token": "{AUTH_TOKEN}",
     "data": {
         "count": 4
     },
     "request_id": "{REQUEST_ID}",
     "revision": "{REVISION}",
     "status": "success"
}

Resource Selectors

About Resource Selectors

Resource selectors is a new way to route Offnet-calls. Old way used regex rules and "flags" for select proper resources (gateways). With new "resource selectors" you have several small modules, which can be organized in "chain" (rules).

Schema

Schema for resource selector document

Key Description Type Default Required Support Level
name Selector name string() true
resource Resource ID string() true
selector Selector data string() true
start_time Start time (Gregorian seconds) integer() false
stop_time Stop time (Gregorian seconds) integer() false
value Extra selector data string() false

Fetch

GET /v2/resource_selectors/rules

Sample Request:

curl -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    http://{SERVER}:8000/v2/resource_selectors/rules
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/resource_selectors/rules', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'application/json'
  }
});

Sample Response:

{
  "data": {
    "rules": [
      {
        "get_resources": {}
      },
      {
        "filter_list": {
          "value_a": "request:Flags",
          "value_b": "resource:flags",
          "action": "keep"
        }
      },
      {
        "filter_regex": {
          "value_a": "number",
          "value_b": "resource:rules",
          "action": "keep",
          "mode": "empty_fail"
        }
      },
      {
        "filter_regex": {
          "value_a": "cid_number",
          "value_b": "resource:cid_rules",
          "action": "keep",
          "mode": "empty_ok"
        }
      },
      {
        "order": {
          "value": "resource:weight_cost",
          "direction": "ascend"
        }
      }
    ],
    "id": "resource_selector_rules"
  },
  "revision": "{REVISION_ID}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Update rules

POST /v2/resource_selectors/rules

Sample Request:

curl -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data": {"rules": [ \
        {"get_resources":{}}, \
        {"filter_list": {"value_a": "request:Flags", "value_b": "resource:flags", "action": "keep"}} \
    ]}}' \
    http://{SERVER}:8000/v2/resource_selectors/rules
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/resource_selectors/rules',
  '{"data": {"rules": [ \\\n        {"get_resources":{}}, \\\n        {"filter_list": {"value_a": "request:Flags", "value_b": "resource:flags", "action": "keep"}} \\\n    ]}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
  "data": {
    "rules": [
      {
        "get_resources": {}
      },
      {
        "filter_list": {
          "value_a": "request:Flags",
          "value_b": "resource:flags",
          "action": "keep"
        }
      },
    ],
    "id": "resource_selector_rules"
  },
  "revision": "{REVISION_ID}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Database selectors

Database selectors - selectors stored in special database. Name of this database account/XX/XX/XXXXXXXXXXXXXXXXXXXXXXXXXXXX-selectors, where XXX...XXX - Account ID. System-wide selectors database use Master Account ID.

Each selector is separate document:

Sample Response:

{
   "_id": "00066509d2648ede97e30635aa5ba097",
   "_rev": "1-5c13654b7a5521778791e6657789bb56",
   "pvt_type": "resource_selector",
   "name": "prefix",
   "selector": "7495",
   "resource": "RES-4",
   "value": "0.37"
}

List selectors

List selectors names

GET /v2/accounts/{ACCOUNT_ID}/resource_selectors/name

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_selectors/name
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_selectors/name', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "data": [
    {
      "lcr2": 12
    },
    {
      "lcr": 36039
    }
  ],
  "revision": "{REVISION_ID}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Here we see 2 selectors, lcr with 12 documents and lcr2 with 36039 documents.

GET /v2/accounts/{ACCOUNT_ID}/resource_selectors/name/{SELECTOR_NAME}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_selectors/name/lcr2
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_selectors/name/lcr2', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "data": [
    {
      "RES-4": 1
    },
    {
      "RES-3": 8
    },
    {
      "RES-2": 3
    }
  ],
  "revision": "{REVISION_ID}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

List resources

GET /v2/accounts/{ACCOUNT_ID}/resource_selectors/resource

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_selectors/resource
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_selectors/resource', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "data": [
    {
      "RES-4": 36040
    },
    {
      "RES-3": 8
    },
    {
      "RES-2": 3
    }
  ],
  "revision": "{REVISION_ID}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

In this example we see resources RES-2 with 3 documents, RES-3 with 8 documents and RES-4 with 1 document.

GET /v2/accounts/{ACCOUNT_ID}/resource_selectors/resource/{RESOURCE_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_selectors/resource/RES-4
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_selectors/resource/RES-4', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "data": [
    {
      "lcr2": 1
    },
    {
      "lcr": 36039
    }
  ],
  "revision": "{REVISION_ID}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Show selectors

GET /v2/accounts/{ACCOUNT_ID}/resource_selectors/resource/{RESOURCE_ID}/name/{SELECTOR_NAME}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_selectors/resource/RES-4/name/lcr
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_selectors/resource/RES-4/name/lcr', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
  "data": [
    {
      "74956785833": "0.20"
    },
    {
      "74999055927": "0.30"
    },
    ...
    "74991234",
    "74951234",
    ...
    {
      "7495": "0.40"
    },
    {
      "7499": "0.40"
    }
  ],
  "revision": "{REVISION_ID}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Here we see selectors for resource RES-4 with selector name lcr. Resulted list can be simple list of strings or list of objects, its depending if there additional value or not.

Manage selectors

Manage (import/delete) resource selectors made via Lumian tasks (CSV file).

Category resource_selectors, action import or delete.

CSV columns: * mandatory * name * selector * resource * optional * stat_time * stop_time * value

Resource Templates

About Resource Templates

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/resource_templates

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_templates
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_templates', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/resource_templates

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_templates
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_templates',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/resource_templates/{RESOURCE_TEMPLATE_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_templates/{RESOURCE_TEMPLATE_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_templates/{RESOURCE_TEMPLATE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/resource_templates/{RESOURCE_TEMPLATE_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_templates/{RESOURCE_TEMPLATE_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_templates/{RESOURCE_TEMPLATE_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Patch

PATCH /v2/accounts/{ACCOUNT_ID}/resource_templates/{RESOURCE_TEMPLATE_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_templates/{RESOURCE_TEMPLATE_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_templates/{RESOURCE_TEMPLATE_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/resource_templates/{RESOURCE_TEMPLATE_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_templates/{RESOURCE_TEMPLATE_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resource_templates/{RESOURCE_TEMPLATE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Resources

About Resources

Resources represent external assets such as TDM hardware, SIP trunks, trans-coders, and other remote termination/originating call services or equipment.

There are two levels of resources, global (or system-wide), and per-account (bring your own carrier). The JSON format for both is identical; only their location in the Lumian database structure defines whether they are globally available or not.

When interacting with an account's resources, the URL structure is as one would expect: /v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}. To modify the global resources, simply omit /accounts/{ACCOUNT_ID} from the URL (your auth token must have super-duper admin privileges).

To perform bulk resource operations use the collections endpoints.

There are two deprecated API endpoints, global_resources and local_resources. These should continue to work as before, but it is recommended to use resources instead, using the presence of an account id to toggle whether the resource is global or not.

About Adding Bulk Numbers

It is possible to add numbers, in bulk, to an account using the Jobs API below. If a job fails to run, there is a recovery process that runs periodically to attempt to resume stalled jobs.

You can configure how frequently the system checks for failed jobs in system_config/crossbar.resources, using the job_recover_timeout_s key (defaults to 6 hours).

You can configure how what is considered a 'stalled' job by defining how old the job is (the last time the job document was modified) relative to the current time. Configure in system_config/crossbar.resources, using the job_recover_threshold_s key (defaults to 1 hour). If a job is not completed, and hasn't been modified in over an hour, there's a good chance the job executor died. A new job executor will be started to pick up where the old one left off.

Schema

Schema for resources

Key Description Type Default Required Support Level
caller_id_options.type Caller ID type to choose `string('internal' 'external' 'emergency')`
caller_id_options Caller ID options object() false
cid_rules.[] string() false
cid_rules Regexps to match against caller ID array(string()) false
classifiers./.+/.emergency Determines if the resource represents emergency services boolean() false false
classifiers./.+/.enabled Determines if the resource is currently enabled boolean() true false
classifiers./.+/.prefix A string to prepend to the dialed number or capture group of the matching rule string(0..64) false
classifiers./.+/.regex regexp to match against dialed number string() false
classifiers./.+/.suffix A string to append to the dialed number or capture group of the matching rule string(0..64) false
classifiers./.+/.weight_cost A value between 0 and 100 that determines the order of resources when multiple can be used integer() 50 false
classifiers./.+/ object() false
classifiers Resource classifiers to use as rules when matching against dialed numbers object() false
emergency Determines if the resource represents emergency services boolean() false false
enabled Determines if the resource is currently enabled boolean() true false
flags.[] string() false
flags A list of flags that can be provided on the request and must match for the resource to be eligible array(string()) [] false
flat_rate_blacklist Regex for determining if a number should not be eligible for flat-rate trunking string() false
flat_rate_whitelist Regex for determining if the number is eligible for flat-rate trunking string() false
format_from_uri When set to true requests to this resource will have a reformatted SIP From Header boolean() false
formatters Schema for request formatters object() false
from_account_realm When formatting SIP From on outbound requests, use the calling account's SIP realm boolean() false false
from_uri_realm When formatting SIP From on outbound requests this can be used to override the realm string() false
gateway_strategy The strategy of choosing gateways from list: sequential or random `string('sequential' 'random')` false
gateways.[].bypass_media The resource gateway bypass media mode boolean() false
gateways.[].caller_id_type The type of caller id to use `string('internal' 'external' 'emergency')`
gateways.[].channel_selection Automatic selection of the channel within the span: ascending starts at 1 and moves up; descending is the opposite `string('ascending' 'descending')` ascending false
gateways.[].codecs.[] `string('G729' 'PCMU' 'PCMA' 'G722_16'
gateways.[].codecs A list of single list codecs supported by this gateway (to support backward compatibility) `array(string('G729' 'PCMU' 'PCMA' 'G722_16'
gateways.[].custom_sip_headers.in Custom SIP Headers to be applied to calls inbound to Lumian from the endpoint #/definitions/custom_sip_headers false
gateways.[].custom_sip_headers.out Custom SIP Headers to be applied to calls outbound from Lumian to the endpoint #/definitions/custom_sip_headers false
gateways.[].custom_sip_headers.^[a-zA-z0-9_\-]+$ The SIP header to add string() false
gateways.[].custom_sip_headers A property list of SIP headers object() false
gateways.[].custom_sip_interface The name of a custom SIP interface string() false
gateways.[].enabled Determines if the resource gateway is currently enabled boolean() true false
gateways.[].endpoint_type What type of endpoint is this gateway `string('sip' 'freetdm' 'skype' 'amqp')`
gateways.[].force_port Allow request only from this port boolean() false false
gateways.[].format_from_uri When set to true requests to this resource gateway will have a reformatted SIP From Header boolean() false
gateways.[].from_uri_realm When formatting SIP From on outbound requests this can be used to override the realm string() false
gateways.[].invite_format The format of the DID needed by the underlying hardware/gateway `string('route' 'username' 'e164' 'npan'
gateways.[].invite_parameters.dynamic.[] `string() string() string('zone') object()`
gateways.[].invite_parameters.dynamic A list of properties that, if found on the inbound call, should be added as an INVITE parameter array() false
gateways.[].invite_parameters.static.[] string() false
gateways.[].invite_parameters.static A list of static values that should be added as INVITE parameters array(string()) false
gateways.[].invite_parameters object() false
gateways.[].media.fax_option Is T.38 Supported? boolean() false
gateways.[].media.rtcp_mux RTCP protocol messages mixed with RTP data boolean() false
gateways.[].media The media parameters for the resource gateway object() false
gateways.[].password SIP authentication password string(0..32) false
gateways.[].port This resource gateway port integer() 5060 false
gateways.[].prefix A string to prepend to the dialed number or capture group of the matching rule string(0..64) false
gateways.[].progress_timeout The progress timeout to apply to the resource gateway integer() false
gateways.[].realm This resource gateway authentication realm string(0..64) false
gateways.[].route A statically configured SIP URI to route all call to string() false
gateways.[].server This resource gateway server string(1..128) true
gateways.[].skype_interface The name of the Skype interface to route the call over string() false
gateways.[].skype_rr Determines whether to round-robin calls amongst all interfaces (overrides "skype_interface" setting) boolean() true false
gateways.[].span The identity of the hardware on the media server string() false
gateways.[].suffix A string to append to the dialed number or capture group of the matching rule string(0..64) false
gateways.[].username SIP authentication username string(0..32) false
gateways A list of gateways available for this resource array(object()) true
grace_period The amount of time, in seconds, to wait before starting another resource integer() 5 false
ignore_flags When set to true this resource is used if the rules/classifiers match regardless of flags boolean() false
media Media options for resources #/definitions/endpoint.media false
name A friendly name for the resource string(1..128) true
require_flags When set to true this resource is ignored if the request does not specify outbound flags boolean() false
rules.[] string() false
rules A list of regular expressions of which one must match for the rule to be eligible, they can optionally contain capture groups array(string()) [] false
rules_test.[] string() false
rules_test A list of regular expressions of which if matched denotes a test rule array(string()) [] false
weight_cost A value between 0 and 100 that determines the order of resources when multiple can be used integer() 50 false

custom_sip_headers

Custom SIP headers applied to an INVITE

Key Description Type Default Required Support Level
^[a-zA-z0-9_\-]+$ The SIP header to add string() false

endpoint.media

Schema for endpoint media options

Key Description Type Default Required Support Level
audio.codecs.[] `string('OPUS' 'CELT@32000h' 'G7221@32000h' 'G7221@16000h'
audio.codecs A list of audio codecs the endpoint supports `array(string('OPUS' 'CELT@32000h' 'G7221@32000h' 'G7221@16000h'
audio The audio media parameters object() {} false
bypass_media Default bypass media mode (The string type is deprecated, please use this as a boolean) `boolean() string('true' 'false' 'auto')`
encryption.enforce_security Is Encryption Enabled? boolean() false false
encryption.methods.[] `string('zrtp' 'srtp')` false
encryption.methods Supported Encryption Types `array(string('zrtp' 'srtp'))` [] false
encryption Encryption Parameters object() {} false
fax_option Is T.38 Supported? boolean() false
ignore_early_media The option to determine if early media from the endpoint should always be ignored boolean() false
progress_timeout The progress timeout to apply to the endpoint (seconds) integer() false
video.codecs.[] `string('H261' 'H263' 'H264' 'VP8')`
video.codecs A list of video codecs the endpoint supports `array(string('H261' 'H263' 'H264' 'VP8'))`
video The video media parameters object() {} false

formatters

Schema for request formatters

Key Description Type Default Required Support Level
^[[:alnum:]_]+$ Key to match in the route request JSON `array(#/definitions/formatters.format_options) #/definitions/formatters.format_options` false

formatters.format_options

Schema for formatter options

Key Description Type Default Required Support Level
direction Only apply the formatter on the relevant request direction `string('inbound' 'outbound' 'both')`
match_invite_format Applicable on fields with SIP URIs. Will format the username portion to match the invite format of the outbound request. boolean() false
prefix Prepends value against the result of a successful regex match string() false
regex Matches against the value, with optional capture group string() false
strip If set to true, the field will be stripped from the payload boolean() false
suffix Appends value against the result of a successful regex match string() false
value Replaces the current value with the static value defined string() false

INVITE Parameters

The INVITE parameters object defines both static and dynamic parameters that should be added to the request URI.

Static parameters are added 'as-is' and can be any format. However, they should follow the SIP standard for the header field format and should not include a semi-colon.

Dynamic parameters obtain the value from properties of the initiating call (requestor) if present, and are ignored if not. Dynamic parameters can be defined either as a string or an object. When defined as a string the property is extracted from the requestor and if found the resulting value used without modification as an INVITE parameter. When defined as an object both a tag as well as a key property must be defined. The key property is used to extract the value from the requestor and the tag is appended as the INVITE parameter name. By default the INVITE parameter name and value are separated by an equals sign but this can be overridden by providing a separator property.

For example, if a resource gateway contains the following object:

           "invite_parameters": {
               "dynamic": [
                   "custom_channel_vars.pass-through",
                   {
                       "tag": "id",
                       "key": "custom_channel_vars.account_id"
                   }
               ],
               "static": [
                   "npid"
               ]
           }

and assuming the requesting call has pass-through (with value pass-through=0288) as well as account_id (with value XXXX) custom channel variables it will result in an INVITE request URI such as:

INVITE sip:+14158867900@10.26.0.88;npid;id=XXXX;pass-through=0288 SIP/2.0

rules_test.[]

The rules_test object defines an array of regular expressions for test patterns of the given resource.

For example, if the resource handles emergency routes in North America:

  "rules_test": [
      "^\\+{0,1}(933)$"
  ],

defining 933 as a test route, will inform teletype this emergency call is a test and will be reflected as such in the notification.

Fetch

GET /v2/accounts/{ACCOUNT_ID}/resources

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
         {"enabled": true,
          "id": "{RESOURCE_ID}",
          "name": "Carrier1",
          "weight": "50"
         },
         {"enabled": true,
          "id": "{RESOURCE_ID}",
          "name": "Carrier2",
          "weight": "50"
         }
    ],
    "page_size": 2,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION_ID}",
    "status": "success"
}

Create a new resource

PUT /v2/accounts/{ACCOUNT_ID}/resources

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"name":"Carrier 3", "gateways":[]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources',
  // '{"data":{"name":"Carrier 3", "gateways":[]}}',
  {
    'data': {
      'name': 'Carrier 3',
      'gateways': []
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "emergency": false,
        "enabled": true,
        "flags": [],
        "gateways": [],
        "grace_period": 5,
        "id": "{RESOURCE_ID}",
        "media": {
            "audio": {
                "codecs": ["PCMU"]
             },
             "video": {
                 "codecs": []
             }
         },
         "name": "Carrier 3",
         "rules": [],
         "weight_cost": 50
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION_ID}",
    "status": "success"
}

Remove a resource

DELETE /v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "caller_id_options": {
            "type": "external"
        },
        "emergency": false,
        "enabled": true,
        "flags": [],
        "gateways": [
            {
                "channel_selection": "ascending",
                "codecs": ["PCMU", "PCMA"],
                "custom_sip_headers": {},
                "emergency": false,
                "enabled": true,
                "endpoint_type": "sip",
                "format_from_uri": false,
                "invite_format": "route",
                "password": "DrWoody",
                "prefix": "+1",
                "progress_timeout": "6",
                "realm": "carrier1.com",
                "server": "carrier1.com",
                "skype_rr": true,
                "suffix": "100",
                "username": "blazemore"
            }
        ],
        "grace_period": 5,
        "id": "{RESOURCE_ID}",
        "media": {
            "audio": {
                "codecs": ["PCMU"]
            },
            "video": {
                "codecs": []
            }
        },
        "name": "Carrier 3",
        "peer": false,
        "rules": [
            "^\\+{0,1}1{0,1}(\\d{10})$"
        ],
        "type": "local",
        "weight_cost": "50"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION_ID}",
    "status": "success"
}

Fetch a resource

GET /v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "caller_id_options": {
            "type": "external"
        },
        "emergency": false,
        "enabled": true,
        "flags": [],
        "gateways": [
            {
                "channel_selection": "ascending",
                "codecs": ["PCMU", "PCMA"],
                "custom_sip_headers": {},
                "emergency": false,
                "enabled": true,
                "endpoint_type": "sip",
                "format_from_uri": false,
                "invite_format": "route",
                "password": "DrWoody",
                "prefix": "+1",
                "progress_timeout": "6",
                "realm": "carrier1.com",
                "server": "carrier1.com",
                "skype_rr": true,
                "suffix": "100",
                "username": "blazemore"
            }
        ],
        "grace_period": 5,
        "id": "{RESOURCE_ID}",
        "media": {
            "audio": {
                "codecs": ["PCMU"]
            },
            "video": {
                "codecs": []
            }
        },
        "name": "Carrier 3",
        "peer": false,
        "rules": [
            "^\\+{0,1}1{0,1}(\\d{10})$"
        ],
        "type": "local",
        "weight_cost": "50"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION_ID}",
    "status": "success"
}

Change a resource

POST /v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{...ResourceData...}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}',
  '{"data":{...ResourceData...}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "caller_id_options": {
            "type": "external"
        },
        "emergency": false,
        "enabled": true,
        "flags": [],
        "gateways": [
            {
                "channel_selection": "ascending",
                "codecs": ["PCMU", "PCMA"],
                "custom_sip_headers": {},
                "emergency": false,
                "enabled": true,
                "endpoint_type": "sip",
                "format_from_uri": false,
                "invite_format": "route",
                "password": "DrWoody",
                "prefix": "+1",
                "progress_timeout": "6",
                "realm": "carrier1.com",
                "server": "carrier1.com",
                "skype_rr": true,
                "suffix": "100",
                "username": "blazemore"
            }
        ],
        "grace_period": 5,
        "id": "{RESOURCE_ID}",
        "media": {
            "audio": {
                "codecs": ["PCMU"]
            },
            "video": {
                "codecs": []
            }
        },
        "name": "Carrier 3",
        "peer": false,
        "rules": [
            "^\\+{0,1}1{0,1}(\\d{10})$"
        ],
        "type": "local",
        "weight_cost": "50"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION_ID}",
    "status": "success"
}

Patch a resource

PATCH /v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"custom_sip_headers":{"X-Reseller-ID":"a1b2c3"}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}',
  {
    'data': {
      'custom_sip_headers': {
        'X-Reseller-ID': 'a1b2c3'
      }
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "caller_id_options": {
            "type": "external"
        },
        "custom_sip_headers": {
            "X-Reseller-ID": "a1b2c3"
        },
        "emergency": false,
        "enabled": true,
        "flags": [],
        "gateways": [
            {
                "channel_selection": "ascending",
                "codecs": ["PCMU", "PCMA"],
                "custom_sip_headers": {},
                "emergency": false,
                "enabled": true,
                "endpoint_type": "sip",
                "format_from_uri": false,
                "invite_format": "route",
                "password": "DrWoody",
                "prefix": "+1",
                "progress_timeout": "6",
                "realm": "carrier1.com",
                "server": "carrier1.com",
                "skype_rr": true,
                "suffix": "100",
                "username": "blazemore"
            }
        ],
        "grace_period": 5,
        "id": "{RESOURCE_ID}",
        "media": {
            "audio": {
                "codecs": ["PCMU"]
            },
            "video": {
                "codecs": []
            }
        },
        "name": "Carrier 3",
        "peer": false,
        "rules": [
            "^\\+{0,1}1{0,1}(\\d{10})$"
        ],
        "type": "local",
        "weight_cost": "50"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION_ID}",
    "status": "success"
}

Fetch a listing of jobs

Do note you can use the created_from and created_to flags to change to time period queried.

The keys failures and successes represent the count of how many numbers failed and succeeded, respectively.

GET /v2/accounts/{ACCOUNT_ID}/resources/jobs

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/jobs
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/jobs', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "failures": 0,
            "successes": 2,
            "id": "201408-394de70ecf6f8252",
            "status": "pending",
            "timestamp": 63575950041,
            "resource_id":"{RESOURCE_ID}"
        },
        {
            "failures": 0,
            "successes": 1,
            "id": "201408-70766ed00a24",
            "status": "pending",
            "timestamp": 63575878379,
            "resource_id":"{RESOURCE_ID}"
        }
    ]
    "page_size": 2,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "start_key": 63573276761,
    "status": "success"
}

Create a new job

PUT /v2/accounts/{ACCOUNT_ID}/resources/jobs

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"numbers":["+12223334444", "+23334445555"], "resource_id":"{RESOURCE_ID}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/jobs
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/jobs',
  // '{"data":{"numbers":["+12223334444", "+23334445555"], "resource_id":"{RESOURCE_ID}"}}',
  {
    'data': {
      'numbers': [
        '+12223334444',
        '+23334445555'
      ],
      'resource_id': '{RESOURCE_ID}'
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "errors": {},
        "id": "201408-39512771f9d2d499",
        "resource_id":"{RESOURCE_ID}",
        "numbers": [
            "+12223334444"
        ],
        "successes": {}
     },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch a job's status

GET /v2/accounts/{ACCOUNT_ID}/resources/jobs/{JOB_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/jobs/{JOB_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/jobs/{JOB_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "resource_id": "{RESOURCE_ID}",
        "errors": {},
        "id": "201408-394de70ecf6f8252",
        "numbers": [
            "3148096310"
        ],
        "status": "pending",
        "successes": {},
        "timestamp": 63575950041
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Create a new collection of resources

PUT /v2/accounts/{ACCOUNT_ID}/resources/collection

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":[{...RESOURCE...}, {...RESOURCE...}]}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/collection
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/collection',
  '{"data":[{...RESOURCE...}, {...RESOURCE...}]}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Change a collection

POST /v2/accounts/{ACCOUNT_ID}/resources/collection

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"numbers":["+12223334444", "+23334445555"], "resource_id":"{RESOURCE_ID}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/collection
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/collection',
  // '{"data":{"numbers":["+12223334444", "+23334445555"], "resource_id":"{RESOURCE_ID}"}}',
  {
    'data': {
      'numbers': [
        '+12223334444',
        '+23334445555'
      ],
      'resource_id': '{RESOURCE_ID}'
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data":{
        "errors":{
            "{RESOURCE_ID}": "{ERROR_MESSAGE}"
        },
        "successes":{
            "{RESOURCE_ID}": "{RESOURCE_DOC}"
        }
    }
}

Working with reverse proxies (HAProxy, Nginx, etc)

Configuring list allowed proxies

Set list in system_config/crossbar, key reverse_proxies. Values can be: - single ip address ("192.168.0.1"); - single ip address in CIDR notation ("192.168.0.1/32") - network in CIDR notation ("192.168.0.0/24")

Configuration reverse proxy

Reverse proxy must set "X-Forwarded-For" header. - HAProxy - Nginx + add proxy_set_header X-Forwarded-For $remote_addr; - Apache 2.2

JSON Schema

Lumian uses JSON Schemas to validate incoming data from clients.

Any fields that aren't defined in the JSON schema will be stored, unmodified, along side the validated fields (assuming all is well). This excludes Lumian-managed private fields (top-level keys prefixed with "_" or "pvt_").

This is the API endpoint to inspect all JSON Schemas.

List All Available Schema

The default fetch will retrieve only the documents meant for the APIs to operate on (doc and system_config schemas). Lumian also has the internal JSON APIs available as JSON schemas, which can be fetched via /v2/schemas?internals=true.

GET /v2/schemas

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/schemas
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/schemas', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        "access_lists",
        "account_rate_limits",
        "accounts",
        "acls",
        "allotments",
        "api_auth",
        "app",
        "audit_logs",
        "blacklists",
        "bookkeepers",
        "call_waiting",
        "caller_id",
        "callflows",
        "callflows.collect_dtmf",
        "callflows.conference",
        "callflows.language",
        "callflows.lookupcidname",
        "callflows.manual_presence",
        "callflows.nomorobo",
        "callflows.pivot",
        "callflows.record_call",
        "callflows.response",
        "callflows.ring_group",
        "callflows.send_dtmf",
        "callflows.tts",
        "callflows.voicemail",
        "cccps",
        "cdr",
        "clicktocall",
        "conferences",
        "connectivity",
        "device_rate_limits",
        "devices",
        "dialplans",
        "directories",
        "domain_hosts",
        "domains",
        "faxbox",
        "faxes",
        "ledgers",
        "limits",
        "list_entries",
        "lists",
        "media",
        "menus",
        "metaflows",
        "notifications",
        "notify.callback",
        "phone_numbers",
        "port_requests",
        "profile",
        "provisioner_v5",
        "queue_update",
        "queues",
        "rates",
        "resource_jobs",
        "resources",
        "service_plans",
        "shared_auth",
        "sms",
        "temporal_rules",
        "temporal_rules_sets",
        "token_restrictions",
        "trunkstore",
        "ubiquiti_auth",
        "user_auth",
        "user_auth_recovery",
        "users",
        "vmboxes",
        "webhook_attempts",
        "webhooks",
        "whitelabels"
    ],
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch the schema definitions

GET /v2/schemas/{SCHEMA_NAME}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/schemas/acls
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/schemas/acls', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "",
    "data": {
        "$schema": "http://json-schema.org/draft-04/schema#",
        "additionalProperties": false,
        "description": "Access Control List entries",
        "id": "acls",
        "properties": {
            "cidr": {
                "description": "Classless Inter-Domain Routing IP notation for use on the ACL",
                "type": "string"
            },
            "description": {
                "description": "Will be added as a comment for quick identification later",
                "maxLength": 30,
                "type": "string"
            },
            "network-list-name": {
                "description": "The trusted list should represent anything that can issue calls without authorization.  The authoritative list should indicate inter-network routing equipment (SBC, etc).",
                "enum": [
                    "authoritative",
                    "trusted"
                ],
                "type": "string"
            },
            "type": {
                "default": "allow",
                "description": "Allow or deny this CIDR",
                "enum": [
                    "allow",
                    "deny"
                ],
                "type": "string"
            }
        },
        "required": [
            "cidr",
            "network-list-name",
            "type"
        ],
        "type": "object"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Perform a validation

Test your request data against the validation schema (without performing a database operation).

PUT /v2/schemas/{SCHEMA_NAME}/validation

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{...}}' \
    http://{SERVER}:8000/v2/schemas/{SCHEMA_NAME}/validation
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/schemas/{SCHEMA_NAME}/validation',
  '{"data":{...}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "auth_token":"",
    "data":{...},
    "request_id":"{REQUEST_ID}",
    "revision":"{REVISION}",
    "status":"success"
}

The Search API allows query a value on databases. You can query different type of entity commonly like accounts (on accounts database), users, devices, callflows, numbers. It possible to search for any document base on it's ID.

Generally you have to provide which document type to search, which field in the document should be searched and a value to search with. How to specify these query parameters is depend on you want to search with a single value or multiple values.

Databases To Search On, Fields To Search With

Search for accounts is done on accounts database. The parameters that you can search with are:

For other type of documents search is done on Account's database. The parameters to search with are:

Document Types

You have to specify what kind of piece of information is you're looking for (t query string parameter). Typically you're interested to search for an account.

Frequent types are:

Create Custom Search Views (Advanced)

Search in Lumian is possible with specific CouchDB view with design document ID search in account's database and accounts database. View index defined in this design document are exposing the common fields (name, id, etc...) in documents for search.

With that in mind, a system administrator can create a custom index to expose extra fields out of document to view. This requires full understanding of how to write CouchDB design documents and a bit of the internal structure of the document in the database. You can look at {ACCOUNT_DB}/_design/search to see how Lumian default search view is implemented.

It's require to name your custom search view index name prefixed with search_by_{YOUR_CUSTOM_VIEW_NAME}, which conventionally {YOUR_CUSTOM_VIEW_NAME} is the name of the field in the document that you're creating this view for.

Search with Single Value

To look up a single value in the specified field. These should defined in request query string.

GET /v2/accounts/{ACCOUNT_ID}/search

Query String Parameters

Key Description Type Default Required
t document type string true
q the field to look in for value string true
v value to search for string or boolean true

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/6134cc9aa43ffaee3e3f0c9a84113d6e/search?t=account&q=name&v=nat
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/6134cc9aa43ffaee3e3f0c9a84113d6e/search', {
  params: {
    't': 'account'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response

Sample Response:

{
    "page_size": 1,
    "start_key": "g2wAAAADbQAAACA2MTM0Y2M5YWE0M2ZmYWVlM2UzZjBjOWE4NDExM2Q2ZW0AAAAHYWNjb3VudG0AAAAHbmF0ZmZmMGo",
    "data": [
        {
            "id": "a391d64a083b99232f6d2633c47432e3",
            "descendants_count": 2,
            "name": "natalie",
            "realm": "natalie.lumian.net"
        }
    ],
    "timestamp": "{TIMESTAMP}",
    "version": "{VERSION}",
    "node": "{NODE_HASH}",
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

Searching for an Account

If you're searching for an account you can remove /accounts/{ACCOUNT_ID} from the URL.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/search?t=account&q=name&v=nat
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/search', {
  params: {
    't': 'account'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

To search with multiple values in a single shot. Search parameters should defined in request query string.

GET /v2/accounts/{ACCOUNT_ID}/search/multi

Query String Parameters

Key Description Type Default Required
t document type string true
by_{view_name} the values to search for in {view_name} results string true

Here {view_name} is referring in what field is available to search. See above.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/accounts/6134cc9aa43ffaee3e3f0c9a84113d6e/v2/search/multi?t=account&by_name=test&by_realm=test&by_id=test
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/accounts/6134cc9aa43ffaee3e3f0c9a84113d6e/v2/search/multi', {
  params: {
    't': 'account'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response

Sample Response:

{
    "data": {
        "name": [
            {
                "id": "a391d64a083b99232f6d2633c47432e3",
                "descendants_count": 2,
                "name": "test",
                "realm": "test.lumian.net"
            }
        ],
        "realm": [
            {
                "id": "a391d64a083b99232f6d2633c47432e3",
                "descendants_count": 2,
                "name": "test",
                "realm": "test.lumian.net"
            }
        ]
    },
    "timestamp": "{TIMESTAMP}",
    "version": "{VERSION}",
    "node": "{NODE_HASH}",
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

Searching for Account

If you're searching for account you can remove /accounts/{ACCOUNT_ID} from the URL.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/search/multi?t=account&by_name=test&by_realm=test
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/search/multi', {
  params: {
    't': 'account'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Security Configuration

Crossbar API to configure authentication for an account.

About Security

Crossbar authenticator modules can have their own account's version configuration to control some aspect of them like enabling/disabling the module or use multi factor authenticator for that specific module.

!!! note This API endpoint is only configuring the authentication for a account, for configuring the system, you should use system_configs instead as super duper admin. System config category is crossbar.auth.

How Crossbar is looking for authentication configuration

Configuration is the merged result of the account's configuration and all its parent's account up to the first reseller, then the system config itself. So the account inherits parent and reseller account and system config.

Enable Multi Factor Authentication for a Crossbar auth module

If you want to use multi factor authentication for a module, set the multi_factor.enabled to true. You can control if the multi factor settings can be applied to the account's children by multi_factor.include_subaccounts.

When setting configuration_id of the multi-factor, you have to set the Account ID which contains the that configuration.

Only a parent Account or the same Account can set configuration_id and account_id unless multi_factor.include_subaccounts is true and a descendant account can use its parent configuration_id.

See Multi Factor Authentication API documentation.

Account Auth Configuration Schema

Key Description Type Default Required
cb_api_auth #/definitions/auth_module_config false
cb_auth #/definitions/auth_module_config false
cb_ip_auth #/definitions/auth_module_config false
cb_ubiquiti_auth #/definitions/auth_module_config false
cb_user_auth #/definitions/auth_module_config false

Auth Module Configuration Schema

Key Description Type Default Required
enabled whether or not this authentication module is enabled boolean false
log_failed_attempts should log failed logging attempts boolean false
log_successful_attempts should log successful logging attempts boolean false
multi_factor control multi factor authentications for this module object false
multi_factor.account_id ID of the account that contains the multi factor configuration string false
multi_factor.configuration_id document ID contains the multi factor configuration string false
multi_factor.enabled turn on/off multi factor authentications for this module boolean false
multi_factor.include_subaccounts should this multi factor authentication settings be applied when used by sub-accounts boolean false
token_auth_expiry_s expiration period of the JWT token (seconds) integer false

Get a List of Available Auth Module

List of all available auth module to be configured.

GET /v2/security

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/security
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/security', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": {
    "available_auth_modules": [
      "cb_api_auth",
      "cb_auth",
      "cb_ip_auth",
      "cb_ubiquiti_auth",
      "cb_user_auth"
    ]
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Fetch All Configurations

Get all configured authenticator module on the account alongside the default settings of merged result of account itself and its parents, reseller and system.

GET /v2/accounts/{ACCOUNT_ID}/security

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": {
    "account": {},
    "inherited_config": {
      "auth_modules": {
        "cb_user_auth": {
          "enabled": true,
          "token_auth_expiry_s": 3600,
          "log_failed_attempts": true,
          "log_successful_attempts": true,
          "multi_factor": {
            "enabled": false,
            "_read_only": {
              "name": "Default System Provider",
              "provider_name": "duo"
            }
          }
        },
        "cb_api_auth": {
          "enabled": true,
          "token_auth_expiry_s": 3600,
          "log_failed_attempts": true,
          "log_successful_attempts": true
        },
        "cb_auth": {
          "enabled": true,
          "token_auth_expiry_s": 3600,
          "log_failed_attempts": true,
          "log_successful_attempts": true
        },
        "cb_ip_auth": {
          "enabled": true,
          "token_auth_expiry_s": 3600,
          "log_failed_attempts": true,
          "log_successful_attempts": true
        },
        "cb_ubiquiti_auth": {
          "enabled": true,
          "token_auth_expiry_s": 3600,
          "log_failed_attempts": true,
          "log_successful_attempts": true
        }
      }
    }
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Change

Customize modules config for the account. Set what settings you want here, crossbar always get the merged config from account and hierarchy.

POST /v2/accounts/{ACCOUNT_ID}/security

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ "data": { "auth_modules" :{ "cb_user_auth": { "token_auth_expiry_s": 604800 }, "cb_api_auth": { "enabled": false } } } }' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security',
  '{ "data": { "auth_modules" :{ "cb_user_auth": { "token_auth_expiry_s": 604800 }, "cb_api_auth": { "enabled": false } } } }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Response

Sample Response:

{
  "data": {
    "auth_modules": {
      "cb_user_auth": {
        "token_auth_expiry_s": 604800
      },
      "cb_api_auth": {
        "enabled": false
      }
    },
    "id": "configs_crossbar.auth"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Patch

Patch field(s) of config for the account customization.

PATCH /v2/accounts/{ACCOUNT_ID}/security

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ "data": { "auth_modules" :{ "cb_api_auth": { "enabled": true } } } }' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security',
  '{ "data": { "auth_modules" :{ "cb_api_auth": { "enabled": true } } } }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Sample Response:

{
  "data": {
    "auth_modules": {
      "cb_user_auth": {
        "token_auth_expiry_s": 604800
      },
      "cb_api_auth": {
        "enabled": false
      }
    },
    "id": "configs_crossbar.auth"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Remove

Delete account's auth config.

DELETE /v2/accounts/{ACCOUNT_ID}/security

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": {
    "auth_modules": {
      "cb_user_auth": {
        "token_auth_expiry_s": 604800
      },
      "cb_api_auth": {
        "enabled": false
      }
    },
    "id": "configs_crossbar.auth"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Set Multi Factor Configuration for a Authentication Module

POST /v2/accounts/{ACCOUNT_ID}/security

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{ "data": { "auth_modules" :{ "cb_user_auth": { "multi_factor": { "enabled": true, "configuration_id": "c757665dca55edba2395df3ca6423f4f", "account_id": "a391d64a083b99232f6d2633c47432e3" } } } } }' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security',
  '{ "data": { "auth_modules" :{ "cb_user_auth": { "multi_factor": { "enabled": true, "configuration_id": "c757665dca55edba2395df3ca6423f4f", "account_id": "a391d64a083b99232f6d2633c47432e3" } } } } }',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Sample Response:

{
  "data": {
    "auth_modules": {
      "cb_user_auth": {
        "multi_factor": {
          "enabled": true,
          "configuration_id": "c757665dca55edba2395df3ca6423f4f",
          "account_id": "a391d64a083b99232f6d2633c47432e3",
          "_read_only": {
            "name": "a nice day",
            "provider_name": "duo"
          }
        }
        }
      }
    },
    "id": "configs_crossbar.auth"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Get a List of All Login Attempts

GET /v2/accounts/{ACCOUNT_ID}/security/attempts

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security/attempts
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security/attempts', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "page_size": 1,
  "data": [
    {
      "id": "201707-5e9c6dc29efb34d87c0a06e8f613b1fd",
      "auth_type": "jwt_auth_token",
      "auth_module": "cb_user_auth",
      "status": "success",
      "message": "authentication resulted in token creation",
      "timestamp": 63667032239,
      "client_ip": "10.1.0.2"
    }
  ],
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Get a Login Attempt Details

GET /v2/accounts/{ACCOUNT_ID}/security/attempts/{ATTEMPT_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security/attempts/201707-5e9c6dc29efb34d87c0a06e8f613b1fd
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/security/attempts/201707-5e9c6dc29efb34d87c0a06e8f613b1fd', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": {
    "auth_type": "jwt_auth_token",
    "status": "success",
    "auth_module": "cb_user_auth",
    "message": "authentication resulted in token creation",
    "client_headers": {
      "host": "10.1.0.2:8000",
      "connection": "keep-alive",
      "content-length": "82",
      "accept": "application/json, text/javascript, */*; q=0.01",
      "x-auth-token": "undefined",
      "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.61 Safari/537.36",
      "origin": "http://127.0.0.1:3000",
      "content-type": "application/json",
      "dnt": "1",
      "referer": "http://127.0.0.1:3000/",
      "accept-encoding": "gzip, deflate",
      "accept-language": "en-US,en;q=0.8"
    },
    "client_ip": "10.1.0.2",
    "crossbar_request_id": "a6edc00018ebd9c7c991fbddf3677fcb",
    "timestamp": 63667032239,
    "metadata": {
      "owner_id": "0528dc7bbbf94bcc5df7d74d808a4ec0",
      "account_id": "6134cc9aa43ffaee3e3f0c9a84113d6e"
    },
    "id": "201707-5e9c6dc29efb34d87c0a06e8f613b1fd"
  },
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Service Plans

About Service Plans

Handle the service plans you can subscribe to.

Service Plan Schema

Describes services offered to sub-accounts

Key Description Type Default Required
bookkeepers #/definitions/bookkeepers false
category Optional category used for grouping service plans string() false
description Describes the service plan offering string() false
manual_recurring Monthly recurring items array(object()) false
manual_recurring.[].name A friendly name for the item string() false
manual_recurring.[].quantity How many of the item are allowed integer() false
manual_recurring.[].rates Item's rate number() false
name A friendly name for the service plan string(1..128) true
plan./.+/ Category name object() false
plan Outlines the service plan for various services object() true

bookkeepers

The bookkeeper modules provided by Lumian

Key Description Type Default Required
braintree object() false
local object() false

service_plan.category

Describes a service plan category

Key Description Type Default Required
.+ Item name object() false
_all.exceptions.[] string() false
_all.exceptions Items that are not included in this item plan array(string()) false
_all Applies item rules to any item in this category object() false

service_plan.item

Describes a service plan item

Key Description Type Default Required
activation_charge What to charge when activating an Item number() false
as Count Item as if it was another Item string() false
cascade Whether to count quantities among all sub-accounts or just the account boolean() false
cumulative_discount Whether to give a discount based on quantities of the account and all sub-accounts boolean() false
cumulative_discount_rate How much of a discount to apply number() false
discounts.cumulative.maximum The most number of Items to apply discount to integer() false
discounts.cumulative.rate The discount to apply, up to maximum Items (if applicable) number() false
discounts.cumulative object() false
discounts object() false
markup_type How rate for this usage is calculated string('fixed_price' or 'percentage' or 'rate') false
minimum The minimum quantity to charge for, if 'quantity' is less than 'minimum' integer() false
name Friendly name for this Item string() false
quantity How many of the item are allowed integer() false
rate The rate to charge number() false
rates./^[0-9]+$/ The rate to charge when under the quantity indicated in the key number() false
rates Tiers of rates based on quantities object() false
single_discount Whether to give a discount to the account boolean() false
single_discount_rate How much of a discount to apply, per-item number() false

Available Fields To Customize

Get a list of fields that can be customize for each service plan.

GET /v2/service_plans/editable

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/service_plans/editable
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/service_plans/editable', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
  "data": {
    "devices": {
      "_all": {
        "activation_charge": {},
        "as": {},
        "discounts": {
          "maximum": {},
          "rate": {}
        },
        "exceptions": {},
        "minimum": {},
        "rate": {}
      },
      "landline": {
        "activation_charge": {},
        "discounts": {
          "maximum": {},
          "rate": {}
        },
        "minimum": {},
        "rate": {}
      }
      "..."
    },
    "limits": {
      "_all": {
        "activation_charge": {},
        "as": {},
        "discounts": {
          "maximum": {},
          "rate": {}
        },
        "exceptions": {},
        "minimum": {},
        "rate": {}
        }
        "..."
    },
    "number_services": {
      "_all": {
        "activation_charge": {},
        "as": {},
        "discounts": {
          "maximum": {},
          "rate": {}
        },
        "exceptions": {},
        "minimum": {},
        "rate": {}
      },
      "cnam": {
        "activation_charge": {},
        "discounts": {
          "maximum": {},
          "rate": {}
        },
        "minimum": {},
        "rate": {}
      }
      "..."
    },
    "phone_numbers": {
      "_all": {
        "activation_charge": {},
        "as": {},
        "discounts": {
          "maximum": {},
          "rate": {}
        },
        "exceptions": {},
        "minimum": {},
        "rate": {}
        },
      "did_us": {
        "activation_charge": {},
        "discounts": {
          "maximum": {},
          "rate": {}
        },
        "minimum": {},
        "rate": {}
      }
      "..."
    },
    "ui_apps": {
      "_all": {
        "activation_charge": {},
        "discounts": {
          "maximum": {},
          "rate": {}
        },
        "minimum": {},
        "rate": {},
        "exceptions": {},
        "as": {}
      },
      "accounts": {
        "activation_charge": {},
        "discounts": {
          "maximum": {},
          "rate": {}
        },
        "minimum": {},
        "rate": {}
      }
      "..."
    },
    "users": {
      "_all": {
        "activation_charge": {},
        "as": {},
        "discounts": {
            "maximum": {},
            "rate": {}
        },
        "exceptions": {},
        "minimum": {},
        "rate": {}
        },
      "admin": {
        "activation_charge": {},
        "discounts": {
          "maximum": {},
          "rate": {}
        },
        "minimum": {},
        "rate": {}
      }
      "..."
    }
  },
  "revision": "{REVISION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "{STATUS}",
  "auth_token": "{AUTH_TOKEN}"
}

Fetch

GET /v2/accounts/{ACCOUNT_ID}/service_plans

Useful for resellers.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "page_size": 1,
    "data": [
        {
            "id": "some_plan_id",
            "name": "Reseller Test plan",
            "description": "Some description"
        }
    ],
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

Adding/Removing multiple service plans on an account

POST /v2/accounts/{ACCOUNT_ID}/service_plans

Useful for resellers.

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {
        "add": ["plan1", "plan2"],
        "delete": ["plan3"]
    }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans',
  '{"data": {\n        "add": ["plan1", "plan2"],\n        "delete": ["plan3"]\n    }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {}, //  Merge of the Service plans if any left
    "status": "success"
}

Removing service plan from an account

DELETE /v2/accounts/{ACCOUNT_ID}/service_plans/{PLAN_ID}

Useful for resellers.

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/{PLAN_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/{PLAN_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Retrieving one of your service plans.

GET /v2/accounts/{ACCOUNT_ID}/service_plans/{PLAN_ID}

Useful for resellers.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/{PLAN_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/{PLAN_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "bookkeepers": {
            "braintree": {
                "devices": {
                    "sip_devices": {
                        "addon": "sip_device",
                        "discounts": {
                            "cumulative": "discount_did_us"
                        },
                        "plan": "SIP_Services"
                    }
                },
                "limits": {
                    "inbound_trunks": {
                        "addon": "inbound_trunk",
                        "plan": "SIP_Services"
                    },
                    "twoway_trunks": {
                        "addon": "twoway_trunk",
                        "plan": "SIP_Services"
                    }
                },
                "number_services": {
                    "e911": {
                        "addon": "e911",
                        "plan": "SIP_Services"
                    }
                },
                "phone_numbers": {
                    "did_us": {
                        "addon": "did_us",
                        "plan": "SIP_Services"
                    },
                    "tollfree_us": {
                        "addon": "tollfree_us",
                        "plan": "SIP_Services"
                    }
                }
            }
        },
        "description": "",
        "id": "plan_macpie",
        "name": "Macpies plan",
        "plan": {
            "devices": {
                "_all": {
                    "activation_charge": 3,
                    "as": "sip_devices",
                    "discounts": {
                        "cumulative": {
                            "maximum": 20,
                            "rate": 5
                        }
                    },
                    "exceptions": [
                        "cellphone",
                        "landline"
                    ],
                    "name": "SIP Device",
                    "rate": 5
                }
            },
            "limits": {
                "inbound_trunks": {
                    "name": "Inbound Trunk",
                    "rate": 6.99
                },
                "twoway_trunks": {
                    "name": "Two-Way Trunk",
                    "rate": 29.99
                }
            },
            "number_services": {
                "e911": {
                    "cascade": true,
                    "discounts": {
                        "single": {
                            "rate": 5
                        }
                    },
                    "name": "E911 Service",
                    "rate": 2
                },
                "inbound_cnam": {
                    "activation_charge": 1,
                    "name": "Inbound CNAM Update",
                    "rate": 2
                },
                "outbound_cnam": {
                    "activation_charge": 5,
                    "name": "Outbound CNAM Update",
                    "rate": 1
                },
                "port": {
                    "activation_charge": 10,
                    "name": "Port Request"
                }
            },
            "phone_numbers": {
                "did_us": {
                    "activation_charge": 3,
                    "cascade": true,
                    "name": "US DID",
                    "rate": 2
                },
                "tollfree_us": {
                    "cascade": true,
                    "name": "US Tollfree",
                    "rate": 4.99
                }
            },
            "users": {
                "_all": {
                    "activation_charge": 3,
                    "as": "user",
                    "cascade": true,
                    "exceptions": [],
                    "name": "User",
                    "rate": 5
                }
            }
        }
    },
    "status": "success"
}

Adding service plan to an account.

POST /v2/accounts/{ACCOUNT_ID}/service_plans/{PLAN_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"id":"{PLAN_ID}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/{PLAN_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/{PLAN_ID}',
  '{"data": {"id":"{PLAN_ID}"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {...},
    "status": "success"
}

Override a plan

POST /v2/accounts/{ACCOUNT_ID}/service_plans/override

!!! note Must be super duper admin

Note: _all override payload.

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {
        "overrides": {
            "{PLAN_ID}": {
                "whitelabel": {
                    "_all": {
                        "activation_charge": 700
                    }
                }
            }
        }
    }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/override
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/override',
  '{"data": {\n        "overrides": {\n            "{PLAN_ID}": {\n                "whitelabel": {\n                    "_all": {\n                        "activation_charge": 700\n                    }\n                }\n            }\n        }\n    }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {
        "whitelabel": {
            "_all": {
                "name": "Whitelabel",
                "as": "whitelabel",
                "exceptions": [],
                "activation_charge": 700,
                "cascade": true,
                "rate": 300
            }
        }
    },
    "status": "success"
}

Retrieving your current plan

GET /v2/accounts/{ACCOUNT_ID}/service_plans/current

This will retrieve the service plan currently applied on your account.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/current
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/current', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "account_quantities": {
            "number_services": {},
            "phone_numbers": {
                "did_us": 4
            },
            "devices": {
                "sip_device": 1,
                "softphone": 2
            },
            "limits": {
                "twoway_trunks": 10,
                "inbound_trunks": 10
            },
            "users": {
                "admin": 1,
                "user": 1
            },
            "ips": {
                "dedicated": 0
            }
        },
        "cascade_quantities": {},
        "plans": {
            "plan_dedicated_install": {
                "account_id": "a0f3b6f2c5c0c95240993acd1bd6e762"
            }
        },
        "billing_id": "1760753c8d022d650418fbbe6a1a10e0",
        "reseller": false,
        "reseller_id": "a0f3b6f2c5c0c95240993acd1bd6e762",
        "dirty": false,
        "in_good_standing": true,
        "items": {
            "number_services": {
                "port": {
                    "category": "number_services",
                    "item": "port",
                    "quantity": 0,
                    "single_discount": false,
                    "single_discount_rate": 0.0,
                    "cumulative_discount": 0,
                    "cumulative_discount_rate": 0.0
                },
                "outbound_cnam": {
                    "category": "number_services",
                    "item": "outbound_cnam",
                    "quantity": 0,
                    "rate": 1.0,
                    "single_discount": false,
                    "single_discount_rate": 0.0,
                    "cumulative_discount": 0,
                    "cumulative_discount_rate": 0.0
                },
                "inbound_cnam": {
                    "category": "number_services",
                    "item": "inbound_cnam",
                    "quantity": 0,
                    "rate": 2.0,
                    "single_discount": false,
                    "single_discount_rate": 0.0,
                    "cumulative_discount": 0,
                    "cumulative_discount_rate": 0.0
                },
                "e911": {
                    "category": "number_services",
                    "item": "e911",
                    "quantity": 0,
                    "rate": 2.0,
                    "single_discount": false,
                    "single_discount_rate": 5.0,
                    "cumulative_discount": 0,
                    "cumulative_discount_rate": 0.0
                }
            },
            "devices": {
                "sip_devices": {
                    "category": "devices",
                    "item": "sip_devices",
                    "quantity": 3,
                    "rate": 5.0,
                    "single_discount": true,
                    "single_discount_rate": 0.0,
                    "cumulative_discount": 3,
                    "cumulative_discount_rate": 5.0
                }
            },
            "phone_numbers": {
                "tollfree_us": {
                    "category": "phone_numbers",
                    "item": "tollfree_us",
                    "quantity": 0,
                    "rate": 4.9900000000000002132,
                    "single_discount": false,
                    "single_discount_rate": 0.0,
                    "cumulative_discount": 0,
                    "cumulative_discount_rate": 0.0
                },
                "did_us": {
                    "category": "phone_numbers",
                    "item": "did_us",
                    "quantity": 4,
                    "rate": 2.0,
                    "single_discount": true,
                    "single_discount_rate": 0.0,
                    "cumulative_discount": 0,
                    "cumulative_discount_rate": 0.0
                }
            },
            "users": {
                "user": {
                    "category": "users",
                    "item": "user",
                    "quantity": 2,
                    "rate": 5.0,
                    "single_discount": true,
                    "single_discount_rate": 0.0,
                    "cumulative_discount": 0,
                    "cumulative_discount_rate": 0.0
                }
            },
            "limits": {
                "twoway_trunks": {
                    "category": "limits",
                    "item": "twoway_trunks",
                    "quantity": 10,
                    "rate": 29.989999999999998437,
                    "single_discount": true,
                    "single_discount_rate": 0.0,
                    "cumulative_discount": 0,
                    "cumulative_discount_rate": 0.0
                },
                "inbound_trunks": {
                    "category": "limits",
                    "item": "inbound_trunks",
                    "quantity": 10,
                    "rate": 6.9900000000000002132,
                    "single_discount": true,
                    "single_discount_rate": 0.0,
                    "cumulative_discount": 0,
                    "cumulative_discount_rate": 0.0
                }
            }
        }
    },
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

Listing Service Plans available to you

GET /v2/accounts/{ACCOUNT_ID}/service_plans/available

This api will list the services plan that can be applied to your account

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/available
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/available', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "page_size": 1,
    "data": [
        {
            "id": "some_plan_id",
            "name": "Test plan",
            "description": "Some description"
        }
    ],
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

Retrieving a plan

GET /v2/accounts/{ACCOUNT_ID}/service_plans/available/{PLAN_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/available/{PLAN_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/service_plans/available/{PLAN_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "name": "Test plan",
        "description": "Some description",
        "plan": {
            "phone_numbers": {
                "did_us": {
                    "name": "US DID",
                    "rate": 2,
                    "activation_charge": 3,
                    "cascade": true
                },
                "tollfree_us": {
                    "name": "US Tollfree",
                    "rate": 4.9900000000000002132,
                    "cascade": true
                }
            },
            "number_services": {
                "outbound_cnam": {
                    "name": "Outbound CNAM Update",
                    "activation_charge": 5,
                    "rate": 1
                },
                "inbound_cnam": {
                    "rate": 2,
                    "name": "Inbound CNAM Update",
                    "activation_charge": 1
                },
                "port": {
                    "name": "Port Request",
                    "activation_charge": 10
                },
                "e911": {
                    "name": "E911 Service",
                    "rate": 2,
                    "cascade": true,
                    "discounts": {
                        "single": {
                            "rate": 5
                        }
                    }
                }
            },
            "limits": {
                "twoway_trunks": {
                    "name": "Two-Way Trunk",
                    "rate": 29.989999999999998437
                },
                "inbound_trunks": {
                    "name": "Inbound Trunk",
                    "rate": 6.9900000000000002132
                }
            },
            "devices": {
                "_all": {
                    "name": "SIP Device",
                    "as": "sip_devices",
                    "exceptions": ["cellphone", "landline"],
                    "activation_charge": 3,
                    "rate": 5,
                    "discounts": {
                        "cumulative": {
                            "maximum": 20,
                            "rate": 5
                        }
                    }
                }
            },
            "users": {
                "_all": {
                    "name": "User",
                    "as": "user",
                    "exceptions": [],
                    "activation_charge": 3,
                    "cascade": true,
                    "rate": 5
                }
            }
        },
        "bookkeepers": {
            "braintree": {
                "phone_numbers": {
                    "did_us": {
                        "plan": "SIP_Services",
                        "addon": "did_us"
                    },
                    "tollfree_us": {
                        "plan": "SIP_Services",
                        "addon": "tollfree_us"
                    }
                },
                "number_services": {
                    "e911": {
                        "plan": "SIP_Services",
                        "addon": "e911"
                    }
                },
                "limits": {
                    "twoway_trunks": {
                        "plan": "SIP_Services",
                        "addon": "twoway_trunk"
                    },
                    "inbound_trunks": {
                        "plan": "SIP_Services",
                        "addon": "inbound_trunk"
                    }
                },
                "devices": {
                    "sip_devices": {
                        "plan": "SIP_Services",
                        "addon": "sip_device",
                        "discounts": {
                            "cumulative": "discount_did_us"
                        }
                    }
                }
            }
        },
        "id": "some_plan_id"
     },
     "status": "success",
     "auth_token": "{AUTH_TOKEN}"
}

Services

About Services

Fetch and inspect account's service.

Schema

Get Account Service

GET /v2/accounts/{ACCOUNT_ID}/services

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Update the Account Billing ID

POST /v2/accounts/{ACCOUNT_ID}/services

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {
        "billing_id":"{BILLING_ID}"
    }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services',
  '{"data": {\n        "billing_id":"{BILLING_ID}"\n    }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Get Account Service Status

GET /v2/accounts/{ACCOUNT_ID}/services/status

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/status
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/status', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch Service Audit Logs

GET /v2/accounts/{ACCOUNT_ID}/services/audit

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/audit
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/audit', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

GET /v2/accounts/{ACCOUNT_ID}/services/audit/{AUDIT_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/audit/{AUDIT_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/audit/{AUDIT_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Get service changes summary per day

Using this API you can a list of services changes (additions/removal/usage) summary per day.

GET /v2/accounts/{ACCOUNT_ID}/services/audit_summary

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/audit_summary
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/audit_summary', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Example

Request:

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/audit_summary?created_from=63721540230&created_to=63722160516
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/audit_summary', {
  params: {
    'created_from': '63721540230'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response:

Sample Response:

{
  "data": {
    "per-minute-voip": [
      {
        "2019-04-03": {
          "last_timestamp": 63721549284,
          "quantity": 300,
          "sum_quantity": true,
          "unit": "sec"
        }
      },
      {
        "2019-04-08": {
          "last_timestamp": 63721972845,
          "quantity": 60,
          "sum_quantity": true,
          "unit": "sec"
        }
      },
      {
        "2019-04-09": {
          "last_timestamp": 63722073234,
          "quantity": 780,
          "sum_quantity": true,
          "unit": "sec"
        }
      },
      {
        "2019-04-10": {
          "last_timestamp": 63722156725,
          "quantity": 1500,
          "sum_quantity": true,
          "unit": "sec"
        }
      },
      {
        "2019-04-11": {
          "last_timestamp": 63722239192,
          "quantity": 960,
          "sum_quantity": true,
          "unit": "sec"
        }
      }
    ],
    "phone_number": [
      {
        "2019-04-04": {
          "addition": 1,
          "last_timestamp": 63721611287,
          "quantity": 53,
          "removal": 0,
          "sum_quantity": false
        }
      }
    ],
    "sip_device": [
      {
        "2019-04-09": {
          "addition": 2,
          "last_timestamp": 63722066155,
          "quantity": 42,
          "removal": 2,
          "sum_quantity": false
        }
      },
      {
        "2019-04-10": {
          "addition": 1,
          "last_timestamp": 63722076464,
          "quantity": 43,
          "removal": 0,
          "sum_quantity": false
        }
      }
    ],
    "user": [
      {
        "2019-04-03": {
          "addition": 1,
          "last_timestamp": 63721540230,
          "quantity": 84,
          "removal": 0,
          "sum_quantity": false
        }
      },
      {
        "2019-04-09": {
          "addition": 1,
          "last_timestamp": 63722056079,
          "quantity": 85,
          "removal": 0,
          "sum_quantity": false
        }
      },
      {
        "2019-04-11": {
          "addition": 1,
          "last_timestamp": 63722160516,
          "quantity": 85,
          "removal": 1,
          "sum_quantity": false
        }
      }
    ]
  },
  "revision": "{REVISION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Get changes summary per day for a single service category

Using this API you can a list of changes (additions/removal/usage) summary per day for a single service category.

GET /v2/accounts/{ACCOUNT_ID}/services/audit_summary/{SOURCE_SERVICE}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/audit_summary/{SOURCE_SERVICE}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/audit_summary/{SOURCE_SERVICE}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Example

Request:

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/audit_summary/user?created_from=63721540230&created_to=63722160516
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/audit_summary/user', {
  params: {
    'created_from': '63721540230'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response:

Sample Response:

{
  "data": [
    {
      "2019-04-11": {
        "addition": 1,
        "last_timestamp": 63722160516,
        "quantity": 85,
        "removal": 1,
        "sum_quantity": false
      }
    },
    {
      "2019-04-09": {
        "addition": 1,
        "last_timestamp": 63722056079,
        "quantity": 85,
        "removal": 0,
        "sum_quantity": false
      }
    },
    {
      "2019-04-03": {
        "addition": 1,
        "last_timestamp": 63721540230,
        "quantity": 84,
        "removal": 0,
        "sum_quantity": false
      }
    }
  ],
  "revision": "{REVISION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE_HASH}",
  "request_id": "{REQUEST_ID}",
  "status": "success",
  "auth_token": "{AUTH_TOKEN}"
}

Change

POST /v2/accounts/{ACCOUNT_ID}/services/{PLAN_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/{PLAN_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/{PLAN_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/services/{PLAN_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/{PLAN_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/{PLAN_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

GET /v2/accounts/{ACCOUNT_ID}/services/manual

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/manual
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/manual', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/services/manual

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/manual
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/manual',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Patch

PATCH /v2/accounts/{ACCOUNT_ID}/services/manual

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/manual
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/manual',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/services/overrides

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/overrides
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/overrides', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/services/overrides

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/overrides
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/overrides',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Patch

PATCH /v2/accounts/{ACCOUNT_ID}/services/overrides

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/overrides
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/overrides',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Change

POST /v2/accounts/{ACCOUNT_ID}/services/quote

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/quote
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/quote',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Change

POST /v2/accounts/{ACCOUNT_ID}/services/reconciliation

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/reconciliation
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/reconciliation',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Change

POST /v2/accounts/{ACCOUNT_ID}/services/synchronization

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/synchronization
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/synchronization',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Change

POST /v2/accounts/{ACCOUNT_ID}/services/topup

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/topup
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/topup',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/services/summary

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/summary
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/summary', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

GET /v2/accounts/{ACCOUNT_ID}/services/available

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/available
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/available', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

GET /v2/accounts/{ACCOUNT_ID}/services/editable

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/editable
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/services/editable', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Shared Authentication

About Shared Authentication

Schema

Provides a local auth-token via a shared auth-token

Key Description Type Default Required Support Level
shared_auth The shared token string(64) true

Fetch

GET /v2/shared_auth

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/shared_auth
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/shared_auth', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/shared_auth

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/shared_auth
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/shared_auth',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Signup

About Signup

Schema

Create

Skels

About Skels

Description of the skel module goes here.

Schema

Fetch

GET /v2/accounts/{ACCOUNT_ID}/skels

More description if needed!

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/skels
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/skels', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": [
        {
            "name": "John",
            "email": "john@email.com",
            "stuff": [
                "stuff1"
            ],
            "age": 30,
            "city": "San Francisco",
            "zipcode": 94109
        },
        {
            "name": "Jane",
            "email": "Jane@email.com",
            "stuff": [
                "stuff2"
            ],
            "age": 28,
            "city": "San Francisco",
            "zipcode": 94109
        }
    ],
    "page_size": 2,
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

Create a resource

PUT /v2/accounts/{ACCOUNT_ID}/skels

More description if needed!

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": { \
        "name": "Jane", \
        "email": "Jane@email.com", \
        "stuff": ["stuff2"], \
        "age": 28, \
        "city": "San Francisco", \
        "zipcode": 94109 \
        }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/skels
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/skels',
  '{"data": { \\\n        "name": "Jane", \\\n        "email": "Jane@email.com", \\\n        "stuff": ["stuff2"], \\\n        "age": 28, \\\n        "city": "San Francisco", \\\n        "zipcode": 94109 \\\n        }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {
        "name": "Jane",
        "email": "Jane@email.com",
        "stuff": [
            "stuff2"
        ],
        "age": 28,
        "city": "San Francisco",
        "zipcode": 94109
    },
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

Delete a resource

DELETE /v2/accounts/{ACCOUNT_ID}/skels/{THING}

More description if needed!

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/skels/{THING}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/skels/{THING}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {},
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

Get a resource

GET /v2/accounts/{ACCOUNT_ID}/skels/{THING}

More description if needed!

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/skels/{THING}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/skels/{THING}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "name": "Jane",
        "email": "Jane@email.com",
        "stuff": [
            "stuff2"
        ],
        "age": 28,
        "city": "San Francisco",
        "zipcode": 94109
    },
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

Update a resource

POST /v2/accounts/{ACCOUNT_ID}/skels/{THING}

PATCH /v2/accounts/{ACCOUNT_ID}/skels/{THING}

More description if needed!

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": { \
        "name": "Jane", \
        "email": "jane@email.com", \
        "stuff": ["some new stuff"], \
        "age": 29, \
        "city": "San Francisco", \
        "zipcode": 94109 \
        }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/skels/{THING}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/skels/{THING}',
  '{"data": { \\\n        "name": "Jane", \\\n        "email": "jane@email.com", \\\n        "stuff": ["some new stuff"], \\\n        "age": 29, \\\n        "city": "San Francisco", \\\n        "zipcode": 94109 \\\n        }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
    "data": {
        "name": "Jane",
        "email": "jane@email.com",
        "stuff": [
            "some new stuff"
        ],
        "age": 29,
        "city": "San Francisco",
        "zipcode": 94109
    },
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "auth_token": "{AUTH_TOKEN}"
}

SMS

About SMS

Lumian offers API-based message services for inbound and outbound messages. Outbound messages sent through the Lumian API must be sent from a telephone number assigned to your Lumian account with an active messaging feature to be eligible for use.

Schema

SMS document

Key Description Type Default Required
body text message string(1..700) true
from caller-id-number, taken from user if absent string() false
to callee-id-number string() true

Fetch

GET /v2/accounts/{ACCOUNT_ID}/sms

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/sms
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/sms', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/sms

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"from": "+15555555555", "to": "+14444444444", "body": "Hello"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/sms
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/sms',
  '{"data": {"from": "+15555555555", "to": "+14444444444", "body": "Hello"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
  "success": true,
  "reason": "SUCCESS",
  "detail": "SUCCESS",
  "result": {
    "referenceId": "d88231b5-13b3-4fb2-8528-bc7164309389",
    "from": "15555555555",
    "text": "Hello",
    "messageType": "SMS",
    "resultResponses": [
      {
        "to": "14444444444",
        "status": "SUCCESS"
      }
    ]
  }
}

Fetch

GET /v2/accounts/{ACCOUNT_ID}/sms/{SMS_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/sms/{SMS_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/sms/{SMS_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/sms/{SMS_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/sms/{SMS_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/sms/{SMS_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Storage

About Storage

Storage plans allow an account to control where data related to their account is stored. This can be critical when compliance with regulations is required.

A storage plan has three main components:

  1. attachments: configuration for where to store attachments (binary data typically, like voicemails or faxes)
  2. connections: connection information to various third-party storage sites
  3. plan: a description of the storage plan(s) for the account.

Attachments

Rather than storing binary data like voicemails, received faxes, and call recordings, in the Lumian databases, it can be convenient to store them in third-party storage services like Amazon S3, Google Drive, etc. This keeps the binary data in a place that an account or user maintains control over. Lumian keeps a pointer to the location when it needs to fetch the binary data (such as when you call into your voicemail box).

The attachments object configures storage back-ends - for instance, if you want to store to S3, you'll add your AWS secret and key and your S3 bucket information here.

Connections

Connections can be used to point to an alternative CouchDB instance for storing the JSON documents or attachments (for instance: putting CDRs in their own cluster). These can be specified in the storage plans.

Plans

Plans determine what to do with certain classes of databases:

  1. account: where to store account data
  2. modb: where to store temporal data, like CDRs or voicemails
  3. system: where to store system data, like prompts

Within the database classification, you can define things like the connection to use when reading/writing, what types of documents should be stored/retrieved, etc.

Enabling the storage endpoint

Crossbar must have the storage endpoint enabled first:

sup crossbar_maintenance start_module cb_storage

Schema

Key Description Type Default Required Support Level
^_ Ignores CouchDB fields prefixed by underscores `string() integer() boolean() object()`
^pvt_ Ignores Lumian private fields prefixed by pvt_ `string() integer() boolean()`
attachments Defines where and how to store attachments. Keys are 32-character identifiers to be used in storage plans #/definitions/storage.attachments false
connections Describes alternative connections to use (such as alternative CouchDB instances #/definitions/storage.connections false
id ID of the storage document string() false
plan Describes how to store documents depending on the database or document type #/definitions/storage.plan false

storage.attachment.aws

schema for AWS attachment entry

Key Description Type Default Required Support Level
handler What AWS service to use string('s3') true
settings.bucket Bucket name to store data to string(6..63) true
settings.bucket_access_method how to access the host. `string('auto' 'vhost' 'path')`
settings.bucket_after_host use bucket after host as part of url boolean() false
settings.host the s3 host, leave empty for default string(1..) false
settings.key AWS Key to use string(1..128) true
settings.port port to use integer() false
settings.region the region where the bucket is located string(1..) false
settings.scheme scheme to use to access host `string('http' 'https')` false
settings.secret AWS Secret to use string(1..128) true
settings AWS API settings object() true

storage.attachment.azure

schema for azure attachment entry

Key Description Type Default Required Support Level
handler What handler module to use string('azure') true
settings.account the azure account name string() true
settings.container the azure container where the files should be saved string() true
settings.key the azure api key string() true
settings Settings for the Azure account object() true

storage.attachment.dropbox

schema for dropbox attachment entry

Key Description Type Default Required Support Level
handler What handler module to use string('dropbox') true
settings.oauth_doc_id Doc ID in the system 'auth' database string(1..) true
settings Settings for the Dropbox account object() true

storage.attachment.google_drive

schema for google drive attachment entry

Key Description Type Default Required Support Level
handler What handler module to use string('google_drive') true
settings.folder_id Folder ID in which to store the file, if any string() false
settings.oauth_doc_id Doc ID in the system 'auth' database string(1..) true
settings Settings for the Google Drive account object() true

storage.attachment.google_storage

schema for google storage attachment entry

Key Description Type Default Required Support Level
handler What handler module to use string('google_storage') true
settings Settings for the Google Storage account object() true

storage.attachment.http

schema for HTTP(s) attachment entry

Key Description Type Default Required Support Level
handler The handler interface to use string('http') true
settings.base64_encode_data Toggles whether to base64-encode the attachment data boolean() false false
settings.send_multipart Toggle whether to send multipart payload when storing attachment - will include metadata JSON if true boolean() false
settings.url The base HTTP(s) URL to use when creating the request string() true
settings.verb The HTTP verb to use when sending the data `string('post' 'put')` put false
settings HTTP server settings object() true

storage.attachment.onedrive

schema for OneDrive attachment entry

Key Description Type Default Required Support Level
handler What handler module to use string('onedrive') true
settings.oauth_doc_id Doc ID in the system 'auth' database string(1..) true
settings Settings for the OneDrive account object() true

storage.attachments

Defines where and how to store attachments. Keys are 32-character identifiers to be used in storage plans

Key Description Type Default Required Support Level
^[a-z0-9]{32}$.name Friendly name for this configuration string() false
^[a-z0-9]{32}$.settings.field_list list of fields to compose destination url ["array(", "[#/definitions/storage.attachments.field](#storageattachments.field)", ")"] false
^[a-z0-9]{32}$.settings.field_separator toplevel, field separator to compose destination url string() false
^[a-z0-9]{32}$.settings.folder_base_path base folder path string() false
^[a-z0-9]{32}$.settings Settings all handlers implement object() false
^[a-z0-9]{32}$ Configuration for the supported storage backends object() false

storage.attachments.field

field used when composing destination url

Key Description Type Default Required Support Level

storage.connection.couchdb

schema for CouchDB connection entry

Key Description Type Default Required Support Level
driver string('lumian_couch') true
name string() false
settings.connect_options.keepalive boolean() false
settings.connect_options object() false
settings.connect_timeout integer() false
settings.credentials.password integer() true
settings.credentials.username string() true
settings.credentials object() false
settings.ip string() true
settings.max_pipeline_size integer() false
settings.max_sessions integer() false
settings.pool.name string() true
settings.pool.size integer() true
settings.pool object() false
settings.port integer() true
settings object() true

storage.connections

Describes alternative connections to use (such as alternative CouchDB instances

Key Description Type Default Required Support Level
^([a-z,0-9]){32}$ #/definitions/storage.connection.couchdb false
local object() false

storage.plan

Describes how to store documents depending on the database or document type

Key Description Type Default Required Support Level
account schema for database storage plan #/definitions/storage.plan.database false
modb schema for database storage plan #/definitions/storage.plan.database false
system schema for database storage plan #/definitions/storage.plan.database false

storage.plan.database

schema for database storage plan

Key Description Type Default Required Support Level
attachments schema for attachment ref type storage plan #/definitions/storage.plan.database.attachment false
connection string() false
database.create_options object() false
database object() false
types.call_recording schema for document type storage plan #/definitions/storage.plan.database.document false
types.fax schema for document type storage plan #/definitions/storage.plan.database.document false
types.mailbox_message schema for document type storage plan #/definitions/storage.plan.database.document false
types.media schema for document type storage plan #/definitions/storage.plan.database.document false
types object() false

storage.plan.database.attachment

schema for attachment ref type storage plan

Key Description Type Default Required Support Level
handler string() false
params.folder_path folder path string() false
params object() false
stub boolean() false

storage.plan.database.document

schema for document type storage plan

Key Description Type Default Required Support Level
attachments schema for attachment ref type storage plan #/definitions/storage.plan.database.attachment false
connection string() false

Fetch

GET /v2/accounts/{ACCOUNT_ID}/storage

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "attachments": {
            "{UUID}": {
                "handler": "s3",
                "name": "S3 Storage",
                "settings": {
                    "bucket": "{S3_BUCKET}",
                    "key": "{AWS_ACCESS_KEY}",
                    "secret": "{AWS_SECRET_KEY}"
                }
            }
        },
        "id": "{ACCOUNT_ID}",
        "plan": {
            "modb": {
                "types": {
                    "mailbox_message": {
                        "attachments": {
                            "handler": "{UUID}"
                        }
                    }
                }
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Create

PUT /v2/accounts/{ACCOUNT_ID}/storage

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

For instance, setting up your HTTP server to receive new voicemails for the account:

Sample Response:

{
  "data": {
    "attachments": {
      "{UUID}": {
        "handler": "http",
        "name": "My HTTP server",
        "settings": {
          "url": "http://my.http.server:37635/some_prefix",
          "verb": "post"
        }
      }
    },
    "plan": {
      "modb": {
        "types": {
          "mailbox_message": {
            "attachments": {
              "handler": "{UUID}"
            }
          }
        }
      }
    }
  }
}

Change

POST /v2/accounts/{ACCOUNT_ID}/storage

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Patch

PATCH /v2/accounts/{ACCOUNT_ID}/storage

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/storage

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

GET /v2/accounts/{ACCOUNT_ID}/storage/plans

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage/plans
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage/plans', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/storage/plans

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage/plans
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage/plans',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/storage/plans/{STORAGE_PLAN_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage/plans/{STORAGE_PLAN_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage/plans/{STORAGE_PLAN_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/storage/plans/{STORAGE_PLAN_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage/plans/{STORAGE_PLAN_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage/plans/{STORAGE_PLAN_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Patch

PATCH /v2/accounts/{ACCOUNT_ID}/storage/plans/{STORAGE_PLAN_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage/plans/{STORAGE_PLAN_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage/plans/{STORAGE_PLAN_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/storage/plans/{STORAGE_PLAN_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage/plans/{STORAGE_PLAN_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage/plans/{STORAGE_PLAN_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Skipping attachment settings validation

When a storage plan is PUT/POSTed to Crossbar, Lumian will attempt to use the attachments' settings and store a small text file to verify that files can be stored remotely. Lumian will then issue a GET request to read the file back to test retrieval.

For "dumb" storage backends this is typically a non-issue as storing/retrieving files is what the backend does!

For "smart" backends, where a custom handler (like an HTTP web app) is accepting the files, adding code to handle this test file's storage/retrieval could place an unnecessary burden on the backend or be redundant after the first test if using the same destination for all accounts. As such, a request parameter can be included to skip this portion of the validation:

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{...}}' \
  http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage?validate_settings=false
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/storage',
  '{"data":{...}}',
  {
    params: {
      'validate_settings': 'false'
    },
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

!!! danger If the storage backend is unable to process the storage request, you could lose the data attempting to be stored.

Enabling This Feature

By default, Lumian will not allow clients to skip settings validation. Clients that include the validate_settings request parameter on these systems will receive a 400 validation error indicating attachment storage settings must be tested.

Sysadmins can allow clients by setting a system_config flag: sup kzs_plan allow_validation_overrides

Disabling it later is similar: sup kzs_plan disallow_validation_overrides

URL formatting

It is possible to craft the URLs used by the handler based on the JSON document and attachment being saved by specifying a field_list array of objects that will help Lumian map values to the generated URL:

Sample Response:

{UUID}:{
    "handler":"{HANDLER}",
    "settings":{
        "field_list":[
            {"arg":"account_id"}
            ,{"arg":"id"}
            ,{"arg":"attachment"}
        ],
        "url":"http://base.your.domain/"
    }
}

In this case (the default for the HTTP handler) the URL provided in the handler's settings will be appended with /{ACCOUNT_ID}/{DOC_ID}/{ATTACHMENT_NAME}.

Field Options

list of fields to compose destination url

Key Description Type Default Required Support Level
arg a argument passed to the handler `string('account_id' 'db' 'id' 'attachment')`
const a constant value added to the string string() false
field a field from the metadata document string() false
group group the inner fields definitions with an empty separator array() false

Examples

Given a base URL of http://my_server.com/storage, an attachment call.mp3 being stored in the account000 account on the abc123 doc defined below:

Sample Response:

{"_id":"abc123"
 ,"foo":"bar"
 ,"bing":"bang"
}

We can create the following generated URLs:

| URL | field_list | | | http://my_server.com/storage/account000/abc123/call.mp3 | [{"arg":"account_id"}, {"arg":"id"}, {"arg":"attachment"}] | | | http://my_server.com/storage?path=/account000/abc123/call.mp3 | [{"const":"?path="}, {"arg":"account_id"}, {"arg":"id"}, {"arg":"attachment"}] | | | http://my_server.com/storage/bar/call.mp3 | [{"field":"foo"}, {"arg":"attachment"}] | | | http://my_server.com/storage/account001_call.mp3 | [{"group":[{"arg":"account_id"}, {"const":"_"}, {"arg":"attachment"}]}] | |

Global Storage settings

If you are the super-duper admin you can setup storage policy across your cluster by using the URL /v2/storage instead of per-account.

The validation request will arrive with /system_data/{DOC_ID}/{ANAME} on the end of the URL.

If you need to revert back to the standard settings you can run sup kzs_plan reset_system_dataplan; and changes will be removed and the initial version reinstalled.

Tasks - Background Jobs

Lumian Tasks enables listing, adding, starting & removing generic background tasks.

Schema

Input data to go through as part of a background task

Key Description Type Default Required Support Level
file_name Human-readable name of a task's input file string() false
records List the rows of input data array(object()) false

Fetch

GET /v2/tasks

Sample Request:

curl -v -X GET \
    http://{SERVER}:8000/v2/tasks?category=services&action=blipblop
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/tasks', {
  params: {
    'category': 'services'
  }
});

Response Error: No such category and/or action

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "message": "invalid request"
    },
    "error": "500",
    "message": "invalid request",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Success Response

Sample Request:

curl -v -X GET \
    http://{SERVER}:8000/v2/tasks?category=services&action=descendant_quantities
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/tasks', {
  params: {
    'category': 'services'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "tasks": {
            "descendant_quantities": {
                "description": "List per-month descendant accounts quantities",
                "doc": "Attempts to create a month-on-month listing of quantities used by descendant accounts.\nThis task returns the following fields:\n* `account_id`: a sub-account of the creator of this task.\n* `year`: integral year as 4 characters.\n* `month`: integral month as 2 characters (left-padded with a zero).\n* `category`: name of the quantity's category.\n* `item`: name of the category's item.\n* `quantity_bom`: integral quantity's value or empty.\n* `quantity_eom`: integral quantity's value or empty.\nNote: some beginning-of-month and end-of-month quantities documents may be missing.\nNote: when both an account's BoM & EoM documents for a given month are missing, no rows are a created for this month.\nNote: in all other cases the documents' value is printed verbatim: if unset the empty string is returned.\nE.g.: an integer quantity (such as 1, 10 or 0 (zero)) represents was the system has. If no quantity was found, the empty value is used.\n"
            }
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

List all tasks

GET /v2/accounts/{ACCOUNT_ID}/tasks

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "account_id": "{ACCOUNT_ID}",
            "auth_account_id": "{AUTH_ACCOUNT_ID}",
            "action": "add",
            "category": "number_management",
            "created": 63632526992,
            "file_name": "my_input_for_add.csv",
            "id": "e5c92c4b50bcec520d5d7e1ce1b869",
            "status": "pending",
            "total_count": 1
        },
        {
            "account_id": "{ACCOUNT_ID}",
            "auth_account_id": "{AUTH_ACCOUNT_ID}",
            "action": "add",
            "category": "number_management",
            "created": 63632526924,
            "end_timestamp": 63632526969,
            "id": "7c17c051d6553f0329d9f8c47b253c",
            "node": "whistle_apps@qwd",
            "start_timestamp": 63632526968,
            "status": "success",
            "success_count": 1,
            "total_count": 1
        }
    ],
    "page_size": 2,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Add a new task

PUT /v2/accounts/{ACCOUNT_ID}/tasks

!!! note There are tasks that run against system resources, only for use by the super duper admin (like rate uploading), which can omit /accounts/{ACCOUNT_ID} from the URI. Leaving the account in the URI should have no impact.

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: text/csv" \
    --data-binary @path/to/your/file.csv \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks?category={CATEGORY}&action={ACTION}&file_name={FILE_NAME}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks?category={CATEGORY}',
  '@path/to/your/file.csv',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'text/csv'
    }
  }
);

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"records":[{RECORDS}], "file_name":"{FILE_NAME}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks?category={CATEGORY}&action={ACTION}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks?category={CATEGORY}',
  '{"data": {"records":[{RECORDS}], "file_name":"{FILE_NAME}"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks?category={CATEGORY}&action={ACTION}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks?category={CATEGORY}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Responses

Success

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "account_id": "{ACCOUNT_ID}",
            "auth_account_id": "{AUTH_ACCOUNT_ID}",
            "action": "{ACTION}",
            "category": "{CATEGORY}",
            "id": "edfb48ea9617fa6832e43ce676c53f",
            "submit_timestamp": 63632025993,
            "total_count": "{RECORDS_COUNT}"
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Unknown category

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        "{CATEGORY}"
    ],
    "error": "404",
    "message": "bad identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Unknown action

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        "{ACTION}"
    ],
    "error": "404",
    "message": "bad identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Bad CSV format

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "csv": {
            "format": {
                "message": "Empty CSV or some row(s) longer than others or header missing"
            }
        }
    },
    "error": "500",
    "message": "invalid request",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Bad input field name

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "attachment": {
            "type": {
                "unknown_fields": [
                    "wef"
                ]
            }
        }
    },
    "error": "500",
    "message": "invalid request",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Missing mandatory fields

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "attachment": {
            "type": {
                "missing_mandatory_fields": [
                    "number",
                    "account_id"
                ]
            }
        }
    },
    "error": "500",
    "message": "invalid request",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Rows or records missing values for mandatory fields

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "attachment": {
            "type": {
                "missing_mandatory_values": [
                    ",+14157215234",
                    "009afc511c97b2ae693c6cc4920988e8,"
                ]
            }
        }
    },
    "error": "500",
    "message": "invalid request",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Remove a completed task

DELETE /v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Success

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "account_id": "{ACCOUNT_ID}",
            "auth_account_id": "{AUTH_ACCOUNT_ID}",
            "action": "add",
            "category": "number_management",
            "end_timestamp": 63632524230,
            "failure_count": 2,
            "id": "{TASK_ID}",
            "node": "whistle_apps@qwd",
            "start_timestamp": 63632524230,
            "status": "failure",
            "submit_timestamp": 63632524207,
            "success_count": 0,
            "total_count": 2
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Task not found

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        "{TASK_ID}"
    ],
    "error": "404",
    "message": "bad identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Task is running

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cause": "{TASK_ID}",
        "message": "bad identifier"
    },
    "error": "404",
    "message": "bad_identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Get a specific task's details

GET /v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Success

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "account_id": "{ACCOUNT_ID}",
            "auth_account_id": "{AUTH_ACCOUNT_ID}",
            "action": "list",
            "category": "number_management",
            "created": 63633924886,
            "failure_count": 0,
            "id": "{TASK_ID}",
            "node": "whistle_apps@qwd",
            "start_timestamp": 63633924909,
            "status": "executing",
            "success_count": 50,
            "csvs":["in.csv"]
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Task does not exist

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        "{TASK_ID}"
    ],
    "error": "404",
    "message": "bad identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Start a task

PATCH /v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Responses

Success

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "account_id": "{ACCOUNT_ID}",
            "auth_account_id": "{AUTH_ACCOUNT_ID}",
            "action": "add",
            "category": "number_management",
            "id": "{TASK_ID}",
            "node": "whistle_apps@qwd",
            "start_timestamp": 63632456149,
            "status": "executing",
            "submit_timestamp": 63632456101,
            "total_count": 2
        }
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Task already started

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cause": "{TASK_ID}",
        "message": "bad identifier",
        "reason": "task already started"
    },
    "error": "404",
    "message": "bad_identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Task does not exist

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cause": "{TASK_ID}",
        "message": "bad identifier"
    },
    "error": "404",
    "message": "bad_identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Stop a running task

Tasks that are processing can be stopped.

Note that they cannot be started again.

PATCH /v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}/stop

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}/stop
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}/stop',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Success Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "_read_only": {
            "account_id": "{ACCOUNT_ID}",
            "action": "list_all",
            "auth_account_id": "{AUTH_ACCOUNT_ID}",
            "category": "number_management",
            "created": 63669534312,
            "end_timestamp": 63669534747,
            "failure_count": 0,
            "id": "{TASK_ID}",
            "start_timestamp": 63669534746,
            "status": "stopped",
            "success_count": 0
        }
    },
    "node": "{NODE}",
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "revision": "{REV}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "{VERSION}"
}

Task is not running

A task that was not yet started or that has already finished cannot be stopped.

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cause": "{TASK_ID}",
        "message": "bad identifier",
        "reason": "task is not running"
    },
    "error": "404",
    "message": "bad_identifier",
    "node": "{NODE}",
    "page_size": 1,
    "request_id": "{REQUEST_ID}",
    "status": "error",
    "timestamp": "{TIMESTAMP}",
    "version": "{VERSION}"
}

Retrieve a task's CSVs

When you GET /v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}, the JSON will include a "csvs":[...]" array with input and output CSVs as appropriate. Use the name(s) in the array to specify which you would like to receive.

GET /v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Accept: text/csv" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}?csv_name=in.csv
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}', {
  params: {
    'csv_name': 'in.csv'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Accept': 'text/csv'
  }
});

You can also use the old way:

GET /v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}/input

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}/input
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}/input', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Success Response

Streams back the task's input in CSV format.

Task does not exist or did not have any input data

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cause": "{TASK_ID}",
        "message": "bad identifier"
    },
    "error": "404",
    "message": "bad_identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Retrieve a task's output CSV

GET /v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}

Sample Request:

curl -v -X GET \
    -H "Accept: text/csv" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}?csv_name=out.csv
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}', {
  params: {
    'csv_name': 'out.csv'
  },
  headers: {
    'Accept': 'text/csv',
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Or the old way:

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}/output
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/tasks/{TASK_ID}/output', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Success Response

Streams back the task's output in CSV format.

Task does not exist or output not yet in database

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "cause": "{TASK_ID}",
        "message": "bad identifier"
    },
    "error": "404",
    "message": "bad_identifier",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Templates

About Templates

Schema

Fetch

GET /v2/templates

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/templates
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/templates', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/templates/{TEMPLATE_NAME}

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/templates/{TEMPLATE_NAME}
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/templates/{TEMPLATE_NAME}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/templates/{TEMPLATE_NAME}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/templates/{TEMPLATE_NAME}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/templates/{TEMPLATE_NAME}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Temporal Rules

About Temporal Rules

Temporal rules provide a flexible way to configure time-based Call routing, e.g. open hours, holidays, close hours, etc...

Schema

Schema for a temporal rules

Key Description Type Default Required Support Level
cycle The recurrence cycle for this rule `string('date' 'daily' 'weekly' 'monthly'
days The recurrence days for this rule array(integer()) false supported
enabled Whether the rule is enabled boolean() false
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
interval The recurrence interval for this rule integer() 1 false supported
month The recurrence month for this rule integer() false supported
name A friendly name for the temporal rule string(1..128) true supported
ordinal The recurrence ordinal for this rule `string('every' 'first' 'second' 'third'
start_date The date that any recurrence should be calculated as starting on integer() 62586115200 false supported
time_window_start Seconds from the start of a day to consider this rule valid integer() false supported
time_window_stop Seconds from the start of a day to stop considering this rule valid integer() false supported
wdays.[] `string('monday' 'tuesday' 'wednesday' 'wensday'
wdays The recurrence weekdays for this rule `array(string('monday' 'tuesday' 'wednesday' 'wensday'

Notes on fields

enabled

Unless you need to override a time of day rule (for example keep an office open longer) keep the property unset.

start_date

It is recommended that a start date always be set to some time in the past if this control is not required to ensure it takes effect on the next cycle.

Setting this property is especially important when using an interval other than 1. For example if the rule should be applied every other year and the start date is in 2010, then it will be active on 2010, 2012, 2014, etc. However, if the start date was in 2011 then it will be active on 2011, 2013, 2015, etc.

ordinal

Not all months have a fifth occurrence of a weekday; the rule is ignored if that is the case.

cycle

When cycle is date, the rule only considers start_date and matches it against the current day.

days

The days array is only valid when cycle is yearly or monthly.

Fetch

GET /v2/accounts/{ACCOUNT_ID}/temporal_rules

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
   "auth_token":"{AUTH_TOKEN}",
   "status":"success",
   "request_id":"{REQUEST_ID}",
   "revision":"{REVISION}",
   "data":[
      {
         "id":"{TEMPORAL_RULE_ID}",
         "name":"Business Hours"
      },
      {
         "id":"{TEMPORAL_RULE_ID}",
         "name":"Holiday"
      }
   ]
}

Create

PUT /v2/accounts/{ACCOUNT_ID}/temporal_rules

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"time_window_start":0,"time_window_stop":86400,"days":[25],"name":"Christmas","cycle":"yearly","start_date":62586115200,"month":12,"ordinal":"every","interval":1}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules',
  '{"data":{"time_window_start":0,"time_window_stop":86400,"days":[25],"name":"Christmas","cycle":"yearly","start_date":62586115200,"month":12,"ordinal":"every","interval":1}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Sample Response:

{
   "auth_token":"{AUTH_TOKEN}",
   "status":"success",
   "request_id":"{REQUEST_ID}",
   "revision":"{REVISION}",
   "data":{
      "time_window_start":0,
      "time_window_stop":86400,
      "days":[25],
      "name":"Christmas",
      "cycle":"yearly",
      "start_date":62586115200,
      "month":12,
      "ordinal":"every",
      "interval":1
   }
}

Fetch

GET /v2/accounts/{ACCOUNT_ID}/temporal_rules/{TEMPORAL_RULE_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules/{TEMPORAL_RULE_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules/{TEMPORAL_RULE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/temporal_rules/{TEMPORAL_RULE_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules/{TEMPORAL_RULE_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules/{TEMPORAL_RULE_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Patch

PATCH /v2/accounts/{ACCOUNT_ID}/temporal_rules/{TEMPORAL_RULE_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules/{TEMPORAL_RULE_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules/{TEMPORAL_RULE_ID}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/temporal_rules/{TEMPORAL_RULE_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules/{TEMPORAL_RULE_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules/{TEMPORAL_RULE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Temporal Rules Sets

About Temporal Rules Sets

A temporal rule set is a collection of temporal rules that can be used in a callflow to match more that one rule. And can also be re-used.

Schema

Schema for a temporal rules sets

Key Description Type Default Required Support Level
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
name A friendly name for the temporal rule set string(1..128) true supported
temporal_rules.[] string() false supported
temporal_rules Temporal Rules array(string()) false supported

Fetch

GET /v2/accounts/{ACCOUNT_ID}/temporal_rules_sets

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules_sets
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules_sets', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create a new rule set

PUT /v2/accounts/{ACCOUNT_ID}/temporal_rules_sets

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data": {"name": "July","temporal_rules": ["{RULE_ID}","{RULE_ID}"]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules_sets
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules_sets',
  // '{"data": {"name": "July","temporal_rules": ["{RULE_ID}","{RULE_ID}"]}}',
  {
    'data': {
      'name': 'July',
      'temporal_rules': [
        '{RULE_ID}',
        '{RULE_ID}'
      ]
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Fetch a rule set

GET /v2/accounts/{ACCOUNT_ID}/temporal_rules_sets/{TEMPORAL_RULE_SET}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules_sets/{TEMPORAL_RULE_SET}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules_sets/{TEMPORAL_RULE_SET}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change a rule set

POST /v2/accounts/{ACCOUNT_ID}/temporal_rules_sets/{TEMPORAL_RULE_SET}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data": {"name": "July","temporal_rules": ["{RULE_ID}","{RULE_ID}"]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules_sets/{TEMPORAL_RULE_SET}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules_sets/{TEMPORAL_RULE_SET}',
  // '{"data": {"name": "July","temporal_rules": ["{RULE_ID}","{RULE_ID}"]}}',
  {
    'data': {
      'name': 'July',
      'temporal_rules': [
        '{RULE_ID}',
        '{RULE_ID}'
      ]
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Patch a rule set

PATCH /v2/accounts/{ACCOUNT_ID}/temporal_rules_sets/{TEMPORAL_RULE_SET}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules_sets/{TEMPORAL_RULE_SET}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules_sets/{TEMPORAL_RULE_SET}',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove a rule set

DELETE /v2/accounts/{ACCOUNT_ID}/temporal_rules_sets/{TEMPORAL_RULE_SET}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules_sets/{TEMPORAL_RULE_SET}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/temporal_rules_sets/{TEMPORAL_RULE_SET}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Token Authentication

Authentication tokens are generated using one of the authentication endpoints exposed by Crossbar. See User Authentication and API Authentication as examples of generating authentication tokens.

About Token Authentication

Once you have an authentication token, you can access various Crossbar resource endpoints to manipulate the system or your account (provided you have the access).

Authentication tokens refresh their pvt_modified timestamp each time they are used in an API request. Once an authentication token's pvt_modified timestamp has passed a configurable timeout (usually one hour), it is automatically cleaned up by the system and no longer valid.

Token Restrictions

The authentication token can be created with restrictions on what resource URIs (and HTTP methods) can be accessed by the requestor. This payload is added to the authentication payload used in any of the authentication methods provided (User, API, etc).

For example, when creating an authentication token via API key, include the following object to restrict the resultant authentication token to read-only:

Sample Response:

{
    "data": {
        "api_key": "{API_KEY}",
        "restrictions": {
            "get": [
                "#"
            ]
        }
    }
}

AMQP binding tokens are used (# and *) to denote wildcards. An example with more fine-grained restrictions:

Sample Response:

{
    "data": {
        "api_key": "{API_KEY}",
        "restrictions": {
            "delete": [
                "accounts/{ACCOUNT_ID}/users/*"
            ],
            "get": [
                "accounts/{ACCOUNT_ID}/users",
                "accounts/{ACCOUNT_ID}/users/*",
                "accounts/{ACCOUNT_ID}/users/*/*"
            ],
            "post": [
                "accounts/{ACCOUNT_ID}/users/*"
            ],
            "put": [
                "accounts/{ACCOUNT_ID}/users"
            ]
        }
    }
}

This would restrict the authentication token to only be able to access {ACCOUNT_ID}'s users resource and perform all of the CRUD actions (as well as quickcall and channel listings for a user). We can simply this restrictions object by using * for the method and # to match any URI with /users:

Sample Response:

{
    "data": {
        "api_key": "{API_KEY}",
        "restrictions": {
            "*": [
                "accounts/{ACCOUNT_ID}/users/#"
            ]
        }
    }
}

Here the # matches 0 or more segments after /users.

Delete an authentication token

DELETE /v2/token_auth

If you'd like to invalidate an authentication token programmatically (versus letting the system expire the token), you can issue a DELETE:

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/token_auth
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/token_auth', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Check auth token validity

GET /v2/token_auth

This will tell you whether the auth token constitutes valid credentials.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/token_auth
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/token_auth', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response when invalid

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "message": "invalid credentials"
    },
    "error": "401",
    "message": "invalid_credentials",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Response when OK

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "account_id": "{ACCOUNT_ID}",
        "account_name": "{ACCOUNT_NAME}",
        "apps": [
        ],
        "id": "{AUTH_TOKEN}",
        "is_reseller": false,
        "language": "en-us",
        "method": "cb_user_auth",
        "owner_id": "8e248327b85591955749e53ea45b6baa",
        "reseller_id": "6b71cb72c876b5b1396a335f8f8a2594"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Token Restrictions

About Token Restrictions

Token restrictions - set of rules saved in auth token document. These rules grant access to API URIs.

If the token document doesn't have any rules then this module won't apply any restrictions to request.

These rules are created when the system creates an auth token. Rules can be loaded from system template or account template. System template located in system_config/crossbar.token_restrictions. Account template located in {ACCOUNT_DB}/token_restrictions.

How it works?

When you make request to Crossbar (API), the system loads rules from auth token (used for authentication) and tries to apply the rules to URI (/v2/accounts/{ACCOUNT_ID}/devices/{DEVICE_ID}/). More information about URI structure can be found here. If Crossbar doesn't find a match for all parameters (endpoint name, account id, endpoint arguments, HTTP method), then it halts the request and returns a 403 error.

Full Example

The following is a full example for reference. It is a rule for only allowing accounts to read/update their account configs (effectively barring them from deleting the account or creating sub-accounts.

Sample Response:

{
    "cb_user_auth": {
        "user":{
            "accounts":[
                {
                    "rules": {
                        "*": ["GET", "POST", "PATCH"]
                    }
                }
            ]
        }
    }
}

Place this on an account, as an admin, thusly:

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"restrictions":{"cb_user_auth": {"user":{"accounts":[{"rules": {"*": ["GET", "POST", "PATCH"]}}]}}}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}',
  '{"data":{"restrictions":{"cb_user_auth": {"user":{"accounts":[{"rules": {"*": ["GET", "POST", "PATCH"]}}]}}}}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Now, when someone logs into this account (via user_auth) and who's user_doc is of type user (and not admin), they will be unable to create sub-accounts, and will receive an error response like this:

Sample Response:

{
  "auth_token": "{AUTH_TOKEN}",
  "data": {
    "cause": "access denied by token restrictions",
    "message": "forbidden"
  },
  "error": "403",
  "message": "forbidden",
  "node": "ghw5cA1GROHLEa43HETSKg",
  "request_id": "{REQUEST_ID}",
  "status": "error",
  "timestamp": "2017-06-07T23:16:25",
  "version": "4.0.0"
}

The following sections break down what's happening here.

Template structure

Each template can have different rules for different authentication methods and user privilege levels.

Sample Response:

{
  "AUTH_METHOD_1": {
    "PRIV_LEVEL_1": {
      "ENDPOINT_RULES"
    },
    "PRIV_LEVEL_2": {
      "ENDPOINT_RULES"
    },
    ...
  },
  "AUTH_METHOD_2": {
    "PRIV_LEVEL_1": {
      "ENDPOINT_RULES"
    }
  },
  ...
}

Auth method and priv level can be matched with "catch all" term - "_". If no exact match for auth method or priv level is found, the system will look for the 'catch all' rules, if any. The rules are loaded into the auth token document when it is created (after successful authentication) and will be applied to any request using the auth token created.

Example template:

Sample Response:

{
  "cb_user_auth": {
    "admin": {
      "RULES_FOR_ADMIN"
    },
    "user": {
      "RULES_FOR_USER"
    }
  },
  "_": {
    "admin": {
      "RULES_FOR_ADMIN"
    },
    "_": {
      "CATCH_ALL_RULES"
    }
  }
}

Rules structure (saved in token document)

Sample Response:

{
  "ENDPOINT_1": [
    {
      "allowed_accounts": [
        "ACCOUNT_ID_1",
        "ACCOUNT_ID_2"
      ],
      "rules": {
        "ARG_1": [
          "VERB_1",
          "VERB_2"
        ],
        "ARG_2": [
          "VERB_3"
        ]
        ...
      }
    },
    {
      "allowed_accounts": [
        "ACCOUNT_ID_3"
      ],
      "rules": {
        "ARG_1": [
          "VERB_1"
        ],
        ...
      }
    }
  ],
  "ENDPOINT_2": [
    {
      "rules": {
        "ARG_1": [
          "_"
        ]
      }
    }
  ],
  ...
}

Match order

Endpoint match

At this step module compare resource from URI with resource names in token restrictions. If URI is /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/{MODIFIER}/ then endpoint will be users, and {USER_ID}, {MODIFIER} are arguments of this endpoint. Rules applied to the last endpoint in URI. You can use "catch all" ("_") endpoint name. First tries exact endpoint name: if not found, try the catch-all (if it exists).

Sample Response:

{
  "account": [
    { ... },
    { ... }
  ],
  "users": [
    { ... },
    { ... }
  ],
  "_": [
    { ... }
  ]
}

If a match is not found for the endpoint, this request is halted and a 403 error returned. Each endpoint contains a list of objects with rules. Appropriate object is selected by "allowed_account" parameter.

Account match

After Crossbar finds the endpoint it tries to find rules for the requested account.

Sample Response:

{
  "devices": [
    {
      "allowed_accounts": [
        "{ACCOUNT_1_ID}",
        "{ACCOUNT_2_ID}",
        "{AUTH_ACCOUNT_ID}"
      ],
      "rules": {
        ...
      }
    },
    {
      "allowed_accounts": [
        "{DESCENDANT_ACCOUNT_ID}"
      ],
      "rules": {
        ...
      }
    }
  ]
}

List of account IDs set in parameter "allowed_accounts". You can write exact IDs or one of the following special macros:

The first endpoint-rule object matched to the requested account will be used in the next step of argument matching.

Endpoint arguments match

Endpoint arguments matched with parameter "rules".

Sample Response:

{
  "devices": [
    {
      "allowed_accounts": [
        "{ACCOUNT_ID}"
      ],
      "rules": {
        "/": [ ... ],
        "{DEVICE_ID}": [ ... ],
        "{DEVICE_ID}/sync": [ ... ],
        "*": [ ... ]
      }
    }
  ]
}

The search is performed in the order in which they appear in the rules for first match. No more search after that.

Rule keys

Key Description
/ match empty argument list (or used as separator between other keys)
* match any single, non-empty argument
# match any count of arguments (or zero arguments)
string match exact string

Examples:

/ - match empty argument list

Matches

Doesn't Match


* - match any single, non-empty argument

Matches

Doesn't Match


# - match any arguments (or no arguments)

Matches


{DEVICE_ID} - exact match

Matches

Doesn't Match

For matching more than one argument, you can use / to delineate how to process the arguments. You can mix and match special characters, explicit strings, etc.


{DEVICE_ID}/quickcall/{DID} - match exact list of arguments

Matches

Doesn't Match


*/*/* - match exactly three arguments

Matches

Doesn't Match


{DEVICE_ID}/# - matches {DEVICE_ID} plus all arguments

Matches

HTTP method match

If endpoint matching fails to find a match, Crossbar will try to match the HTTP method used.

Sample Response:

{
  "devices": [
    {
      "allowed_accounts": [
        "{ACCOUNT_ID}"
      ],
      "rules": {
        "/": [
          "GET",
          "PUT"
        ],
        "{DEVICE_ID}": [
          "_"
        ],
        "#": [
          "GET"
        ]
      }
    }
  ]
}

List can contain any valid HTTP method ("GET", "PUT", "POST", "PATCH", "DELETE") or the "catch all" - "_".

Schema

Schema for token restrictions

Key Description Type Default Required Support Level
restrictions./^\w+$/./^\w+$/./^\w+$/.[].allowed_accounts.[] string() false
restrictions./^\w+$/./^\w+$/./^\w+$/.[].allowed_accounts Account allowed to match this item array(string()) false
restrictions./^\w+$/./^\w+$/./^\w+$/.[].rules./^[\w/#*]+$/.[] `string('GET' 'PUT' 'POST' 'PATCH'
restrictions./^\w+$/./^\w+$/./^\w+$/.[].rules./^[\w/#*]+$/ verbs `array(string('GET' 'PUT' 'POST' 'PATCH'
restrictions./^\w+$/./^\w+$/./^\w+$/.[].rules Rules applied to endpoint parameters object() false
restrictions./^\w+$/./^\w+$/./^\w+$/ array(object()) false
restrictions./^\w+$/./^\w+$/ Level of user privilege. '_' matches any priv level object() false
restrictions./^\w+$/ Name of authentication method used when creating token. '_' matches any auth method object() false
restrictions object() false

Fetch

DELETE /v2/accounts/{ACCOUNT_ID}/token_restrictions

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/token_restrictions
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/token_restrictions', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch account's token restrictions

GET /v2/accounts/{ACCOUNT_ID}/token_restrictions

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/token_restrictions
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/token_restrictions', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change account's token restrictions

POST /v2/accounts/{ACCOUNT_ID}/token_restrictions

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d @data.txt \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/token_restrictions
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/token_restrictions',
  '@data.txt',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

File data.txt contains this restrictions:

Sample Response:

{
  "data": {
    "restrictions": {
      "_": {
        "admin": {
          "_": [
            {
              "rules": {
                "#": [
                  "_"
                ]
              }
            }
          ]
        },
        "operator": {
          "devices": {
            "rules": {
              "#": [
                "GET",
                "POST",
                "PUT"
              ]
            }
          },
          "callflows": {
            "rules": {
              "#": [
                "_"
              ]
            }
          },
          "_": {
            "rules": {
              "#": [
                "GET"
              ]
            }
          }
        },
        "accountant": {
          "transactions": {
            "rules": {
              "#": [
                "GET"
              ]
            }
          },
          "_": {
            "rules": {
              "#": []
            }
          }
        },
        "user": {
          "users": {
            "rules": {
              "#": [
                "GET"
              ]
            },
            "devices": {
              "rules": {
                "#": [
                  "GET"
                ]
              },
              "_": {
                "rules": {
                  "#": []
                }
              }
            }
          }
        }
      }
    }
  }
}

Transactions

About

The transactions endpoint allows you to list debits and credits made to a specified account.

List Transactions

GET /v2/accounts/{ACCOUNT_ID}/transactions

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/transactions
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/transactions', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {"description": "monthly rollup",
         "id": "09dd02e20e07dbb65401802ba20cfb32",
         "amount": 10.179999999999999716,
         "reason": "database_rollup",
         "type": "credit",
         "created": 63598331974,
         "version": 2,
         "code": 9999
        }
        ,{"metadata": {
            "auth_account_id": "{AUTH_ACCOUNT_ID}"
        },
        "id": "7dd1c20894587e9cbacb2d7fa2de80ab",
        "amount": 1.0,
        "reason": "admin_discretion",
        "type": "debit",
        "created": 63598591394,
        "version": 2,
        "code": 3005
     }],
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Fetch

GET /v2/accounts/{ACCOUNT_ID}/transactions/{TRANSACTION_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/transactions/{TRANSACTION_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/transactions/{TRANSACTION_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/transactions/sale

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/transactions/sale
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/transactions/sale',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Create

PUT /v2/accounts/{ACCOUNT_ID}/transactions/refund

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/transactions/refund
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/transactions/refund',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

User Authentication

About User Authentication

Using your user name and password, along with an account identifier, will instruct Crossbar to create an authentication token to be used on subsequent requests requiring authentication.

Schema

Provides an auth-token via user credentials

Key Description Type Default Required Support Level
account_name The account name of the user string(1..128) false
account_realm The account realm of the user string(4..253) false
credentials A hash of the uses credentials string(1..64) true
method The hash method `string('md5' 'sha')` md5 false
phone_number A phone number assigned to the users account string(1..64) false

Create

PUT /v2/user_auth

Sample Request:

curl -v -X PUT \
    -H "Content-Type: application/json" \
    -d '{"data":{"credentials":"{CREDENTIALS_HASH}", "account_name":"{ACCOUNT_NAME"}, "method":[md5|sha]}}' \
    http://{SERVER}:8000/v2/user_auth
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/user_auth',
  '{"data":{"credentials":"{CREDENTIALS_HASH}", "account_name":"{ACCOUNT_NAME"}, "method":[md5|sha]}}',
  {
    headers: {
      'Content-Type': 'application/json'
    }
  }
);

Where {CREDENTIALS_HASH} is MD5 or SHA1 hash of {username}:{password}.

Creating MD5 User/Pass credentials hash

Sample Request:

$ echo -n 'john@example.com:m32c6NfqYEt' | md5sum
82a2dc91686ec828a67152d45a5c5ef7  -

Responses

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "account_id": "{ACCOUNT_ID}",
        "apps": [],
        "is_reseller": true,
        "language": "en-US",
        "owner_id": "{OWNER_ID}",
        "reseller_id": "{RESELLER_ID}"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch Token Auth Information

GET /v2/user_auth/{AUTH_TOKEN}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/user_auth/{AUTH_TOKEN}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/user_auth/{AUTH_TOKEN}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": {
        "account_id": "{ACCOUNT_ID}",
        "owner_id": "{USER_ID}",
        "method": "cb_user_auth",
        "id": "{AUTH_TOKEN}",
        "reseller_id": "{RESELLER_ID}",
        "is_reseller": false,
        "account_name": "{ACCOUNT_NAME}",
        "language": "en-us",
        "apps": [{
            "id": "8bda62bf7ccf8f8acc219d5d2c515376",
            "name": "accounts",
            "api_url": "http://192.168.0.2:8000/v2/",
            "label": "Accounts Manager"
        }, {
            "id": "99d5f033f0a4176640f9bf1c4e81abed",
            "name": "numbers",
            "api_url": "http://192.168.0.2:8000/v2/",
            "label": "Number Manager"
        }, {
            "id": "0306d5162bad2c7a951b6842483f73cd",
            "name": "voip",
            "api_url": "http://192.168.0.2:8000/v2/",
            "label": "Smart PBX"
        }]
    },
    "auth_token": "{AUTH_TOKEN}",
    "status": "success"
}

Password Recovery

Sometimes it is necessary to recover a password. Similar to user authentication, you can supply the account realm, the account name, or a phone number associated with the account to send a password reset to the user's email. This email will contain a link that one then click to verify identity & proceed with recovery.

Schema

Key Description Type Default Required
account_name The account name of the user string(1..64) false
account_realm The account realm of the user string(1..64) false
phone_number A phone number assigned to the user's account string(1..64) false
username The user's API username string(1..254) true

PUT /v2/user_auth/recovery

Sample Request:

curl -v -X PUT \
    -H "content-type: application/json" \
    -d '{"data":{"username":"API_USERNAME", "account_realm":"ACCOUNT_REALM", "ui_url": "{UI_URL}"}}' \
    http://{SERVER}:8000/v2/user_auth/recovery
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/user_auth/recovery',
  // '{"data":{"username":"API_USERNAME", "account_realm":"ACCOUNT_REALM", "ui_url": "{UI_URL}"}}',
  {
    'data': {
      'username': 'API_USERNAME',
      'account_realm': 'ACCOUNT_REALM',
      'ui_url': '{UI_URL}'
    }
  },
  {
    headers: {
      'content-type': 'application/json'
    }
  }
);

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {},
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Send the {RESET_ID} collected in the recovery-email.

POST /v2/user_auth/recovery

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"reset_id": "{RESET_ID}"}}' \
    http://{SERVER}:8000/v2/user_auth/recovery
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/user_auth/recovery',
  '{"data": {"reset_id": "{RESET_ID}"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Responses

Success

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {},
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Unknown {RESET_ID}

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "user": {
            "not_found": {
                "cause": "{RESET_ID}",
                "message": "The provided reset_id did not resolve to any user"
            }
        }
    },
    "error": "500",
    "message": "invalid request",
    "request_id": "{REQUEST_ID}",
    "status": "error"
}

Impersonate a User

You can impersonate as another user in your sub account if you're already is logged in as an admin in your master account. This features a useful way to login as your customer to debug/test issues with the user system's point of view.

PUT /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/user_auth

Sample Request:

curl -v -X PUT \
    -H "Content-Type: application/json" \
    -d '{ "action": "impersonate_user", "data": {} }' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/user_auth
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/user_auth',
  // '{ "action": "impersonate_user", "data": {} }',
  {
    'action': 'impersonate_user',
    'data': {}
  },
  {
    headers: {
      'Content-Type': 'application/json'
    }
  }
);

Responses

A standard Crossbar authentication token.

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "account_id": "{ACCOUNT_ID}",
        "apps": [],
        "is_reseller": true,
        "language": "en-US",
        "owner_id": "{OWNER_ID}",
        "reseller_id": "{RESELLER_ID}"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Users

About Users

Users represent just that, your users of the system. You can assign multiple devices to a user, put the user in a callflow, and all devices will ring.

Schema

Schema for a user

Key Description Type Default Required Support Level
call_forward.direct_calls_only Determines if the calls that are not directly sent to the device should be forwarded boolean() false false
call_forward.enabled Determines if the call forwarding should be used boolean() false false
call_forward.failover Enable the call-forwarding parameters if the device is offline boolean() false false
call_forward.ignore_early_media The option to determine if early media from the call forwarded number should ignored boolean() true false
call_forward.keep_caller_id Determines if the caller id is kept when the call is forwarded, if not the devices caller id is used boolean() true false supported
call_forward.number The number to forward calls to string(0..35) false supported
call_forward.require_keypress Determines if the callee is prompted to press 1 to accept the call boolean() true false
call_forward.substitute Determines if the call forwarding replaces the device boolean() true false
call_forward The device call forward parameters object() false
call_recording endpoint recording settings #/definitions/call_recording false
call_restriction Device level call restrictions for each available number classification object() {} false
call_waiting Parameters for server-side call waiting #/definitions/call_waiting false
caller_id The device caller ID parameters #/definitions/caller_id false
caller_id_options.outbound_privacy Determines what appears as caller id for offnet outbound calls. Values: full - hides name and number; name - hides only name; number - hides only number; none - hides nothing `string('full' 'name' 'number' 'none')`
caller_id_options custom properties for configuring caller_id object() false
contact_list.exclude If set to true the device is excluded from the contact list boolean() false supported
contact_list Contact List Parameters object() {} false
dial_plan A list of rules used to modify dialed numbers #/definitions/dialplans false
directories Provides the mappings for what directory the user is a part of (the key), and what callflow (the value) to invoke if the user is selected by the caller. object() false
do_not_disturb.enabled Is do-not-disturb enabled for this user? boolean() false
do_not_disturb DND Parameters object() false
email The email of the user string(3..254) false supported
enabled Determines if the user is currently enabled boolean() true false supported
feature_level The user level for assigning feature sets string() false
first_name The first name of the user string(1..128) true supported
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
formatters Schema for request formatters object() false
hotdesk.enabled Determines if the user has hotdesking enabled boolean() false false
hotdesk.id The users hotdesk id string(0..15) false
hotdesk.keep_logged_in_elsewhere Determines if user should be able to login to multiple phones simultaneously boolean() false false
hotdesk.pin The users hotdesk pin number string(4..15) false
hotdesk.require_pin Determines if user requires a pin to change the hotdesk state boolean() false false
hotdesk The user hotdesk parameters object() {} false
language The language for this user string() false supported
last_name The last name of the user string(1..128) true supported
media Configure audio/video/etc media options for this user #/definitions/endpoint.media false
metaflows The device metaflow parameters #/definitions/metaflows false
music_on_hold.media_id The ID of a media object that should be used as the music on hold string(0..128) false
music_on_hold The music on hold parameters used if not a property of the device owner object() {} false
password The GUI login password string() false supported
presence_id Static presence ID (used instead of SIP username) string() false supported
priv_level The privilege level of the user `string('user' 'admin')` user false
profile User's profile data #/definitions/profile false
pronounced_name.media_id The ID of a media object that should be used as the music on hold string(0..128) false
pronounced_name Name pronounced by user to introduce himself to conference members object() false
require_password_update UI flag that the user should update their password. boolean() false false
ringtones.external The alert info SIP header added when the call is from internal sources string(0..256) false
ringtones.internal The alert info SIP header added when the call is from external sources string(0..256) false
ringtones Ringtone Parameters object() {} false
timezone User's timezone string() false supported
username The GUI login username - alpha-numeric, dashes, at symbol, periods, plusses, and underscores allowed string(1..256) false supported
verified Determines if the user has been verified boolean() false false
vm_to_email_enabled Determines if the user would like voicemails emailed to them boolean() true false
voicemail.notify.callback Schema for a callback options #/definitions/notify.callback false
voicemail.notify object() false
voicemail object() false

call_recording

endpoint recording settings

Key Description Type Default Required Support Level
any settings for any calls to/from the endpoint #/definitions/call_recording.source false
inbound settings for inbound calls to the endpoint #/definitions/call_recording.source false
outbound settings for outbound calls from the endpoint #/definitions/call_recording.source false

call_recording.parameters

Key Description Type Default Required Support Level
enabled is recording enabled boolean() false
format What format to store the recording on disk `string('mp3' 'wav')` false
record_min_sec The minimum length, in seconds, the recording must be to be considered successful. Otherwise it is deleted integer() false
record_on_answer Recording should start on answer boolean() false
record_on_bridge Recording should start on bridge boolean() false
record_sample_rate What sampling rate to use on the recording integer() false
time_limit Time limit, in seconds, for the recording integer() false
url The URL to use when sending the recording for storage string() false

call_recording.source

Key Description Type Default Required Support Level
any settings for calls from any network #/definitions/call_recording.parameters false
offnet settings for calls from offnet networks #/definitions/call_recording.parameters false
onnet settings for calls from onnet networks #/definitions/call_recording.parameters false

call_waiting

Parameters for server-side call waiting

Key Description Type Default Required Support Level
enabled Determines if server side call waiting is enabled/disabled boolean() false

caller_id

Defines caller ID settings based on the type of call being made

Key Description Type Default Required Support Level
asserted.name The asserted identity name for the object type string(0..35) false
asserted.number The asserted identity number for the object type string(0..35) false
asserted.realm The asserted identity realm for the object type string() false
asserted Used to convey the proven identity of the originator of a request within a trusted network. object() false
emergency.name The caller id name for the object type string(0..35) false
emergency.number The caller id number for the object type string(0..35) false
emergency The caller ID used when a resource is flagged as 'emergency' object() false
external.name The caller id name for the object type string(0..35) false
external.number The caller id number for the object type string(0..35) false
external The default caller ID used when dialing external numbers object() false
internal.name The caller id name for the object type string(0..35) false
internal.number The caller id number for the object type string(0..35) false
internal The default caller ID used when dialing internal extensions object() false

dialplans

Permit local dialing by converting the dialed number to a routable form

Key Description Type Default Required Support Level
system.[] string() false
system List of system dial plans array(string()) false

endpoint.media

Schema for endpoint media options

Key Description Type Default Required Support Level
audio.codecs.[] `string('OPUS' 'CELT@32000h' 'G7221@32000h' 'G7221@16000h'
audio.codecs A list of audio codecs the endpoint supports `array(string('OPUS' 'CELT@32000h' 'G7221@32000h' 'G7221@16000h'
audio The audio media parameters object() {} false
bypass_media Default bypass media mode (The string type is deprecated, please use this as a boolean) `boolean() string('true' 'false' 'auto')`
encryption.enforce_security Is Encryption Enabled? boolean() false false
encryption.methods.[] `string('zrtp' 'srtp')` false
encryption.methods Supported Encryption Types `array(string('zrtp' 'srtp'))` [] false
encryption Encryption Parameters object() {} false
fax_option Is T.38 Supported? boolean() false
ignore_early_media The option to determine if early media from the endpoint should always be ignored boolean() false
progress_timeout The progress timeout to apply to the endpoint (seconds) integer() false
video.codecs.[] `string('H261' 'H263' 'H264' 'VP8')`
video.codecs A list of video codecs the endpoint supports `array(string('H261' 'H263' 'H264' 'VP8'))`
video The video media parameters object() {} false

formatters

Schema for request formatters

Key Description Type Default Required Support Level
^[[:alnum:]_]+$ Key to match in the route request JSON `array(#/definitions/formatters.format_options) #/definitions/formatters.format_options` false

formatters.format_options

Schema for formatter options

Key Description Type Default Required Support Level
direction Only apply the formatter on the relevant request direction `string('inbound' 'outbound' 'both')`
match_invite_format Applicable on fields with SIP URIs. Will format the username portion to match the invite format of the outbound request. boolean() false
prefix Prepends value against the result of a successful regex match string() false
regex Matches against the value, with optional capture group string() false
strip If set to true, the field will be stripped from the payload boolean() false
suffix Appends value against the result of a successful regex match string() false
value Replaces the current value with the static value defined string() false

metaflow

A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to

Key Description Type Default Required Support Level
children./.+/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
children Children metaflows object() false
data The data/arguments of the metaflow module object() {} false
module The name of the metaflow module to execute at this node string(1..64) true

metaflows

Actions applied to a call outside of the normal callflow, initiated by the caller(s)

Key Description Type Default Required Support Level
binding_digit What DTMF will trigger the collection and analysis of the subsequent DTMF sequence `string('1' '2' '3' '4'
digit_timeout How long to wait between DTMF presses before processing the collected sequence (milliseconds) integer() false
listen_on Which leg(s) of the call to listen for DTMF `string('both' 'self' 'peer')`
numbers./^[0-9]+$/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
numbers A list of static numbers with their flows object() false
patterns./.+/ A metaflow node defines a module to execute, data to provide to that module, and one or more children to branch to #/definitions/metaflow false
patterns A list of patterns with their flows object() false

notify.callback

Schema for a callback options

Key Description Type Default Required Support Level
attempts How many attempts without answer will system do integer() false
disabled Determines if the system will call to callback number boolean() false
interval_s How long will system wait between call back notification attempts integer() false
number Number for callback notifications about new messages string() false
schedule Schedules interval between callbacks array(integer()) false
timeout_s How long will system wait for answer to callback integer() false

profile

Defines user extended properties

Key Description Type Default Required Support Level
addresses.[].address To specify the address string() false
addresses.[].types To specify types of the address array() false
addresses To specify the components of the addresses array(object()) false
assistant To specify the user's assistant string() false
birthday To specify the birth date of the user string() false
nicknames.[] string() false
nicknames To specify the text corresponding to the nickname of the user array(string()) false
note To specify supplemental information or a comment that is associated with the user string() false
role To specify the function or part played in a particular situation by the user string() false
sort-string To specify the family name or given name text to be used for national-language-specific sorting of the FN and N types string() false
title To specify the position or job of the user string() false

Fetch

GET /v2/accounts/{ACCOUNT_ID}/users

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "email": "user1@account_realm.com",
            "features": [
                "caller_id",
                "vm_to_email"
            ],
            "first_name": "User",
            "id": "{USER_ID}",
            "last_name": "One",
            "priv_level": "admin",
            "timezone": "America/Los_Angeles",
            "username": "user1@account_realm.com"
        },
        {
            "email": "user2@account_realm.com",
            "features": [
                "caller_id",
                "vm_to_email"
            ],
            "first_name": "User",
            "id": "{USER_ID}",
            "last_name": "Two",
            "priv_level": "user",
            "timezone": "America/Los_Angeles",
            "username": "user2@account_realm.com"
        }
    ],
    "page_size": 2,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Create a new user

PUT /v2/accounts/{ACCOUNT_ID}/users

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"first_name":"User", "last_name":"Three"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users',
  // '{"data":{"first_name":"User", "last_name":"Three"}}',
  {
    'data': {
      'first_name': 'User',
      'last_name': 'Three'
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Responses

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "call_restriction": {},
        "caller_id": {},
        "contact_list": {},
        "dial_plan": {},
        "enabled": true,
        "first_name": "User",
        "hotdesk": {
            "enabled": false,
            "keep_logged_in_elsewhere": false,
            "require_pin": false
        },
        "id": "{USER_ID}",
        "last_name": "Three",
        "media": {
            "audio": {
                "codecs": [
                    "PCMU"
                ]
            },
            "encryption": {
                "enforce_security": false,
                "methods": []
            },
            "video": {
                "codecs": []
            }
        },
        "music_on_hold": {},
        "priv_level": "user",
        "profile": {},
        "require_password_update": false,
        "ringtones": {},
        "verified": false,
        "vm_to_email_enabled": true
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Remove a user

This request will return the current JSON object of the now-deleted user.

DELETE /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "call_restriction": {},
        "caller_id": {},
        "contact_list": {},
        "dial_plan": {},
        "enabled": false,
        "first_name": "User",
        "hotdesk": {
            "enabled": false,
            "keep_logged_in_elsewhere": false,
            "require_pin": false
        },
        "id": "{USER_ID}",
        "last_name": "Three",
        "media": {
            "audio": {
                "codecs": [
                    "PCMU"
                ]
            },
            "encryption": {
                "enforce_security": false,
                "methods": []
            },
            "video": {
                "codecs": []
            }
        },
        "music_on_hold": {},
        "priv_level": "user",
        "profile": {},
        "require_password_update": false,
        "ringtones": {},
        "verified": false,
        "vm_to_email_enabled": true
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch a user

GET /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Responses

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "call_restriction": {},
        "caller_id": {},
        "contact_list": {},
        "dial_plan": {},
        "enabled": true,
        "first_name": "User",
        "hotdesk": {
            "enabled": false,
            "keep_logged_in_elsewhere": false,
            "require_pin": false
        },
        "id": "{USER_ID}",
        "last_name": "Three",
        "media": {
            "audio": {
                "codecs": [
                    "PCMU"
                ]
            },
            "encryption": {
                "enforce_security": false,
                "methods": []
            },
            "video": {
                "codecs": []
            }
        },
        "music_on_hold": {},
        "priv_level": "user",
        "profile": {},
        "require_password_update": false,
        "ringtones": {},
        "verified": false,
        "vm_to_email_enabled": true
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Patch a user's doc

PATCH /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"enabled":false}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}',
  {
    'data': {
      'enabled': false
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Responses

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "call_restriction": {},
        "caller_id": {},
        "contact_list": {},
        "dial_plan": {},
        "enabled": false,
        "first_name": "User",
        "hotdesk": {
            "enabled": false,
            "keep_logged_in_elsewhere": false,
            "require_pin": false
        },
        "id": "{USER_ID}",
        "last_name": "Three",
        "media": {
            "audio": {
                "codecs": [
                    "PCMU"
                ]
            },
            "encryption": {
                "enforce_security": false,
                "methods": []
            },
            "video": {
                "codecs": []
            }
        },
        "music_on_hold": {},
        "priv_level": "user",
        "profile": {},
        "require_password_update": false,
        "ringtones": {},
        "verified": false,
        "vm_to_email_enabled": true
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Change the user doc

This requires posting the full user's document in the request body

Sync: See the documentation on device sync for more info on check-sync. One can add the field "sync": true to the JSON document in order to attempt a check-sync on every registered device this user has.

POST /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"first_name":"User","last_name":"Three","call_restriction":{},"caller_id":{},"contact_list":{},"dial_plan":{},"enabled":false,"hotdesk":{"enabled":false,"keep_logged_in_elsewhere":false,"require_pin":false},"media":{"audio":{"codecs":["PCMU"]},"encryption":{"enforce_security":false,"methods":[]},"video":{"codecs":[]}},"music_on_hold":{},"priv_level":"user","profile":{},"require_password_update":false,"ringtones":{},"verified":false,"vm_to_email_enabled":true}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}',
  {
    'data': {
      'first_name': 'User',
      'last_name': 'Three',
      'call_restriction': {},
      'caller_id': {},
      'contact_list': {},
      'dial_plan': {},
      'enabled': false,
      'hotdesk': {
        'enabled': false,
        'keep_logged_in_elsewhere': false,
        'require_pin': false
      },
      'media': {
        'audio': {
          'codecs': [
            'PCMU'
          ]
        },
        'encryption': {
          'enforce_security': false,
          'methods': []
        },
        'video': {
          'codecs': []
        }
      },
      'music_on_hold': {},
      'priv_level': 'user',
      'profile': {},
      'require_password_update': false,
      'ringtones': {},
      'verified': false,
      'vm_to_email_enabled': true
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Responses

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "call_restriction": {},
        "caller_id": {},
        "contact_list": {},
        "dial_plan": {},
        "enabled": false,
        "first_name": "User",
        "hotdesk": {
            "enabled": false,
            "keep_logged_in_elsewhere": false,
            "require_pin": false
        },
        "id": "{USER_ID}",
        "last_name": "Three",
        "media": {
            "audio": {
                "codecs": [
                    "PCMU"
                ]
            },
            "encryption": {
                "enforce_security": false,
                "methods": []
            },
            "video": {
                "codecs": []
            }
        },
        "music_on_hold": {},
        "priv_level": "user",
        "profile": {},
        "require_password_update": false,
        "ringtones": {},
        "verified": false,
        "vm_to_email_enabled": true
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch (or create) a vCard

vCard is a file format typically used in emails as a form of business card. Lumian currently generates a 3.0 compatible vCard.

GET /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/vcard

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Accept: text/x-vcard" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/vcard
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/vcard', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Accept': 'text/x-vcard'
  }
});

Responses

Sample Response:

BEGIN:VCARD
VERSION:3.0
FN:User Three
N:Three;User
END:VCARD

Remove the photo from the user

DELETE /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/photo

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/photo
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/photo', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch the user's photo, if any

Set the Accept header to either application/base64 or application/octet-stream to retrieve the picture's contents.

If the result is successful, you will want to pipe the response into a file.

GET /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/photo

Sample Request:

curl -v -X GET \
    -H "Accept: application/base64" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/photo
[binary data]
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/photo', {
  headers: {
    'Accept': 'application/base64'
  }
})

Create or change the user's photo

Use application/octet-stream as the content type.

POST /v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/photo

Sample Request:

curl -v -X POST \
    -H "Content-Type: application/octet-stream" \
    --data-binary @/path/to/image.jpg \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/photo
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users/{USER_ID}/photo',
  '@/path/to/image.jpg',
  {
    headers: {
      'Content-Type': 'application/octet-stream'
    }
  }
);

Responses

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {},
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Quickcalls

See the quickcall docs for how to perform this action.

Voicemail

About Voicemail

Voicemail boxes store messages, recorded from the caller, for the voicemail box owner to listen to at a later time.

Differences between Lumian version 3.x and 4.x

As of Lumian 4.0 all new voicemail messages will be stored in the account MODbs.

Regarding this change voicemail API will no longer returns the messages array when fetching mailbox settings. The existing /messages API should be used to manage messages in a voicemail box.

For more information about voicemail changes see documentation for lumian_voicemail.

Schema

Schema for a voicemail box

Key Description Type Default Required Support Level
announcement_only Determine if the mailbox should only play announcements boolean() false false unsupported
check_if_owner Determines if when the user calls their own voicemail they should be prompted to sign in boolean() true false supported
delete_after_notify Move the voicemail to delete folder after the notification has been sent boolean() false false supported
flags.[] string() false supported
flags Flags set by external applications array(string()) false supported
include_message_on_notify Whether or not to include the attachment when sending a new voicemail to email notification boolean() true false supported
include_transcription_on_notify Whether or not to include the transcription when sending a new voicemail to email notification boolean() true false supported
is_setup Determines if the user has completed the initial configuration boolean() false false supported
is_voicemail_ff_rw_enabled callflow allow fastforward and rewind during voicemail message playback boolean() false false
mailbox The voicemail box number string(1..30) true supported
media.unavailable The ID of a media object that should be used as the unavailable greeting string(32) false supported
media The media (prompt) parameters object() {} false supported
media_extension Voicemail audio format `string('mp3' 'mp4' 'wav')` mp3
name A friendly name for the voicemail box string(1..128) true supported
not_configurable Determines if the user can configure this voicemail. boolean() false false supported
notify.callback Schema for a callback options #/definitions/notify.callback false
notify object() false supported
notify_email_addresses.[] string() false supported
notify_email_addresses List of email addresses to send notifications to (in addition to owner's email, if any) array(string()) [] false supported
oldest_message_first Play older voicemail messages before new ones boolean() false false supported
owner_id The ID of the user object that 'owns' the voicemail box string(32) false supported
pin The pin number for the voicemail box string(4..6) false supported
require_pin Determines if a pin is required to check the voicemail from the users devices boolean() false false supported
save_after_notify Move the voicemail to save folder after the notification has been sent (This setting will override delete_after_notify) boolean() false false supported
seek_duration_ms callflow fastforward and rewind seek duration integer() 10000 false
skip_envelope Determines if the envelope should be skipped boolean() false false beta
skip_greeting Determines if the greeting should be skipped boolean() false false supported
skip_instructions Determines if the instructions after the greeting and prior to composing a message should be played boolean() false false supported
timezone The default timezone string(5..32) false supported
transcribe Transcribe voicemail using ASR engine boolean() false false supported

notify.callback

Schema for a callback options

Key Description Type Default Required Support Level
attempts How many attempts without answer will system do integer() false
disabled Determines if the system will call to callback number boolean() false
interval_s How long will system wait between call back notification attempts integer() false
number Number for callback notifications about new messages string() false
schedule Schedules interval between callbacks array(integer()) false
timeout_s How long will system wait for answer to callback integer() false

Fetch

GET /v2/accounts/{ACCOUNT_ID}/vmboxes

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "id": "3a63acc3694ba189947235ae4727941b",
            "name": "VMBox 0",
            "mailbox": "3000",
            "owner_id": "f1d98a5df729f95cd208ee9430e3b21b",
            "messages": 4
        }
    ],
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Create a new voicemail box

PUT /v2/accounts/{ACCOUNT_ID}/vmboxes

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data": {"name": "VMBox 0", "require_pin": true, "is_setup": false, "pin": "0000", "mailbox": "3000", "timezone": "America/Los_Angeles", "check_if_owner": true, "delete_after_notify": false, "not_configurable": false, "notify_email_addresses": [], "save_after_notify": false, "skip_greeting": false, "skip_instructions": false, "owner_id": "f1d98a5df729f95cd208ee9430e3b21b", "media":{}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes',
  // '{"data": {"name": "VMBox 0", "require_pin": true, "is_setup": false, "pin": "0000", "mailbox": "3000", "timezone": "America/Los_Angeles", "check_if_owner": true, "delete_after_notify": false, "not_configurable": false, "notify_email_addresses": [], "save_after_notify": false, "skip_greeting": false, "skip_instructions": false, "owner_id": "f1d98a5df729f95cd208ee9430e3b21b", "media":{}}}',
  {
    'data': {
      'name': 'VMBox 0',
      'require_pin': true,
      'is_setup': false,
      'pin': '0000',
      'mailbox': '3000',
      'timezone': 'America/Los_Angeles',
      'check_if_owner': true,
      'delete_after_notify': false,
      'not_configurable': false,
      'notify_email_addresses': [],
      'save_after_notify': false,
      'skip_greeting': false,
      'skip_instructions': false,
      'owner_id': 'f1d98a5df729f95cd208ee9430e3b21b',
      'media': {}
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "name": "VMBox 0",
        "require_pin": true,
        "is_setup": false,
        "pin": "0000",
        "mailbox": "3000",
        "timezone": "America/Los_Angeles",
        "check_if_owner": true,
        "delete_after_notify": false,
        "not_configurable": false,
        "notify_email_addresses": [],
        "save_after_notify": false,
        "skip_greeting": false,
        "skip_instructions": false,
        "id": "3a63acc3694ba189947235ae4727941b",
        "owner_id": "f1d98a5df729f95cd208ee9430e3b21b",
        "media": {}
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

list all voicemail messages on an account

GET /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages?paginate=true
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages', {
  params: {
    'paginate': 'true'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "0e820108c0f4ca391500f3be1b02bdfa": {
                "timestamp": 63630058722,
                "from": "1001@aeac33.sip.lumian.net",
                "to": "1000@aeac33.sip.lumian.net",
                "caller_id_number": "1001",
                "caller_id_name": "userb userb",
                "call_id": "79959ZDNmM2I5ZTliMzA0NzA4N2FjNjlmODA5OWVkZjUxZWU",
                "folder": "new",
                "length": 3140,
                "media_id": "201605-6aadef09f6fcf5fd8bcdfca312e923ba"
            }
        },
        {
            "0e820108c0f4ca391500f3be1b02bdfa": {
                "timestamp": 63630058413,
                "from": "1002@aeac33.sip.lumian.net",
                "to": "1000@aeac33.sip.lumian.net",
                "caller_id_number": "1002",
                "caller_id_name": "userd userd",
                "call_id": "79959MmNiMmJiMTIxODhjZjk0ZDhmOGNkMjJkN2MwNGQyNWY",
                "folder": "new",
                "length": 5500,
                "media_id": "201605-f0c3c16551a5ff7b5753a381892e2e01"
            }
        }
    ],
    "next_start_key": [],
    "page_size": 50,
    "revision": "{REVERSION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Remove a voicemail box

DELETE /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "name": "VMBox 0",
        "require_pin": true,
        "is_setup": false,
        "pin": "0000",
        "mailbox": "3000",
        "timezone": "America/Los_Angeles",
        "check_if_owner": true,
        "delete_after_notify": false,
        "not_configurable": false,
        "notify_email_addresses": [],
        "save_after_notify": false,
        "skip_greeting": false,
        "skip_instructions": false,
        "id": "3a63acc3694ba189947235ae4727941b",
        "owner_id": "f1d98a5df729f95cd208ee9430e3b21b",
        "media": {}
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Fetch a voicemail box

GET /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "name": "VMBox 0",
        "require_pin": true,
        "is_setup": false,
        "pin": "0000",
        "mailbox": "3000",
        "timezone": "America/Los_Angeles",
        "check_if_owner": true,
        "delete_after_notify": false,
        "not_configurable": false,
        "notify_email_addresses": [],
        "save_after_notify": false,
        "skip_greeting": false,
        "skip_instructions": false,
        "id": "3a63acc3694ba189947235ae4727941b",
        "owner_id": "f1d98a5df729f95cd208ee9430e3b21b",
        "media": {}
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Patch a voicemail box

PATCH /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"some_key":"some_value"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}',
  '{"data":{"some_key":"some_value"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "name": "VMBox 0",
        "require_pin": true,
        "is_setup": false,
        "pin": "0000",
        "mailbox": "3000",
        "timezone": "America/Los_Angeles",
        "check_if_owner": true,
        "delete_after_notify": false,
        "not_configurable": false,
        "notify_email_addresses": [],
        "save_after_notify": false,
        "skip_greeting": false,
        "skip_instructions": false,
        "id": "3a63acc3694ba189947235ae4727941b",
        "owner_id": "f1d98a5df729f95cd208ee9430e3b21b",
        "media": {},
        "some_key": "some_value"
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Change a voicemail box's settings

POST /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data": {"name": "VMBox 0", "require_pin": true, "is_setup": false, "pin": "0000", "mailbox": "3000", "timezone": "America/Los_Angeles", "check_if_owner": true, "delete_after_notify": false, "not_configurable": false, "notify_email_addresses": [], "save_after_notify": false, "skip_greeting": false, "skip_instructions": false, "owner_id": "f1d98a5df729f95cd208ee9430e3b21b", "media":{}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes \
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes',
  // '{"data": {"name": "VMBox 0", "require_pin": true, "is_setup": false, "pin": "0000", "mailbox": "3000", "timezone": "America/Los_Angeles", "check_if_owner": true, "delete_after_notify": false, "not_configurable": false, "notify_email_addresses": [], "save_after_notify": false, "skip_greeting": false, "skip_instructions": false, "owner_id": "f1d98a5df729f95cd208ee9430e3b21b", "media":{}}}',
  {
    'data': {
      'name': 'VMBox 0',
      'require_pin': true,
      'is_setup': false,
      'pin': '0000',
      'mailbox': '3000',
      'timezone': 'America/Los_Angeles',
      'check_if_owner': true,
      'delete_after_notify': false,
      'not_configurable': false,
      'notify_email_addresses': [],
      'save_after_notify': false,
      'skip_greeting': false,
      'skip_instructions': false,
      'owner_id': 'f1d98a5df729f95cd208ee9430e3b21b',
      'media': {}
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "name": "VMBox 0",
        "require_pin": true,
        "is_setup": false,
        "pin": "0000",
        "mailbox": "3000",
        "timezone": "America/Los_Angeles",
        "check_if_owner": true,
        "delete_after_notify": false,
        "not_configurable": false,
        "notify_email_addresses": [],
        "save_after_notify": false,
        "skip_greeting": false,
        "skip_instructions": false,
        "id": "3a63acc3694ba189947235ae4727941b",
        "owner_id": "f1d98a5df729f95cd208ee9430e3b21b",
        "media": {}
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Create a new voicemail message

There are two methods for creating a new voicemail message - they differ in how you attach the media file.

In the first method, you can create a voicemail document first in one request and then put the media file into the document with a second request using /messages/{VM_MSG_ID} API endpoint.

PUT /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d {"data":{"caller_id_name":"someone","caller_id_number":"6001","folder":"new","from":"someone@farfaraway.com"}} \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages
import axios from 'axios';

const response = await axios.put(
  'http://{data:caller_id_number:6001}',
  '{data:caller_id_name:someone}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
            "timestamp": 63630058722,
            "from": "someone@farfaraway.com",
            "to": "1000@sip.somewhere.com",
            "caller_id_number": "6001",
            "caller_id_name": "someone",
            "call_id": "79959ZDNmM2I5ZTliMzA0NzA4N2FjNjlmODA5OWVkZjUxZWU",
            "folder": "new",
            "length": 3140,
            "media_id": "201605-fadnew0mf6fcfgfd8bcdfca312e924bq"
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

And then you can use PUT method on /messages/201605-fadnew0mf6fcfgfd8bcdfca312e924bq to add the media to file (see PUT method for a message below).

In the second method, you can use a single PUT request and send a multipart content-type to add both the JSON metadata about the message and the media file itself, in a single request.

Sample Request:

curl -v -X PUT \
     -H "Content-Type: multipart/mixed" \
     -F "content=@message.json; type=application/json" \
     -F "content=@voice.mp3; type=audio/mp3" \
     -H 'X-Auth-Token: {AUTH_TOKEN}' \
     http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/messages
import axios from 'axios';
import FormData from 'form-data';
import * as fs from 'fs';

const form = new FormData();
form.append('content', fs.readFileSync('message.json'), 'message.json');
form.append('content', fs.readFileSync('voice.mp3'), 'voice.mp3');

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/messages',
  form,
  {
    headers: {
      ...form.getHeaders(),
      'Content-Type': 'multipart/mixed',
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

The response is same as above.

Remove all or a list of messages from a voicemail box

DELETE /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages

Deleting all message is easy, just use DELETE method on message API endpoint to delete all account's messages.

Optional payload for deleting a group of messages:

!!! note If you didn't move voicemail messages to the new format already, messages that are in old format will be moved to the new MODB format, which will cause their message id to change to the new format.

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "succeeded": ["201605-6aadef09f6fcf5fd8bcdfca312e923ba"],
        "failed": [{"201605-49be0985ea3a33046f8073083517d27b":"not_found"}]
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Fetch all messages for a voicemail box

GET /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "timestamp": 63630058722,
            "from": "1001@aeac33.sip.lumian.net",
            "to": "1000@aeac33.sip.lumian.net",
            "caller_id_number": "1001",
            "caller_id_name": "userb userb",
            "call_id": "79959ZDNmM2I5ZTliMzA0NzA4N2FjNjlmODA5OWVkZjUxZWU",
            "folder": "new",
            "length": 3140,
            "media_id": "201605-6aadef09f6fcf5fd8bcdfca312e923ba"
        },
        {
            "timestamp": 63630058413,
            "from": "1002@aeac33.sip.lumian.net",
            "to": "1000@aeac33.sip.lumian.net",
            "caller_id_number": "1002",
            "caller_id_name": "userd userd",
            "call_id": "79959MmNiMmJiMTIxODhjZjk0ZDhmOGNkMjJkN2MwNGQyNWY",
            "folder": "new",
            "length": 5500,
            "media_id": "201605-f0c3c16551a5ff7b5753a381892e2e01"
        }
    ],
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Change a list of messages

POST /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages

Provide an array of message ids, e.g {"data": {"messages": ["MSG_ID1", "MSG_ID2", "MSG_ID3"]}} you can do following change operations on them. It will return two objects: the first is all the message ids that were successfully changed and the second one is those that failed with the reasons.

!!! note If you didn't move voicemail messages to the new format already, messages that are in old format will be moved to the new MODB format, which will cause their message id to change to the new format.

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data": {"folder": "saved", "messages": ["MSG_ID1", "MSG_ID2", "MSG_ID3"]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages',
  // '{"data": {"folder": "saved", "messages": ["MSG_ID1", "MSG_ID2", "MSG_ID3"]}}',
  {
    'data': {
      'folder': 'saved',
      'messages': [
        'MSG_ID1',
        'MSG_ID2',
        'MSG_ID3'
      ]
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "succeeded": ["201605-6aadef09f6fcf5fd8bcdfca312e923ba"],
        "failed": [{"201605-49be0985ea3a33046f8073083517d27b":"not_found"}]
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Fetch the raw audio of a list of messages as a ZIP file

POST /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/raw

You can provide a list of voicemail message ID in the payload and get raw audio of them in a single ZIP file.

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -H "Accept: application/zip" \
    -d '{"data": {"messages": ["MSG_ID1", "MSG_ID2", "MSG_ID3"]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/raw
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/raw',
  // '{"data": {"messages": ["MSG_ID1", "MSG_ID2", "MSG_ID3"]}}',
  {
    'data': {
      'messages': [
        'MSG_ID1',
        'MSG_ID2',
        'MSG_ID3'
      ]
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json',
      'Accept': 'application/zip'
    }
  }
);

Remove a message from the voicemail box

DELETE /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/{VM_MSG_ID}

!!! note If you didn't move voicemail messages to the new format already, messages that are in old format will be moved to the new MODB format, which will cause their message id to change to the new format.

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/201605-6aadef09f6fcf5fd8bcdfca312e923ba
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/201605-6aadef09f6fcf5fd8bcdfca312e923ba', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "timestamp": 63630058722,
        "from": "1001@aeac33.sip.lumian.net",
        "to": "1000@aeac33.sip.lumian.net",
        "caller_id_number": "1001",
        "caller_id_name": "userb userb",
        "call_id": "79959ZDNmM2I5ZTliMzA0NzA4N2FjNjlmODA5OWVkZjUxZWU",
        "folder": "new",
        "length": 3140,
        "media_id": "201605-6aadef09f6fcf5fd8bcdfca312e923ba",
        "transcription": {
            "result": "success",
            "text": "This is a test of the voicemail transcription."
        }
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Fetch a message from the voicemail box

GET /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/{VM_MSG_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/201605-6aadef09f6fcf5fd8bcdfca312e923ba
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/201605-6aadef09f6fcf5fd8bcdfca312e923ba', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

!!! note If message doesn't have a folder assign to it by any chance, it will be set to new by this method. Please also refer to the note for change the folder of a message regards of possible change of message id.

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "timestamp": 63630058722,
        "from": "1001@aeac33.sip.lumian.net",
        "to": "1000@aeac33.sip.lumian.net",
        "caller_id_number": "1001",
        "caller_id_name": "userb userb",
        "call_id": "79959ZDNmM2I5ZTliMzA0NzA4N2FjNjlmODA5OWVkZjUxZWU",
        "folder": "new",
        "length": 3140,
        "media_id": "201605-6aadef09f6fcf5fd8bcdfca312e923ba",
        "transcription": {
            "result": "success",
            "text": "This is a test of the voicemail transcription."
        }
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Change a message

POST /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/{VM_MSG_ID}

!!! note If you didn't move voicemail messages to the new format already, messages that are in old format will be moved to the new MODB format, which will cause their message id to change to the new format.

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"folder": "saved"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/201605-6aadef09f6fcf5fd8bcdfca312e923ba
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/201605-6aadef09f6fcf5fd8bcdfca312e923ba',
  '{"data": {"folder": "saved"}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "timestamp": 63630058722,
        "from": "1001@aeac33.sip.lumian.net",
        "to": "1000@aeac33.sip.lumian.net",
        "caller_id_number": "1001",
        "caller_id_name": "userb userb",
        "call_id": "79959ZDNmM2I5ZTliMzA0NzA4N2FjNjlmODA5OWVkZjUxZWU",
        "folder": "saved",
        "length": 3140,
        "media_id": "201605-6aadef09f6fcf5fd8bcdfca312e923ba",
        "transcription": {
            "result": "success",
            "text": "This is a test of the voicemail transcription."
        }
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Fetch the raw audio of the message

GET /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/{VM_MSG_ID}/raw

!!! note If message doesn't have a folder assign to it by any chance, it will be set to new by this method. Please also refer to the note for change the folder of a message regards of possible change of message id.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/201605-6aadef09f6fcf5fd8bcdfca312e923ba/raw
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/201605-6aadef09f6fcf5fd8bcdfca312e923ba/raw', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Add a new voicemail media file to a message

If you added a message based on the first method mentioned above (using PUT method on /messages), you can use this to upload the media file for the created message.

!!! note If there's already a media file attachment inside the message document it will be removed and replaced with the new media file!

PUT /v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/{VM_MSG_ID}/raw

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: multipart/mixed" \
    -F "content=@voice.mp3; type=audio/mp3" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/201605-fadnew0mf6fcfgfd8bcdfca312e924bq/raw
import axios from 'axios';
import FormData from 'form-data';
import * as fs from 'fs';

const form = new FormData();
form.append('content', fs.readFileSync('voice.mp3'), 'voice.mp3');

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/vmboxes/{VM_BOX_ID}/messages/201605-fadnew0mf6fcfgfd8bcdfca312e924bq/raw',
  form,
  {
    headers: {
      ...form.getHeaders(),
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'multipart/mixed'
    }
  }
);

Response

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
            "timestamp": 63630058722,
            "from": "someone@farfaraway.com",
            "to": "1000@sip.somewhere.com",
            "caller_id_number": "6001",
            "caller_id_name": "someone",
            "call_id": "79959ZDNmM2I5ZTliMzA0NzA4N2FjNjlmODA5OWVkZjUxZWU",
            "folder": "new",
            "length": 3140,
            "media_id": "201605-fadnew0mf6fcfgfd8bcdfca312e924bq"
    },
    "revision": "{REVISION}",
    "request_id": "{REQUEST_ID}",
    "status": "success"
}

Webhooks

About Webhooks

Webhooks allow Lumian to send HTTP requests to a third-party web server, alerting that server of events occurring within Lumian. Typically, events would be fired for new calls, when a call is answered, and when a call is finished, though other events will be added in the future.

Schema

Web Hooks are subscriptions to allowed events that, when the event occurs, the event data is sent to the uri set in the Web Hook document.

Key Description Type Default Required Support Level
custom_data These properties will be added to the event and will overwrite existing values. object() false
enabled Is the webhook enabled and running boolean() true false
format What Body format to use when sending the webhook. only valid for 'post' & 'put' verbs `string('form-data' 'json')` form-data false
hook The trigger event for a request being made to 'callback_uri'. string() true supported
http_verb What HTTP method to use when contacting the server `string('get' 'post' 'put')` post
include_internal_legs Whether to filter out call legs that are internal to the system (loopback) boolean() true false
include_subaccounts Should the webhook be fired for subaccount events. boolean() false supported
name A friendly name for the webhook string() true supported
retries Retry the request this many times (if it fails) integer() 2 false supported
uri The 3rd party URI to call out to an event string() true supported

Fetch

GET /v2/webhooks

Sample Request:

curl -v -X GET \
    -H "Content-Type:application/json" \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/webhooks
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/webhooks', {
  headers: {
    'Content-Type': 'application/json',
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "description": "Events when calls end",
            "id": "channel_destroy",
            "name": "channel_destroy"
        },
        {
            "description": "Events when new calls start",
            "id": "channel_create",
            "name": "channel_create"
        },
        {
            "description": "Events for when the channel is answered by the endpoint",
            "id": "channel_answer",
            "name": "channel_answer"
        },
        {
           "description": "Receive notifications when objects in Lumian are changed",
           "id": "object",
           "modifiers": {
               "action": {
                   "description": "A list of object actions to handle",
                   "items": [
                       "doc_created",
                       "doc_edited",
                       "doc_deleted"
                   ],
                   "type": "array"
               },
               "type": {
                   "description": "A list of object types to handle",
                   "items": [
                       "account",
                       "callflow",
                       "device",
                       "faxbox",
                       "media",
                       "user",
                       "vmbox"
                   ],
                   "type": "array"
               },
               "types": {
                   "description": "A list of object types to handle",
                   "items": {
                       "type": "string"
                   },
                   "type": "array"
               }
           },
           "name": "object"
       }
   ],
   "page_size": 4,
   "request_id": "{REQUEST_ID}",
   "revision": "{REVISION}",
   "status": "success"
}

Get sample payloads of all webhook events

GET /v2/webhooks/samples

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/webhooks/samples
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/webhooks/samples', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Example

Request:

Sample Request:

curl -H 'Content-Type: application/json' 'http://{SERVER}:8000/v2/webhooks/samples'
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/webhooks/samples', {
  headers: {
    'Content-Type': 'application/json'
  }
});

Response:

Sample Response:

{
  "data": [
    "webhooks_channel_answer",
    "webhooks_channel_bridge",
    "webhooks_channel_create",
    "webhooks_channel_destroy",
    "webhooks_notifications",
    "webhooks_object",
    "webhooks_parking"
  ],
  "revision": "{REVISION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success"
}

Get sample payloads of a webhook event

GET /v2/webhooks/samples/{SAMPLE_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/webhooks/samples/{SAMPLE_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/webhooks/samples/{SAMPLE_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

You can use regular Crossbar query string filters to narrow down the samples, for example:

Sample Request:

curl -H 'Content-Type: application/json' 'http://{SERVER}:8000/v2/webhooks/samples/webhook_notifications?filter_event_name=missed_call'
curl -H 'Content-Type: application/json' 'http://{SERVER}:8000/v2/webhooks/samples/webhook_object?filter_action=doc_created'

Example

Request:

Sample Request:

curl -s -H 'Content-Type: application/json' 'http://{SERVER}:8000/v2/webhooks/samples/webhooks_parking'
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/webhooks/samples/webhooks_parking', {
  headers: {
    'Content-Type': 'application/json'
  }
});

Response:

Sample Response:

{
  "page_size": 1,
  "data": [
    {
      "account_id": "5a2d994fbae69b1d6b01eb9f0e7dfe62",
      "call_id": "OWU4NzEwOTgyZWNiMjM0MzI0NjRkZDc4MWVmMjEyOWI",
      "callee_id_name": "Test Name",
      "callee_id_number": "5355543456",
      "caller_id_Number": "+15555432345",
      "caller_id_name": "Superman",
      "event_name": "PARK_PARKED",
      "parking_slot": 1
    }
  ],
  "revision": "{REVISION}",
  "timestamp": "{TIMESTAMP}",
  "version": "{VERSION}",
  "node": "{NODE}",
  "request_id": "{REQUEST_ID}",
  "status": "success"
}

List webhooks

GET /v2/accounts/{ACCOUNT_ID}/webhooks

Any webhooks with disable_reason in the summary has been auto-disabled.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create webhook

PUT /v2/accounts/{ACCOUNT_ID}/webhooks

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data": { \
        "name": "New Calls", \
        "uri": "http://my.{SERVER}/calls/new.php", \
        "http_verb": "post", \
        "hook": "channel_create", \
        "retries":3 \
    }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks',
  '{"data": { \\\n        "name": "New Calls", \\\n        "uri": "http://my.{SERVER}/calls/new.php", \\\n        "http_verb": "post", \\\n        "hook": "channel_create", \\\n        "retries":3 \\\n    }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Get details of the webhook

GET /v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Edit webhook

POST /v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data": { \
        "name": "New Calls", \
        "uri": "http://my.{SERVER}/calls/new_calls.php", \
        "http_verb": "post", \
        "hook": "channel_create", \
        "retries": 3 \
    }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}',
  '{"data": { \\\n        "name": "New Calls", \\\n        "uri": "http://my.{SERVER}/calls/new_calls.php", \\\n        "http_verb": "post", \\\n        "hook": "channel_create", \\\n        "retries": 3 \\\n    }}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Patch webhook

PATCH /v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}

You can also patch an existing webhook:

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"enabled":true}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}',
  {
    'data': {
      'enabled': true
    }
  },
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/json'
    }
  }
);

Delete a webhook

DELETE /v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

List Webhook Attempts

Webhooks tracks attempts to send the hook payloads to your URIs. You can get a listing of the more recent attempts to help debug what went wrong.

GET /v2/accounts/{ACCOUNT_ID}/webhooks/attempts

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks/attempts
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks/attempts', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "client_error": "nxdomain",
            "hook_id": "{HOOK_ID}",
            "reason": "lumian http client error",
            "result": "failure",
            "retries left": 2,
            "timestamp": 63590996563
        },
        {
            "hook_id": "{HOOK_ID}",
            "result": "success",
            "timestamp": 63590996562
        }
    ],
    "page_size": 2,
    "request_id": "{REQUEST_ID}",
    "status": "success"
    }

List attempts for a specific attempt

GET /v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}/attempts

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}/attempts
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks/{WEBHOOK_ID}/attempts', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Re-enable auto-disabled hooks in bulk

Webhooks will auto-disable failing hooks (if Lumian can't reach your server, or you take too long to respond with 200 OK, for instance). Especially if you're a reseller with webhooks in your client accounts, it can be tedious to have to iterate through all your accounts and re-enable each hook. Fortunately, you can perform this bulk-enable action against an account or an account and its descendants.

Enable an account's hooks

PATCH /v2/accounts/{ACCOUNT_ID}/webhooks

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"re-enable":true}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/webhooks',
  '{"data":{"re-enable":true}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Enable an account's and descendant accounts' hooks

PATCH /v2/accounts/{ACCOUNT_ID}/descendants/webhooks

Sample Request:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data":{"re-enable":true}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/descendants/webhooks
import axios from 'axios';

const response = await axios.patch(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/descendants/webhooks',
  '{"data":{"re-enable":true}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

Hook Payload

Here's what you can expect to receive when a webhook fires to your server:

Base Payload

Most of these fields should be present on all payloads.

Hook Specific

Hook Specific Custom Data

To restrict the kind of document or the action or both. You can set the custom data to:

Sample Response:

{
   "type": "user",
   "action": "doc_edited"
}

Websockets

About Websockets

Fetch information about what bindings can be subscribed to, what sockets are active, and the active bindings of a socket.

Available Websocket Bindings

Lists all available Websocket bindings.

GET /v2/websockets

Sample Request:

curl -v -X GET \
    http://{SERVER}:8000/v2/websockets
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/websockets');

Response

Sample Response:

{
    "data": {
        "call": [
            "call.CHANNEL_CREATE.{CALL_ID}",
            "call.CHANNEL_ANSWER.{CALL_ID}",
            "call.CHANNEL_DESTROY.{CALL_ID}",
            "call.CHANNEL_HOLD.{CALL_ID}",
            "call.CHANNEL_UNHOLD.{CALL_ID}",
            "call.CHANNEL_BRIDGE.{CALL_ID}",
            "call.PARK_PARKED.{CALL_ID}",
            "call.PARK_RETRIEVED.{CALL_ID}",
            "call.PARK_ABANDONED.{CALL_ID}"
        ],
        "conference": [
            "conference.event.{CONFERENCE_ID}.{CALL_ID}",
            "conference.command.{CONFERENCE_ID}"
        ],
        "fax": [
            "fax.status.{FAX_ID}",
            "fax.object.{ACTION}"
        ],
        "object": [
            "object.doc_created.account",
            "object.doc_created.callflow",
            "object.doc_created.device",
            "object.doc_created.faxbox",
            "object.doc_created.media",
            "object.doc_created.user",
            "object.doc_created.vmbox",
            "object.doc_created.fax",
            "object.doc_created.mailbox_message",
            "object.doc_created.call_recording",
            "object.doc_edited.account",
            "object.doc_edited.callflow",
            "object.doc_edited.device",
            "object.doc_edited.faxbox",
            "object.doc_edited.media",
            "object.doc_edited.user",
            "object.doc_edited.vmbox",
            "object.doc_edited.fax",
            "object.doc_edited.mailbox_message",
            "object.doc_edited.call_recording",
            "object.doc_deleted.account",
            "object.doc_deleted.callflow",
            "object.doc_deleted.device",
            "object.doc_deleted.faxbox",
            "object.doc_deleted.media",
            "object.doc_deleted.user",
            "object.doc_deleted.vmbox",
            "object.doc_deleted.fax",
            "object.doc_deleted.mailbox_message",
            "object.doc_deleted.call_recording"
        ]
    },
    "node": "{NODE}",
    "request_id": "{REQUEST_ID}",
    "status": "success",
    "timestamp": "{TIMESTAMP}",
    "version": "{VERSION}"
}

Fetch Socket IDs

GET /v2/accounts/{ACCOUNT_ID}/websockets

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/websockets
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/websockets', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{
    "data": [
      {"bindings":["object.doc_created.user"]
       ,"websocket_session_id":"{SOCKET_ID}"
       ,"timestamp":{CONNECTION_TIMESTAMP}
       ,"destination":"{WS_SERVER}"
       ,"source":"{CLIENT_IP}"
      }
    ],
    "status": "success"
}

Fetch Socket's Bindings

GET /v2/accounts/{ACCOUNT_ID}/websockets/{SOCKET_ID}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/websockets/{SOCKET_ID}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/websockets/{SOCKET_ID}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Sample Response:

{"data":{
   "bindings": ["{CLIENT_BINDING}"],
   "timestamp":{CONNECTION_TIMESTAMP},
   "destination":"{BLACKHOLE_SERVER}",
   "source":"{CLIENT_IP}",
   "websocket_session_id": "{SOCKET_ID}"
 },
 "status": "success"
}

Whitelabel

About Whitelabel

Schema

Whitelabel settings

Key Description Type Default Required Support Level
company_name The company name to display to users string() false supported
domain This is the whitelabeled domain that users will be entering to reach the UI string() false supported
fake_api_url This is a whitelabeled API URL, primarily used by the developer application string() false beta
hide_credits When checked this hides the credits boolean() false false beta
hide_powered When checked this hides the powered by Lumian on the bottom right boolean() false false supported
hide_registration When checked this hides the ability to register for a new account boolean() false false beta
inbound_trunks_price The price to show for inbound trunks, this is currently only for display purposes string() false beta
nav.help The URL to use when the help link is clicked string() false supported
nav.learn_more The URL to use when the 'Learn More!' link is clicked string() false supported
nav Properties related to navigation in the UI object() false
outbound_trunks_price The price to show for outbound trunks, this is currently only for display purposes string() false beta
port.authority The account ID(s) to be used for administrating port requests `string() array(string())` false
port.features The URL to use when the features link is clicked string() false supported
port.loa The URL to use when the LOA link is clicked string() false supported
port.resporg The URL to use when the resporg link is clicked string() false supported
port.support_email The support email address to display to the user string() false supported
port.terms The URL to use when the terms and conditions link is clicked string() false supported
port Parameters related to white-labeling port requests object() false
sso_providers ["array(", "[#/definitions/sso_provider](#sso_provider)", ")"] false
twoway_trunks_price The price to show for twoway trunks, this is currently only for display purposes string() false beta

Fetch

GET /v2/accounts/{ACCOUNT_ID}/whitelabel

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Create

PUT /v2/accounts/{ACCOUNT_ID}/whitelabel

Sample Request:

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel
import axios from 'axios';

const response = await axios.put(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Change

POST /v2/accounts/{ACCOUNT_ID}/whitelabel

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Remove

DELETE /v2/accounts/{ACCOUNT_ID}/whitelabel

Sample Request:

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel
import axios from 'axios';

const response = await axios.delete('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

GET /v2/accounts/{ACCOUNT_ID}/whitelabel/{WHITELABEL_DOMAIN}

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/{WHITELABEL_DOMAIN}
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/{WHITELABEL_DOMAIN}', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

GET /v2/accounts/{ACCOUNT_ID}/whitelabel/domains

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/domains
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/domains', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/whitelabel/domains

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/domains
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/domains',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/whitelabel/welcome

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/welcome
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/welcome', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/whitelabel/welcome

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/welcome
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/welcome',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/whitelabel/icon

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/icon
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/icon', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/whitelabel/icon

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/icon
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/icon',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/whitelabel/logo

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/logo
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/logo', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Change

POST /v2/accounts/{ACCOUNT_ID}/whitelabel/logo

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/logo
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/logo',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Fetch

GET /v2/accounts/{ACCOUNT_ID}/whitelabel/{WHITELABEL_DOMAIN}/welcome

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/{WHITELABEL_DOMAIN}/welcome
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/{WHITELABEL_DOMAIN}/welcome', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

GET /v2/accounts/{ACCOUNT_ID}/whitelabel/{WHITELABEL_DOMAIN}/icon

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/{WHITELABEL_DOMAIN}/icon
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/{WHITELABEL_DOMAIN}/icon', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Fetch

GET /v2/accounts/{ACCOUNT_ID}/whitelabel/{WHITELABEL_DOMAIN}/logo

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/{WHITELABEL_DOMAIN}/logo
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/{WHITELABEL_DOMAIN}/logo', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

Whitelabeling

Whitelabling is one of the core functionality of the Lumian which allows to make your own brand.

Schema

Whitelabel settings

Key Description Type Default Required Support Level
company_name The company name to display to users string() false supported
domain This is the whitelabeled domain that users will be entering to reach the UI string() false supported
fake_api_url This is a whitelabeled API URL, primarily used by the developer application string() false beta
hide_credits When checked this hides the credits boolean() false false beta
hide_powered When checked this hides the powered by Lumian on the bottom right boolean() false false supported
hide_registration When checked this hides the ability to register for a new account boolean() false false beta
inbound_trunks_price The price to show for inbound trunks, this is currently only for display purposes string() false beta
nav.help The URL to use when the help link is clicked string() false supported
nav.learn_more The URL to use when the 'Learn More!' link is clicked string() false supported
nav Properties related to navigation in the UI object() false
outbound_trunks_price The price to show for outbound trunks, this is currently only for display purposes string() false beta
port.authority The account ID(s) to be used for administrating port requests `string() array(string())` false
port.features The URL to use when the features link is clicked string() false supported
port.loa The URL to use when the LOA link is clicked string() false supported
port.resporg The URL to use when the resporg link is clicked string() false supported
port.support_email The support email address to display to the user string() false supported
port.terms The URL to use when the terms and conditions link is clicked string() false supported
port Parameters related to white-labeling port requests object() false
sso_providers ["array(", "[#/definitions/sso_provider](#sso_provider)", ")"] false
twoway_trunks_price The price to show for twoway trunks, this is currently only for display purposes string() false beta

Fetch

GET /v2/accounts/{ACCOUNT_ID}/whitelabel/domains

When you white label Lumian's services, DNS settings are needed to make sure your hostname maps appropriate for the various DNS entries (CNAM, A, NAPTR, etc). If the system admin has configured their settings on the backend, you can query Crossbar to show you what your settings should map to.

You have two options on the request for what domain to use:

  1. If you've already configured your whitelabel domain for the account, the API will use that value.
  2. If you specify domain=some.realm.com on the request, some.realm.com will be used instead.

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/domains?domain=some.realm.com
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/domains', {
  params: {
    'domain': 'some.realm.com'
  },
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}'
  }
});

OR

Sample Request:

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {"domain": "some.realm.com"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/domains
import axios from 'axios';

const response = await axios.get('http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/domains', {
  headers: {
    'X-Auth-Token': '{AUTH_TOKEN}',
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  data: '{"data": {"domain": "some.realm.com"}}'
});

Assuming your whitelabel domain is "mydomain.com" you should receive a payload similar to:

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "A": {
            "us-central.mydomain.com": {
                "mapping": [
                    "166.78.105.67"
                ],
                "name": "Secondary Proxy",
                "zone": "us-central"
            },
            "us-east.mydomain.com": {
                "mapping": [
                    "8.36.70.3"
                ],
                "name": "Primary Proxy",
                "zone": "us-east"
            },
            "us-west.mydomain.com": {
                "mapping": [
                    "8.30.173.3"
                ],
                "name": "Tertiary Proxy",
                "zone": "us-west"
            }
        },
        "CNAM": {
            "api.mydomain.com": {
                "mapping": [
                    "api.zswitch.net"
                ],
                "name": "API"
            },
            "portal.mydomain.com": {
                "mapping": [
                    "ui.zswitch.net"
                ],
                "name": "Web GUI"
            }
        },
        "MX": {},
        "NAPTR": {
            "proxy-central.mydomain.com": {
                "mapping": [
                    "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.proxy-central.mydomain.com."
                ],
                "name": "Central NAPTR"
            },
            "proxy-east.mydomain.com": {
                "mapping": [
                    "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.proxy-east.mydomain.com."
                ],
                "name": "East NAPTR"
            },
            "proxy-west.mydomain.com": {
                "mapping": [
                    "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.proxy-west.mydomain.com."
                ],
                "name": "West NAPTR"
            }
        },
        "SRV": {
            "_sip._udp.proxy-east.mydomain.com": {
                "mapping": [
                    "10 10 7000 us-east.mydomain.com.",
                    "15 15 7000 us-central.mydomain.com.",
                    "20 20 7000 us-west.mydomain.com."
                ],
                "name": "East SRV"
            }
        },
        "TXT": {}
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Here you can see which DNS records are supported and where they should point to access the Lumian cluster.

Testing your domains

POST /v2/accounts/{ACCOUNT_ID}/whitelabel/domains

Lumian will attempt to validate your whitelabel settings if you send it a POST to do so:

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/domains
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/whitelabel/domains',
  '',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}'
    }
  }
);

Similar to the GET, you can include a domain= parameter in the request to test your domains before you create the whitelabel document. A sample response is below:

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
     "data": {
         "A": {
             "us-central.r1.244.com": {
                 "actual": [
                     "{IP_ADDRESS}"
                 ],
                 "expected": [
                     "166.78.105.67"
                 ]
             },
             "us-east.r1.244.com": {
                 "actual": [
                     "{IP_ADDRESS}"
                 ],
                 "expected": [
                     "8.36.70.3"
                 ]
             },
             "us-west.r1.244.com": {
                 "actual": [
                     "{IP_ADDRESS}"
                 ],
                 "expected": [
                     "8.30.173.3"
                 ]
             }
         },
         "CNAM": {
             "api.r1.244.com": {
                 "actual": [],
                 "expected": [
                     "api.zswitch.net"
                 ]
             },
             "portal.r1.244.com": {
                 "actual": [],
                 "expected": [
                     "ui.zswitch.net"
                 ]
             }
         },
         "MX": {},
         "NAPTR": {
             "proxy-central.r1.244.com": {
                 "actual": [],
                 "expected": [
                     "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.proxy-central.r1.244.com."
                 ]
             },
             "proxy-east.r1.244.com": {
                 "actual": [],
                 "expected": [
                     "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.proxy-east.r1.244.com."
                 ]
             },
             "proxy-west.r1.244.com": {
                 "actual": [],
                 "expected": [
                     "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.proxy-west.r1.244.com."
                 ]
             }
         },
         "SRV": {
             "_sip._udp.proxy-east.r1.244.com": {
                 "actual": [],
                 "expected": [
                     "10 10 7000 us-east.r1.244.com.",
                     "15 15 7000 us-central.r1.244.com.",
                     "20 20 7000 us-west.r1.244.com."
                 ]
             }
         },
         "TXT": {}
     },
     "request_id": "{REQUEST_ID}",
     "revision": "{REVISION}",
     "status": "success"
}

You should be able to compare your hosts in each DNS type against the expected values configured by the system admin and adjust your DNS settings as appropriate.

Configuring the Domains (System Administrators only)

System administrators can set/update the domains object that is used when resellers whitelabel the service. The generic format of the JSON object is:

Sample Response:

{
    "{DNS_RECORD_TYPE}":{
        "{WHITELABEL_ABLE_DOMAIN}":{
            "mapping":["{IP_ADDRESS}", "{SRV_RECORD}", "{NAPTR_RECORD}"],
            "name":"Friendly name",
            "zone":"{Lumian_ZONE}"
        }
     }
    }

To set the system domains object, the API is:

POST /v2/whitelabel/domains

Sample Request:

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -d '{"data": {DOMAINS_OBJECT}}' \
    http://{SERVER}:8000/v2/whitelabel/domains
import axios from 'axios';

const response = await axios.post(
  'http://{SERVER}:8000/v2/whitelabel/domains',
  '{"data": {DOMAINS_OBJECT}}',
  {
    headers: {
      'X-Auth-Token': '{AUTH_TOKEN}',
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

If you receive a 400 when POSTing with a response like:

Sample Response:

{
    "auth_token": "{AUTH_TOKEN}",
     "data": {
         "domains": {
             "required": {
                 "message": "The domains schema is missing, unable to validate request"
             }
         }
     },
     "error": "400",
     "message": "invalid data",
     "request_id": "{REQUEST_ID}",
     "status": "error"
    }

You will need to run sup kapps_maintenance refresh system_schemas to ensure the domains schema is available.