API Overview

The API enables automated access to and management of data and services on user and account scopes. The API is REST-based with a few concessions for security and simplicity:

  1. The API client must use the API "auth" command to get a token to use for successive API calls.
  2. The API authentication code is sent as a secure cookie, rather than using HTTP Basic Authentication. This is because it is not really a username and password and because it is just as easy, if not easier in many cases, to send a cookie.

Contents

  1. API Integrations
    1. Create an API Integration
    2. Edit Your API Integration
    3. Retrieve your API Integration Keys
    4. API Integration Properties
    5. Protect your API Integration
    6. API Access Controls
  2. User and Account Scope
  3. Calling the API
    1. API Requests and Responses
    2. Example API Response
    3. Authentication
    4. Authenticated Requests & signature Cookie
    5. Authentication Revocation
    6. Requests with File Upload
    7. Response Codes

API Integrations

In order to use the API, you must define an "API Integration". This gives you a specific unique API token code, a secret key, and allows you to configure the scope and access that will be granted to programs using your API Integration. Your account can have multiple API Integrations configured so that you can use different codes and access levels for different applications.

Create an API Integration

To create an API Integration:

  1. Login to your LuxSci account as an account administrator
  2. Go to "Admin > API" in the top-left menu
  3. Click on "Add an API Integration"
    1. Enter a name for the Integration
    2. Select a "scope" (i.e., is this for users only and will send a username/password as part of the authentication process or is this for account-level commands and/or commands allowed to perform user-level actions without a username and password?)
    3. Press "Create Integration"

Edit Your API Integration

  1. Login to your LuxSci account as an account administrator
  2. Go to "Admin > API" in the top-left menu
  3. Find the API Integration in question
  4. Click on the "Pencil" icon to view/edit the API Integration details
  5. Update any settings that you need to change.
  6. Press "Save Changes"

Retrieve your API Integration Keys

  1. Login to your LuxSci account as an account administrator
  2. Go to "Admin > API" in the top-left menu
  3. Find the API Integration in question
  4. Click on the "Pencil" icon to view/edit the API Integration details
  5. Save the Host, Token, and Key for use in your application.

API Integration Properties

Your API Integration has a number of properties that determine how it works and what you can do with it.

Property Description
Integration Name Text description of the API Integration for your own use
Enabled? When not enabled, the requests using this API's codes will be rejected.
Scope
  • User: User-level API functions only. Also, a username/password or API token combination must be passed to gain access to these functions for a specific user.
  • Account: Account-level API functions. Optionally, user-level API functions can also be performed without using a username/password.
API Host The host name that your API calls must be addressed towards. Use of other host names will be rejected by the API. The default host is "rest.luxsci.com"; you may have a different API host if you have a dedicated WebMail or API server.
Public Token Unique API Identifier passed along as part of the authentication phase of API usage.
Secret Key Unique code used to verify your access to this API. This should be treated like a very sensitive password and never disclosed or saved in locations that can be viewed by end users. Access to your Token + Key can grant people access to use of your API.
Limits
  • User-level requests/minute.
    Maximum number of user-level API requests/minute this API can make across all users.
  • Account-level requests/minute.
    Maximum number of account-level and global-level API requests/minute this API can make across all accounts.

Free Trial accounts have small per-minute and per-day limits that are intended "just for testing things out".

Accounts using shared services (i.e., shared API/WebMail servers) have larger limits (e.g. 60 requests/minute; 6000 requests/day); these limits can be increased somewhat by support; however, there is a fee for that.

Accounts using dedicated servers, and which have dedicated WebMail, API, and Private Labeling can have their API processed through their own dedicated server; in this case, the API limits can be made very much higher.

Please contact support to inquire about changing your API limits.

Access Controls (See the section on Access Controls, below)

Protect your API Integration

If you create a program that uses your API, you must be very careful of who can access the API Key and Token! E.g., if you create an application and distribute it, people could garner your Key and Token from the API code and then use your API Integration to do whatever your API is allowed to do. This is a shortcoming of any API.

We have some specific recommendations for you:

  1. Do not share your API Key and Token with anyone, if you can avoid it.
  2. If you are making a public application or one that will be distributed out of your control, be sure to only use "user" scope with user name and password authentication required as part of the API authentication process. At least in this way, the API token and key are not useful for doing anything without an additional username and password.
  3. If you can, restrict what IP addresses will have access to your API (see Access Controls, below).
  4. Only grant your API Integration permission to do the things that it actually needs to do – and nothing more. This will limit the scope of possible abuse of your API Integration.

