Setting up custom destinations

11 minute read

Sometimes the best way to integrate Avo is through a custom destination. For example:

  • When using Avo Functions alongside existing tracking implementation
  • When sending data to analytics platforms through an API (not using their SDK)
  • When using a combination of a programming language and analytics destination not yet directly supported by Avo
  • When you want to have control over the initialization and the lifecycle of the Analytics SDK

Please contact us if you are unsure of what is the best solution for you or if there are any analytics destinations you'd like to see supported in Avo.

To get started using a custom destination you do the following:

  1. Create a Custom Destination in the Connections tab
  2. Pull the updated code by doing avo pull in the Avo CLI
  3. Initialize Avo with a custom destination
  4. Fill in the empty methods for sending the data to your analytics destination

Custom Destinations overview

We will be referencing Avo functions, events, actions and other Avo concepts in this document. Please read the Tracking Plan doc and subdocs to get familiar with Avo concepts.

With a custom destination you'll get access to callback methods for Avo actions. The Avo generated code triggers the callbacks when corresponding Avo functions are called.

Callback methods

  • make: Triggered during Avo initialization. Here you will usually initialize an analytics SDK with a development or production key based on the env parameter. If analytics SDK has already been initialized before you can leave this function blank.

    Example: analytics.init(env)

  • logEvent: All your analytics events are managed in the Tracking Plan in Avo. Each event gets a generated Avo function. Avo events can have a Log Event action attached. This callback is triggered when an Avo function with Log Event action is called. Here you perform the actual event tracking, calling the track/log methods of the analytics SDK. Event name and event properties are provided as parameters.

    Example: analytics.track(eventName, eventProperties)

  • identify: You can add the Identify User action to an event in the Tracking Plan in Avo. When calling an Avo function that includes the Identify User action you'll need to provide a user ID. Calling an Avo function that has the Identify User action will trigger this callback. The main use cases are signup and login. Here you would pass the user ID to the analytics SDK for it to create a new user or attach a session to an existing user.

    Example: analytics.identify(userId)

  • unidentify: If you call an Avo function with the Unidentify User action, this callback will be triggered. Here you would call an analytics SDK method to detach subsequent actions from the currently identified user.

    Example: analytics.identify(null)

  • revenue: If you add a Log Revenue action to Avo Function this callback will be invoked. Here you would log the revenue to your analytics destination, many of them have a special method to log revenue.

    Example: analytics.revenue(productId, quantity, price)

  • page: If you add a Log Page View action to Avo Function this callback will be invoked. Here you would report navigation to your analytics destination.

    Example: analytics.page(pageName, eventProperties)

  • setUserProperties: You can add user properties to events in the Avo dashboard. When an Avo function with attached user properties is called, this callback is triggered. Here you would attach user properties to the currently identified user in your analytics platform.

    Example: analytics.setUserProperties(userId, userProperties)

Initialization instructions

Below are instructions on how to initialize Avo with a custom destination for each of the supported languages:

Web

Javascript

Import the Avo library into your code:

JavaScript
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Avo from './Avo';
// An object representing your custom destination
// that you pass into initAvo():
var customDestination = {
make: function (env) {
// TODO implement
},
logEvent: function (eventName, eventProperties) {
// TODO implement
},
setUserProperties: function (userId, userProperties) {
// TODO implement
},
identify: function (userId) {
// TODO implement
},
unidentify: function () {
// TODO implement
},
revenue: function(amount, eventProperties) {
// TODO implement
},
page: function(pageName, eventProperties) {
// TODO implement
}
};

Call the initAvo function:

JavaScript
Copy
1
Avo.initAvo({ env: 'dev' }, {}, {}, customDestination);

Typescript

TypeScript
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Avo from './Avo';
// An object representing your custom destination
// that you pass into initAvo():
let customDestination = {
make: function (env: AvoEnv) {
// TODO implement
},
logEvent: function (eventName: string, eventProperties: any) {
// TODO implement
},
setUserProperties: function (userId: string, userProperties: any) {
// TODO implement
},
identify: function (userId: string) {
// TODO implement
},
unidentify: function () {
// TODO implement
},
logPage: function(pageName: string, eventProperties: object) {
// TODO implement
}
};

Call the initAvo function:

TypeScript
Copy
1
Avo.initAvo({ env: Avo.AvoEnv.Dev }, {}, customDestination);

Mobile

iOS

Objective-C

Objective C
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Avo
// An object representing your custom destination
// that you pass into initAvo():
@interface CustomDestination : NSObject <AVOCustomDestination>
@end
@implementation CustomDestination : NSObject
- (void)make:(AVOEnv)avoEnv
{
// TODO implement
}
- (void)logEvent:(nonnull NSString*)eventName withEventProperties:(nonnull NSDictionary*)eventProperties
{
// TODO implement
}

Call the initAvo function:

Objective C
Copy
1
2
3
CustomDestination * customDestination = [[CustomDestination alloc] init];
[Avo initAvoWithEnv:AVOEnvDev
customDestination:customDestination];

