Introduction
From here on, this document uses https://example.com/api/odyssey/ as the example base URL
This document describes a specification for a generic API that exposes clickstream data about converted customer journeys. If your API follows this spec, you can create an Odyssey account which uses the data your API exposes.
A Swagger specification of this document is also available in both JSON and YAML formats.
Authorization
# Switch the language above to see an example of how to validate Odyssey requests in your API
export KEY = 'kqvtKkut8l50IKHBOYuWAS8G1MCgWqLuNZV0fKua'
<?php
$msg = 'https://example.com/api/odyssey/' . $_SERVER['REQUEST_URI'];
$key = 'kqvtKkut8l50IKHBOYuWAS8G1MCgWqLuNZV0fKua';
$calculated_hmac = hash_hmac('sha256', $msg, $key);
if ($sig !== $calculated_hmac) {
http_response_code(401);
die('Unauthorized');
}
<?php
class OdysseyAuthentication {
public function handle(Request $request, Closure $next) {
$sig = $request->header('X-Odyssey-Signature');
$data = $request->fullUrl();
$calculated_hmac = hash_hmac('sha256', $data, config('services.odyssey.key'));
if ($sig === $calculated_hmac) {
return $next($request);
} else {
return response('Unauthorized', 401);
}
}
}
class APIAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
key = 'kqvtKkut8l50IKHBOYuWAS8G1MCgWqLuNZV0fKua'
msg = request.build_absolute_uri()
sig = request.META.get('HTTP_X_ODYSSEY_SIGNATURE', '')
calculated_hmac = hmac.new(
key,
msg=msg,
digestmod=hashlib.sha256
).hexdigest()
if calculated_hmac != sig:
raise exceptions.AuthenticationFailed('Invalid API signature')
return None
var crypto = require("crypto");
app.use(function(req, res, next) {
var key = "kqvtKkut8l50IKHBOYuWAS8G1MCgWqLuNZV0fKua";
var msg = req.protocol + '://' + req.get('host') + req.originalUrl;
var sig = req.header("X-Odyssey-Signature");
var calculated_hmac = crypto.createHmac("sha256", key).update(msg).digest("hex");
if (sig !== calculated_hmac) {
res.status(401).send('Unauthorized');
return;
}
next();
});
class Authenticator
def initialize(app)
@app = app
end
def call(env)
request = Rack::Request.new(env)
key = "kqvtKkut8l50IKHBOYuWAS8G1MCgWqLuNZV0fKua"
msg = request.url
sig = request.get_header('X-Odyssey-Signature')
calculated_hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('sha256'), key, msg)
.strip()
if calculated_hmac == sig
@app.call(env)
else
Rack::Response.new([], 401, {}).finish
end
end
end
To make sure only Odyssey can access your data, all requests to your API will be signed with a pre-shared key. When an agency sets up a new account in the Odyssey dashboard, they enter this key.
In your API, you should verify that Odyssey sent a correct signature.
The signature is created by taking the HMAC SHA256 of the full request URI.
In the case of the GET /data/{day}
call, the message might be https://example.com/api/odyssey/data/2017-01-01
.
Odyssey sends the signature in HTTP header X-Odyssey-Signature
.
Customer journeys
List journeys for all customers that have converted on a certain day
SIG = 'fdc3f7469d511293ce1b18643718963658cc86eecb9e2aae8633ff69c737db8f'
curl -H "X-Odyssey-Signature: $SIG" https://example.com/api/odyssey/data/2017-01-01
HTTP Request
GET /data/{day}
Parameters
Name | Located in | Description | Required | Type |
---|---|---|---|---|
day | path | Date where paths have been converted, in Y-m-d. | Yes | date |
Response
The endpoint should return JSON that follows this format:
[
{
"transactionId": "as3da64234dada",
"date": "2017-06-18",
"transactionRevenue": 199.12,
"events": [
{
"step": 1,
"eventTime": 1497793010,
"trafficSource": "Source",
"channel": "Referring sites"
}
]
}
]
Description
Code | Description |
---|---|
200 | successful operation |
404 | no conversions on this date |
When there is no data available for the given date, you can also return an empty array.
Conversion
Name | Type | Description |
---|---|---|
transactionId | string | The unique identifier of the translation |
date | string | The date the conversion has been converted, in Y-m-d |
transactionRevenue | double | The revenue generated by the conversion |
events | array | Array of events, see Event |
Event
Name | Type | Description |
---|---|---|
step | string | Increments for every event in conversion, the first event in an conversion should have step 1 |
eventTime | int | Timestamp for the start of the event |
trafficSource | string | The traffic source where the event originated from |
channel | string | The channel group the traffic source is associated with |
Errors
Your API can return the following error codes:
Error Code | Meaning |
---|---|
401 | Unauthorized – The given signature is incorrect. |
404 | Not Found – Data could not be found. |
429 | Too Many Requests – Odyssey is sending too many requests. We’ll use an incremental back-off strategy and try again later. |
500 | Internal Server Error – Your server is having issues. Odyssey will immediately stop trying to connect and mark the data collection as failed. |