App Conversions Installation for Kotlin

Send in-app conversion events server-side from your Android app with Kotlin, OkHttp, and coroutines — complete code examples for Purchase, ViewContent, AddToCart, Lead, CompleteRegistration, and Contact events.

In This Page

Introduction

App Conversions allows mobile app developers to track and measure user interactions and conversions within their mobile apps. It enables developers to send app events directly from their apps to advertising platforms' servers, providing valuable data for ad optimization and tracking the effectiveness of their app marketing campaigns.

With App Conversions, developers can track various app events, such as app installs, app opens, in-app purchases, and other user interactions. These events are sent to advertising platforms, where advertisers can use them to target specific audiences and optimize their ad campaigns for better performance.

Get Started

The examples below use OkHttp for the HTTP call, org.json for building the request body, and Kotlin coroutines to keep networking off the main thread. Add the following imports to your project:

Imports
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.io.IOException

Extended Device Information

Send device information, such as screen width and height, in your app event call using /{app-id}/activities?extinfo. Values are separated by commas and must be in the order indexed in the /application/activities reference guide. When using extinfo, all values are required.

  • version must be a2 for Android
  • version must be i2 for iOS

Get Mobile Cookies for Android

We encourage you to associate app events with an advertiser_id. However, for Android devices and iOS devices earlier than iOS 6, you can also use the attribution parameter set to the mobile cookie of the device.

Note

Mobile cookies are not derived from any user or device attributes. These cookies are not persistent and are designed to be refreshed frequently. Do not use mobile cookies for re-targeting ads.

The cookie is a random 22-character alphanumeric string. Get the Facebook attribution ID using ContentProvider:

Attribution ID via ContentProvider
public static final Uri ATTRIBUTION_ID_CONTENT_URI =
    Uri.parse("content://com.facebook.katana.provider.AttributionIdProvider");

public static final String ATTRIBUTION_ID_COLUMN_NAME = "aid";

public static String getAttributionId(ContentResolver contentResolver) {
    String[] projection = {ATTRIBUTION_ID_COLUMN_NAME};
    Cursor c = contentResolver.query(ATTRIBUTION_ID_CONTENT_URI, projection, null, null, null);
    if (c == null || !c.moveToFirst()) {
        return null;
    }
    String attributionId = c.getString(c.getColumnIndex(ATTRIBUTION_ID_COLUMN_NAME));
    c.close();
    return attributionId;
}

Set Up Deduplication

The deduplication mechanism is required to remove duplicate event traffic between the Conversions API integration and all other existing integrations you have with app events, including the SDK and App Events API.

For app events, we apply the same deduplication functionality that exists for web events. The logic leverages event_id and event_name based deduplication (Conversions API and SDK / App Events API events that carry the same event_id). The event_id parameter is an identifier that can uniquely distinguish between similar events. Inaccurate event IDs may cause your conversion to be wrongly deduplicated, further impacting conversion reporting and campaign performance.

Logging a custom event with a deduplication parameter
logger = AppEventsLogger.newLogger(this);
// Let's say you want to log a custom event
Bundle parameters = new Bundle();
parameters.putString("event_id", "123");  // deduplication parameter
// Add other event parameters if required
logger.logEvent("achievedLevel", parameters);

Install events

For app install events, there is already a deduplication mechanism that makes sure only one install is attributed in the last 90-day window. We keep the first event and drop the later ones no matter the action source. No extra deduplication is required for install-related events.

Events

Please implement the following events in your app:

  1. Purchase Event
  2. View Content Event
  3. Add To Cart Event
  4. Lead Event
  5. Complete Registration Event
  6. Contact Event

1. Purchase Event

ApiClient.kt — Purchase
object ApiClient {
    // The base URL for the API endpoint
    private const val BASE_URL = "https://cpi.ssevt.com/push"
    private const val TOKEN = "ABC123" // Predefined Bearer Token

