ayeT-Studios Publisher Android SDK Integration Guide (v3.6)
Publisher Documentation Overview
- API Placement - Documentation
- Android Placement - SDK Integration Guide
- iOS Placement - Swift SDK Integration Guide
- Unity SDK Integration Guide
- Web Placement - In-App Webview Offerwall Integration Guide
- Web Placement - Mobile & Desktop Browser Offerwall Integration Guide
- Rewarded Video HTML5 Web Integration Guide
Updates
2022-04-12: v3.6 - Updated SDK to Android Api 30 (Android 11) and restored compliance with updated privacy requirements
2022-02-11: v3.5.2 - Removed Rewarded Video from documentation
2020-08-11: v3.5.1 - Updated documentation to introduce new parametars to differentiate chargebacks from conversions
2020-07-29: v3.5 - Improved UI / handling, removed deprecated INSTALL_REFERRER, performance improvements and bugfixes
2019-08-20: v3.4 - Updated Android SDK to support decimal places in payouts (currency amount) for native offers
2019-07-19: v3.3 - Updated Android SDK and documentation to reflect the adslot changes
2019-06-21: v3.2 - Native Offer Feed functionality
2019-02-01: v3.1 - Fixed conversion tracking issues under certain conditions (application lifecycle monitoring improved)
2018-11-29: v3.0 - Full support for API 26+ build targets, bugfixes, simplified use & improved handling under poor network conditions
2018-07-02: v2.1 - Fixed video ad orientation & resolution issues on Android 8.1
2018-06-18: v2.0 - Video Ads & Rewarded Video Ads
2017-11-01: v1.1 - Fixed incompatibilities with present GSON dependencies
2017-10-29: v1.0 - Initial Release of our Publisher SDK (Android)
Quick 3.1 To 3.6 Migration Guide
- Update your AyetSdk.showOfferwall() calls to pass the target offerwall adslot name as second parameter
- Update your OfferwallActivity declaration in AndroidManifest.xml according to the documentation
Quick 2.1 To 3.1 Migration Guide
- remove the PACKAGE_ADDED BroadcastReceiver from AndroidManifest.xml
- call AyetSdk.init(...) with "getApplication()" instead of "getApplicationContext()"
Quick 1.1 To 2.1 (Video Ads) Migration Guide
- add video activity to AndroidManifest.xml (see topic 3)
- update your proguard-rules file (see topic 9)
Introduction
The ayeT-Studios Publisher SDK allows you to easily monetize your app and reward your users with in-app currency. Your users get access to our offerwalls, allowing them to earn currency for completing tasks such as trying new apps, answering surveys, signing up for free trials or watching video ads. The integration is simple, allows both managed (our servers store user information and balances) and unmanaged (you receive callbacks and handle user wallets yourself) currencies and also works well alongside traditional in-app purchases.Topics
- Prerequisites
- Download the library
- Add the library to your Android Studio project
- Initialize the SDK & Receive Managed Balances
- Show the Offerwall
- Request Native Offer Feed
- Show Video Ads
- Appendix I: Unmanaged Currency Handling / Conversion Callbacks
- Appendix II: Proguard Rules / Release Builds
- Appendix III: Multiple INSTALL_REFERRER Receivers
1. Prerequisites
Before integrating the SDK in your app, you should sign up for a publisher account. Afterwards login as publisher and create a new Android App Placement using the correct package name you intend to use for your final app.
This is important since we'll generate an APP_KEY / Identifier for you which has to be added to your AndroidManifest (see step 3).
This is important since we'll generate an APP_KEY / Identifier for you which has to be added to your AndroidManifest (see step 3).
2. Download the library
3. Add the library to your Android Studio project
Copy the downloaded jar library to your Android Studio project (in the app/libs/ folder).
Go to "Module Settings" (F12 or right-click your app in the Project View) and check the dependencies tab to make sure the library is added as a file dependency and set to "Compile":
Afterwards open your AndroidManifest.xml and add our offerwall activity to your application scope:
Another AndroidManifest.xml requirement is your AYET_APP_KEY which you can fetch from our publisher dashboard in your placement or adslot details - add it to the application scope as well:
Also make sure to check your permissions in the AndroidManifest.xml:
This is an example for a complete AndroidManifest.xml:
Go to "Module Settings" (F12 or right-click your app in the Project View) and check the dependencies tab to make sure the library is added as a file dependency and set to "Compile":

