Playbooks
Serving Moments in Mobile Apps
Kotlin - Moments API Integration Guide
16 min
overview the moments api docid\ duaxqa4vbbpvihg4cqaac 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 to explore a working example, see the momentscience android demo on github prerequisites before you begin, ensure the following requirements are met api key before you start the integration, you must acquire a unique api key follow the instructions provided here to obtain your key android version target android sdk 26 or higher add permission add internet permission in your androidmanifest 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 to learn more about request structure, see the moments api documentation https //docs adspostx com/moments api#rqin step 1 fetch offers use the following function to request offers from the moments api using okhttp you can adapt the logic for retrofit or other http clients as needed fetchoffers suspend fun fetchoffers( context context, 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 val payload = mapof( "adpx fp" to "\<unique value>", "pub user id" to "\<unique value>", "placement" to "checkout", "ua" to "\<user agent value>", ) use the fetchoffers function to retrieve offers from the moments api you can adapt this logic for retrofit or other http clients as needed request parameters parameter type description required api key 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 dev 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 unique user identifier pub user id string a unique, non pii identifier for the end user placement string an attribute that represents the specific page, section, or location where the offer unit was triggered dev string set to "1" to enable test mode 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 moments api documentation https //docs momentscience com/moments api refer offersapi kt and 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 offercontainerview , offerview , 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 for a detailed explanation of how each field in the offer object is used refer to the offer anatomy docid\ t8o0a 3bctma448n5pyhw this guide will help you understand how to map api fields to ui components and apply dynamic styling correctly 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 offercontainerview usage 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 see offercontainerview\ kt and offersviewmodel kt for full implementation examples 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 class, following the mvvm pattern offerviewpreview\ kt 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() } ) for advanced usage and dynamic styling, refer to offerview\ kt if you prefer to use your own layout, styling, or framework specific widgets, you can parse the offer response manually use the offer anatomy docid\ t8o0a 3bctma448n5pyhw 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 sending beacon requests use this helper function to send get requests to the beacon urls provided in each offer example sending beacon requests 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() } } firing the close beacon send the close beacon when the user dismisses the offer container fun onclose(offer offer) { offer beacons? close? let { url > sendgetrequest(url) } // additional cleanup or ui logic } firing the βno thanksβ beacon track when a user explicitly rejects an offer fun onnegativeclick(offer offer) { offer beacons? nothanksclick? let { url > sendgetrequest(url) } // handle navigation or dismissal } firing pixel events on offer display call these tracking urls as soon as the offer is shown to the user fun onofferdisplayed(offer offer) { offer pixel? let { url > sendgetrequest(url) } offer advpixelurl? let { url > sendgetrequest(url) } } see offersviewmodel kt in the demo app for full examples of how tracking events are integrated into offer flow logic next steps we recommend that you go through the moments api implementation 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