Swift

Import the Avo library into your code:

Swift
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Avo
// An object representing your custom destination
// that you pass into initAvo():
class CustomDestination : AvoCustomDestination {
   func make(env: AvoEnv) {
       // TODO implement
   }
   
   func logEvent(eventName: String, eventProperties: [String : Any]) {
       // TODO implement
   }
   
   func setUserProperties(userId: String, userProperties: [String : Any]) {
       // TODO implement
   }
   
   func identify(userId: String) {
       // TODO implement
   }
   
   func unidentify() {
       // TODO implement
   }
}

Call the initAvo function:

Swift
Copy
1
2
3
4
5
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
Avo.initAvo(env: .dev, customDestination: CustomDestination())
}

Where customDestination is the name of the destination in Avo.

Android

Java

Import the Avo library into your code:

Java
Copy
1
import Avo

Call the initAvo() function:

Java
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Avo.initAvo(application, context, Avo.AvoEnv.DEV, new Avo.ICustomDestination() {
public void make(Avo.AvoEnv env) {
// TODO implement
}
public void logEvent(String eventName, Map<String, Object> eventProperties) {
// TODO implement
}
public void setUserProperties(String userId, Map<String, Object> userProperties) {
// TODO implement
}
public void identify(String userId) {
// TODO implement
}
public void unidentify() {
// TODO implement
}
});

Kotlin

Kotlin
Copy
1
import Avo

Call the initAvo function:

Kotlin
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Avo.initAvo(application=application, context=this, env=AvoEnv.DEV, customDestination=object : ICustomDestination {
override fun make(env: AvoEnv) {
// TODO implement
}
override fun logEvent(eventName: String, eventProperties: Map<String, *>) {
// TODO implement
}
override fun setUserProperties(userId: String, userProperties: Map<String, *) {
// TODO implement
}
override fun logPage(pageName: String, eventProperties: Map<String, *>) {
// TODO implement
}
override fun identify(userId: String) {
// TODO implement
}
override fun unidentify() {
// TODO implement
}
})

React Native

Javascript

Import the Avo library into your code:

JavaScript
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Avo from './Avo';
// An object representing your custom destination
// that you pass into initAvo():
var customDestination = {
make: function (env) {
// TODO implement
},
logEvent: function (eventName, eventProperties) {
// TODO implement
},
setUserProperties: function (userId, userProperties) {
// TODO implement
},
identify: function (userId) {
// TODO implement
},
unidentify: function () {
// TODO implement
},
revenue: function(amount, eventProperties) {
// TODO implement
},
page: function(pageName, eventProperties) {
// TODO implement
}
};

Call the initAvo function:

JavaScript
Copy
1
Avo.initAvo({ env: 'dev' }, {}, {}, customDestination);

Javascript to Mixpanel

Here is a more specific example of how you can use a custom destination in Javascript on React Native to integrate with your Mixpanel SDK. In this example we're using the official mixpanel-react-native SDK.

JavaScript
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { Mixpanel } from 'mixpanel-react-native';
import Avo from './Avo';
// An object representing your Mixpanel destination
// that you pass into initAvo():
var mixpanelDestination = {
make: function (env) {
Mixpanel.init(env == "dev" ? "<Your Mixpanel dev token>" : "<Your Mixpanel prod token>");
},
logEvent: function (eventName, eventProperties) {
mixpanel.track(eventName, eventProperties);
},
setUserProperties: function (userId, userProperties) {
mixpanel.identify(userId);
Object.keys(userProperties).forEach(key => mixpanel.getPeople().set(key, userProperties[key]));
},
identify: function (userId) {
mixpanel.identify(userId);
},
unidentify: function () {
mixpanel.reset();
},
revenue: function(amount, eventProperties) {
mixpanel.getPeople().trackCharge(amount, eventProperties);
},
page: function(pageName, eventProperties) {
console.log("The React Native Mixpanel SDK does not provide native page or screen tracking");
}
};

Call the initAvo method and pass in your mixpanelDestination:

JavaScript
Copy
1
Avo.initAvo({ env: 'dev' }, {}, {}, mixpanelDestination);

Typescript

TypeScript
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Avo from './Avo';
// An object representing your custom destination
// that you pass into initAvo():
let customDestination = {
make: function (env: AvoEnv) {
// TODO implement
},
logEvent: function (eventName: string, eventProperties: any) {
// TODO implement
},
setUserProperties: function (userId: string, userProperties: any) {
// TODO implement
},
identify: function (userId: string) {
// TODO implement
},
unidentify: function () {
// TODO implement
},
logPage: function(pageName: string, eventProperties: object) {
// TODO implement
}
};

Call the initAvo function:

TypeScript
Copy
1
Avo.initAvo({ env: Avo.AvoEnv.Dev }, {}, customDestination);

Backend

Node JS

Javascript

Import the Avo library into your code:

JavaScript
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
var Avo = require('./Avo.js');
// An object representing your custom destination
// that you pass into initAvo():
var customDestination = {
logEvent: async function (userId, eventName, eventProperties) {
// TODO implement
},
setUserProperties: function (userId, userProperties) {
// TODO implement
},
};

