# Custom Events

Track user activity and conversions in your Airship channels, submit external events to Airship, trigger messages and sequences, and segment your audience with custom events.

## Overview

Custom events extend our [default events](https://www.airship.com/docs/guides/audience/events/events/). Such events as *opens* or *tag
change* are applicable to all apps and websites. Custom events, on the other hand, are
specific to the needs of your app or website. For example, you may be interested in tracking the *order
created* event in your e-commerce app, the *playback started* event in your content delivery
platform, or the *OnParticipatedInExperimentNotification* event in
your A/B testing application for tracking user participation.

You may track custom events either via our mobile and web SDKs or by using any other event tracking system.

In this guide, we will show you how to tie your digital engagement activities together
using custom events:

Set up via templates or directly in code
: Setting up a custom event for your app is easy. For common event types, we provide ready-made templates
  in our [Mobile and Web](https://www.airship.com/docs/sdk-topics/custom-events/) SDKs.
  Alternatively, you can set up custom events directly in your code by instantiating a class from the SDK
  that implements the custom event API and passing a string that represents the name of the
  event.

Extend with properties
: To provide more detail and customization to the events you are tracking, you may make use of
  event properties. These are key/value pairs, where the key represents the name of a
  property and the value is the information associated with this property. Examples of event
  properties are *a product SKU on a purchase event*, or *a category on a viewed video
  event*.

Stream in real time
: Our *Real-Time Data Streaming* supports streaming custom events to your
  business systems in real time. See our
  [Data Streaming API Reference](https://www.airship.com/docs/developer/rest-api/connect/schemas/events/#custom) for details.

Trigger automation or sequence
: You may set up custom events and their properties to trigger an automation or sequence:
  members of your audience may receive personalized messages with
  [Dynamic Content](https://www.airship.com/docs/reference/glossary/#dynamic_content) that includes information from the custom event.

  For example, if your custom event has a `user_name` property, you can add `{{user_name}}`
  to your message, and anybody receiving the message would see their *user name* — the value of the `user_name` property.

## Emitting Custom Events from Message Actions

You can emit custom events when users interact with [Scenes](https://www.airship.com/docs/reference/glossary/#scene), [push notifications](https://www.airship.com/docs/reference/glossary/#push_notification), or [in-app messages](https://www.airship.com/docs/reference/glossary/#in_app_message). This allows you to track user interactions with specific message elements without implementing SDK code.

**Scenes** — Emit an event when a user taps a button, image, or screen. See [Emit a Custom Event](https://www.airship.com/docs/guides/messaging/in-app-experiences/configuration/button-actions/#emit-a-custom-event) in *Actions* for Scenes.

**Push notifications and in-app messages** (iOS SDK 20+) (Android SDK 20+) — Emit an event when a user taps the message or a button within the message. See:
   * [Creating content](https://www.airship.com/docs/guides/messaging/messages/content/app/push-notifications/#creating-content) in *Push Notification Content*
   * [Creating content](https://www.airship.com/docs/guides/messaging/messages/content/app/in-app-messages/#creating-content) in *In-App Message Content*
   * [Add buttons to message content](https://www.airship.com/docs/guides/messaging/messages/buttons/#add-buttons-to-message-content) in *Buttons*

To emit Custom Events from the API, use the [add_custom_event](https://www.airship.com/docs/developer/rest-api/ua/schemas/push/#actionsobject) notification action.

## Tracking Custom Events

We recommend starting by identifying a few most important actions that
users perform in your app or site. These could include:

* Registering for an account
* Using a new feature
* Watching a video
* Viewing a specific screen
* Saving or sharing content
* Adding a product to a list or cart
* Making a purchase

Custom events track *actions* and not *objects*. For this reason, a good
practice is to apply the *verb-first* naming convention to custom events.

You can also track these events in varying levels of detail, based on how you
name them, and optionally include values with each event.
You may have other systems that track the granular specifics of your
e-commerce engine or content consumption. To measure the impact of push,
consider keeping reports at a higher level when starting out. For example, as
a publisher, you could track a purchase event in a few ways:

* Purchased a Magazine
* Purchased a Sports Magazine
* Purchased *Sports Illustrated*, Volume 17, Issue 9

While the third variation above might provide the most detail, you can start with
the first level of granularity to keep your reports easy to digest and roll-up, and
still see which push notifications were driving the most engagement or ROI.

Here are some examples in more detail:

> **Tip:** In our examples below, event names are lowercased. We use dashes to separate levels of granularity, and underscores
> for multi-word names/values.
> For example:
> 
> * event_name-event_category
> * purchased_item-sports_magazine


| Market vertical | Description | Example event name | Event value |
| --- |  --- |  --- | --- |
| All | User registers for an account | registered_account |
| All | User stars any product, article, or content | starred_content |
| Media | User browses a category articles or content | browsed_content |
| Media | User plays a video *Could use the event value to set the amount of time spent watching the video. Report the event after the user navigates away from the video or stops the video.* | consumed_content | Percentage of content consumed |
| Media | User shares content | shared_content |
| Retail | User adds item to cart | added_to_cart |
| Retail | User purchases an item *Use event value to capture the value of the item* | purchased | Purchase Price |

We provide ready-made templates for our mobile and web SDKs to get you started with a number of common
account-, media-, and retail-related events. See the templates in our [Custom Events](https://www.airship.com/docs/sdk-topics/custom-events/) SDK documentation.

With our mobile and web SDKs, tracking custom events is similar to [adding an Airship segmentation tag](https://www.airship.com/docs/developer/sdk-integration/web/segmentation/). Set specific properties and assign a range of values that must be met in order to trigger
automation rules. Custom event properties can contain objects and arrays of objects. Use dot notation to access nested properties: `parent_property.child_property`.

* API: [Custom Event Selectors](https://www.airship.com/docs/developer/rest-api/ua/schemas/pipeline-objects/#eventidentifier)
* Dashboard: [Manage Events](https://www.airship.com/docs/guides/audience/events/manage/)

> **Note:** Custom event properties used to be referenced in the `$data.events.[0].properties` namespace. If you have templates referencing properties in this namespace, they'll still work, but you'll have to continue using this namespace until you [contact Airship Support](https://support.airship.com/) and move over to the simplified namespace for custom event properties.

To minimize battery consumption, custom events are automatically combined and sent batched in the background. For more information, see [Tips and Code Samples](#tips-and-code-samples).

Simple example
: Here is a simple example that demonstrates how to track a custom event named **consumed_content**:

  **iOS**

  ```swift
var event = CustomEvent(name: "consumed_content", value: 123.12)

  // Set custom event properties
  event.setProperty(bool: true, forKey: "boolean_property")
  event.setProperty(string: "string_value", forKey: "string_property")
  event.setProperty(double: 11.0, forKey: "number_property")

  // Record the event in analytics
  event.track()
```


  **Android**

  ```java
// Create and name an event
  Builder builder = CustomEvent.newBuilder("consumed_content");

  // Set custom event properties on the builder
  builder.addProperty("bool_property", true);
  builder.addProperty("string_property", "string_property_value");
  builder.addProperty("int_property", 11);
  builder.addProperty("double_property", 11.0d);
  builder.addProperty("long_property", 11L);

  ArrayList<String> collection = new ArrayList<String>();
  collection.add("string_array_value_1");
  collection.add("string_array_value_2");
  collection.add("string_array_value_3");
  builder.addProperty("collection_property", JsonValue.wrapOpt(collection));

  CustomEvent event = builder.build();

  // Start tracking the event
  event.track();
```


  **Web**

  ```js
// Create and name an event
  var event = new sdk.CustomEvent("consumed_content")

  // Start tracking the event
  event.track()
```


Assigning a value to a custom event
: In this example, you will create an event with a value for advanced analytics reporting:

  **iOS**

  ```swift
// Create and name an event with a value
  let event = CustomEvent(name: "event_name" value: 123.12)
  // Track the event
  event.track()
```


  **Android**

  ```java
// Create and track a custom event with a value
  CustomEvent.newBuilder("event_name")
        .setEventValue(123.12)
        .build()
        .track();
```


  **Web**

  ```js
// Create and name an event with a value
  var event = new sdk.CustomEvent('event_name', 123.12)

  // Start tracking the event
  event.track()
```


## Server-Side Events

Server-side events are sent through the [Custom Events API](https://www.airship.com/docs/developer/rest-api/ua/operations/custom-events/). When you submit an event, you'll provide the [channel ID](https://www.airship.com/docs/reference/glossary/#channel_id) or [Named User ID](https://www.airship.com/docs/reference/glossary/#named_user) of the user you want to associate the event with.

You can use [Event identifiers](https://www.airship.com/docs/developer/rest-api/ua/schemas/pipeline-objects/#eventidentifier) to send automated messages to the *channel ID* or *named user* associated with each event. Attributing events to named users can help you better represent user actions and trigger automations for individual users without having to map channels to external IDs. Server-side events are involved in a significant number of Airship integrations.

> **Note:** [Server-side events](https://www.airship.com/docs/guides/audience/events/custom-events/#server-side-events) cannot be used to trigger an [In-App Automation](https://www.airship.com/docs/reference/glossary/#iaa) or [Scene](https://www.airship.com/docs/reference/glossary/#scene).
> 
> Custom Events used for In-App Automation or Scene triggers must be client-side events. Web Scenes can only use Custom Events tracked using the Web SDK. App Scenes and In-App Automations can only use Custom Events tracked using the Mobile SDK.

**Sample Custom Event**
```json
[
   {
      "occurred": "2016-05-02T02:31:22",
      "user": {
         "named_user_id": "hugh.manbeing"
      },
      "body": {
         "name": "purchased",
         "value": 239.85,
         "transaction": "886f53d4-3e0f-46d7-930e-c2792dac6e0a",
         "interaction_id": "your.store/us/en_us/pd/shoe/pid-11046546/pgid-10978234",
         "interaction_type": "url",
         "properties": {
            "description": "Sneaker purchase",
            "brand": "Victory Sneakers",
            "colors": [
             "red",
             "blue"
            ],
            "items": [
               {
                  "text": "New Line Sneakers",
                  "price": "$ 79.95"
               },
               {
                  "text": "Old Line Sneakers",
                  "price": "$ 79.95"
               },
               {
                  "text": "Blue Line Sneakers",
                  "price": "$ 79.95"
               }
            ],
            "name": "Hugh Manbeing",
            "userLocation": {
               "state": "CO",
               "zip": "80202"
            }
         },
         "session_id": "22404b07-3f8f-4e42-a4ff-a996c18fa9f1"
      }
   }
]
```


## Custom Events vs. Audience Segmentation Tags

Custom events are similar to [audience segmentation tags](https://www.airship.com/docs/developer/sdk-integration/web/segmentation/). While tags target users via audience segmentation, custom events inform analytics reporting and trigger automated messages.

For example, a user can be tagged as a *purchaser* after making a purchase. This user can
be segmented later for sending follow-up messages. You may use a custom event to track when the
user *purchased shoes*. This is something that happened, and you may wish to calculate its
business impact across all users.

Audience tags are useful to identify users for future campaigns. A person who has purchased before may be receptive to a
different type of future messaging. A tag only tells you that this user purchased at least
once. Because it is useful to understand how many purchases are made in total during a
period of time, we have custom events so you can keep track of both types of data.

## Event Reporting

Once you have the latest SDK installed and a snippet of code
tracking custom events, you'll start to see event data show up in your message reports.

These events will appear in each message report, and in an aggregate app
report, with information on whether each event occurred on an Airship
delivery channel (Landing Page or Message Center), or in a custom location in
your app or website.

These reports display summary data, and a CSV export option will provide full
data that you can manipulate as needed or import into business
intelligence or analytics tools.

See [View Attributed Events](https://www.airship.com/docs/guides/reports/message/#view-attributed-events) in *Message Reports* and [Scene Detail](https://www.airship.com/docs/guides/messaging/in-app-experiences/scenes/create/scene-reports/#scene-detail) in *Scene Reports*.

## Push Attribution

Because Airship has visibility into both the sending and receiving (via
SDK) of each push, our Push Attribution model ties these custom events back to
each message so that you can see the full
story of conversions following both direct and indirect opens.

We track how each custom event is attributed to a specific push. The following options are available:

* Direct Attribution of Events
* Indirect Attribution of Events
* Unattributed Events

Direct attribution
: If a push notification is sent to a device and the user taps on the
  notification to open the app and complete a custom event, then the event will
  be recorded with the **Direct** attribution to that push notification.

Indirect attribution
: If a push notification is sent to the device and the user does not tap on the
  notification, but opens the app later that day (within a 12 hour *attribution window*) and
  completes a custom event it will be recorded with **Indirect** attribution to
  that push notification. If the event is completed after 12 outside of the attribution window, then the event will be categorized as **Unattributed**.

Unattributed events
: If a user completes a custom event during a time when no push was sent, or that
  user is opted-out, then the event will be categorized as **Unattributed**.

![Custom Event push attribution flowchart](https://www.airship.com/docs/images/custom_events_flowchart_hu_dc4de354521cbfdf.webp)

*Custom Event push attribution flowchart*

## Tips and Code Samples

When setting up custom events, remember:

* Start by tracking two to five key activities or conversion points in your app.
* When naming events, keep the total number of unique event names reasonable, so your reports are easy to read.
* Event will be ignored if their names contain more than 255 characters.
* Use consistent naming conventions for your events. We lowercase all incoming events for consistency in reports.
* Event Values must be between -2<sup>31</sup> and [2<sup>31</sup> - 1] or the value will be ignored.
* Event Values cannot contain `$` or `,` characters or the value will be ignored (decimals only).

### JavaScript for Rich Pages

Send a [Message Center page](https://www.airship.com/docs/guides/features/messaging/message-center/) or [landing page](https://www.airship.com/docs/guides/features/messaging/landing-pages/) with the following HTML to set a
custom event on the button. In the example below, we create a button (named *buy-button*)
which fires the "bought book" event with a value of `10.99`.

```html
<html>
    <head>
        <title>Store</title>
        <script>
            document.addEventListener('ualibraryready', onUAReady)

            function onUAReady() {
              // Name buttons below
              // The output variable is used for debugging - comment out if needed
              var buy_button = document.querySelector('[name=buy-button]')
                , output = document.querySelector('[name=ua-attributes]')

              // For debugging - can be commented out
              // Write all the results of all getters
              output.innerHTML += 'ATTRIBUTES \n'
              output.innerHTML += '---------- \n'
              output.innerHTML += 'User Id:' + UAirship.getUserId() +
               '\n Device Model: ' + UAirship.getDeviceModel() +
               '\n Message Id: ' + UAirship.getMessageId() +
               '\n Message Title: ' + UAirship.getMessageTitle() +
               '\n Message Sent Date: ' + UAirship.getMessageSentDate() +
               '\n Message Sent Date MS: ' + UAirship.getMessageSentDateMS() +
               '\n\n'

              // Enable buttons once the ualibrary is up and running on the page
              buy_button.disabled = false

              // Listen for taps/clicks
              buy_button.addEventListener('click', onclick)
            }

            function onclick(ev) {
              // The output variable is used for debugging - comment out if needed
              var output = document.querySelector('[name=ua-custom-event-info]')
                , message_id = UAirship.getMessageId()

              var custom_event_object = {}
                , el = ev.currentTarget

              // get value and name from the element
              custom_event_object.event_value = el.getAttribute('data-event-value')
              custom_event_object.event_name = el.getAttribute('data-event-name')

              if(!UAirship.getMessageId()) {
                 // If we can't get a messageId, then we must be in a web
                 // view, which, in this case, implies we are on a landing
                 // page.

                 custom_event_object.interaction_type = 'ua_landing_page'
                 custom_event_object.interaction_id = '' + window.location
              }

              // For debugging - can be commented out
              output.innerHTML += 'Sending Event: \n' +
                JSON.stringify(custom_event_object, null, 4) + '\n'

              //Send the event to the SDK
              UAirship.runAction('add_custom_event_action', custom_event_object, ready)

              function ready(error, result) {
                if(error) {
                  // For debugging - can be commented out
                  output.innerHTML += 'Woops! ' +
                    error.message + '\n'

                  return
                }
                // For debugging - can be commented out
                output.innerHTML += 'Success! The server responded:\n' +
                   JSON.stringify(result, null, 4) + '\n'
              }
            }
        </script>
    </head>
    <body>
       <!-- CUSTOMIZE EVENT DATA FOR BUTTON js is looking for data-event-name and data-event-value -->
      <button name="buy-button" data-event-name="clicked_button-Buy_Now" data-event-value="10.99" disabled>
          Buy Now
      </button>
      <!-- START EVENT DATA OUTPUT FOR DEBUGGING COMMENT OUT IF NEEDED-->
      <pre name="ua-attributes">
      </pre>
      <pre name="ua-custom-event-info">
      </pre>
      <!-- END EVENT DATA OUTPUT FOR DEBUGGING -->
    </body>
</html>
```


Our example detects if a page is a landing page, sets the
`interaction_type` and `interaction_id` if needed, emits an event to the Custom Events
system, and writes debugging information to the DOM.

Under the hood, a *landing page* is simply a web view, so in order to propagate
the knowledge that the event was fired from a landing page to the custom events
system, we must set the `interaction_type` to `ua_landing_page` and the
`interaction_id` to the URL of the landing page.

For a [Message Center page](https://www.airship.com/docs/guides/features/messaging/message-center/), the Airship SDK is able to detect that the event is fired from a
Message Center page so `interaction_type` and the `interaction_id`
will be taken care of by the SDK.

### Injecting the Airship JavaScript Interface

If you have a WebView that exists outside of a Message Center, you can use the
following examples to inject the Airship JavaScript interface in to your
WebView to enable sending custom events.

> **Important:** The Airship JavaScript interface runs Airship actions and
> exposes information about the application. It should only be loaded in WebViews
> that are displaying content from trusted sources and that only link to other
> trusted sources.


#### iOS Example

The Airship JavaScript interface can be added to any `WKWebView`
whose navigation delegate is an instance of [NativeBridge](https://urbanairship.github.io/ios-library/v20/AirshipCore/documentation/airshipcore/nativebridge)
. Custom WebViews can also
implement the [NativeBridgeExtensionDelegate](https://urbanairship.github.io/ios-library/v20/AirshipCore/documentation/airshipcore/nativebridgeextensiondelegate)
 to extend the JavaScript environment
or respond to SDK-defined JavaScript commands.

```swift
self.nativeBridge = NativeBridge()
self.nativeBridge.nativeBridgeExtensionDelegate = self.nativeBridgeExtension
self.nativeBridge.forwardNavigationDelegate = self
webView.navigationDelegate = self.nativeBridge
```


Optionally, enable `UAirship.close()` by having the controller implement the `close` method in the `NativeBridgeDelegate` protocol:

```swift
func close() {
  // Close the current window
}
```


#### Allowlist Rules

For iOS, the [URLAllowList](https://urbanairship.github.io/ios-library/v20/AirshipCore/documentation/airshipcore/airshipurlallowlist)
 controls which URLs the Airship SDK is able to act on. As of SDK 17, all URLs are allowed by default. See: [iOS: Configuring Airship](https://www.airship.com/docs/developer/sdk-integration/apple/installation/advanced-integration/#url-allowlist).

#### Android and Fire OS example

The Airship JavaScript interface can be added to any web view as long as JavaScript is enabled and the client is set to an
instance of UAWebViewClient.

```java
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new UAWebViewClient())
```


#### Allowlist Rules

For Android, the [UrlAllowList](https://www.airship.com/docs/reference/libraries/android-kotlin/latest/urbanairship-core/com.urbanairship/-url-allow-list)
 controls which URLs the Airship SDK is able to act on. As of SDK 17, all URLs are allowed by default. See: [URL Allowlist](https://www.airship.com/docs/developer/sdk-integration/android/installation/advanced-integration/#url-allowlist).
