NAV Navbar
shell PHP Laravel Django Express Sinatra


From here on, this document uses 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.


# Switch the language above to see an example of how to validate Odyssey requests in your API
export KEY = 'kqvtKkut8l50IKHBOYuWAS8G1MCgWqLuNZV0fKua'
$msg = '' . $_SERVER['REQUEST_URI'];
$key = 'kqvtKkut8l50IKHBOYuWAS8G1MCgWqLuNZV0fKua';
$calculated_hmac = hash_hmac('sha256', $msg, $key);
if ($sig !== $calculated_hmac) {
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 =

        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) {
class Authenticator
    def initialize(app)
        @app = app

    def call(env)
        request =

        key = "kqvtKkut8l50IKHBOYuWAS8G1MCgWqLuNZV0fKua"

        msg = request.url
        sig = request.get_header('X-Odyssey-Signature')

        calculated_hmac = OpenSSL::HMAC.hexdigest('sha256'), key, msg)

        if calculated_hmac == sig
  [], 401, {}).finish

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

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"

HTTP Request

GET /data/{day}


Name Located in Description Required Type
day path Date where paths have been converted, in Y-m-d. Yes date


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"


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.


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


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


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.