---
title: Android (Kotlin) - MomentPerks API Integration Guide
slug: android-kotlin-momentperks-api-integration-guide
icon: {"faIcon":"fa-brands fa-android"}
docTags: 
createdAt: 2025-07-25T19:53:51.007Z
---

# Overview

The [MomentPerks API](https://docs.momentscience.com/momentperks-api) enables you to present personalized offers to users within your Android application. This guide walks you through the integration steps, from setup to offer rendering and event tracking. so you can deliver high-converting experiences with minimal development effort.

:::hint{type="success"}
To explore a working example, see the [MomentScience Android demo on GitHub](https://github.com/AdsPostX/examples/tree/main/android-native/MomentsAPIDemoApp-Android)
:::

# Prerequisites

Before you begin, ensure the following requirements are met:

- **API Key:&#x20;**&#x42;efore you start the integration, you must acquire a unique API key. Follow the [instructions provided here](https://docs.momentscience.com/getting-your-api-key) to obtain your key.
- **Android Version:** Target Android SDK `26`or higher.&#x20;
- **Add Permission:&#x20;**&#x41;dd Internet permission in your `AndroidManifest.xml`

:::CodeblockTabs{indent="2"}
```xml
<uses-permission android:name="android.permission.INTERNET" />
```
:::

# Integration Steps

The Moments API delivers personalized offer data based on a set of query parameters and request payload values. In this section, you’ll implement a utility function to retrieve and normalize offers for use in your Android UI.

:::hint{type="success"}
To learn more about request structure, see the [MomentPerks API documentation](https://docs.momentscience.com/momentperks-api).
:::

## Step 1:  Fetch Offers

Use the following function to request offers from the Moments API. You can adapt the logic for Retrofit or other HTTP clients as needed.

:::CodeblockTabs
fetchOffers

```kotlin
suspend fun fetchOffers(
    apiKey: String,
    loyaltyBoost: String?,
    creative: String?,
    campaignId: String?,
    isDevelopment: Boolean = false,
    payload: Map<String, String> = emptyMap()
) {
    withContext(Dispatchers.IO) {
        var connection: HttpURLConnection? = null
        try {
            // Build URL with query parameters
            val baseUrl = "https://api.adspostx.com/native/v4/offers.json"
            val urlBuilder = StringBuilder(baseUrl)
            urlBuilder.append("?api_key=").append(URLEncoder.encode(apiKey, "UTF-8"))
            
            // Add optional parameters only if they are not null
            loyaltyBoost?.let { 
                urlBuilder.append("&loyaltyboost=").append(URLEncoder.encode(it, "UTF-8"))
            }
            creative?.let { 
                urlBuilder.append("&creative=").append(URLEncoder.encode(it, "UTF-8"))
            }
            campaignId?.let { 
                urlBuilder.append("&campaignId=").append(URLEncoder.encode(it, "UTF-8"))
            }
            
            val url = URL(urlBuilder.toString())
            
            // Create JSON body
            val bodyJson = JSONObject(payload)
            if (isDevelopment) {
                bodyJson.put("dev", true)
            }
            val requestBody = bodyJson.toString()
            
            // Configure HttpURLConnection
            connection = url.openConnection() as HttpURLConnection
            connection.apply {
                requestMethod = "POST"
                doOutput = true
                doInput = true
                connectTimeout = 30000 // 30 seconds
                readTimeout = 30000 // 30 seconds
                setRequestProperty("Content-Type", "application/json")
            }
            
            // Write request body
            connection.outputStream.use { outputStream ->
                outputStream.write(requestBody.toByteArray(Charsets.UTF_8))
                outputStream.flush()
            }
            
            // Read response
            val responseCode = connection.responseCode
            val inputStream = if (responseCode >= 200 && responseCode < 300) {
                connection.inputStream
            } else {
                connection.errorStream
            }

            val responseBody = inputStream?.use { stream ->
                stream.bufferedReader(Charsets.UTF_8).readText()
            } ?: ""
            
            if (responseCode >= 200 && responseCode < 300 && responseBody.isNotEmpty()) {
                Log.d("FetchOffers", "Response: $responseBody")
            } else {
                Log.e("FetchOffers", "HTTP $responseCode: ${responseBody.ifEmpty { "Empty response" }}")
            }
            
        } catch (e: IOException) {
            Log.e("FetchOffers", "Network error: ${e.message}", e)
        } catch (e: Exception) {
            Log.e("FetchOffers", "Unexpected error: ${e.message}", e)
        } finally {
            connection?.disconnect()
        }
    }
}
```

Payload Example

```kotlin
val payload = mapOf(
    "adpx_fp" to "<unique_value>",
    "pub_user_id" to "<unique_value>",
    "placement" to "checkout",
    "ua" to "<user_agent_value>",
)

```
:::

:::hint{type="info"}
Use the `fetchOffers` function to retrieve offers from the Moments API. You can adapt this logic for Retrofit or other HTTP clients as needed
:::

**Parameters**

| **Parameter**   | **Type**             | **Description**                                                                                  | **Required** |
| --------------- | -------------------- | ------------------------------------------------------------------------------------------------ | ------------ |
| `apiKey`        | String               | The API key associated with your MomentScience account.                                          | Yes          |
| `loyaltyboost`  | String               | Sets the loyalty boost level for the offers. Accepts "0", "1", or "2".                           | No           |
| `creative`      | String               | Determines the creative mode for the offers. Accepts "0" or "1".                                 | No           |
| `campaignId`    | String               | Optional campaign tracking ID.                                                                   | No           |
| `isDevelopment` | String               | Enables development mode. Set to "1" for testing environments.                                   | No           |
| `payload`       | Map\<String, String> | Pass any extra data required for offer targeting or tracking as a map of string key-value pairs. | No           |

**Common payload fields:**

| **Field**     | **Type** | **Description**                                                                                                                                    |
| ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `adpx_fp`     | String   | A unique identifier for the end user                                                                                                               |
| `pub_user_id` | String   | A unique, non-PII identifier for the end user. This value links offers to individual users and must remain consistent across sessions and devices. |
| `placement`   | String   | An attribute that represents the specific page, section, or location where the Offer Unit was triggered                                            |
| `dev`         | String   | Use "1" to return test offers.                                                                                                                     |
| `ua`          | String   | User-Agent String                                                                                                                                  |

**Response**

A successful response returns a JSON object containing the available offers and related metadata. For detailed response structure and field descriptions, refer to the [official MomentPerks API documentation](https://docs.momentscience.com/momentperks-api).

:::hint{type="info"}
Refer [OffersApi.kt ](https://github.com/AdsPostX/examples/blob/main/android-native/MomentsAPIDemoApp-Android/app/src/main/java/com/momentscience/android/msapidemoapp/service/api/OffersApi.kt)and [OffersResponse.kt](https://github.com/AdsPostX/examples/blob/main/android-native/MomentsAPIDemoApp-Android/app/src/main/java/com/momentscience/android/msapidemoapp/model/OffersResponse.kt) for demo app implementation.
:::

***

## Step 2: Build the Offer UI

After retrieving offers from the API, use Jetpack Compose to present them in a scrollable or modal container. This section walks through the structure of both the container and individual offer views.

:::::VerticalSplit{layout="middle"}
::::VerticalSplitItem
:::hint{type="warning"}
[`OfferContainerView`](https://github.com/AdsPostX/examples/blob/main/android-native/MomentsAPIDemoApp-Android/app/src/main/java/com/momentscience/android/msapidemoapp/ui/components/OfferContainerView.kt), [`OfferView`](https://github.com/AdsPostX/examples/blob/main/android-native/MomentsAPIDemoApp-Android/app/src/main/java/com/momentscience/android/msapidemoapp/ui/components/OfferView.kt), [`OffersViewModel.kt`](https://github.com/AdsPostX/examples/blob/main/android-native/MomentsAPIDemoApp-Android/app/src/main/java/com/momentscience/android/msapidemoapp/ui/viewmodel/OffersViewModel.kt) and other components shown below are reference implementations. You are free to implement your own UI components based on your app’s design system and platform conventions.
:::


::::

:::VerticalSplitItem


![](https://api.archbee.com/api/optimize/ELjiwjWcrv0a1IejQFFAF/mfMauv2_lvH0QZDe41Rxl_image.png "Example App UI")
:::
:::::

:::hint{type="success"}
For a detailed explanation of how each field in the offer object is used refer to the [Offer Anatomy documentation](https://docs.momentscience.com/offer-anatomy). This guide will help you understand how to map API fields to UI components and apply dynamic styling correctly.&#x20;
:::

### &#x20;Display the Offer Container

Use the `OfferContainerView` composable to display a list of offers with navigation controls and basic styling. This container also handles user actions like close, accept, decline, and pagination.

:::CodeblockTabs
OfferContainerView usage

```kotlin
OfferContainerView(
    offers = apiOffers,
    styles = apiStyles,
    currentOfferIndex = 0,
    onClose = { viewModel.dismissOffers() },
    onPositiveClick = { offer -> viewModel.handlePositiveAction(offer) },
    onNegativeClick = { offer -> viewModel.handleNegativeAction(offer) },
    onPreviousClick = { viewModel.showPreviousOffer() },
    onNextClick = { viewModel.showNextOffer() }
)
```
:::

This container UI handles:

- Loading and error states
- Modal or embedded offer presentation
- Navigation across multiple offers
- Action tracking for positive CTA tap, negative CTA tap, and close CTA tap.

:::hint{type="info"}
See [OfferContainerView.kt](https://github.com/AdsPostX/examples/blob/main/android-native/MomentsAPIDemoApp-Android/app/src/main/java/com/momentscience/android/msapidemoapp/ui/components/OfferContainerView.kt) and [OffersViewModel.kt](https://github.com/AdsPostX/examples/blob/main/android-native/MomentsAPIDemoApp-Android/app/src/main/java/com/momentscience/android/msapidemoapp/ui/viewmodel/OffersViewModel.kt) for full implementation examples.&#x20;
:::

### Render Individual Offer

Each offer is displayed using the `OfferView` composable, which presents offer details such as title, image, description, and call-to-action buttons with dynamic styling. The business logic and state for the offer presentation are managed by the [`OffersViewModel`](https://github.com/AdsPostX/examples/blob/main/android-native/MomentsAPIDemoApp-Android/app/src/main/java/com/momentscience/android/msapidemoapp/ui/viewmodel/OffersViewModel.kt) class, following the MVVM pattern.

:::CodeblockTabs
OfferViewPreview\.kt

```kotlin
OfferView(
    offer = Offer(
        title = "Special Offer",
        description = "Limited time discount!",
        image = "https://example.com/image.jpg",
        ctaYes = "Claim Now",
        ctaNo = "Maybe Later"
    ),
    styles = apiStyles,
    onPositiveClick = { handlePositiveAction() },
    onNegativeClick = { handleNegativeAction() }
)
```
:::

:::hint{type="info"}
For advanced usage and dynamic styling, refer to[ OfferView.kt.](https://github.com/AdsPostX/examples/blob/main/android-native/MomentsAPIDemoApp-Android/app/src/main/java/com/momentscience/android/msapidemoapp/ui/components/OfferView.kt)
:::

:::hint{type="success"}
If you prefer to use your own layout, styling, or framework-specific widgets, you can:

- Parse the offer response manually.
- Use the [Offer Anatomy](https://docs.momentscience.com/offer-anatomy) documentation to map fields like title, image, cta\_yes, etc.
- Apply any visual styling or logic defined in your own architecture.

This approach gives you full control over the user experience, while still integrating with the core Moments API logic.
:::

***

## Step 3. Track User Interactions

To monitor engagement and optimize performance, fire event beacons at key user interaction points. The `sendGetRequest` function handles sending these tracking beacons.

1. **Sending Beacon Requests:&#x20;**&#x55;se this helper function to send GET requests to the beacon URLs provided in each offer

:::CodeblockTabs{indent="2"}
Example: Sending Beacon Requests

```kotlin
import java.net.HttpURLConnection
import java.net.URL

fun sendGetRequest(fullUrl: String) {
    val url = URL(fullUrl)
    val connection = url.openConnection() as HttpURLConnection
    try {
        connection.requestMethod = "GET"
        val response = connection.inputStream.bufferedReader().use { it.readText() }
        println("Response Body: $response")
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        connection.disconnect()
    }
}
```
:::

2. **Firing the Close Beacon:&#x20;**&#x53;end the `close` beacon when the user dismisses the offer container

:::CodeblockTabs{indent="2"}
```kotlin
fun onClose(offer: Offer) {
    offer.beacons?.close?.let { url ->
        sendGetRequest(url)
    }
    // Additional cleanup or UI logic
}
```
:::

3. **Firing the “No Thanks” Beacon:&#x20;**&#x54;rack when a user explicitly rejects an offer

:::CodeblockTabs{indent="2"}
```kotlin
fun onNegativeClick(offer: Offer) {
    offer.beacons?.noThanksClick?.let { url ->
        sendGetRequest(url)
    }
    // Handle navigation or dismissal
}
```
:::

4. **Firing Pixel Events on Offer Display:&#x20;**&#x43;all these tracking URLs as soon as the offer is shown to the user

:::CodeblockTabs{indent="2"}
```kotlin
fun onOfferDisplayed(offer: Offer) {
    offer.pixel?.let { url ->
        sendGetRequest(url)
    }
    offer.advPixelUrl?.let { url ->
        sendGetRequest(url)
    }
}
```
:::

:::hint{type="info"}
See[ OffersViewModel.kt ](https://github.com/AdsPostX/examples/blob/main/android-native/MomentsAPIDemoApp-Android/app/src/main/java/com/momentscience/android/msapidemoapp/ui/viewmodel/OffersViewModel.kt)in the demo app for full examples of how tracking events are integrated into offer flow logic.&#x20;
:::

# Next Steps

We recommend that you go through [the MomentPerks API Implementation Checklist ](https://docs.momentscience.com/momentperks-integration-checklist)to verify your integration. Completing this checklist ensures that all best practices and requirements are met for a successful Moments API deployment.

***

📢 If you're running into any issues while going through the integration process, feel free to contact us at [help@momentscience.com](#)&#x20;
