The API is based on HTTP REST (Intro to RESTful API Design) with the data returned in json (default) or xml format.
To access the API you'll need the correct endpoint:
https://<company>.leading2lean.com
The <company> value is the name of your LES deployment, and will typically be your company name. All API access is via SSL/HTTPS.
You will also need an API Key to authenticate each API HTTP request. This API Key can be created by Leading2Lean support and must be used by your API client programs. You may also need to cryptographically sign requests for certain API calls. For more information on authentication methods, please see the section on Authentication.
The Leading2Lean LES solution can be integrated to a wide range of external 3rd party systems. These include Enterprise Resource Planning (ERP), Computerized Maintenance Management Systems (CMMS), Manufacturing Execution Systems (MES), Time & Attendance, Document Control, Traceability, and Training solutions. This document describes the most common integrations methods and data (application areas) and for integrating 3rd party applications to the Leading2Lean LES application. You should use it as a planning tool to help identify the areas of integration and the methods/scenarios that most closely correspond to the business process you are trying to support.
Sample Code
You can use the following code examples to help you start using the API. Please send us your submission code for other languages and systems you use, and we'll post it here.
API Best Practices
- Proper URL Escaping
-
Ensure that you properly escape your url's, especially the query argument values. Most major programming languages and their respective http client libraries already do this for you. If you're not using a library, consider finding one to get this functionality. For example, spaces in query argument values must be escaped to '%20'. A query url like:
/api/1.0/lines/?code=Line 33
will fail with a 400 status code as a bad request. It must have the whitespace properly escaped as:
/api/1.0/lines/?code=Line%2033
- Error Handling
- Log and review your error messages. Each API call will return an response object with success field (True/False) and if False an Error or Errors field. Don't rely on the HTTP status code for the success of failure of your API request, since the request might successfully make it to us, but still fail on field validation or backend errors. In these cases we will return an HTTP status code of 200, but with success=False and error="Problem with...".
- Review the Documentation and Test First
- With the API we've setup, you have a lot of power. Be careful, test in the sandbox, make sure you know what you are attempting before you do it in a production site. Read the API documentation carefully so you understand the ramifications of your actions. Feel free to contact support if you have any questions.
- Tread Lightly, Don't DOS your Servers
- It's very possible to create a Denial Of Service (DOS) attack against your L2L servers. A simple error in your integration code can spawn hundreds or thousands of API requests per second effectively bringing the servers to their knees. Be considerate and put a delay between your API calls to spread them out over time, smoothing the impact on the service.
- Create a Separate User / API Auth Key per Integration
- By creating a separate User / Auth Key per Integration you can more easily track down problems to the specific offending integration. It also protects your other integrations from being shutoff if one integration fails in some spectacular way that triggers our support team to disable it remotely by disabling the API key.
Intelligence Throttling and Exponential BackoffIf you start to see HTTP timeouts, HTTP 500 errors, or Rate Limiting Errors from your API calls, it probably means the service is under significant load. Your integration code should be smart enough to throttle itself and start adding delays between API calls to try to lighten the traffic to the server until it recovers. For more information regarding the concept of Exponential Backoff, see https://en.wikipedia.org/wiki/Exponential_backoffUse Pagination when Syncing DataWe have recently enforced a 2000 item limit when using the record area APIs. This is to ensure performance of the service when doing syncing operations. Get to know how the limit, offset, and order_by parameters work. This will make your code much more efficient and scalable when working with large datasets. It will also help with the overall performance of the service. For more information, see the HTTP GET section below.Do Not Assume Data Record Field Ordering Is GuaranteedWhen parsing records, never assume that the order of fields within an individual record will be consistent. For data sequences that represent key/value style dictionaries, in either xml or json formats, the sequences of fields can and will change from time to time. Use intelligent parsing algorithms that use key names to find values instead of field indexes - these data records must be treated as dictionaries, not arrays.
API Properties
L2L allows administrators to customize label names inside the app. This makes possible to customize the software to match the terminology used at a site. We do not change property names in the API, however. The API is, in a sense, a contract that cannot change with system settings. In order to help the developers building integration code, here is a list of localized terms and how they are represented in the API.
Generic Label | Customized Label | API Property |
---|---|---|
Area | Area | area |
Line | Line | line |
Machine | Machine | machine |
Tooling | Tooling | tooling |
Dispatch | DISPATCH | dispatch |
Dispatch Type | Dispatch Type | dispatch_type |
Value Stream | Value Stream | valuestream |
Technology | Technology | technology |
Trade | Trade | trade |
Name | Name | name |
Description | Description | description |
Scrap | Defect | scrap |
HTTP GET
To retrieve record(s), use an HTTP GET request using a URL that looks like this:
https://<company>.leading2lean.com/api/1.0/<record_area>/<id>/?<query arguments>
The URL Query arguments must follow the URL Path and start with a '?', and are <key>=<value> pairs separated with a '&'.
URL Path Parameters:
- company (Required) - This is most likely the name of your company.
- record_area (Required) - This is the record area data you're requesting. (See above)
- id (Optional) - If you want to filter the results to retrieve a single record, specify this optional id value.
URL Query Arguments
- auth=<API_Key> (Required) - This is your authentication (API Key).
-
Optional Query Arguments - You can also filter/limit/order the list by adding key/value query parameters to the end of the url.
-
Proxy User
- realuser=<loginid> - Specify a username for a user that this api call is being made for. This allows the api client to make changes on behalf of the given user. If not specified, the api client user is the one tracked as the user making changes.
-
Formatting
- format=xml - returns the results in xml format instead of json (default).
- l2lfmt_microseconds=true - returns datetime fields formatted to a precision of microseconds rather than milliseconds when using the default JSON format.
-
Filtering
- field=<value> - You can specify a field/value pair to list only results where the field is equal to the value. eg. site=1
- field__lt=<value> - Less Than filtering: add "__lt" to the end of the field name. eg. description__lt=Sandbox
- field__lte=<value> - Less Than or Equal filtering: add "__lte" to the end of the field name. eg. description__lte=Sandbox
- field__gt=<value> - Greater Than filtering: add "__gt" to the end of the field name. eg. description__gt=Sandbox
- field__gte=<value> - Greater Than or Equal filtering: add "__gte" to the end of the field name. eg. description__gte=Sandbox
- field__isnull=<value> - Is Null filtering: add "__isnull" to the end of the field name, and value must be 'True' or 'False'. eg. completed__isnull=False
- NOTE: If using datetime values for lt/gt comparisons (i.e. with DateTimeField's), you should use a UTC date/time in ISO 8601 format like: 2016-05-05T22:39:47 that is url encoded. When using a date value for comparison with a DateField, use a date encoded in ISO 8601 format like: 2016-05-05. You can compare DateTimeField's to plain date strings as well.
-
Pagination
- limit=<value> - Limit the number of records returned. eg. limit=100. Note that there is a max limit for this that varies by the API area, but defaults to 2000. If this value exceeds the max the api call will fail.
- offset=<value> - Starting point for the records to be returned. eg. offset=0 (default)
-
Results Customization
- order_by=<field> - Order the results (ascending) by the specified field. Placing "-" before the field name will order the results in descending order.
- fields=fieldname1,fieldname2,... - Allows you to specify the fields to return as a comma delimited list. The API will display all fields, if you don't provide this parameter.
-
Proxy User
Returns:
By default the API will return a json object with the following fields.
- success - Returns either true or false.
- data - On success it will be a list of records or an individual record.
- error (Optional) - Error message if success is false.
- errors (Optional) - A list of error messages if success is false. Used when submitting data.
- offset (Optional) - If the requested data results in a list, then the current pagination offset (starting count of current page) is provided.
- limit (Optional) - If the requested data results in a list, then the current pagination limit (page size) is provided.
Custom Properties
Some record areas, such as Machines, support adding custom properties to the model. To include the custom properties in the GET response data as filterable fields, a site_id must be included in the request. To filter by a custom property, the query argument must be prefixed by the key word custom__ I.e. custom__property_name.
Record Areas With Custom Property Support |
---|
Machines |
Examples:
Retrieve a list of all Site records (assuming there are 5 total) using the curl utility, using paging logic. Using paging logic is important when retrieving more records than the default maximum limits, and this pattern can be reused with all record areas (but note that you can use a much bigger limit than 2).
First you would run a get with your limit set and the offset set to 0:
$ curl https://example.leading2lean.com/api/1.0/sites/?auth=1234567890&limit=2&offset=0
Which returns:
{ "limit": 2, "data": [ { "description": "Sandbox", "created": null, "site": 1, "lastupdated": null, "timezone": "America/Los_Angeles", "id": 3 }, { "description": "Site 1", "created": "2010-12-30 11:52:50", "site": 43, "lastupdated": "2011-01-10 17:26:47", "timezone": "GMT+4", "id": 8 }, ], "success": true, "offset": 0 }
Since the number of records returned matches the payload limit, there is more data to retrieve. Note that the returned limit may not match the limit you asked for, if the record area supports a smaller limit than what you specified. Make another get call with the returned limit and your offset set to the number of records returned in the previous call:
$ curl https://example.leading2lean.com/api/1.0/sites/?auth=1234567890&limit=2&offset=2
Which returns:
{ "limit": 2, "data": [ { "description": "Site 2", "created": null, "site": 1, "lastupdated": null, "timezone": "America/Los_Angeles", "id": 5 }, { "description": "Site 3", "created": "2010-12-30 11:52:50", "site": 43, "lastupdated": "2011-01-10 17:26:47", "timezone": "GMT+4", "id": 6 }, ], "success": true, "offset": 2 }
Finally make another get call with the returned limit and the total number of records retrieved so far:
$ curl https://example.leading2lean.com/api/1.0/sites/?auth=1234567890&limit=2&offset=4
Which returns:
{ "limit": 2, "data": [ { "description": "Site 4", "created": null, "site": 1, "lastupdated": null, "timezone": "America/Los_Angeles", "id": 9 } ], "success": true, "offset": 4 }
Since fewer records than the limit were returned, you know that you have iterated over all records and can stop iterating.
To retrieve an single Site record:
$ curl https://example.leading2lean.com/api/1.0/sites/3/?auth=1234567890
{ "data": { "description": "Sandbox", "created": null, "site": 1, "lastupdated": null, "timezone": "America/Los_Angeles", "id": 3 }, "success": true }
XML Examples:
Retrieve a list of Sites in XML format:
curl https://example.leading2lean.com/api/1.0/sites/?auth=1234567890&format=xml
<?xml version="1.0" encoding="utf-8"?> <response> <limit>100</limit> <data> <resource> <description>Sandbox</description> <created>None</created> <site>1</site> <lastupdated>None</lastupdated> <timezone>America/Los_Angeles</timezone> <id>3</id> </resource> <resource> <description>Production Plant 43</description> <created>2010-12-30 11:52:50</created> <site>43</site> <lastupdated>2011-01-10 17:26:47</lastupdated> <timezone>GMT+4</timezone> <id>8</id> </resource> </data> <success>True</success> <offset>0</offset> </response>
or an individual Site record:
curl https://example.leading2lean.com/api/1.0/sites/3/?auth=1234567890&format=xml
<?xml version="1.0" encoding="utf-8"?> <response> <data> <description>Sandbox</description> <created>None</created> <site>1</site> <lastupdated>None</lastupdated> <timezone>America/Los_Angeles</timezone> <id>3</id> </data> <success>True</success> </response>
Python example for paging through data
To retrieve a list of all machines, here's some python 2.7 code that uses the requests library to do that paging using the max limit of 2000.
import requests finished = False machines = [] url = 'https://example.leading2lean.com/api/1.0/machines/' apikey = 'fakeapikey' limit = 2000 site = 1 while not finished: resp = requests.get(url, {'auth': apikey, 'site': site, 'limit': limit, 'offset': len(machines)}, timeout=60) if resp.ok: resp_js = resp.json() if not resp_js['success']: raise Exception("api call failed with error: %s" % resp_js['error']) machines.extend(resp_js['data']) if len(resp_js['data']) < limit: finished = True else: raise Exception("Failed request, error: %d" % resp.status_code)
HTTP POST
Use an HTTP POST to create, update, and delete data in the LES system. Due to some clients not being able to submit HTTP PUT and DELETE requests, this method has the ability to Create or Update records depending on whether an ID field is passed as a parameter. For example, if an id is specified, an existing record will be updated. If an id is not specified, then a new record will be created. You can also delete records by passing the parameter "_action=delete" and including the record id as a query argument or a post body argument.
The API uses HTTP POST method standards. Arguments in a POST request will be sent in the body of the request. Similar to HTTP GET requests, arguments in a POST request must be URL Encoded, and the "Content-Type" header will be set to "application/x-www-form-urlencoded." In many cases you can interface directly with the Record Area to add or update records:
URL: https://<company>.leading2lean.com/api/1.0/<record_area>/<id>/?<query arguments>
URL Parameters:
- company (Required) - Your LES deployment name (most likely the name of your company).
- record_area (Required) - The name of the Record Area you are accessing.
- id (Optional) - The id of the Record you are modifying. May also be a POST argument.
URL Query Arguments
- auth=<API_Key> (Required) - This is your authentication (API Key).
- format=xml (Optional) - Returns the results in xml format instead of json.
- _action=delete (Optional) - You can optionally delete a record by passing this parameter. This is useful when your client is unable to perform an HTTP DELETE call (I.e. via browser javascript).
- realuser=<loginid> (Optional) - Specify a username for a user that this api call is being made for. This allows the api client to make changes on behalf of the given user. If not specified, the api client user is the one tracked as the user making changes.
POST Arguments
- id=<id> (Optional) - The individual record to update.
- <field>=<value> (Optional) - specify field/value pairs to set on the record. i.e. site=1
Returns:
By default the API will return a json object with the following fields.
- success - Returns either true or false.
- data - If success if will return either a list of records or an individual record.
- error (Optional) - Error message if success is false.
- errors (Optional) - A list of error messages if success is false. Used when submitting data to provide validation errors.
Python Example
import requests url = 'https://example.leading2lean.com/api/1.0/machines/' apikey = 'fakeapikey' site = 1 resp = requests.post(url, {'auth': apikey, 'site': site, 'code': 'WLD0001', 'description': 'Welder for line 1', 'shortdescription': 'Welder 1', 'linecode': 'Line1'}, timeout=60) if resp.ok: resp_js = resp.json() if not resp_js['success']: raise Exception("api call failed with error: %s" % resp_js['error']) else: raise Exception("Failed request, error: %d" % resp.status_code)
HTTP PUT
Using an HTTP PUT you can update data using the same format/parameters as an HTTP POST request.
HTTP DELETE
Using an HTTP DELETE you can delete data by passing in the ID of the record as described by the HTTP POST documentation.
Authentication
You will need an API Key to authenticate each API HTTP request. Depending on the API, you may also need a Secret Key to cryptographically sign your API requests to ensure it wasn't tampered with. API Keys are linked to user accounts and are subject to the security access of that account. Any actions taken using the API are tracked by that username. The best practice is to create a separate API user for each integration, to make it easier to track down problems with individual integrations.
How to request an API Key
API Keys are created by Leading2Lean support. To request an API Key, first setup the associated API user account under the setup menu. Then submit a request to support@leading2lean.com including the username of the API user and a description of the integration project you are working on.
How to use an API Key
All API requests need to include the API Key. This API Key can be set in an HTTP Header called "L2LAUTH", in an url query parameter called "auth", or in a POST argument called "auth", with the value set to your API Key. For example, if my API Key was "key123" and I wanted to pull a list of areas from the Area API I could use the following URL:
- https://company.leading2lean.com/api/1.0/areas/?auth=key123
What is a Secret Key?
For most normal API access, your API Key is sufficient. But, for specific more security sensitive API calls, you will need a Secret Key to sign requests as an added security measure to ensure the request came from you. Unlike an API Key, a Secret Key is linked to a server name. Your Secret Key should never be passed in an API request. You Secret Key is used to cryptographically sign an API request. Secret Keys are issued by Leading2lean support. Please contact support@leading2lean.com for more details.
How do I use Secret Key to sign an API request?
For most normal API access, your API Key is sufficent. But for specific API calls, you will need a Secret Key to sign requests as an added security measure to ensure the request came from you.
Leading2Lean uses a custom HTTP scheme based on a keyed-HMAC (Hash Message Authentication Code) for authentication. To authenticate a request, you first concatenate selected elements of the request to form a string. You then use your your Secret Key to calculate the HMAC of that string. The output of the HMAC algorithm is the signature. You add this signature as a parameter of the request along with the server name identifing which Secret Key you are using.
When the system receives an authenticated request, it fetches the Secret Key that you specify using the supplied Server name. It then uses it's own copy of the Secret Key in the same way to compute a RFC 2104 HMAC-SHA512 signature of selected elements from the request it received. The system then compares the signature it calculated against the signature passed in the request. If the two signatures match, the system concludes that you must have the Secret Key and the request is valid. If the two signatures do not match, the request is dropped and the system responds with an error message.
For each API call there will be an ordered list of parameters that are used to calculate the signature. This list always starts with the server name (parameter name: _server) associated with the Secret Key you are using, followed by the expiration date in UTC (parameter name: _expiration) of the request in the following format: "YYYY-MM-DD HH:MM:SS". For example: "2016-03-31 13:59:59". Generally, the best practice is to set the expiration to now in UTC plus 15 minutes to ensure the request can't be reused in the future. You must concatenate the values of those parameters, separated by a "\n" or newline, in order to build the StringToSign. Then next step is to calculate the HMAC-SHA512 of the StringToSign to produce a message digest. The message digest is then converted to a Base64 string to produce the signature (parameter name: _signature).
The following is pseudocode that shows the construction of the signature. (In the example, \n means the Unicode code point U+000A, commonly called newline).
SecretKey = "Your-Secret-Key-Goes-Here" server = "Corporate HR Server" expiration = "2016-03-31 13:00:00" username = "johnsmith" active = "True" StringToSign = server + "\n" + expiration + "\n" + username + "\n" + active Signature = base64(hmac_sha512(SecretKey, UTF-8-Encoding-Of(StringToSign)))
HMAC-SHA512 is an algorithm defined by RFC 2104 - Keyed-Hashing for Message Authentication using the SHA512 algorithm instead of SHA1. The algorithm takes as input two byte-strings, a key and a message. Use your Secret Key as the key, and the UTF-8 encoding of the StringToSign as the message. The output of HMAC-SHA512 is also a byte string, called the digest. The Signature is constructed by Base64 encoding this digest.
How can I proxy API calls for other users?
If you want to make API calls on behalf of other users (i.e. the other user is the one who actually makes the changes to records), you can use the realuser parameter. This is available on all API calls, but only when using the auth parameter. Currently, the realuser user must have site access that is equal to or a subset of the api user's site access, and the api user must have admin access in order to use the realuser parameter.