    // Function to send a POST request to the API
    suspend fun sendPostRequest() = withContext(Dispatchers.IO) {
        // Create an instance of OkHttpClient to make the HTTP request
        val client = OkHttpClient()
        // Define the media type for the request body (application/json)
        val mediaType = "application/json".toMediaType()
        // Create the request body
        val jsonBody = JSONObject().apply {
        
            // Required
            put("eid", "d290f1ee-6c54-4b01-90e6-d701748f0851") // Order ID or Transaction ID For Purchase Event
            put("et", "Purchase") // Event Name
            put("ua", "App") // App Static Value
        
            put("p", JSONObject().apply {
                // Required
                put("event_time", 1644778428)
                put("platform", "android")
                put("os_version", "33") // Android API version
                put("att", 1) // Advertiser Tracking Enabled
                put("ate", 1) // Application Tracking Enabled
                put("device_model", "Galaxy A23")
                put("currency", "USD")
                put("value", 142.54)
                put("app_id", "com.example.myapp")
        
                // Optional
                put("screen_width", "762")
                put("screen_height", "1278")
                put("external_storage", "128")
                put("free_space", "63")
                put("device_timezone", "Europe/London")
                // Add other optional properties here
                // ...
        
                // Required
                put("contents", JSONArray().apply {
                    put(JSONObject().apply {
                        put("id", "ABC123")
                        put("quantity", 1)
                    })
                    put(JSONObject().apply {
                        put("id", "DEF456")
                        put("quantity", 1)
                    })
                })
        
                // Required
                put("content_type", "product")
            })
        
                    // Advanced Matching Parameters
                    put("amp", JSONObject().apply {
                        // Required
                        put("anon_id", "123456abc") // The ID of a person who has installed the app anonymously
                        put("madid", "7497f910905fb413fe27a64a557778063") // IDFA or Google advertising ID
        
                        // Optional
                        put("ph", "8a187a62e69697497f910905fb413fe27a64a5577780636db7b18305e57cacaf") // Phone
                        put("em", "55e79200c1635b37ad31a378c39feb12f120f116625093a19bc32fff15041149") // Email
                        put("ge", "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111") // Gender
                        put("db", "d5234ccb6fb4dd59cbe76a3e5e1f396c27d2862246c7dbd7bc9e8fd72d664aaf") // Date Of Birth
                        put("ct", "350c754ba4d38897693aa077ef43072a859d23f613443133fecbbd90a3512ca5") // City
                        put("zp", "3ebb904517b0c6cf6b2d387f1e5f0ba715258125efc4012d88c4053d0da62233") // Zip Code
                        put("cn", "79adb2a2fce5c6ba215fe5f27f532d4e7edbac4b6a5e09e1ef3a08084a904621") // Country
                    })
        }

        // Create the request body from the JSON body and media type
        val requestBody = RequestBody.create(mediaType, jsonBody.toString())

        // Build the POST request with the request body and headers, including Bearer token
        val request = Request.Builder()
            .url(BASE_URL)
            .post(requestBody)
            .addHeader("Content-Type", "application/json")
            .addHeader("Authorization", "Bearer $TOKEN") // Adding Bearer Token
            .build()

        try {
            // Execute the request using OkHttpClient
            val response = client.newCall(request).execute()

            // Check if the response was successful (HTTP status code 200-299)
            if (response.isSuccessful) {
                val responseData = response.body?.string()
                val jsonResponse = JSONObject(responseData)
                println("Response: $jsonResponse")
            } else {
                println("Error: ${response.code} ${response.message}")
            }
        } catch (e: IOException) {
            e.printStackTrace()
        } catch (e: JSONException) {
            e.printStackTrace()
        }
    }
}

The other events reuse this client

Only the jsonBody changes between events. The sections below show the body for each event — the OkHttp request code stays exactly the same as in the Purchase example.

Parameter List