API Access Controls

There are many layers of access controls built into the API. In order for your API requests to function at all:

  • The connection must be over SSL (TLS v1.2+ with very good ciphers)
  • Your LuxSci account must be active (not closed or disabled)
  • Your LuxSci account must have API access granted by Technical Support
  • Your API Integration must exist in your account
  • Your API Integration must be enabled
  • Your connection must be to the assigned API Host
  • Your API credentials (Key and Token) must be correct
  • You must not be exceeding your per- minute or per-day API request limits
  • Your requests must be properly signed (see the Calling the API section, below).

Additionally, you can control access to your API and the functionality of your API in the Access Control area of your API Interface configuration/ edit screen.

IP Address Allow List

The first layer is the IP Address Allow List. This is a list of individual IP Addresses (e.g. 4.2.2.1) or CIDR blocks (e.g. 4.2.2.1/24). When editing your allow list, you can delimit the entries with new lines, spaces, and commas. Here is how it works:

  1. If your allow list is empty, the requests can be made from any IP address.
  2. If the allow list is not empty, then requests can be made only from IP addresses matching at least one of the entries.
  3. CIDR blocks cannot be more general than "/12". E.g. "4.2.2.1/24" is OK but "4.2.2.1/10" is not.

Protected Users

You can optionally select specific users in your account that will be protected from the API. I.e.,

  1. All user-scope API commands targeting these users will be rejected
  2. All account-scope API commands that try to perform account administrative actions on these users will be rejected (this may not include some read-only reporting commands).

User protection is appropriate when you have certain important or administrative users that you want to exclude from API access, or when you do not fully trust your API client.

Scope-Level Command Access

Each API scope contains a number of commands. The API provides opt-in access to these commands (for the most part – some read-only operations that do not involve sensitive data -- are available generally). This allows you to be very specific about what commands your API can perform and protects you from your API "gaining unwanted access" in the future just because new commands have been added to the API.

Please refer to the API documentation for each level of scope to see what sets of commands can be opted into by your API.

User and Account Scope

All API requests, except authentication and authentication revocation, take place in a "scope" with is either "user" or "account".

Scope Description
User

An API Integration whose “scope” is set to “user” must send user authentication information (i.e., username and password) as part of the authentication request. This authentication codes returned can then be only used for user scope commands for the user authenticated. You can use application-specific usernames that have API access (e.g., user@domain.com/api-access) if you have created such in the user security settings pages.

This method of access is appropriate when your application is distributed and you cannot ensure that your API Key cannot be discovered. API calls thus require both that key and the user’s credentials.

This method is also appropriate when you will know the user’s authentication credentials.

All user requests have API URLS that start with: /perl/v2/api/user/[username] where “username” is the login email address (e.g. “joe@domain.com”) of the user with respect to whom the command will apply. The “username” can also be just the user’s unique ID number. Note, while you may use an application-specific username and password (e.g., user@domain.com/api- access) for authentication, the “[username]” in the URL must always be the canonical username (e.g., user@domain.com) or unique ID number.

Account

An API Integration whose “scope” is set to “account” and where you have enabled “Permit user-level commands without username and password” in your API access control settings can also access user-scope commands. In this case, you do not need the user’s authentication credentials and you API client, once authenticated, can make user- scope requests for any permitted user without needing to re-authenticate to change user or scope.

This is appropriate when designing API clients that will manage your account or accounts in an automated manner and where your API Key can be kept safe.

API Integrations explicitly allow one of:

  • Only user-scope commands. Such Integrations also require the username and password of the user involved. Because the username and password are required for API access, it is safe to expose the Integration secret key to third parties. I.e., this type of integration is ideal for use insecure situations, such as in publicly deployed applications or code.
  • Only account-scope commands.
  • both account and user scope commands. This kind of Integration allows you to perform API commands in any scope without needing a user's password. It is the fullest "administrative mode" and should be used with care.

API URLs by Scope

The API endpoint URLs for user and account scope commands differ. This table shows the difference. Note that the rest.luxsci.com hostname used in the examples may be different for your account.

Scope URL
User

https://rest.luxsci.com/perl/api/v2/user/[email]/...

[email] will be the login username/email address of the user in question.

Account

https://rest.luxsci.com/perl/api/v2/account/[account_id]/...

[account_id] is the LuxSci account number for the account in question.

Calling the API

