Making A Request
All requests are authenticated and authorized. This requires a few pieces of information from the client. The first two are a user’s private and public API keys. Both keys are found within the Administration-User portion of the system application. They will appear as long alphanumeric strings (128 characters). The private key is used to generate a HMAC of the request while the public key is used by the service to confirm the authenticity of the signature.
Generating the HMAC is dependent on the programming language used to make the request (most languages have readily-available libraries for generating the code). For this service, using the SHA-512 algorithm is expected when generating the code. In addition, the message to code has a specific format and uses the following pieces of information:
Request method (i.e. GET, POST, etc.)
Full URI path of request (including the domain and any query string parameters)
Please note that the full URI path will always be of the format: [DOMAIN].api.adorbit.com where the domain is the instance name for the given customer account. For instance, if the user were to access the Ad Orbit system by accessing stage.adorbit.com, then the domain is ‘stage’ and the root URI for the API’s will be found at stage.api.adorbit.com.
Proper case is very important. This means “GET/companies” produces a very different result than “get/companies”. In addition, the latter version will never produce the expected result because HTTP methods need to be upper-case. Once a signature is generated, the request method and URI cannot be changed prior to sending the request. This will result in a signature mismatch.
Here’s a PHP example of how to generate the signature:
<?php // Key abbreviated for clarity. $privateKey = 'a983928347a9dsf87...'; // Note the use of a new line between the HTTP method and URI. $message = "GET\nhttp://path/to/service/"; // While not directly related to the HMAC, we need to encode the signature to avoid any non-alphanumeric character issues. $signature = base64_encode(hash_hmac('sha512', $message, $privateKey));
Here is a Python example of how to generate the signature:
import requests import hmac import base64 import hashlib #api base url SERVER_URL = 'https://YOURINSTANCE.api.adorbit.com' #add the endpoint to the url endpoint = SERVER_URL+'/companies' #set public and private keys public_key = '0123456789abcdef.............' private_key = 'fedcba9876543210' #build the message variable, includes request type and endpoint message = 'GET\n'+endpoint #build the signature signature = hmac.new(bytes(private_key,'UTF-8'),message.encode(),hashlib.sha512).hexdigest() #encode the signature encoded_signature = base64.b64encode(bytes(signature, 'UTF-8')).decode() #build and send the response response = requests.get(endpoint, headers={'Accept': 'application/vnd.adorbit.companies+json;version=1.0', 'Authorization': 'ADORBIT '+public_key+':'+encoded_signature} ) print(response.content)
or if using a tool like Postman with JavaScript:
Pre-Request Script: var msg = "GET\nhttp://<URI of services>"; var pub = "<public API key>"; var key = "<private API key>"; var hash = CryptoJS.HmacSHA512(msg, key); var crypt = CryptoJS.enc.Utf8.parse(hash.toString()); var base64 = CryptoJS.enc.Base64.stringify(crypt); postman.setEnvironmentVariable("hash_hmac","adorbit " + pub + ":" + base64);
Headers: Authorization : {{hash_hmac}} Accept : application/vnd.adorbit.companies+json;version=1.0
While technically possible to send the signature unencoded via HTTP, the service always does encoding to minimize any unforeseen problems.
Once the signature has been generated, it needs to be placed in the request’s Authorization header along with the public key in the format: adorbit public-key:signature
Formatting the response is dependent on the Accept header: application/vnd.resource-type+format; version=1.0 where responses will only be properly encoded when format is json. Additionally, a valid version identifier must be supplied. Otherwise, a 406 error is returned
.
Passing an invalid Accept header for a given resource will result in a 406 error as the returned response.Accept Headers:
{ "root": "application/vnd.adorbit+json;version=1.0", "login": "application/vnd.adorbit.login+json;version=1.0", "logout": "application/vnd.adorbit.logout+json;version=1.0", "companies": "application/vnd.adorbit.companies+json;version=1.0", "company": "application/vnd.adorbit.company+json;version=1.0", "company-contacts": "application/vnd.adorbit.company-contacts+json;version=1.0", "company-orders": "application/vnd.adorbit.company-orders+json;version=1.0", "company-activities": "application/vnd.adorbit.company-activities+json;version=1.0", "company-categories": "application/vnd.adorbit.company-categories+json;version=1.0", "company-category": "application/vnd.adorbit.company-category+json;version=1.0", "company-attribute-values": "application/vnd.adorbit.company-attribute-values+json;version=1.0", "company-attribute-value": "application/vnd.adorbit.company-attribute-value+json;version=1.0", "company-attribute-fields": "application/vnd.adorbit.company-attribute-fields+json;version=1.0", "company-attribute-field": "application/vnd.adorbit.company-attribute-field+json;version=1.0", "companyassets": "application/vnd.adorbit.companyassets+json;version=1.0", "contacts": "application/vnd.adorbit.contacts+json;version=1.0", "contact": "application/vnd.adorbit.contact+json;version=1.0", "contact-activities": "application/vnd.adorbit.contact-activities+json;version=1.0", "activities": "application/vnd.adorbit.activities+json;version=1.0", "activity": "application/vnd.adorbit.activity+json;version=1.0", "orders": "application/vnd.adorbit.orders+json;version=1.0", "order": "application/vnd.adorbit.order+json;version=1.0", "subscribers": "application/vnd.adorbit.subscribers+json;version=1.0", "subscriber": "application/vnd.adorbit.subscriber+json;version=1.0", "subscriber-subscriptions": "application/vnd.adorbit.subscriber-subscriptions+json;version=1.0", "subscriber-creditcard": "application/vnd.adorbit.subscriber+json;version=1.0", "subscriptions": "application/vnd.adorbit.subscriptions+json;version=1.0", "subscription": "application/vnd.adorbit.subscription+json;version=1.0", "subscriptionplans": "application/vnd.adorbit.subscriptionplans+json;version=1.0", "publications": "application/vnd.adorbit.publications+json;version=1.0", "publication": "application/vnd.adorbit.publication+json;version=1.0", "publication-issues": "application/vnd.adorbit.publication-issues+json;version=1.0", "issue": "application/vnd.adorbit.issue+json;version=1.0", "editorials": "application/vnd.adorbit.editorials+json;version=1.0", "editorial": "application/vnd.adorbit.editorial+json;version=1.0", "vendors": "application/vnd.adorbit.vendors+json;version=1.0", "vendor": "application/vnd.adorbit.vendor+json;version=1.0", "personnel": "application/vnd.adorbit.personnel+json;version=1.0" }
If the request is authenticated, authorized, and the URI is valid for the service a response code of 200 is returned along with the data. If either authentication or authorization fails a code of 401 is returned along with a message why the request is denied:
{ "error": { "code": "401", "message": "Not authorized." } }
An invalid URI leads to a 404 error:
{ "error": { "code": "404", "message": "Could not find route." } }
An invalid method tied to a resource will generate a 405 error:
{ "error": { "code": "405", "message": "The requested method is not allowed for this resource. Please try one of the following: [GET, POST]." } }
While an invalid Accept header returns a 406 error:
The service will default to rendering responses in JSON for invalid Accept headers. Additionally, the service does not parse the full header. It only selects the first item. Therefore, concatenated mime types with or without precedence values are ignored.
{ "error": { "code": "406", "message": "This service cannot format a response based on the supplied Accept header." } }
The Ad Orbit service implements portions of the HATEOAS approach when generating responses. The general idea is that a client is disconnected from the logic that makes the service work. This includes URI construction, paging between results, etc. Therefore, all the client needs is knowledge of the response’s format. Navigating through the service is merely an act of parsing the responses and replacing placeholders when necessary. While the contents of the message might change between releases, the structure and keys of the response will remain consistent (at least between major releases).
Updated 4/11/2023