Advanced Matching Parameters — * required fields
User informationParameterFormatExample
Anonymous ID*anon_idUnique ID — do not hash123456abc
Advertising ID (IDFA / GAID)*madidUnique ID — do not hash7497f910905fb413fe27a64a557778063
E-mailemLowercase + SHA256 hash[email protected]
First namefnLowercase + SHA256 hashjohn
Last namelnLowercase + SHA256 hashdoe
TelephonephOnly numbers, incl. country and area code + SHA256 hash12125357525
GendergeA single lowercase letter, f or m + SHA256 hashf
BirthdaydbOnly numbers: year, month, day + SHA256 hash19910526 for May 26, 1991
CityctLowercase, no spaces + SHA256 hashnewyork
Postal / zip codezpOnly numbers + SHA256 hash34000
CountrycnTwo-letter country code, lowercase + SHA256 hashtr

PII Hashing

Every personal identifier except anon_id and madid must be normalized and SHA256-hashed before sending. Use our PII Hashing guide and live tool to verify your hashes.

2. View Content Event

jsonBody — ViewContent
val jsonBody = JSONObject().apply {

    // Required
    put("eid", "d290f1ee-6c54-4b01-90e6-d701748f0851") // Any Unique Identifier
    put("et", "ViewContent") // Event Name
    put("ua", "App") // App Static Value

    put("p", JSONObject().apply {
        // Required
        put("event_time", 1644778428)
        put("platform", "android")
        put("os_version", "33") // Android API version
        put("att", 1) // Advertiser Tracking Enabled
        put("ate", 1) // Application Tracking Enabled
        put("device_model", "Galaxy A23")
        put("currency", "USD")
        put("value", 142.54)
        put("app_id", "com.example.myapp")

        // Optional
        put("screen_width", "762")
        put("screen_height", "1278")
        put("external_storage", "128")
        put("free_space", "63")
        put("device_timezone", "Europe/London")
        // Add other optional properties here
        // ...

        // Required
        put("contents", JSONArray().apply {
            put(JSONObject().apply {
                put("id", "ABC123")
                put("quantity", 1)
            })
            put(JSONObject().apply {
                put("id", "DEF456")
                put("quantity", 1)
            })
        })

        // Required
        put("content_type", "product")
    })

            // Advanced Matching Parameters
            put("amp", JSONObject().apply {
                // Required
                put("anon_id", "123456abc") // The ID of a person who has installed the app anonymously
                put("madid", "7497f910905fb413fe27a64a557778063") // IDFA or Google advertising ID

                // Optional
                put("ph", "8a187a62e69697497f910905fb413fe27a64a5577780636db7b18305e57cacaf") // Phone
                put("em", "55e79200c1635b37ad31a378c39feb12f120f116625093a19bc32fff15041149") // Email
                put("ge", "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111") // Gender
                put("db", "d5234ccb6fb4dd59cbe76a3e5e1f396c27d2862246c7dbd7bc9e8fd72d664aaf") // Date Of Birth
                put("ct", "350c754ba4d38897693aa077ef43072a859d23f613443133fecbbd90a3512ca5") // City
                put("zp", "3ebb904517b0c6cf6b2d387f1e5f0ba715258125efc4012d88c4053d0da62233") // Zip Code
                put("cn", "79adb2a2fce5c6ba215fe5f27f532d4e7edbac4b6a5e09e1ef3a08084a904621") // Country
            })
}

3. Add To Cart Event