You can skip this section if you are using an SDK. This section provides detailed documentation on the proper way to program your API clients to make API calls to the LuxSci API, how to authenticate, how to invalidate your API session, and how to make calls containing file attachments.

Here are the quick specifics you should know about all API calls:

  • The API is accessed using REST-like calls
  • HTTPS must be used (TLS v1.2+) for all calls
  • UTF-8 encoded JSON is used as the data encapsulation format for both requests and responses
  • The content type "application/json" must be supplied with all JSON request bodies
  • SHA256 HMAC is used everywhere. These HMACs will always be HEX encoded. We will refer to these as just HMAC in the rest of the document.
  • All API calls, except for the authentication calls, must include two cookies. One for your authentication code and one for a request HMAC signature.
  • You have limits on the number of requests/minute and requests/day that your API integration can perform. Plan accordingly.
  • Successful requests will return an HTTP status code of 200-299; Failures will return a code of 400-599.
  • File uploads are performed through multi-part form POST / PUT syntax with additional parameters sent via JSON.

API Requests and Responses

Response HTTP Headers

The HTTP responses include the following response headers:

Content-Type application/json
X-RateLimit-Limit The maximum number of requests permited per minute from this API conifguration.
X-RateLimit-Remaining The maximum number of additional requests permitted during the current 1-minute window before a "429" error will be returned.
X-RateLimit-Reset A timestamp of the "next minute," specified as seconds-since-epoch. I.e., at this time, your "X-RateLimit-Remaining" will reset to the value of "X-RateLimit-Limit" allowing more API requests.

JSON Response Body

All API calls will return a response with a JSON body (and content type "application/json"). These responses can all include the following keywords:

success Value will be "1" on success; "0" on failure.
error_message Appears only in error responses (where success=0). This is a plain text description of the error.
comment Optional plain text message that can occur in successful requests (success=1). This is usually only present if the data keyword is omitted / not needed.
data JSON object containing response data specific to the particular request made
auth New API authentication code. Every success=1 request will send you back a new "auth" code. Each auth code is valid for 15 minutes. By using fresh auth codes, you can continue to make requests without the need to re- authenticate your API client.

Error responses for all API commands will have a consistent format. The HTTP Status code will be somewhere from 400 to 599, where the code number corresponds to the error type. The response body will be standard JSON with the "success" keyword set to "0" and the "error_message" keyword containing a plain text description of the reason for the failure.

Note that it is possible that in some cases the JSON error body would be missing; e.g. in situations where the error involved is unrelated to the API itself and so the API is not involved in the request and cannot return a standard error format. Examples of this could include cases 503 errors from a server being unavailable, bad gateway errors related to proxy load balancers, etc.

Note that Unicode and other characters can be embedded in double quoted strings in JSON response bodies. These should be properly parsed when the response body is converted from plain text JSON to an actual object in your code. Examples of common embedded characters include "n" (for linefeed), "u0073" (for the letter "s" … to prevent the word "script" from showing up in the response, as that can trigger issues with filters). For example you may see in a raw response the string "de u0073cription". Parsed, this becomes the string "description" – but the "s" is quoted in the raw response to limit the appearance of the raw text "script" … which could trigger content filters.

Example API Responses

Successful authentication responses

{"auth":"965823916111409-1426025141-b3486 705691ba0bd15e31292" + "166e3494034c748cb3fdfed0b9d456c647012763","success":1}

Failure to authenticate example

{"success":0,"error_message":"Invalid authentication credentials."}

Authentication

In order to use any API commands, at any level, your client must first authenticate and obtain an authentication code. This authentication code can then be used to make one or more calls to API functions. Your authentication "session" can also be revoked, immediately invalidating all authentication codes received for it (e.g. this is a "log out").

In order to authenticate, you will need your API Token, API Key, and your API Host name. This API request is different from authenticated requests in that you do not need to send a cookie, and the way that the request is signed is different.

Request Method POST
Auth Cookie none
Request URL /perl/api/v2/auth
Request Query String none
Request Body Possible JSON object keywords:
  • token
    Required. Your API Token
  • date
    Required. The current date and time. This can be in "epoch seconds" (e.g. "1426025141") or in a standard date format that will be parsed and converted to epoch seconds. E.g., these formats are all OK: "Wed, 3 Mar 2015 13:12:15 -0400"
    "Wed, 3 Mar 2015 13:12:15 GMT"
    "2015-03-03 13:12:15 -0400"
    "03-Mar-2015 13:12:15 GMT"
    Your date and time (clock skew) must be no later than 15 minutes behind and no more than 1 minute ahead of our server time, or else the request will be rejected.
  • signature
    Required. HMAC using your API Key as the HMAC key.
    signature = HMAC( text_to_sign, API KEY) text_to_sign = token + "n" + date + "n"
    If you are sending username and password information in this authentication request, then:
    text_to_sign = token + "n" + date + "n" + user + "n" + pass + "n"
  • user
    Optional. If you are authenticating into "user" scope with an explicit username and password, include the login email address of the user in question. E.g. "joe@domain.com"
  • pass
    Optional. If you are authenticating into "user" scope with an explicit username and password, include the plain text password for the user.