Afterwards open your AndroidManifest.xml and add our offerwall activity to your application scope:
<activity android:name="com.ayetstudios.publishersdk.OfferwallActivity" android:configChanges="orientation|screenSize"> <intent-filter android:label="offer"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="offer" android:host="com.example.myapplication" /> <!-- Replace with your lower case package name --> </intent-filter> </activity> <activity android:name="com.ayetstudios.publishersdk.VideoActivity" android:configChanges="orientation|screenSize" />
Another AndroidManifest.xml requirement is your AYET_APP_KEY which you can fetch from our publisher dashboard in your placement or adslot details - add it to the application scope as well:
<meta-data android:name="AYET_APP_KEY" android:value="xxxxxxxxxxxxxxxx" />
Also make sure to check your permissions in the AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" /> <!-- mandatory permission --> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- optional --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- optional -->
This is an example for a complete AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapplication"> <!-- Replace with your lower case package name --> <uses-permission android:name="android.permission.INTERNET" /> <!-- mandatory permission --> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- optional --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- optional --> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- Publisher SDK Specific--> <activity android:name="com.ayetstudios.publishersdk.OfferwallActivity" android:configChanges="orientation|screenSize"> <intent-filter android:label="offer"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="offer" android:host="com.example.myapplication" /> <!-- Replace with your lower case package name --> </intent-filter> </activity> <activity android:name="com.ayetstudios.publishersdk.VideoActivity" android:configChanges="orientation|screenSize"></activity> <meta-data android:name="AYET_APP_KEY" android:value="xxxxxxxxxxxxxxxx" /> <!-- End of: Publisher SDK Specific--> </application> </manifest>
4. Initialize the SDK & Managed User Balances
In this step, we are going to initialize the SDK in the main activity. We also use callbacks to track the user's account balance - this is optional and not required if you're planning to manage user balances on your own servers.
Attention: The username or external identifier passed in the init call (e.g. username, hashed email address, etc.) will be accessible in the conversion callbacks through the {external_identifier} parameter.
If you want to make sure the SDK has been initialized and is ready to display the offerwall or video ads, you can use the following function:
If you want to spend user currency, for example if the user clicks a "purchaseInAppItem" button, you can utilize the deductUserBalance function:
Attention: The username or external identifier passed in the init call (e.g. username, hashed email address, etc.) will be accessible in the conversion callbacks through the {external_identifier} parameter.
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); AyetSdk.init(getApplication(), "username (external identifier)", new UserBalanceCallback() { // UserBalanceCallback is optional if you want to manage balances on your servers @Override public void userBalanceChanged(SdkUserBalance sdkUserBalance) { Log.d("AyetSdk" , "userBalanceChanged - available balance: "+sdkUserBalance.getAvailableBalance()); // this is the new total available balance for the user } @Override public void userBalanceInitialized(SdkUserBalance sdkUserBalance) { Log.d("AyetSdk" , "SDK initialization successful"); Log.d("AyetSdk" , "userBalanceInitialized - available balance: "+sdkUserBalance.getAvailableBalance()); // this is the total available balance for the user Log.d("AyetSdk" , "userBalanceInitialized - spent balance: "+sdkUserBalance.getSpentBalance()); // this is the total amount spent with "AyetSdk.deductUserBalance(..)" Log.d("AyetSdk" , "userBalanceInitialized - pending balance: "+sdkUserBalance.getPendingBalance()); // this is the amount currently pending for conversion (e.g. user still has offer requirements to meet) } @Override public void initializationFailed() { Log.d("AyetSdk", "initializationFailed - please check APP API KEY & internet connectivity"); } }); setContentView(R.layout.activity_main); }
If you want to make sure the SDK has been initialized and is ready to display the offerwall or video ads, you can use the following function:
if (AyetSdk.isInitialized()) { Log.d("AyetSdk" , "SDK is ready"); } else { Log.d("AyetSdk" , "SDK is NOT ready"); }
If you want to spend user currency, for example if the user clicks a "purchaseInAppItem" button, you can utilize the deductUserBalance function:
mPurchaseInAppItemButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { int amount=100; AyetSdk.deductUserBalance(getApplication(), amount, new DeductUserBalanceCallback() { @Override public void success() { Log.d("AyetSdk" , "deductUserBalance - successful, new available balance: "+AyetSdk.getAvailableBalance()); // TODO: activate the purchased content } @Override public void failed() { Log.d("AyetSdk" , "deductUserBalance - failed"); // this usually means that the user does not have sufficient balance in his account } }); } });
5. Show the Offerwall
Showing the offerwall for an offerwall adslot is straight-forward, you can simply call showOfferwall from any Activity:
AyetSdk.showOfferwall(getApplication(), "offerwall adslot name");showOfferwall starts the offerwall activity for the adslot you pass as second parameter and displays available tasks for the user to complete.
6. Request Native Offer Feed
If you want to display the offers manually in a custom form (e.g. "native offers"), you can call getNativeOffers for a Native Offer Feed adslot you created in your placement:
After retrieving a list of native offers, you can store them and later activate one of the offers like this:
Class members for each AyetOffer entry are:
AyetSdk.getNativeOffers(getApplication(), "native adslot name", new NativeOffersCallback() { @Override public void onResult(boolean success, NativeOfferList responseMessage) { if (success) { Log.e("JSON Native Offers", new Gson().toJson( responseMessage.offers ) ); MyApplication.this.nativeOfferCache=responseMessage.offers; } } });getNativeOffers fetches all offers fitting this user / device in the background and calls NativeOffersCallback on completion. This usually takes between 0.5s and 4s depending on your adslot configuration and traffic sources.
After retrieving a list of native offers, you can store them and later activate one of the offers like this:
AyetSdk.activateOffer(MainActivity.this, MyApplication.nativeOfferCache.get(0).getId(), new ActivateOfferCallback() { @Override public void onFailed() { // This is triggered if the offer is not available anymore or if connectivity problems are present } @Override public void onSuccess() { // This is triggered right before starting a Google Play intent or trying to open the default browser } });activateOffer takes an offer id found in the native offer feed, checks the requirements and tries to reserve it for the user.
Class members for each AyetOffer entry are:
String id; // the id of the offer, used in activateOffer call String name; // display name of the offer String icon; // url to the offer icon int category; // NativeOfferList.CATEGORY_INCENT | NativeOfferList.CATEGORY_NONINCENT int type; // NativeOfferList.TYPE_CPI | NativeOfferList.TYPE_CPA | NativeOfferList.TYPE_CPL String description; // a short description of the offer String instructions; // conversion instructions for the user int conversionTime; // average / estimated conversion time in seconds int payout; // the payout in the configured virtual currency ArrayList<AyetCreative> creatives; // optional: additional creative data (images, videos) for the offer
7. Show Video Ads
Introduction to video ads
Video ads are a great way to monetize your apps & games. Compared to banner or static interstitial ads, they deliver an outstanding eCPM and are widely accepted by users - if implemented correctly.While video ads are non incentivized offers which either pay the publisher per view (CPM) or per installation / action (CPI / CPA), we give publishers the opportunity to reward their users for watching an optional (skippable) "rewarded" video ad.
Regardless of the video ad type, it's strictly forbidden to prompt users to install apps or interact with the promoted assets.
Normal video ads work like TV commercials. They are between 15s and 30s long and cannot be interrupted. A typical use case is a commercial break in games between levels.
Rewarded video ads are optional and user initiated. Users can skip rewarded ads at any point, but will be rewarded with in-app items or virtual currency if they watch it completely.
Normal videos are not limited in terms of frequency and capping, but for rewarded video ads it's strongly advised to check and configure the placement correctly (Settings > Rewarded Video Ads > Rewarded Video Capping & Frequency) to prevent abuse.
Both video ads and rewarded video ads send publisher earning callbacks without user or device information (so only offer information and payout_usd is set) if your callback URL is configured in the placement overview.
Please note that for CPM offers, it's possible that multiple views for a campaign are grouped into one earning and callback to avoid congestion.
Rewarded video ads can additionally (and independently from publisher earning callbacks) send user currency callbacks, if Settings > Rewarded Video Ads > Enable S2S View Callbacks is enabled. Given that the placement callback url is set and the currency amount per rewarded video view is greater than 0, this will send a S2S callback for each completed rewarded video view (currency_amount is set, but payout_usd is always 0).
If not, go to Placement > Settings > Video Ads and request access or contact your account manager to have them enabled.
To show a non-interruptable, fullscreen video ad (15-30 seconds depending on your placement video settings), you can make the following showVideoAd call:
AyetSdk.showVideoAd(getApplication(), "video adslot name", AyetSdk.FLAG_DEFAULT, new VideoCallbackHandler() { @Override public void nofill() { // This is called when either no video is available to watch, the AyetSdk has not been initialized or there were network/connectivity issues Log.d(TAG , "AyetSdk.showVideoAd::nofill()"); } @Override public void finished() { // This is called when the video activity is done (video completed, aborted or other problems like network speed) Log.d(TAG , "AyetSdk.showVideoAd::finished()"); } @Override public void willBeShown() { // This is called after selecting a video and right before the video activity is started to play it Log.d(TAG , "AyetSdk.showVideoAd::willBeShown()"); } });
If you want to restrict video ads to wifi connectivity only, you can append the AyetSdk.FLAG_WIFI_ONLY flag:
AyetSdk.showVideoAd(getApplication(), "video adslot name", AyetSdk.FLAG_DEFAULT | AyetSdk.FLAG_WIFI_ONLY, new VideoCallbackHandler() {...
By default, videos will be shown in their optimized orientation (which is landscape in most cases). If your app should strictly stay in a fixed orientation, you can utilize the AyetSdk.FLAG_ORIENTATION_PORTRAIT or AyetSdk.FLAG_ORIENTATION_LANDSCAPE flags to enforce the video orientation:
AyetSdk.showVideoAd(getApplication(), "video adslot name", AyetSdk.FLAG_DEFAULT | AyetSdk.FLAG_ORIENTATION_PORTRAIT, new VideoCallbackHandler() {...
Another capability of the SDK is to asychronously request a video ad. This gives you the opportunity to decide whether the video should actually play or if it should be discarded.
Hint: The average request-to-play time is around 1.5-3 seconds for video ads under good network conditions.
To request a video ad asynchonously, you can use the following code (note the different callback interface):
AyetSdk.showVideoAd(MainActivity.this, "video adslot name", AyetSdk.FLAG_DEFAULT | AyetSdk.FLAG_ASYNC, new VideoAsyncCallbackHandler() { @Override public void nofill() { // This is called when either no video is available to watch, the AyetSdk has not been initialized or there were network/connectivity issues Log.d(TAG , "AyetSdk.showVideoAdAsync::nofill()"); } @Override public void finished() { // This is called when the video activity is done (video completed, aborted or other problems like network speed) Log.d(TAG , "AyetSdk.showVideoAdAsync::finished()"); } @Override public void willBeShown() { // This is called after selecting a video, after "videoAd.showVideo()" has been called in "ready(...)" and right before the video activity is started to play it Log.d(TAG , "AyetSdk.showVideoAdAsync::willBeShown()"); } @Override public void ready(final VideoAdInterstitial videoAd) { // This is called when a video has been selected and is ready to be shown. You have about 60 seconds to decide whether you want to play the video by calling "videoAd.showVideo();" // If showVideo isn't called, the video will be discarded and the video activity won't start. Log.d(TAG , "AyetSdk.showVideoAdAsync::ready()"); // As an example, we're starting a timer task here who is signaling the SDK to show the prefetched video after a delay of 4 seconds new Timer().schedule(new TimerTask() { @Override public void run() { videoAd.showVideo(); } } , 4000); } });
8. Appendix I: Unmanaged Currency Handling / Conversion Callbacks
If you want to manually manage your users currencies on your own servers, you can configure a conversion callback url in our publisher dashboard.
To do so, navigate to Placements / Apps, edit your app placement and set the Callback Url to your server's postback url:
If this is the callback url your set for your app placement:
A typical conversion callback sent by our server will look like this:
Important: Your server must always reply with an HTTP 200 status code to our postbacks. Otherwise we will resend the postback 12 times over a span of one hour before giving up.
Available Macros for Postback URLs:
Postback Verification with HMAC Security Hash (optional):
Our server will always add a custom header, X-Ayetstudios-Security-Hash, containing a SHA256 HMAC hash of the request parameters and your publisher api key.
Your API key can be found in your dashboard at ayetstudios.com under settings.
To verify the hash, perform the following steps:
(1) Get all request parameters
(2) Order the request parameters alphabetically
(3) Build and compare the HMAC hash using the ordered request parameter string and your API key
PHP Example:
If your want to restrict postbacks to our callback server IPs, please whitelist the following IPs and check back regularly for possible changes:
To do so, navigate to Placements / Apps, edit your app placement and set the Callback Url to your server's postback url:

If this is the callback url your set for your app placement:
https://your-server.com/callback?network=ayetstudios&amount={currency_amount}&uid={external_identifier}&device={advertising_id}&payout_usd={payout_usd}
A typical conversion callback sent by our server will look like this:
https://your-server.com/callback?network=ayetstudios&amount=360&uid=username&device=[GAID]&payout_usd=0.36* Note: This assumes you set the user identifier to username in your AyetSdk.init(..) call, the currency conversion rate in your placement was 1000 per $1 and the user completed an offer with a $0.36 payout.
Important: Your server must always reply with an HTTP 200 status code to our postbacks. Otherwise we will resend the postback 12 times over a span of one hour before giving up.
Available Macros for Postback URLs:
{transaction_id} | string | Unique transaction id - use for duplicate checks. If chargeback it's prepend with r- |
{payout_usd} | float | The actual conversion payout in USD. If chargeback value is negative. |
{currency_amount} | float | The amount of currency the user earned (taken from your offerwall currency configuration). If chargeback value is negative. |
{external_identifier} | string | The user identifier set in your app for this user when initializing the sdk |
{user_id} | integer | Our internal id for this offerwall user |
{placement_identifier} | string | The placement_identifier for which the conversion occured |
{adslot_id} | int | The id of the adslot for which the conversion occured |
{ip} | string | Converting device's IP address if known, 0.0.0.0 otherwise |
{offer_id} | int | Offer ID of the converting offer |
{offer_name} | string | Name / title of the converting offer |
{device_uuid} | string | ayeT-Studios internal device identificator |
{device_make} | string | Device manufacturer |
{device_model} | string | Device model |
{advertising_id} | string | Device advertising id (GAID/IDFA) if known, otherwise empty |
{sha1_android_id} | string | Device sha1 hashed android id if known, otherwise empty |
{sha1_imei} | string | Device sha1 hashed imei if known, otherwise empty |
{is_chargeback} | int | Either 0 or 1. Indicator if the callback is a conversion (0) or a chargeback (1). |
{chargeback_reason} | string | Reason why chargeback created. Only available if is_chargeback set to 1. |
{chargeback_date} | string | Date of chargeback creation. Only available if is_chargeback set to 1. |
{task_name} | string | Only available for cpe campaigns, shows individual task name for that conversion. |
{currency_identifier} | string | Shows virtual currency name as set in adslot. |
{currency_conversion_rate} | decimal | Shows currency conversion rate used to calculate user currency for the given conversion. |
Postback Verification with HMAC Security Hash (optional):
Our server will always add a custom header, X-Ayetstudios-Security-Hash, containing a SHA256 HMAC hash of the request parameters and your publisher api key.
Your API key can be found in your dashboard at ayetstudios.com under settings.
To verify the hash, perform the following steps:
(1) Get all request parameters
(2) Order the request parameters alphabetically
(3) Build and compare the HMAC hash using the ordered request parameter string and your API key
PHP Example:
ksort($_REQUEST, SORT_STRING); $sortedQueryString = http_build_query($_REQUEST, '', '&'); // "adslot_id=123¤cy_amount=100&payout_usd=1.5...." $securityHash = hash_hmac('sha256', $sortedQueryString, 'YOUR PUBLISHER API KEY'); if($_SERVER['HTTP_X_AYETSTUDIOS_SECURITY_HASH']===$securityHash) { // actually sent as X-Ayetstudios-Security-Hash but converted by apache2 in this example // success } else { // invalid signature }
If your want to restrict postbacks to our callback server IPs, please whitelist the following IPs and check back regularly for possible changes:
35.165.166.40 35.166.159.131 52.40.3.140Last IP List Update: 2017-04-07
9. Appendix II: Proguard Rules / Release Builds
If you're going to use Proguard in your release build to obfuscate your application, make sure to add the following rules to your proguard-rules.pro files:
Important: It's always highly recommended to test your release builds before publishing them on Google Play to verify that they behave as intended!
-keep class com.ayetstudios.publishersdk.messages.** {*;} -keep public class com.ayetstudios.publishersdk.AyetSdk -keepclassmembers class com.ayetstudios.publishersdk.AyetSdk { public *; } -keep public interface com.ayetstudios.publishersdk.interfaces.UserBalanceCallback { *; } -keep public interface com.ayetstudios.publishersdk.interfaces.DeductUserBalanceCallback { *; } -keep class com.ayetstudios.publishersdk.models.VastTagReqData { *; }
Important: It's always highly recommended to test your release builds before publishing them on Google Play to verify that they behave as intended!