jsonBody — AddToCart
val jsonBody = JSONObject().apply {

    // Required
    put("eid", "d290f1ee-6c54-4b01-90e6-d701748f0851") // Any Unique Identifier
    put("et", "AddToCart") // Event Name
    put("ua", "App") // App Static Value

    put("p", JSONObject().apply {
        // Required
        put("event_time", 1644778428)
        put("platform", "android")
        put("os_version", "33") // Android API version
        put("att", 1) // Advertiser Tracking Enabled
        put("ate", 1) // Application Tracking Enabled
        put("device_model", "Galaxy A23")
        put("currency", "USD")
        put("value", 142.54)
        put("app_id", "com.example.myapp")

        // Optional
        put("screen_width", "762")
        put("screen_height", "1278")
        put("external_storage", "128")
        put("free_space", "63")
        put("device_timezone", "Europe/London")
        // Add other optional properties here
        // ...

        // Required
        put("contents", JSONArray().apply {
            put(JSONObject().apply {
                put("id", "ABC123")
                put("quantity", 1)
            })
            put(JSONObject().apply {
                put("id", "DEF456")
                put("quantity", 1)
            })
        })

        // Required
        put("content_type", "product")
    })

            // Advanced Matching Parameters
            put("amp", JSONObject().apply {
                // Required
                put("anon_id", "123456abc") // The ID of a person who has installed the app anonymously
                put("madid", "7497f910905fb413fe27a64a557778063") // IDFA or Google advertising ID

                // Optional
                put("ph", "8a187a62e69697497f910905fb413fe27a64a5577780636db7b18305e57cacaf") // Phone
                put("em", "55e79200c1635b37ad31a378c39feb12f120f116625093a19bc32fff15041149") // Email
                put("ge", "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111") // Gender
                put("db", "d5234ccb6fb4dd59cbe76a3e5e1f396c27d2862246c7dbd7bc9e8fd72d664aaf") // Date Of Birth
                put("ct", "350c754ba4d38897693aa077ef43072a859d23f613443133fecbbd90a3512ca5") // City
                put("zp", "3ebb904517b0c6cf6b2d387f1e5f0ba715258125efc4012d88c4053d0da62233") // Zip Code
                put("cn", "79adb2a2fce5c6ba215fe5f27f532d4e7edbac4b6a5e09e1ef3a08084a904621") // Country
            })
}

4. Lead Event

jsonBody — Lead
val jsonBody = JSONObject().apply {

    // Required
    put("eid", "d290f1ee-6c54-4b01-90e6-d701748f0851") // Any Unique Identifier
    put("et", "Lead") // Event Name
    put("ua", "App") // App Static Value

    put("p", JSONObject().apply {
        // Required
        put("event_time", 1644778428)
        put("platform", "android")
        put("os_version", "33") // Android API version
        put("att", 1) // Advertiser Tracking Enabled
        put("ate", 1) // Application Tracking Enabled
        put("device_model", "Galaxy A23")

        // Optional
        put("screen_width", "762")
        put("screen_height", "1278")
        put("external_storage", "128")
        put("free_space", "63")
        put("device_timezone", "Europe/London")
        // Add other optional properties here
        // ...
    })

            // Advanced Matching Parameters
            put("amp", JSONObject().apply {
                // Required
                put("anon_id", "123456abc") // The ID of a person who has installed the app anonymously
                put("madid", "7497f910905fb413fe27a64a557778063") // IDFA or Google advertising ID

                // Optional
                put("ph", "8a187a62e69697497f910905fb413fe27a64a5577780636db7b18305e57cacaf") // Phone
                put("em", "55e79200c1635b37ad31a378c39feb12f120f116625093a19bc32fff15041149") // Email
                put("ge", "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111") // Gender
                put("db", "d5234ccb6fb4dd59cbe76a3e5e1f396c27d2862246c7dbd7bc9e8fd72d664aaf") // Date Of Birth
                put("ct", "350c754ba4d38897693aa077ef43072a859d23f613443133fecbbd90a3512ca5") // City
                put("zp", "3ebb904517b0c6cf6b2d387f1e5f0ba715258125efc4012d88c4053d0da62233") // Zip Code
                put("cn", "79adb2a2fce5c6ba215fe5f27f532d4e7edbac4b6a5e09e1ef3a08084a904621") // Country
            })
}

5. Complete Registration Event