Success Response Standard JSON response. No "data" keyword will be sent. See the "auth" keyword for your new authentication token.

Example Authentication Request

POST https://rest.luxsci.com/perl/api/v2/auth
Content-Type: application/json

{ "token": "pJsvioyq8LvtIthmqn8k1u4z0wbpnKwqotupx5DB1aM", "date":"1426025141", "signature": "1428c39c8d1893e4cab65181e6fe09ca36c3a18b5718c1df79b97f24c0c92c6e" }

Example Success Response

HTTP/1.1 201 Created
Content-Type: application/json
Pragma: no-cache
Server: Apache
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1668889680

{"auth":"965823916111409-1426025141-b3486705691ba0bd15e31292" + "166e3494034c-748cb3fdfed0b9d456c647012763","success":1}

Authenticated Requests & signature cookie

Authenticated API requests include all non-authentication requests. These work similarly to authentication requests, described above, except that you must also send along a cookie named "signature". This cookie validates the request. The content of this cookie is defined as follows:

signature: auth_code:signature_code

auth_code: a currently valid authorization code received from an Authorization API call, or from another successful recent API call.

signature_code: HMAC( text_to_sign, API Key)

text_to_sign: auth_code + "n" + request_method + "n" + request_path + "n" + query_string + "n" + request_body_hash + "n"

request_method: the HTTP request method being used, in all upper case. E.g. "GET" or "POST" or "DELETE"

request_path: The portion of the request URL after the protocol and hostname, but before the ? and query string, if any. E.g. "/perl/api/v2/auth"

query_string: The portion of the request URL after the "?". If you are not making a request that includes a query string, then you should treat this value as an empty string.

request_body_hash: If your request does not include a JSON request body, then you should treat this value as an empty string. Otherwise it is a SHA 256 Digest of the complete JSON request body:

request_body_hash: SHA_256_HEX( Trimmed JSON_Request_Body )

Trimmed JSON Request Body: the raw JSON Request Body content in the request with all leading and training white spaces removed (e.g. spaces, tabs, carriage returns, and line feeds before and after the actual JSON content).

For an example of this, see the next section: Authentication Revocation Requests.

Authentication Revocation

To revoke your authentication session and invalidate all authorization codes received, you need to send an authenticated API request as follows:

Request Method DELETE
Auth Cookie required
Request URL /perl/api/v2/auth
Request Query String none
Request Body none
Success Response Standard JSON response. No "data" keyword will be sent.

Example: Authentication Followed by Revocation

Authentication request

POST https://rest.luxsci.com/perl/api/v2/auth
User-Agent: Doctor
Content-Type: application/json

{"signature":"67e2873a262d64bbdf06dacee633698683362df81b979c30b3f063dd1738c9a6","date": "1426087957","token":"pJsvioyq8LvtIthmqn8k1u4z0wbpnKwqotupx5DB1aM"}

Note: signature = HMAC( "pJsvioyq8LvtIthmqn8k1u4z0wbpnKwqotupx5DB1aM" + "\n" + "1426087957" + "\n", API_KEY)

Success response

HTTP/1.1 201 Created
Pragma: no-cache
Content-Type: application/json

{"auth":"151-1426087958-34ca90493592726104b237e98d8129fe8626f181e38f502fa2b99dc066e72298","success":1}

Authentication Revocation

DELETE https://rest.luxsci.com/perl/api/v2/auth
Cookie: signature=151-1426087958-34ca90493592726104b237e98d8129fe8626f181e38f502fa2b9 9dc066e72298:fd66fc402b9c58f136105d768225a67dec4b8eeb756743bbca35c2caca0fd57f

Note: signature = "151-1426087958-34ca90493592726104b237e98d8129fe8626f181e38f502fa2b99dc066e72298" + ":" + signature_code
signature_code = HMAC("151-1426087958-34ca90493592726104b237e98d8129fe8626f181e38f5 02fa2b99dc066e72298" + "\n" + "DELETE" + "\n" + "/perl/api/v2/auth" + "\n" + "" + "\n" + "" + "\n", API_KEY)