If you need to be able to pass an Anonymous Id from the Avo function to your custom destination, reach out to us and we'll enable that functionality for your workspace. When the Anonymous Id pass through has been enabled for your workspace the custom destination interface will look like this:

JavaScript
Copy
1
2
3
4
5
6
7
8
9
10
var Avo = require('./Avo.js');
var customDestination = {
logEvent: async function (anonymousId, userId, eventName, eventProperties) {
// TODO implement
},
setUserProperties: function (anonymousId, userId, userProperties) {
// TODO implement
},
};

Call the initAvo() function:

JavaScript
Copy
1
Avo.initAvo({ env: 'dev' }, {}, {}, customDestination);

By passing in the customDestination object, its logEvent() function can be used to deliver an event to any custom destination or pipeline.

Typescript

TypeScript
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
import Avo from './Avo';
// An object representing your custom destination
// that you pass into initAvo():
let customDestination = {
logEvent: async function (userId, eventName, eventProperties) {
// TODO implement
},
setUserProperties: function (userId, userProperties) {
// TODO implement
},
};

Call the initAvo function:

TypeScript
Copy
1
Avo.initAvo({ env: Avo.AvoEnv.Dev }, {}, customDestination);

Java

Import the Avo library into your code:

Java
Copy
1
2
import Avo

Call the initAvo() function:

Java
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
Avo.initAvo(Avo.AvoEnv.DEV, new Avo.ICustomDestination() {
public void make(Avo.AvoEnv env) {
// TODO implement
}
public void logEvent(String anonymousId, String userId, String eventName, Map<String, Object> eventProperties) {
// TODO implement
}
public void setUserProperties(String userId, Map<String, Object> userProperties) {
// TODO implement
}
});

PHP

PHP
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require_once './Avo';
// An object representing your custom destination
// that you pass into init_avo():
class CustomDestination {
public function make($env) {
// TODO implement
}
public function log_event($userId, $event_name, $event_properties) {
// TODO implement
}
public function set_user_properties($user_id, $user_properties) {
// TODO implement
}
}

Call the init_avo function:

PHP
Copy
1
2
3
4
Avo::init_avo([
'env' => 'dev',
'custom_destination_instance' => new CustomDestination,
]);

Python

Import the Avo library into your code and define the Custom Destination class:

Python
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from __future__ import print_function
import avo
# An object representing your custom destination
# that you pass into init_avo():
class CustomDestination(object):
    def __init__(self):
        self.env = None
    def make(self, env):
pass
    def track_event(self, user_id, event_name, event_properties):
pass
    def set_user_properties(self, user_id, user_properties):
pass

Call the init_avo() function and pass in the Custom Destination:

Python
Copy
1
avo.init_avo(options={'env': 'dev'}, custom_destination=CustomDestination())

where the name of the custom_destination parameter is the name of your custom destination in Avo.

Python to Segment

Here is a more specific example of how you can use a custom destination in Python to integrate with your Segment SDK. In this example we're using the analytics Python SDK from Segment

Python
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from __future__ import print_function
import avo
import analytics
# An object representing your custom destination
# that you pass into init_avo():
class SegmentDestination(object):
    def __init__(self):
        self.env = None
    def make(self, env):
analytics.write_key = 'YOUR_WRITE_KEY'
    def track_event(self, user_id, event_name, event_properties):
analytics.track(user_id, event_name, event_properties)
    def set_user_properties(self, user_id, user_properties):
analytics.identify(user_id, user_properties)

Call the init_avo() function:

Python
Copy
1
avo.init_avo(options={'env': 'dev'}, custom_destination=SegmentDestination())

where the name of the custom_destination parameter is the name of your custom destination in Avo.

Ruby

Ruby
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Avo
// An object representing your custom destination
// that you pass into initAvo():
class CustomDestination
def make(env:)
# TODO implement
end
def track_event(anonymous_id:, user_id:, event_name:, event_properties:)
# TODO implement
end
def set_user_properties(anonymous_id:, user_id:, user_properties:)
# TODO implement
end
end

Call the initAvo function:

Ruby
Copy
1
Avo.init_avo(options: {:env => :dev}, custom_destination: CustomDestination.new)

Game Engines

Unity (C#)

C#
Copy
1
using Avo;

Implement the custom destination:

C#
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class CustomDestination : Avo.IDestination {
public void Make(Avo.AvoEnv env) {
// TODO implement
}
public async Task LogEvent(string eventName, IDictionary<string, object> eventProperties) {
// TODO implement
}
public async Task SetUserProperties(string userId, IDictionary<string, object> userProperties) {
// TODO implement
}
public async Task Identify(string userId) {
// TODO implement
}
public async Task Unidentify() {
// TODO implement
}
}

Call the initAvo function:

C#
Copy
1
Avo.initAvo(env=Avo.AvoEnv.Dev, customDestination=new CustomDestination());