jsonBody — CompleteRegistration
val jsonBody = JSONObject().apply {

    // Required
    put("eid", "d290f1ee-6c54-4b01-90e6-d701748f0851") // Any Unique Identifier
    put("et", "CompleteRegistration") // Event Name
    put("ua", "App") // App Static Value

    put("p", JSONObject().apply {
        // Required
        put("event_time", 1644778428)
        put("platform", "android")
        put("os_version", "33") // Android API version
        put("att", 1) // Advertiser Tracking Enabled
        put("ate", 1) // Application Tracking Enabled
        put("device_model", "Galaxy A23")

        // Optional
        put("screen_width", "762")
        put("screen_height", "1278")
        put("external_storage", "128")
        put("free_space", "63")
        put("device_timezone", "Europe/London")
        // Add other optional properties here
        // ...
    })

            // Advanced Matching Parameters
            put("amp", JSONObject().apply {
                // Required
                put("anon_id", "123456abc") // The ID of a person who has installed the app anonymously
                put("madid", "7497f910905fb413fe27a64a557778063") // IDFA or Google advertising ID

                // Optional
                put("ph", "8a187a62e69697497f910905fb413fe27a64a5577780636db7b18305e57cacaf") // Phone
                put("em", "55e79200c1635b37ad31a378c39feb12f120f116625093a19bc32fff15041149") // Email
                put("ge", "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111") // Gender
                put("db", "d5234ccb6fb4dd59cbe76a3e5e1f396c27d2862246c7dbd7bc9e8fd72d664aaf") // Date Of Birth
                put("ct", "350c754ba4d38897693aa077ef43072a859d23f613443133fecbbd90a3512ca5") // City
                put("zp", "3ebb904517b0c6cf6b2d387f1e5f0ba715258125efc4012d88c4053d0da62233") // Zip Code
                put("cn", "79adb2a2fce5c6ba215fe5f27f532d4e7edbac4b6a5e09e1ef3a08084a904621") // Country
            })
}

6. Contact Event

jsonBody — Contact
val jsonBody = JSONObject().apply {

    // Required
    put("eid", "d290f1ee-6c54-4b01-90e6-d701748f0851") // Any Unique Identifier
    put("et", "Contact") // Event Name
    put("ua", "App") // App Static Value

    put("p", JSONObject().apply {
        // Required
        put("event_time", 1644778428)
        put("platform", "android")
        put("os_version", "33") // Android API version
        put("att", 1) // Advertiser Tracking Enabled
        put("ate", 1) // Application Tracking Enabled
        put("device_model", "Galaxy A23")

        // Optional
        put("screen_width", "762")
        put("screen_height", "1278")
        put("external_storage", "128")
        put("free_space", "63")
        put("device_timezone", "Europe/London")
        // Add other optional properties here
        // ...
    })

            // Advanced Matching Parameters
            put("amp", JSONObject().apply {
                // Required
                put("anon_id", "123456abc") // The ID of a person who has installed the app anonymously
                put("madid", "7497f910905fb413fe27a64a557778063") // IDFA or Google advertising ID

                // Optional
                put("ph", "8a187a62e69697497f910905fb413fe27a64a5577780636db7b18305e57cacaf") // Phone
                put("em", "55e79200c1635b37ad31a378c39feb12f120f116625093a19bc32fff15041149") // Email
                put("ge", "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111") // Gender
                put("db", "d5234ccb6fb4dd59cbe76a3e5e1f396c27d2862246c7dbd7bc9e8fd72d664aaf") // Date Of Birth
                put("ct", "350c754ba4d38897693aa077ef43072a859d23f613443133fecbbd90a3512ca5") // City
                put("zp", "3ebb904517b0c6cf6b2d387f1e5f0ba715258125efc4012d88c4053d0da62233") // Zip Code
                put("cn", "79adb2a2fce5c6ba215fe5f27f532d4e7edbac4b6a5e09e1ef3a08084a904621") // Country
            })
}