Successful revocation

HTTP/1.1 200 OK
Pragma: no-cache
Content-Type: application/json

{"success":1,"comment":"Authentication session revoked."}

Requests with File Upload

Some authenticated API requests allow you to include file attachments. When including files in your POST or PUT request, the HTTP syntax of the request is different: it uses a "multipart/form-data" content type instead of an "application/json" content type.

In particular:

  1. The request body must use a "multipart/form-data" content type instead of an "application/ json" content type.
  2. The JSON payload must be uploaded as one of the "parts."
    1. The content type of that part should be "application/json"
    2. The "name" for the part must be "json"
    3. The filename should be "json.js"
    4. The content should be binary-encoded (not base64 or quoted printable) and be UTF-8.
  3. Each file uploaded will be additional, separate parts of the multipart upload
    1. The content type should be appropriate for the file
    2. The name for the part must be "files" (the name will be the same for all files uploaded in the same request).
    3. The filename for the part must be the filename of the file.
    4. The content should be binary-encoded (not base64 or quoted printable)
  4. In the JSON payload, you must include a top-level keyword named "attachments". This is an array of JSON objects, one per uploaded file. These JSON objects contain the following keyword:
    1. "name" – This must match exactly the "filename" for one of the uploaded attachments. We do not current support uploading multiple attachments with the SAME name in the SAME request.
    2. "hash" – This is a SHA256 hex-encoded digest of the uploaded file. We will check this hash against a hash of the file for validation before we process your request.

Example of a File Upload Request

Here is an example SecureLine Send request that includes a simple text file as an attachment. The signature cookie is truncated to save space.

POST https://rest.luxsci.com/perl/api/v2/user/user@domain.com/email/compose/secureline/send
Content-Type: multipart/form-data; boundary=-----------------LUXSCI-363789.496459765---
Cookie: signature=1623-1454003053-...a36d376c86313fbf65d8a
-------------------LUXSCI-363789.496459765---
Content-Disposition: form-data; name="json"; filename="json.js"
Content-Type: application/json

{"attachments":[{"hash":"975867324207d82a1d1e01fa2b6e582907756e737aa7771d92b17609e41 bce16","name":"test-attachment.txt"}],"from_address":"bob@doctorbob.com","subject":"SecureLine message test","no_tls_only":1,"body":"Message body","to":["user@test-domain.com"],"from_name":"Dr. Bob","receipt":1,"body_type":"text"}
-------------------LUXSCI-363789.496459765---
Content-Disposition: form-data; name="files"; filename="test-attachment.txt"
Content-Type: text/plain
File Content!!
-------------------LUXSCI-363789.496459765-----

NOTE: If you are crafting these requests by hand, remember that you must use "\r\n" (i.e. char(13) char(10) ) as line terminators at the ends of the boundaries, the MIME headers, and blank spacer lines between MIME headers and your raw/binary attachment content.

Response Codes

The LuxSci REST API uses standard HTTP status codes to indicate the success or failure of requests. The codes commonly returned by the API and their meanings are below. In many cases, the API will also return a JSON object with a descriptive error string which provides more specific information.

  • 200 — Success
  • 201 — Success; Something was created
  • 400 — Bad request. I.e., could be a validation error.
  • 401 — Unauthorized. I.e., no permission to make the request.
  • 403 — Forbidden. I.e., you have reached a service rate limit, or the action can not be completed for reasons other than permissions.
  • 404 — Not found. I.e., you are attempting to delete an object that does not exist.
  • 405 — Bad method. I.e., no such API endpoint.
  • 408 — Timeout. The API request took too long to complete.
  • 409 — Conflict. I.e, trying to make an object that already exists.
  • 417 — Expectation Failed. Ensure that your request does not send an "Expect" HTTP header.
  • 426 — Upgrade. The request can not be completed without your account first being upgraded.
  • 429 — Too many. I.e., you already have too many objects of the specified type.
  • 475 — Incomplete request. The back-end API server did not reeive the complete API request payload for some reason. This request should be re-tried, possibly after a short delay.
  • 500 — Internal Error. Something went wrong on the server side. The error would be logged and visible to support.
  • 501 — Not Implemented. The request has not yet been fully implemented.
  • 503 — Service Unavailable. The request could not be completed because, for example, your server is down/could not be reached.