Concepts
General Documentation Information
Data Types
The api is written in C#, and as a result the data types in the documentation refer to C# data types. These map cleanly to similar types in other languages and generally do not need expanded upon. The only exception is the ?
operator. This indicates a value may be null. So for example, if a data type is specified as bool?
that indicates that the value may be null, and if it is not, then the type will be a bool. This also indicates that the api will accept a null in this field.
API Access Keys
The Vendor API uses AWS api keys to control access to the api and to manage rate limiting. If you do not have a token, email us!. To use your key all your requests MUST include the header x-api-key: your-aws-api-key
. We have a route to help you test that this is setup properly, see the API key test route for more information.
If you do not have an API key, perform the following:
- Create one or more User(s) within the Organization that requires API access.
a. We suggest
api@organizationName.com
for the login email. b. Set a strong password - the password for this user will be used in conjunction with an organization specific API key to authenticate against the API. - Contact api-request@vendnovation.com and provide your Organization Name and the login email(s) of those users you wish to enable API access for.
- VendNovation will enable API access for those Users and send you your Organization specific API Key.
Usage Plan Details
All new API access keys are limited to the following restrictions:
Limits | |
---|---|
Rate | 20 requests per second |
Burst | 100 requests |
Quota | 5,000 requests per day |
Proper call optimization should allow you to stay within the limits. Remember that all calls support retrieving multiple data sets by ID simultaneously.
API Stages
The Vendor API has multiple deployments you can access. It is recommended you use the most current version of the API. The stages available currently are:
Stage | Description |
---|---|
rc | An early access deployment of the next release. Code deployed to this stage can be expected to work. If the release contains breaking changes, those changes will generally be posted here one month before full deployment. Non-breaking changes may be posted closer to the full deployment date. |
v1 | The original production release of the api. |
v2 | A large rewrite featuring standardized request bodies and an enhanced feature set features. Highlights are templating and role/rights support. |
v2.1 | A large non-breaking enhancement of v2. Additions include session management and support for uploading files and linking them to various records. |
It is our goal to have the APIs exist in perpetuity, even when they are no longer receiving bugfixes. However, should a security-related issue be discovered that cannot be fixed in a non-breaking way, you will be given as much time as possible to migrate to a newer, secure version of the API.
Data Retrieval and Filters
{
"requestFilter": {
"organizationIds": [1],
"siteIds": [1, 4, 6]
}
}
The Vendor API is not structured like a standard REST API (parent-resource/{parent-resource-id}/child-resource) and is instead built around bulk operations and achieving as much as possible in a single request while still having resource driven routes. To support this goal, api urls are flat. Even though accounts are a logical child resource of organizations, they are accessed at <base-url>/accounts
. As the change in URL structure would imply, you are able to retrieve accounts from multiple parent organizations at the same time. The default behavior of the API is to send you all the data about the specified resource type that you have access to. So if I have access to two organizations, and I call GET /machines, the api will return a result set of every single machine in both organizations at once.
As this is often too broad for a given use case, the API is filter driven. Almost every request accepts the standard request filter (found here) and supports multiple filters at the same time. In addition to the standard request filter, some route specific filters are also available and links to them will be found in the routes where applicable. Returning to our machines example, It could be narrowed down to only just the machines at any of the specified sites that also belong to a specific organization (see the example to the right). To get a single (or a few specific machines) you would only specify machineIdentifiers. These principles apply to all routes in the API.
It is worth noting that not only retrieval is a bulk action. Every call in the api supports bulk actions across multiple tiers of parent resources. All bulk actions are treated as atomic sets, so if one element in your action fails, the entire set will be rolled back to it's initial state, and you will be notified of such, with as helpful an error message as can be provided.
DateTime Handling
Unless otherwise stated, all DateTimes are in UTC.
A Note on Time Zones
When working with DateTime filters, there are additional considerations regarding timezones. The DateTimes in our systems are stored in the local time as configured for the site/machine. The stored time for things like transactions report is the local time reported by the machine. As a result, to make working with DateTimes easier if you are not using the website for data tracking, you may consider configure your sites to be UTC.
StartDate and EndDate Clamping
When you provide a StartDate and/or EndDate in your request filter, the dates you provided will not necessarily be the dates used in your request. We apply a series of clamping rules to your DateTimes to restrict your maximum search range to no more than the maximum range (which varies between calls as performance allows) as well as generate either or both DateTimes if they were not both provided. The maximum search range varies from route to route so please refer to the individual route documentation for this info.
Here is a breakdown of this to help you understand how this works
StartDate Provided | EndDate Provided | Result |
---|---|---|
Yes | Yes | Your end date will be modified if the time span between provided start and end is larger than the maximum range |
Yes | No | Your end date will be set to maximum range number of days from your start date |
No | Yes | The start date will be generated as your end date minus range maximum |
No | No | End date will be generated as DateTime.Now , and your start date will be range maximum days earlier |
When applicable, the API response will include additional headers to help you detect when we have modified date-time search parameters.
search-range-start-date
- will contain the modified search start date and time, if applicable.search-range-end-date
- will contain the modified search end date and time, if applicable.
Also of note, the time portion of the DateTime is optional. If omitted, StartDate will be given a time of 00:00:00
and EndDate will be given a time of 23:59:59
.
Pagination
Paging is universally supported for get requestsin the API. Page parameters are specified by headers.
Header | Default Value | Meaning |
---|---|---|
page | 1 | Which page of results are you requesting. |
size | 50 | How many results per page would you like to retrieve. Will be clamped to 2000 if set higher. |
The response's headers will include the paging information we used while processing the request as well as the total number of pages available to be retrieved.
Header Name | Meaning |
---|---|
page | The page of data returned by the API. |
size | The page size the api used while processing your request |
total-pages | How many pages of data are available to be retrieved. |
Standardized Request Body
{
"requestFilter": {
// Request filter values, if any, go here.
},
"routeFilter": {
// Route filter values, if any, go here.
},
"body": {
//Your data, if any, goes here. For updates and creates this is an array of objects, not a single object
}
}
Requests made to the api have standardized JSON keys to ensure uniformity of interaction with the API.
- The request filter is to be assigned to a key named
requestFilter
. - Any route specific filter is to be assigned to a key named
routeFilter
. - Any data models you provide to the api (eg for creates or updates) is to be assigned to a key named
body
.
To simplify making requests, you can put this in every request body (sans comments) and add data on an as needed basis.
Error Handling
{
"code": "DATA_ACCESS", // programatically useful error code
"message": "You tried to update one or more sites outside of your organization. No changes have been made.", // human friendly error message
"pointers": [1, 4, 7], // an identifier used to find the problem records in your payload
"pointerName": "Site.Id" // a string that tells you what field the pointers represent
}
When the api encounters an error, it will send back a list of error objects like the one seen in the sidebar. These objects should make it easy to programmatically detect and adjust for errors. Below is a table documenting these common error codes. If a route can have other errors not found in this table a similar table will be found in that route's documentation.
HTTP Code | Error Code | Definition |
---|---|---|
401 | API_TOKEN | Your api token was not present or has expired. Call /authenticate to get one. |
403 | NO_API_ACCESS | You do not have api access. Please contact vendnovation. |
500 | ACCESS_ERROR | Something went wrong while trying to retrieve your data access permissions. |
403 | DATA_ACCESS | An operation was requested for resources outside of your organizations. |
500 | UNKNOWN_ERROR | Something went wrong that may be a problem on our end. Reach out to vendnovation support. |
400 | GENERIC_ERROR | We encountered an error processing your request. |
400 | UNEXPECTED_RESULT_COUNT | Fewer rows were affected by your request than expected, changes may have been made elsewhere. |
404 | MISSING_RECORD | One or more of the specified records could not be found. |
421 | MISSING_REQUIRED_DATA | Your request was serializable but was missing some data required by the business logic. Please refer to the documentation. |
400 | DESERIALIZATION_ERROR | One of your fields could not be deserialized to the correct type. |
400 | INVALID_DATE_RANGE | Your date range's start date was after its end date. |
400 | DATE_PARSING | One or more of your dates was unparseable. |