This page is the starting point for developers creating integrations with InEight. This page contains information about InEight APIs, where to find them, how to use them, what security and authentication measures are taken, and other general information that is common to all cloud application APIs.
This page does not cover specific details of each API, such as data elements, structure, prerequisites, and usage descriptions. For those details you will need to reference the InEight Integration Catalog that cover the specific APIs.
While integrations for InEight are designed to be agnostic of who or what system will be using them from outside of InEight, many of them are dependent on specific business requirements for the flow of data. For example, data exposure about time worked for employees and equipment only occurs when specific activities are performed in the UI that indicate the data for those records has reached a specific state. It is important to note here because each integration might require attention to when and how it can be used. Any pertinent information regarding how, when, and what rules apply to the use of an integration are covered in the integration’s specification document.
InEight integrations are comprised of either of the following:
External Integration APIs
Request and Responses are in Asynchronous in nature
Primarily used to push data to InEight Platforms
Some Get APIs are exposed for retrieving the data from InEight Platforms
All data is expected or provided in JSON data packages
All are managed through our API Management Portal
InEight supports integrations at any interval required by a customer or third-party application. However, the actual frequency of an integration should be determined by business need and amount of data being transmitted at any one time. For example, updates to Employee records could be sent to InEight in near real- time based on events such as hiring or termination that occur in an ERP or HCM system because these are infrequent and will update a very small set of records at a time. Updates to cost items for actuals, forecast, or budget have a much larger payload (potentially tens of thousands of records) and can result in better performance if it occurs either on demand by a user in InEight or at scheduled off-hour intervals.
When creating, or using integrations for InEight, discussion of the individual integrations, payloads, and frequencies must occur to determine the best impact to all systems.
InEight generally does not perform transformation or mapping of any data due to the number of unique cases and needs across different customers. Data provided to external systems from InEight will be representative of the raw data in the InEight’s business operations. It is generally the responsibility of the customer receiving systems, or a middle tier of an integration to handle mapping and transformation logic.
InEight integrations provide all data through externally (to InEight) accessible APIs that produce records in a JSON format. Transformation of records from JSON to other formats (e.g., XML, CSV, etc.) must be handled by the customer via a business logic application such as SAP Process Integrator, Azure Logic Apps, Microsoft BizTalk, Dell Boomi, or other proprietary logic/code.
Transformation of data in a field from one context to another (e.g., a set of statuses that must be converted to a true/false flag) and mapping of customer data to an integration field between a customer system and the output from InEight APIs or expected input to InEight integrations must be handled by the customer’s integration system or middleware.
Business logic required to conform data, fill fields, or evaluate specific data conditions must be handled by the customer. Some examples of business logic are as follows:
Providing default data when a field from InEight is left blank.
Evaluation of one or more data fields to determine an expected end result. For example, if an employee has an active status AND does not have hours, set an "at work" field to false.
InEight takes into consideration the "system of record" for all stored data. When the data originates from sources that are external to the InEight cloud platform, a unique identifier is required to be provided by the external system for each record to ensure a common value can be used when exchanging, creating, or updating records through integrations. InEight integrations commonly refer to this field as "SourceSystemId".
When managing "Master Data" (data that is normally static and used to classify or provide dimensional data to transactions), it is standard practice for each InEight entity to contain the following types of fields:
SourceSystemId: The unique identifier that will be used to match records exchanged in integrations. This field is not normally displayed in the InEight UI.
Natural Key: This is also a unique identifier, but one that is recognizable by users and is always displayed in InEight UIs. In InEight integrations, this field may be labeled as "DisplayId", or "Name", or have a unique label to accommodate specific usability concerns (e.g., Email is used as the natural key for Users).
InEight separates the concept of record identification into these two fields to allow the possibility of using both a system generated identifier such as a GUID as the SourceSystemId to make system-to-system interactions easier, while also using a human recognizable unique value for all business processes handled through user interaction. In cases where an external system uses a natural key as the system-identifiable unique value, it is acceptable to set the SourceSystemId and Natural Key to the same value.
The following example shows the JSON format for the Master Data entity "Cost Centers". In this example, there is a specific field called "SourceSystemId" and a field called "CostCenterDisplayId" that represents the Natural Key that will be used in UIs.
When the records were created the API request may have contained several records at once and appeared as follows.
When the integration is processed, records appear in the UI as follows. Notice that "SourceSystemId" is not shown, and the "CostCenterDisplayId" field is renamed to "ID" in the UI.
If a record needs to be updated, the original SourceSystemId of the record will need to be provided as reference. The below JSON shows the "Project Operations" Cost Center being deleted as an option from Cost Centers.
Several InEight integrations represent the union of two or more entities to create an association of master data. The following example uses the Project Craft integration which allows customers to determine which Craft records will be allowed for use on a specific Project.
In the ProjectCraft JSON, "ProjectId" is the "SourceSystemId" of the Project entity, and "CraftId" is the SourceSystemId of the Craft entity.
Each integration contains a field labeled "SourceSystemName", which is meant to help InEight understand the general source of the data being received. No actions are taken with this information, but it can be useful at a later point to help determine specific system behaviors attached to the incoming data. Work with the InEight Implementations or Professional Services team to establish a unique value that should be contained in these fields for your specific implementations of InEight integrations.
InEight uses restful APIs that are gated by the InEight API Management Portal (APIM). This allows InEight to manage security, version control, and external data schemas in a single endpoint, thereby reducing customer disruption as our products and integrations evolve. To access the InEight APIM Developer Portals, use the following link: https://portal-apitest2.ineight.com/
Go to the InEight APIM Home page (Use above link), and then click Sign up to send a request for access to InEight API. After you have access you can see a list of available APIs, view API details, obtain an export of the API Definition, plus more.
When sending integration request messages for InEight APIs, the following URL convention is used. Specific URL addresses for each API can be found in the API’s details in APIM.
https://apitest2.ineight.com/{api name}/{operation name}/{method | GET parameters}
All API requests made through APIM will require the presence of a Subscription Key in the header of the request.
The following instructions assume that you have been granted access to an APIM environment. If you have multiple APIM environments (Test & Production), these procedures must be followed in each one where you want to test or use APIs.
To obtain a Subscription Key, on the Products page select External Integrations, and then click Subscribe by providing a name.
After the subscription keys are generated, they can be copied from your APIM Profile. Your Profile page can be accessed from the top right corner of the APIM page. The screen below shows the opened Profile page and previously generated subscription keys.
All InEight APIs require specific information to be contained within the header of the request message. The following table provides information about the headers.
API requests made to InEight require two authorizations.
Ocp-Apim-Subscription-Key: The first is your authorization to use APIM, which is handled by creating an account in InEight’s APIM portal, subscribing the account to Products in APIM, and then passing the generated Subscription Key in the "Ocp-Apim-Subscription-Key" header of the API request. This is described in more detail in the "Generating an APIM Subscription Key" section of this document.
Authorization: The second authorization is done in two parts. Part one is authentication of an Active Directory account associated to a User in InEight. Part 2 is authorization of that User to perform actions in InEight. Both parts of this authorization are handled by passing a Bearer Token in the "Authorization" header of an API request. The Bearer Token is used to authenticate an Active Directory account, look up the User associated to that account, and determine if the User has the appropriate permission in InEight.
The Bearer token is acquired by sending an HTTP request as follows:
If the request is successful, the response looks something like this:
For subsequent API requests to InEight, pass the returned <AccessToken> value in the Authorization header. The AccessToken is typically good for one hour. You can determine the exact time the token will expire by taking the value for "expires_on" and adding that number of seconds to Jan 1, 1970 12:00 AM GMT. When that time nears or passes, you should make the request to acquire a new token before making additional API calls to InEight.
Several InEight integrations allow customers to retrieve data using a Get method on the API request. Most of those integrations use an asynchronous pattern for processing the data as outlined below. Any integrations that support a Get method that do not follow the asynchronous pattern will have their processing method described in detail within their integration specification.
1. When a Get request is received by the InEight API it first validates the request matches the expected JSON payload criteria. If it passes, a response of 202 Accepted is returned to the caller along with an element labeled as Location that provides the URL address where the requested data can be retrieved.
a. If the requested data is less than 1kb, the location URL will contain the data in JSON format.
Example for Countries_Get using $filter=startswith(Name, ‘Be’)
b. If the requested data exceeds 1kb, a file containing the requested data in a JSON format will be placed in the URL.
c. If no data was able to be found based on filter criteria ($filter) added to the request, the location will contain a message stating: "No Result Values".
2. The actual processing of the request and any additional OData or parameterized query information is then handed off to the business logic of the InEight application that owns the data entities involved. It can take anywhere from milliseconds to a couple of minutes for the application to fulfill the request and place the data file in the expected "Location".
3. During this processing time, customer systems poll the "Location" URL to see if the file is available.
a. If it is not available, the poll request will return a response of "202".
b. Once available, the response will change to "200" and the file will be included.
InEight API endpoints return a 202 Accepted response rather than a simple 200 OK response. The 202 Accepted response includes a Location header with a URL that customers can call periodically, which will return either another 202 Accepted response indicating that the import is still in progress, a 200 OK response with an optional result payload indicating the import completed successfully/partially, or a 4XX/5XX response indicating that the import failed or timed out.
The Location header endpoint (https://{tenant- prefix}.hds.ineight.com/externalsystem/messages/status?messageId={messageId}) could return the following responses:
InEight APIs that follow this pattern make all the data for the requested entity available at any time. For instance, a request can be made at any time to retrieve every Employee or Equipment resource available in the customer’s InEight environment. However, to prevent an unnecessarily large request from processing each time, some APIs require, or optionally ask for specific query parameters to filter the data to a smaller subset.
To prevent accidental overloading of system resources, GET requests that follow this pattern will return a maximum number of records each time the request is made. To return all available records for an entity, it is up to the customer’s systems to make as many requests as necessary (using the OData $skip parameter) until all records are received.
All APIs provided by the Core application that support a GET method include additional information about record counts when the OData $count parameter is included and set to "true" in the request. This additional information should help developers using the Core APIs to determine how many records they should expect to receive based on their request parameters and determine how many times they might need to make subsequent requests using $skip before all records are received.
The additional record count information includes:
@odata.count – The count of all records in the entity
QueryCount – The total number of records that should be returned from the entity based on any OData filters or other criteria added to the request.
StartingRecord – Based on the QueryCount and any "$skip" request, determines which record number is represented as the first record within the response data.
EndingRecord - Based on the QueryCount and any "$skip" request, determines which record number is represented as the last record within the response data.
Examples
Example 1 - No specific $filter criteria
Customer requests data using the "Regions_GET" API.
Core contains 3,531 records for regions (returned as "@odata.count")
Core provides the first 1,000 records to the requester as the returned data set
The JSON payload contains the following information:
The customer now knows that there are 3,531 total records to retrieve, and they have 1,000 of them so far...
"@odata.count": 3531
"QueryCount": 3531
"StartingRecord": 1
"EndingRecord": 1000
Customer makes another request using "Regions_GET", but specifies a $skip value of 1000
Core provides the next 1,000 records as the returned data set
The JSON payload contains the following information:
"@odata.count": 3531
"QueryCount": 3531
"StartingRecord": 1001
"EndingRecord": 1000
The customer continues to make requests using $skip until the EndingRecord reaches 3531.
Example 2 - Customer uses $filter criteria
Customer requests data using the "Regions_GET" API with $filter=startswith(CountryISOCode, "C")
Core contains 3,531 records for regions (returned as "@odata.count")
After applying the filter there are still 1,212 records that meet the request of the customer
Core provides the first 1,000 records to the requester as the returned data set
The JSON payload contains the following information:
"@odata.count": 3531
"QueryCount": 1212
"StartingRecord": 1
"EndingRecord": 1000
The customer now knows that there are 1212 total records to retrieve, and they have 1,000 of them so far...
Customer makes another request using "Regions_GET" with the same $filter criteria and specifies a $skip value of 1000
Core provides the next 1,000 records as the returned data set
The JSON payload contains the following information:
"@odata.count": 3531
"QueryCount": 1212
"StartingRecord": 1001
"EndingRecord": 1212
The customer does not need to make any more requests because the EndingRecord now matches the QueryCount.
The following example show the record counts after setting the $count parameter to true and setting the $top parameter to 1.
{
"@odata.context":"https://apitest.ineight.com/integrations/$metadata#Trades",
"value":
[
{
"TradeDisplay":"AD",
"TradeDescription":"",
"IsActive":true,
"SourceSystemId":"AD",
"SourceSystemName":null
}
],
"@odata.count":853,
"QueryCount":853,
"StartingRecord":1,
"EndingRecord":1
}
Using the same data set and setting the $count parameter to true and $skip to 300 results in the following record counts (note that each request returns 500 records for this API):
"@odata.count": 853,
"QueryCount":853,
"StartingRecord":301,
"EndingRecord":800
Again using the same data set; setting $count to true and $filter to "startswith(TradeDisplay, 'A')" results in the following record counts:
"@odata.count":853,
"QueryCount":40,
"StartingRecord":1,
"EndingRecord":40
The following example shows the "happy path" that would be followed when retrieving data for an entity and does not include any handling of error conditions. In this example the API returns 500 records at a time and the entity being queried has 742 records.
When InEight receives records within an integration, the following actions are performed:
Look for a match on the SourceSystemId field.
If a match is found,
If IsActive is set to true.
Update the matching InEight record with the values provided in the integration record.
If IsActive is set to false.
Soft delete the InEight record.
If a match is not found,
Create a new record in InEight using the values from the integration record.
Send a record via the integration that does not have an existing match on the SourceSystemId field, which is identified in each integration specification.
Send a record via the integration that has a match on the SourceSystemId field with the values that have changed for the record in the full payload. When using a POST to update a record, the full payload is expected and therefore if a field is not passed, it is considered empty and is updated accordingly. To update only specific fields in a record, such as for Employees, use a PATCH API.
InEight does not permanently delete records from the database. Records are instead flagged as "Inactive" to hide them from system functionality. This allows the records to remain intact to preserve referential integrity where needed.
To remove a record, send the record via the integration that has a match on the SourceSystemId field and set the IsActive field to false.