Only this pageAll pages
Powered by GitBook
1 of 25

3.4.0

Monetize with Playtime!

Loading...

Integration

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Resources

Loading...

Loading...

Loading...

Reporting APIs

Loading...

Loading...

Intro to Playtime

Maximize your app’s revenue and engagement effortlessly with Playtime - adjoe’s mobile-apps-centric rewarded ad unit for Android and iOS. Integrate it within your app, and start rewarding users with your in-app currency for using advertized mobile games and apps.

We offer a native SDK solution for Android and iOS, as well as a web-based option for iOS. Playtime seamlessly integrates with our Reporting APIs, allowing you to track your KPIs, monitor Playtime's performance, and achieve your revenue targets. Select your preferred platform and get started!

Playtime SDK for AndroidPlaytime SDK for iOSPlaytimeWeb for iOSUser Ad Data Report APIRevenue API

Playtime SDK for Android

A New Way to Monetize and Engage

Maximize your app’s revenue and engagement effortlessly with the Playtime SDK, adjoe’s mobile-app-centric rewarded ad unit. Integrate it within your app, and start rewarding users with your in-app currency for playing advertised mobile games and apps.

Playtime taps into your users' love for gaming and mobile engagement, offering you industry-leading eCPM and engagement metrics in return. To learn more about how adjoe can boost your app's monetization and retention, click here.

If you are not yet a customer, please reach out to our .

adjoe's Unique Rewarded Ad Format

Example of a Time-based Campaign
Example of an Event-Based Campaign

Supported SDK Wrappers

Android (Kotlin/Java)
React Native
Unity
Cordova
Flutter

Requirements

  • Android 16+

  • Gradle 7.2.2+

  • minSdkVersion 21+

  • compileSdkVersion 23+

Androidx apps are fully supported.

Starting June 30th, 2025, version 3.0.0 is the minimum supported version of the Playtime SDK.

Rewards users for the time they spend in the advertised app, e.g. Earn 50 coins per minute for playing a game.

Rewards users for achieving a designated combination of events within advertised apps, e.g. in-app purchases, viewing video ads, reaching specified game levels or signing-up for app. Users are rewarded after completing each task. Example: "Up to 5 Coins per Task" or "Complete Level 10", "Make first transfer".

partnerships team

API Reference

Version
Link

v1.9.0

v1.8.0

v1.7.0

Changelog

1.9.1

Published: Aug 5th, 2025

Features and Improvements ✨

  • Fix bug with FAQ page

1.9.0

Published: July 23rd, 2025

Features and Improvements ✨

  • Support auto-initialization.

  • Add Swift package manager (SPM) support.

  • Minor bug fixes.

1.8.0

Published: June 10th, 2025

Features and Improvements ✨

  • Support subId parameters that can be passed to showCatalog.

1.7.0

Published: May 15th, 2025

Features and Improvements ✨

  • Support Playtime APIs on Native iOS and React Native

Swift
Swift
Swift

Troubleshooting

Solve any SDK issues quickly with this guide, covering common errors and setup problems.

Troubleshooting Cocoapods

If you see this Sandbox: rsync error while building the app, check the steps below on how to solve this:

Sandbox Error

Select the project of your application in the Project Navigation:

  • Select your target

  • Go to Build Settings

  • Find the Build Option User Script Sandboxing

  • Set the value to NO.


Sandbox Error

Playtime Integration and Launch Checklist

🔧 Integration & Setup

General setup

  • Access to Playtime Publisher Dashboard confirmed

  • Server endpoint prepared on your servers for S2S payouts

  • The s2s_token for S2S payouts received from Account Manager

  • The sid verified and matches the parameter sent in the callback from adjoe

  • S2S payouts contain all desired parameters

  • IP-Level blocking implemented

  • Separate adjoe app IDs are used for production and non-production environments

  • (optional) Playtime customizations applied

  • (optional) Ad data report received via API

  • (optional) Revenue report retrieved via API

Playtime SDK for Android Only:

  • Test devices registered with correct GAIDs

  • Playtime SDK Dependency integrated into app

  • SDK initialized at app start without errors

  • SDK is re-initialized upon the app going into foreground (in onResume or didChangeAppLifecycleState

Playtime SDK for iOS only:

  • Test devices registered with correct IDFAs

  • Playtime SDK Dependency integrated into app

  • App Attest Integrated

  • Private Key and App ID sent to adjoe

PlaytimeWeb only:

  • Redirect URL successfully integrated into app

  • Catlog loads without errors

  • All desired parameters included in URL

  • Installed games appear in "My Apps" section

🚀 Launch Checklist

  • Server endpoint for S2S payouts verified and tested. Successful payouts are confirmed

  • All desired visual customizations applied

  • (optional) Application rollout initiated with a small percentage of users

Playtime SDK for iOS

A New Way to Monetize and Engage

Maximize your app’s revenue and engagement effortlessly with the Playtime SDK, adjoe’s mobile-app-centric rewarded ad unit. Integrate it within your app, and start rewarding users with your in-app currency for playing advertised mobile games and apps.

Using the Playtime SDK, you can tap into your users' love for gaming and mobile engagement, offering you industry-leading eCPM and engagement metrics in return. To learn more about how adjoe can boost your app's monetization and retention, click here.

If you are not yet a customer, please reach out to our .

adjoe's Unique Rewarded Ad Format

Example of an Event-Based Campaign

Supported SDK Wrappers

Swift (Native)
React Native

Requirements

  • Minimum version to use the SDK's functionality: iOS 14.0

Current version: 3.4.0. View Changelog.

Troubleshooting

Solve any SDK issues quickly with this guide, covering common errors and setup problems.

Troubleshoot Common Issues

Operation fails with the exception "not available for this user".

Beta Program

Want to be the first to experience new SDK features? Test out the beta of the SDK and shape the future of our product.

Stay tuned for updates!

) without errors
  • Playtime Catalog launch setup completed

  • Logs enabled (Native only)

  • Catalog listener set up (Native only)

  • SDK initialized at app start without errors
  • Playtime Catalog launch setup completed

  • Your device has been blocked. If your device was previously configured as a test device, please inform us so we can address the issue. If not, follow these steps:
    1. Uninstall the app containing the SDK. Ensure its data is also removed from any automatic backups.

    2. Reset your GAID: Go to Settings > Google > Ads and tap on Reset Ad-ID.

    3. Set up your device as a test device using the new GAID.

    You don't see any games in the catalog.

    Your device may be blocked. Proceed as described in the issue above.

    Notification of reward received, but the SDK does not display the new rewards.

    Check your device's time setting; incorrect time (e.g., set to the future or past) often causes this. If the time is correct and the issue persists, contact us and we will investigate this issue.

    App compilation fails with "Duplicate class" or "Duplicate entry" exceptions.

    This can happen when Gradle cannot resolve a dependency conflict, such as when two dependencies use the same transitive dependency but with different versions. To resolve this conflict you must either:

    • Remove one of the conflicting dependencies.

    • Align the versions of the conflicting dependencies to match.

    If the affected dependency is one of your app's dependencies, simply adjust or remove it from the dependencies list in your build.gradle. If the affected dependency is transitive, you must exclude it from the dependency which specifies it as a transitive dependency.

    Duplicate class due to androidx.work:work-runtime:$workVersion.

    Our SDK supports pre-AndroidX projects, so we are using the work-runtime library that predates AndroidX. There is an issue with the Android Gradle plugin <3.6.0 where the Jetifier does not correctly replace the dependency. If you are not already using a supported version of Gradle, you need to bump the version in your root project's build.gradle file.

    Please also ensure that you have enabled the Jetifier by placing the line android.enableJetifier=true into your gradle.properties file.

    If you continue to have issues due to a conflict between the versions of the androidx.work:work-runtime library in your project, you can fix this issue by adding the following to your app level build.gradle file.

    The currently supported version of work-runtime is up to 2.0.1


    buildscript {
    
        repositories {
            google()
            mavenCentral()
            ...
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:3.6.0' // Set this to 3.6.0 or higher.
            ...
            // NOTE: Do not place your application dependencies here; they belong
            // in the individual module build.gradle files
        }
    }
    configurations.all {
    
        resolutionStrategy.dependencySubstitution {
            // Substitute one module dependency for another
            substitute module('android.arch.work:work-runtime:1.0.1') with module('androidx.work:work-runtime:2.0.1')
        }
    
        resolutionStrategy.force 'androidx.work:work-runtime:2.0.1'
    }
    
    dependencies {
        implementation 'androidx.work:work-runtime:2.0.1'
       ....
    }

    Rewards users for achieving a designated combination of events within advertised apps, e.g. in-app purchases, viewing video ads, reaching specified game levels or signing-up for app. Users are rewarded after completing each task. Example: "Up to 5 Coins per Task" or "Complete Level 10", "Make first transfer".

    partnerships team

    PlaytimeWeb for iOS

    A New Way to Monetize and Engage

    Maximize your app’s revenue and engagement effortlessly with PlaytimeWeb for iOS, adjoe’s mobile-games-centric rewarded ad unit. Integrate it within your app, and start rewarding users with your in-app currency for playing advertised mobile games.

    Playtime taps into your users' love for gaming and mobile engagement, offering you industry-leading eCPM and engagement metrics in return. To learn more about how adjoe can boost your app's monetization and retention, click here.

    adjoe's Unique Rewarded Ad Format

    Example of an Event-Based Campaign

    Server-to-Server Payouts

    Once a user meets the campaign criteria for being rewarded, it's time to initiate the payout—sending the rewards to the user. Publishers issue these payouts upon receiving a payout request callback from adjoe's backend. The request is made to the S2S API URL configured for the publisher for their SDK in the .

    Set Up Server-to-Server (S2S) Payouts

    To enable S2S payouts, set up an endpoint on your server to receive notifications about user rewards from adjoe. When a request is made to your endpoint, you then must manage the distribution of these rewards to the users. Be aware that once a reward is distributed, it cannot be allocated again.

    Endpoint Structure

    The key names can be customized, or you can use the default names. Please ensure that no special characters should be included in the values, otherwise our backend may transform the request, which could cause issues with the payout. Some of these values include: [] . =

    Default

    GET / https://example.com/example?sid={sid}&trans_uuid={trans_uuid}&user_uuid={user_uuid}&currency={currency}&coin_amount={coin_amount}&device_id={device_id}&sdk_app_id={sdk_app_id}&app_id={app_id}&reward_level={reward_level}&app_name={app_name}&reward_type={reward_type}&publisher_sub_id1={publisher_sub_id1}&publisher_sub_id2={publisher_sub_id2}&publisher_sub_id3={publisher_sub_id3}&publisher_sub_id4={publisher_sub_id4}&publisher_sub_id5={publisher_sub_id5}&placement={placement}&ua_network={ua_network}&ua_channel={ua_channel}&ua_subpublisher_encrypted={ua_subpublisher_encrypted}&ua_subpublisher_cleartext={ua_subpublisher_cleartext}

    Customized (Example)

    GET / https://example.com/example?user_id={user_uuid}&sid={sid}&point_amount={coin_amount}&points={currency}&trans_uuid={trans_uuid}&ua_network={ua_network}&ua_channel={ua_channel}&playtime_placement={placement}&custom_id={publisher_sub_id1}

    S2S Parameters

    `Example Response URL:

    https://s2sexamplerequestendpoint.com/payout?user_uuid=a79d7158-6f9c-4e5b-ae7a-98143c77d396&sid=0de7e36d2965269f172d2d89bc9a8026da7f0819&coin_amount=100&currency=dollars&trans_uuid=e7b1a95f-8c72-4ed8-af69-ecf8d06b1d89&sdk_app_id=example.test.app&ua_network=tiktok&ua_channel=direct&publisher_sub_id1=RandomString

    Calculate the SID

    For security measures, a request signature is necessary to verify the authenticity of the request. It's important to calculate the sid parameter in your backend and compare it with the parameter received in the callback URL. The sid is generated as an SHA1 hash using the required query parameters and the encoding is hexadecimal. Your Account Manager will provide you with the necessary parameter, the s2s_token.

    The general format for this is as follows - no other parameters are included in the calculation:

    If any of the S2S payout URL parameters are missing, simply remove them from the concatenation. The order of the remaining parameters will remain the same as the above format. For example, if sdk_app_id is not present in the endpoint URL, the sid will then be calculated as follows:

    Below is an example script that shows how to calculate the sid and compare it to the value in the callback URL sent by adjoe. You would implement some version of this in your backend.

    S2S Security

    When we make payout request to publishers, we send an HTTP request from any of the following IP addresses:

    To enhance security, use these IP addresses for IP-level blocking on endpoints receiving adjoe's S2S payout requests. Allow only these specific IPs access.

    Retry Logic

    We have a built-in retry mechanism for failed S2S requests that return a 4xx error from your endpoint.

    If a request fails, we retry after 10 minutes. A second failure prompts a retry in another 10 minutes. Subsequent failures lead to retries every 2 hours from the initial failed attempt. In total, this retry cycle spans approximately 12 hours, giving multiple chances for the request to succeed.

    If you observe an extended period of unsuccessful requests, contact your Account Manager for further investigation into the issue.

    Testing Payouts

    Follow these steps to verify the correct functioning of payouts:

    1. Install games from the catalog, include "event-based" and "time-based" campaigns.

    2. Play the games and complete the tasks required to earn rewards (e.g., finishing a task or playing for a specific duration).

    3. Verify that the completed tasks are reflected in the game's reward card.

    4. Ensure that the earned rewards, such as points, are credited to the app's wallet. For example, if you completed a task for 5 points, check that these points are available in the wallet.

    5. Confirm that a 200 status code is logged in your records. Anything other than a 200 status code means that your endpoint has not been configured correctly to receive requests from adjoe.

    Best Practices for Rewarding Users

    If you payout with real money, we recommend that you implement KYC processes, reward cooldowns, and individual limits on the amount of rewards the user can get:

    • KYC or Know Your Customer: This verification process, used by businesses and financial institutions, confirms the identity of your users to prevent fraud. It involves collecting and verifying identification documents, such as government-issued IDs or proof of address, ensuring that users are who they claim to be.

    • Reward Cooldowns: Temporarily restrict users from receiving additional rewards after receiving one. This helps maintain fairness and prevents exploitation of the rewards system, like receiving rewards too frequently.

    • Individual Limits: To prevent abuse of the rewards systems, set daily limits on the amount of rewards a user can earn. For example, you might cap rewards at $50 USD per day.

    Monetize Dashboard

    Rewards users for achieving a designated combination of events within advertised apps, e.g. in-app purchases, viewing video ads, and/or reaching specified game levels. Users are rewarded after completing each task. Example: "Up to 5 Coins per Task" or "Complete Level 10".

    User Ad Data Report API

    The adjoe User Ad Data Report API allows publishers to request user data reports. The required parameter for using this API is the SSP API Token, which you can obtain from your adjoe Account Manager.

    There are two steps to using the User Ad Data Report API:

    1. Get the Report URL: Use your SSP API Token and Date to request a URL.

    2. Download the Report: Access the URL to obtain your report.

    Step 1: Obtain the Report URL

    GET https://prod.adjoe.zone/v1/ssp-api/<your_token>/user-ad-data-report/app/<your_app_id>?date=[YYYY-MM-DD]

    Required values:

    <your_token> - 32-character hex string provided by the adjoe Account Manager.

    <your_app_id> - Android package name or iOS Appstore ID. Example: com.king.candycrush

    Query Parameters

    Name
    Type
    Description

    Response: OK, Returns a JSON object with the report URL

    Content-Type: application/json; charset=utf-8

    Content: {"ReportURL": "https://prod-adjoe-user-ad-data.s3.eu-central-1.amazonaws.com/acbd18db4cc2f85cedef654fccc4a4d8/2020-02-01_6f2fb2d3def4f99053edab239195f146.csv"}

    Response: Unauthorized "Invalid Token"

    Content-Type: application/json; charset=utf-8

    Content: {"error" : "invalid token"}

    Example Request


    Step 2: Obtain the Report

    After obtaining the URL, download the report in CSV format. A successful download will prompt an HTTP 200: OK status, along with the CSV file. An empty file indicates no data is available yet.

    Example Report 1:

    Example Report 2:

    Schema Definition

    Value
    Explanation

    Availability

    Data is available daily from 2:00 UTC, covering the previous day's activities. To ensure successful API queries, initiate calls after 2:00 UTC. Access to reports is granted after your Account Manager has authorized them. For example, if your report is enabled on 2025-01-01, you can access it from 2:00 UTC on 2025-01-02. Note: Reports for periods before the enablement date, such as before 2025-01-01, are not accessible.

    import hashlib
    from urllib.parse import urlparse, parse_qs
    
    def calculate_sid(user_uuid, trans_uuid, currency, coin_amount, device_id, sdk_app_id, s2s_token):
        data = ''.join([trans_uuid, user_uuid, currency, str(coin_amount), device_id, sdk_app_id, s2s_token])
        hashed_data = hashlib.sha1(data.encode()).hexdigest()
        return hashed_data
    
    def extract_parameters(url):
        parsed_url = urlparse(url)
        query_params = parse_qs(parsed_url.query)
        return query_params
    
    def compare_sids(url, calculated_sid):
        query_params = extract_parameters(url)
        sid_from_url = query_params.get('sid', [None])[0]
        return sid_from_url == calculated_sid
    
    # Example URL
    url = "https://s2sexamplerequestendpoint.com/payout?user_uuid=a79d7158-6f9c-4e5b-ae7a-98143c77d396&sid=2477185c00a5b1ac94fdf94798798d5e846d8aee&coin_amount=100&currency=dollars&trans_uuid=e7b1a95f-8c72-4ed8-af69-ecf8d06b1d89&device_id=9a7e993a-80b4-4c3b-832f-97a5f501e2f1&sdk_app_id=com.example.android.gamename&ua_network=tiktok&ua_channel=direct&publisher_sub_id1=RandomString"
    
    # Extract parameters from URL
    query_params = extract_parameters(url)
    user_uuid = query_params.get('user_uuid', [None])[0]
    sid = query_params.get('sid', [None])[0]
    trans_uuid = query_params.get('trans_uuid', [None])[0]
    currency = query_params.get('currency', [None])[0]
    coin_amount = int(query_params.get('coin_amount', [None])[0])
    device_id = query_params.get('device_id', [None])[0]
    sdk_app_id = query_params.get('sdk_app_id', [None])[0]
    s2s_token = " " #the s2s_token will be supplied by your Account Manager.
    
    # Calculate SID
    calculated_sid = calculate_sid(user_uuid, trans_uuid, currency, coin_amount, device_id, sdk_app_id, s2s_token)
    
    # Compare SIDs
    result = compare_sids(url, calculated_sid)
    print("SID Comparison Result:", result)
    
    sid = sha1(concatenate(trans_uuid, user_uuid, currency, coin_amount, device_id, sdk_app_id, s2s_token))
    sid = sha1(concatenate(trans_uuid, user_uuid, currency, coin_amount, device_id, s2s_token))
    3.121.65.44
    18.185.166.67
    52.29.52.48

    UserUUID

    Used by adjoe to identify the user.

    External User ID

    Used by the publisher to identify the user.

    SDKSubID1

    Sub ID 1 set in the SDK.

    SDKSubID2

    Sub ID 2 set in the SDK.

    eCPM

    in USD.

    Revenue

    Revenue in USD for this user on this date.

    Impression Count

    The number of impressions for this user on this date.

    date*

    date

    Reports are queried by date, with only one date allowed per SDK token query. Format:[YYYY-MM-DD]

    Date

    Date

    SDK Hash

    The unique identifier to the SDK.

    Platform

    Android or iOS.

    Package Name

    Either the Android Package name or the iOS App Store ID of the SDK app.

    Country Code

    2-letter country code. Example: de, us, fr.

    Google Advertiser ID | IDFA

    A Google Advertising ID, also known as an Android ID, Android Device ID, or Android Advertising ID (AAID), is Google’s unique device identifier. The IDFA (identifier for advertisers) is a random iOS device identifier given by Apple.

    Get Started

    Current version: 3.4.0. View .

    Follow this guide to configure your app to work with the Playtime SDK using any of our supported wrappers.

    Step 1: Get Your SDK Hash

    Contact your Account Manager to get an SDK hash for your app. Or if you already have access, it can be retrieved from the Monetize Dashboard.

    The SDK hash is a unique 32-character code that uniquely identifies your app within adjoe's systems. Each app receives a distinct hash. Remember to keep your hash confidential to prevent unauthorized use.

    Step 2: Enable SandboxMode

    To fully test the Playtime SDK integration and access all campaign types, configure your application to be in "SandboxMode". Contact your Account Manager to have this setting enabled for your environment.

    Troubleshooting:

    • If you do not see any apps returned or if you run into any errors after this setting is enabled, please try a fresh install.

    Step 3: Add the Playtime SDK Dependency

    1. Add adjoe's repository to your root build.gradle (project level):

    1. Add the adjoe dependency to your app's build.gradle (module level):


    If you use Kotlin DSL for adding dependencies, add the repo in the settings.gradle.kts

    You have an option to integrate the dependencies via Groovy or Kotlin. To understand the differences of integrating dependencies via these platforms, refer to the .

    Step 4: Enable Logs (Native only)

    Enabling logging per device within the SDK is crucial for troubleshooting. Turn on logging and include the logs with any bug reports to help us accurately replicate and address issues.

    Here’s how to enable logging:

    1. Connect the device to your computer and enable ADB.

    2. In a terminal window, run the following command to activate logging:

    You can now filter LogCat using the Playtime tag to monitor relevant log output. If you wish to disable logging, simply execute the command:

    This sets the logging level to ERROR, effectively reducing the verbosity of the logs.

    FAQ

    1. Playtime SDK for Android

    2. Playtime SDK for iOS

    3. PlaytimeWeb for iOS

    4. React Native / Flutter


    Playtime SDK for Android

    Is it necessary to provide the userId at app start-up with the init method?

    No. Since the init method should happen at app start-up, there are circumstances where you don’t yet have this data. For example, if the user hasn’t signed in yet. In these cases, it’s still critical to init at the app start up, but you can pass in the UA Params or User ID in an OnResume / AppState / didChangeAppLifecycleState function when you have the data.

    Remember that the userId is critical for doing S2S payouts, without it your users will not be able to get their rewards. It is also important to note that whatever the last supplied userId was is what will be sent in the S2S payout URL. Therefore, the last supplied User ID was is what will be sent in the S2S payout URL.

    Do I need to do anything with the Playtime SDK after the user logs out?

    No. But if the user can only access the Playtime Catalog after logging-in, then you should remove any calls to init when they log out.

    What is the size of SDK?

    SDK size depends on the wrapper, but it is roughly <0.5MB.

    Can I show the adjoeActivity in a Fragment?

    No, this is not supported.

    How often is the SDK updated?

    The SDK is updated every couple of months. Please keep an eye out on our .

    What kind of data does the SDK track? I need to share this information with the Google Play Store.

    All of the data that we collect can be found in the AndroidManifest.xml of the SDK.

    How do users know that they are able to receive a reward?

    For Android users, we show a toast message on the top of the screen whenever a user is able to receive a reward.

    I'm not seeing campaigns/games in the catalog?

    The most common reason this happens is because of VPN usage. If you use a VPN, you might not see any campaigns, even if you set yourself as a test user. We still filter out IP addresses that are anonymous.

    After playing some time-based reward games, I stopped receiving rewards after 15 mins. Why?

    It can happen because you stopped progressing through the game. To fix this, please keep playing and progressing through the game.

    What's the difference between time-based and event-based campaigns?

    We have more information about this in our docs. Please refer to them .

    What is the use of the GAID?

    We use GAID to identify users. We provide these data to so they can count attribution.

    Why does the user need to accept Access to Advertising Info?

    We have more information about this in our docs. Please refer to them .


    Playtime SDK for iOS

    What are the benefits of using Playtime SDK vs PlaytimeWeb?

    • Improved Anti-fraud protection for iOS

    • Feature parity with Android SDK

    • Stronger margin protection

    • Native offerwall rendering for better UX and performance

    I'm interested in switching from PlaytimeWeb to the Playtime SDK - what would that process look like?

    Any user created via PlaytimeWeb will also exist in the Playtime SDK, and vice versa. This also includes the user's UI, install and play history. Once you've gotten the SDK up and running, the end user experience would essentially stay the same.


    PlaytimeWeb for iOS

    Does the the PlaytimeWeb use cookies?

    We only keep cookies in the page during the session. It allows us to keep track of the user (like after authentication) and make sure that they get their rewards.

    What is the use of the IDFA?

    We use the IDFA to identify users and to accurately identify the user for rewarding purposes, ensuring any currency earned via PlaytimeWeb is promptly paid out. We provide the IDFA to so they can count attribution.

    Why does the user need to accept App Tracking?

    We have more information about this in our docs. Please refer to them .

    Why does the user need to accept TOS?

    We have more information about this in our docs. Please refer to them .


    React Native / Flutter

    I'm using React Native - can I use your Playtime Android and iOS SDK together?

    Yes, at the moment we do not support a unified import for the Playtime react native library. Both SDK libraries would need to be independently imported. We plan to support a single import in the future.

    I'm using Flutter or React Native - can I use your Android SDK and iOS Playtime solution together?

    Yes, you can use the Platform properties provided by the Flutter and React Native libraries to set up the code in a way to conditionally execute certain code blocks based on the platform the app is running on. Since we only have an SDK on Android, you may need to use platform-based imports for RN or add platform-specific dependencies in the pubspec.yaml for Flutter.

    Example for React Native:


    Business

    How do I give one of my coworkers access to the dashboard?

    You should reach out to your Account Manager. They can provide them with access.

    What customization options do you provide for the Playtime Catalog?

    Most parts of the Playtime Catalog can be customized so that it fits your branding - including images, colors, texts, fonts, and currency. Your Account Manager will reach out to you separately to set up these customizations.

    Where can I track KPIs like Revenue or Active User Rates?

    These KPIs can be tracked in the . They are also available via API.


    Get Further Support

    Still need help with something? Have an idea for a Feature Request? Please use this template to reach out to your Account Manager for help.

    • Issue Type: [Bug/Feature Request]

    • Framework: [iOS/Android/Flutter/React Native/Unity/Cordova]

    • SDK Version:

    • Priority: [High/Medium/Low]

    Please include any relevant logs, crash reports, API calls, or screenshots to support your query.

    Revenue API

    The adjoe Revenue API allows publishers to query user data and generate detailed reports. The required parameter for using this API is the SSP API Token, which you can obtain from your adjoe Account Manager. You can retrieve a report for all apps, as well as for a specific app.

    Retrieve a Report for All Apps

    GET https://prod.adjoe.zone/v1/ssp-api/<your_token>/aggregation/daily

    Required values:

    <your_token>: A 32-character hex string provided by your adjoe Account Manager.

    You can query data for specific time frames. To query data for a single day, set both start_at and stop_at to that day. Data grouping is customizable; by default, it's grouped by date, country, and platform. For alternative groupings, supply a comma-separated list of fields in the groupBy parameter.

    Query Parameters

    Name
    Type
    Description

    Response: OK Returns a JSON object with the report URL

    Content-Type: application/json; charset=utf-8

    Content: [ { "SDKHash":"11123ABCDF123123123123123", "StoreID":"de.adjoe.com", "AppName":"happy adjoe app", "Platform": "android", "Date":"2019-02-11", "Revenue":995.00, "Currency": "USD", "eCPM": 209.95, "OfferwallShown":4739, "SDKBootups":275887, "CoinSum":300 } ]

    Response: Bad Request "start_at date format is wrong. Please use YYYY-MM-DD"

    Content-Type: application/json; charset=utf-8

    Content:

    Example Request


    Receive a Report a Specific App

    GET https://prod.adjoe.zone/v1/ssp-api/<your_token>/aggregation/sdkHash/<sdk_hash>/daily

    Required values:

    <your_token>: A 32-character hex string token provided by your adjoe Account Manager.

    <sdk_hash>: The unique identifier for the SDK, also provided by your adjoe Account Manager.

    Data can be queried for specific time ranges. To query for a single day, ensure start_at and stop_at parameters are the same. The default data grouping is by date and app platform. For custom groupings, input a comma-separated list of desired fields in the group_by parameter.

    Query Parameters

    Name
    Type
    Description

    Response: OK Returns a JSON object with the report URL

    Content-Type: application/json; charset=utf-8

    Content: [ { "SDKHash":"11123ABCDF123123123123123", "StoreID":"de.adjoe.com", "AppName":"happy adjoe app", "Platform": "android", "Date":"2019-02-11", "Revenue":995.00, "Currency": "USD", "Country" : "DE", "eCPM": 209.95, "OfferwallShown":4739, "SDKBootups":275887, "CoinSum":300 } ]

    Response: Unauthorized "Invalid Token"

    Content-Type: application/json; charset=utf-8

    Content: {"error" : "invalid token"}

    Example Request:

    Schema Definition

    Value
    Explanation

    Availability

    Data is available daily from 2:00 UTC, covering the previous day's activities. To ensure successful API queries, initiate calls after 2:00 UTC.

    Use the SDK

    1

    Import the Library

    Import the SDK to your file.

    Get Started

    Follow this guide to configure your app to work with the Playtime SDK using any of our .

    Part 1: Pre-Requisites

    1

    Integrate PlaytimeWeb

    Get Started

    To seamlessly integrate PlaytimeWeb into your iOS app, you only need to offer users the option to open the PlaytimeWeb URL. We suggest placing this option prominently in your app, such as in a native banner or button on the homepage. The basic structure of the Redirect URL is:

    The user_id and idfa parameters in the URL help accurately identify the user for rewarding purposes, ensuring any currency earned via PlaytimeWeb is promptly paid out.

    Additionally, you can include extra parameters in the URL, and these values will be sent back to you through the payout URL

    curl -X GET
    'https://prod.adjoe.zone/v1/ssp-api/acbd18db4cc2f85cedef654fccc4a4d8/user-ad-data-report/app/com.king.candycrush4?date=2025-02-01'
    "2025-02-01","6f2fb2d3def4f99053edab239195f146","android","com.king.candycrush4","DE","3d497f11-4e04-469c-821a-1f57429efb1a","0ca80a38-bb08-4365-8887-5014bf25373b","1ba8e52e-a967-422c-b67e-528511b9780b","Placement one","Home Screen",0.000,0.000,7
    "2025-02-01","9e304d4e8df1b74cfa009913198428ab","ios","1225867923","DE","3d49"
    Effective Cost Per Mille

    Response: Unauthorized "Invalid Token"

    Content-Type: application/json; charset=utf-8

    Content:

    {"error" : "invalid token"}

    Response: Bad Request start_at date format is wrong. Please use YYYY-MM-DD

    Content-Type: application/json; charset=utf-8

    Content:

    Revenue

    Revenue in USD for this user on this date.

    Currency

    Type of currency Example: USD or Euro.

    OfferwallShown

    Number of times the offerwall was shown to all users.

    StoreID

    Android package name or iOS Appstore ID. Example: com.king.candycrush.

    SDKBootups

    Count of the SDK initializations.

    CoinSum

    Sum of the total number of rewards.

    start_at

    Date

    Start date

    stop_at

    Date

    End date

    group_by

    String

    Possible options: date, sdk_hash,

    platform, country

    start_at

    Date

    Start date

    stop_at

    Date

    End date

    group_by

    String

    Possible options: date, sdk_hash, platform, country

    Date

    Date

    SDKHash

    The unique identifier to the SDK.

    Platform

    Android or iOS.

    Country

    2-letter country code. Example: de, us, fr.

    UserUUID

    Used by adjoe to identify the user.

    eCPM

    Effective Cost Per Mille in USD.

        {
        "error": "start_at date format is wrong. Please use YYYY-MM-DD"
        }
        {
        "error": "start_at date format is wrong. Please use YYYY-MM-DD"
        }
     curl -X GET \
     'https://prod.adjoe.zone/v1/ssp-api/<your_token>/aggregation/daily?
     start_at=2019-02-18&stop_at=2019-02-18'
    curl -X GET
    https://prod.adjoe.zone/v1/ssp-api/<your_token>/aggregation/sdkHash/<sdk_hash>/daily? start_at=2025-02-18&stop_at=2025-02-18&group_by=platform,country
    1. Add adjoe's repository to the settings.gradle file:

    2. Add the adjoe dependency to your app's build.gradle (module level):

    https://github.com/adjoeio/adjoe-react-native-sdk

    1. Open your project's package.json file.

    2. Add the adjoe as a dependency under the dependencies section. You can change the version number to the desired version of Playtime SDK.

    1. Open your build.gradle file in the android folder and add the following section:

    1. Run the command:

    https://github.com/adjoeio/adjoe-flutter-sdk

    To integrate the adjoe Flutter SDK into your Flutter project, follow these steps:

    1. Open your project's pubspec.yaml file.

    2. Add the Playtime SDK as a dependency under the dependencies section. You can change the version number to the desired version of Playtime SDK.

    1. Save the pubspec.yaml

    2. In your IDE run either of the the following two commands:

      1. pub get

      2. flutter pub get

    Adjoe 3.3.2 uses the Play Services Resolver, you can download a version of the Playtime SDK which is compatible wit the Play Services Resolver here. It contains only the source code and the AdjoeDependencies.xml file specifying the Playtime SDK: https://releases.adjoe.io/files/playtime/unity/3.3.2/adjoe_sdk_android_unity_3.3.2_psr.unitypackage

    1. Add the adjoe SDK to your app by clicking:

    Assets > Import package > Custom package....

    1. Select adjoe's .unitypackage, click Open and the select Import. This will load the adjoe SDK into Assets/Adjoe.

    • Download the 3.3.2 version of the adjoe SDK for Capacitor here: adjoe-capacitor-plugin-3.3.2.tgz.

    • After you have downloaded it, add the Adjoe SDK to your app by running the command: $ npm install --save path/to/adjoe-capacitor-plugin-3.3.2.tgz in your app's root directory. This will extract the files from the tarball, add them to the node_modules folder and add an entry to your package.json.

    • If you already added Android platform to your project you can skip this step and go to step 4 Install Android platfrom: npm install @capacitor/android Add Android platfrom: npx cap add android

    • Open your build.gradle file in the android folder and add the following section:

      Copy

    • Sync your project: npx cap sync android

    https://github.com/adjoeio/adjoe-cordova-sdk

    1. Open your project's package.json file.

    2. Add the Playtime SDK as a dependency under the dependencies section. You can change the version number to the desired version of Playtime SDK.

    1. Save the package.json and run the following command:

    dependencies {
        ...
        implementation 'io.adjoe:adjoe-sdk-android:3.4.0'
    }
    official Android docs
    Changelog
    allprojects {
        repositories {
            ...
            maven {
                url  "https://releases.adjoe.io/maven"
            }
        }
    }
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
            maven { url = uri("https://releases.adjoe.io/maven") }
        }
    }
    Visual customizations via Monetize Dashboard (no code changes needed)
  • Cross-platform support: React Native

  • Access to adjoe's optimized catalog, built for performance and user experience

  • Crash: [Yes/No]

    • Devices Affected: (e.g., Samsung, Pixel. iPhone 12)

    • User Impact: Number or percentage

    • Environment: [Production/Test]

  • Issue/Feature Description: Provide a detailed description. For feature requests, explain the necessity and potential business impact.

  • Reproduction Steps:

  • Expected Behavior:

  • Actual Behavior:

  • Business
    Get Further Support
    changelog
    here
    MMPs
    here
    MMPs
    here
    here
    Monetize Dashboard
    Example: Toast message when user receives a reward.
    2

    Initialize the SDK

    To make all of the features of the Playtime SDK available, you need to initialize it first. This initialization happens asynchronously in the background.

    Automatic

    To trigger automatic initialization at the app's start, you should include the adjoe-playtime.plist file containing your SDK hash in your app's target. See the example file below:

    Manual

    To make all of the features of the Playtime SDK available, you need to initialize it first. This initialization happens asynchronously in the background.

    Best Practices

    • Initialize the SDK immediately after the app launches.

    • Re-initialize the SDK and include the userId after the user has logged-in.

    At app launch, call Playtime.initialize(...) . You can use :

    At app launch, call [Playtime initialize ...] . You can use :

    Call Playtime.initialize at the start of the application, for example, in the App.js's componentDidMount() method:

    3

    Show the Catalog

    It's required to pass userId here if auto-init is enabled.

    Call Playtime.showCatalog() to open a webview of the Playtime catalog:

    Call [Playtime showCatalog] to open a webview of the Playtime catalog:

    Call Playtime.showCatalog() to open a webview of the Playtime catalog:

    Parameters

    Parameter
    Explanation

    userId

    A custom identifier you can assign to each user. If you haven't set one, the Playtime SDK generates it automatically. This ID is necessary for S2S payouts, thus required. Ensure this value is url-safe and that the value is never empty.

    sdkHash

    SDK hash that you received from the adjoe Monetize dashboard or Account Manager.

    uaNetwork

    User Acquisition Network. The network through which the user was acquired, e.g., Adwords, Facebook, or Organic.

    uaChannel

    User Acquisition Channel. The channel within the network from which the user was acquired, e.g. incentivized or video campaigns.

    uaSubPublisherCleartext

    The cleartext package name of the sub-publisher's app ID within the network from which the user was acquired, e.g., com.domain.appName.

    uaSubPublisherEncrypted

    The encrypted package name or app ID of the sub-publisher's app from the network where the user was acquired.

    App Tracking

    While the IDFA parameter is not required, we strongly recommend including it. It's purpose is to help attribute installs and ensure that users are rewarded.

    The App Tracking Transparency popup should be triggered in the app before calling showCatalog() . The user must then "Allow" tracking.

    When a user selects "Play Now" in the Playtime Catalog, we use a pop-up to remind them to allow tracking.

    #import <PlaytimeMonetize/PlaytimeMonetize-Swift.h> 
    import PlaytimeMonetize
    import Playtime from 'react-native-adjoe-sdk-ios';
    Get Your SDK Hash

    Contact your Account Manager to get an SDK hash for your app. Or if you already have access, it can be retrieved from the Monetize Dashboard.

    The SDK hash is a unique 32-character code that uniquely identifies your app within adjoe's systems. Each app receives a distinct hash. Remember to keep your hash confidential to prevent unauthorized use.

    2

    Enable SandboxMode

    To fully test the Playtime SDK integration and access all campaign types, configure your application to be in "SandboxMode". Contact your Account Manager to have this setting enabled for your environment.

    Troubleshooting:

    • If you do not see any apps returned or if you run into any errors after this setting is enabled, please try a fresh install.

    3

    Add the Dependency

    The Swift Package Manager automates the distribution of Swift code. To use Playtime with SPM, add the dependency from https://github.com/adjoeio/adjoe-monetize-spm .

    With Xcode

    Go to File → Add Package Dependencies in the Menu Bar in Xcode. Search for `https://github.com/adjoeio/adjoe-monetize-spm and add it to your app's target.

    Using Package.swift file

    If your project uses Package.swift file, add Playtime as a dependency:

    And then add the following product to any target that needs access to the library:

    is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website.

    1. In your project directory, create a Podfile if one doesn’t already exist.

    2. Inside the Podfile, add the following configuration to include the Playtime SDK as a dependency within your app’s target. You can also specify a version or version range—see the CocoaPods Podfile syntax reference for more details.

    3. Open a terminal and navigate to your project’s root directory.

    1. Obtain a release archive from adjoe and extract the .xcframework from it. Place within your project’s root folder.

    2. Link the framework with your target:

      • Go to the Project Navigator

    Install React Native dependency

    To integrate the adjoe React Native module into your React Native project, follow these steps:

    1. Open your project's package.json file.

    2. Add the adjoe as a dependency under the dependencies section. You can change the version number to the desired version of adjoe SDK you want to integrate.

    Alternatively, you can use your project's package managers to perform the installation:

    Part 2: App Attest Integration

    The Playtime SDK for iOS makes use of Apple's DeviceCheck APIs to ensure that the app is launched on a real device and that there haven't been any malicious alterations. This allows us to better protect your app and your revenues.

    To ensure that the SDK runs as expected, you must complete the following steps, even if the app builds normally. Failure to complete a step could cause the app to disfunction or be rejected from the App Store. You can read more about the App Attest service here.

    1

    Enable App Attest

    1. Go to Apple Developer Certificates, IDs & Profiles

    2. Select your app ID

    3. Ensure App Attest is enabled under Capabilities

    2

    Add required capabilities

    1. Select the project of your application in the Project Navigator

    3

    Gather Credentials:

    In order for adjoe's services to work with your app, the following credentials will need to be enabled for your app. They can be uploaded at the app-level in the Monetize Dashabord.

    If you plan on using a dev and a prod environment, you will need to make sure distinct credentials are added for both app bundles, e.g., playtime.app.dev and playtime.app.

    4

    Upload Credentials

    Add the Credentials under Edit Application in the .

    Part 3: Configure Environment

    After adding the App Attest capability, you must configure your entitlements file to use the production environment. The adjoe backend only accepts production App Attest attestations.

    Steps:

    1

    Locate your entitlements file

    • When you add the App Attest capability, Xcode automatically creates a .entitlements file in your project (e.g., YourApp.entitlements)

    • Find it in the Project Navigator

    2

    Set to production environment

    • Open the .entitlements file

    3

    Verify build configurations

    • In Xcode, go to your Target → Build Settings

    Why production is required:

    Unlike typical Apple services, the adjoe backend does not support development App Attest attestations

    • Both your development/testing builds and production builds must use the production environment

    • Using development will cause authentication failures with the error: "AAGUID not found in metadata"

    Troubleshooting: If you see errors related to "AAGUID not found" in your logs, it means your entitlements are still set to development. Double-check steps 1-3 above, clean your build folder (Product → Clean Build Folder), and rebuild.

    supported wrappers
    Supported URL Parameters

    To make the most of PlaytimeWeb, to maximize your revenue and provide the best user experience, use all of these parameters for data-driven optimizations. For instance, you can monitor the performance of different Playtime button placements or determine the effectiveness of different UA sources in the Monetize Dashboard.

    user_id*

    A custom identifier you can assign to each user. If you haven't set one, adjoe generates it automatically. This ID is necessary for , thus required. Ensure this value is url-safe.

    required

    idfa*

    The Identifier for Advertisers (IDFA) is a random device identifier assigned by Apple to a user’s device and is used by advertisers to track user's advertising data.

    recommended

    placement

    The placement of the PlaytimeWeb button inside your app, e.g., "Main Screen", "Shop", "Interstitial", "More Diamonds Screen", etc.

    You can also choose to use adjoe's Apps feature, showing the Playtime catalog with distinct gaming or app experiences.

    optional

    ua_channel

    User Acquisition Channel. The channel within the network from which the user was acquired, e.g. incentivized or video campaigns.

    optional

    ua_network

    User Acquisition Network. The network through which the user was acquired, e.g., Adwords, Facebook, or Organic.

    Accept Tracking

    While the IDFA parameter is not required, we recommend including it. This means you should trigger the App Tracking Transparency popup so the user can "Allow" tracking. When a user selects "Play Now" in the Playtime Offerwall, we use a pop-up to remind them to allow tracking.

    Use a WebView

    A WebView or SFSafariViewController or iFrame can be used to show the PlaytimeWeb URL. You may need to configure the navigation actions to open the app-store URL, as the webView may have it's own protocols for intercepting the URL requests. If you do not allow these url-schemes, certain functionality - like our support chatbot - will not work.

    • Allow the following url-schemes

      1. https

      2. itms-appss

      3. itms-apps

      4. about:srcdoc

    Test the Integration

    1. Open PlaytimeWeb from your app.

    2. Download a game.

    3. Return to PlaytimeWeb. You should see the game under the "My Games" section.

    4. If you do not see the game in "My Games" try resetting your IDFA.

    When a game is downloaded, we receive a postback from the MMP letting us know. It is usually is delivered instantly. In some cases, it takes 24 hours. If the app was previously installed, you will not be able to receive rewards through PlaytimeWeb.

    Reset the IDFA

    1. Delete the game if it was already installed.

    2. Go to Settings > Privacy > Tracking, toggle Allow Apps to Request to Track off.

    3. Return to PlaytimeWeb and select Ask Apps to Stop Tracking (if prompted), then toggle Allow Apps to Request to Track back on.

    4. Install the game again from PlaytimeWeb.

    User Experience

    Terms of Service

    Users need to accept adjoe's Terms of Service (TOS) to use Playtime and access games/apps. Agreeing to the TOS is mandatory before installing any partner apps.

    iCloud Relay

    For iOS users Apple provides an opportunity to use the service, which can be enabled in the device settings. If the user has it enabled, then we cannot track the user’s real IP, since all the requests would be proxied via Apple servers. This can have an impact on our ability to reward users for playing games or using apps. In cases where iCloud Private Relay enabled, users would see the following messages asking them to disable it. We will not permit users to user Playtime without disabling this feature.

    Server-to-Server
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
            maven { url = uri("https://releases.adjoe.io/maven") 
            }
        }
    }
    dependencies {
        ...
        implementation 'io.adjoe:adjoe-sdk-android:3.4.0'
    }
    dependencies: {
      "react-native-adjoe-sdk": "https://github.com/adjoeio/adjoe-react-native-sdk#v3.4.0"
    }
    dependencies:
      adjoe:
        git:
          url: https://github.com/adjoeio/adjoe-flutter-sdk
          ref: v3.3.2
    dependencies: {
      "cordova-plugin-adjoe": "https://github.com/adjoeio/adjoe-cordova-sdk#v3.3.2"
    }
    adb shell setprop log.tag.Playtime INFO
    adb shell setprop log.tag.Playtime ERROR
    const showAdjoeOfferwall = async () => {
      if (Platform.OS === 'ios') {
        return navigation.navigate('InAppBrowser', {
          url: `https://{your_company_name}.playtimeweb.com/play?user_id={user_id}&idfa={IDFA}`
        });
      }
    
      if (adjoeInitialized && Platform.OS === 'android') {
        return await Adjoe.showOfferwall().catch(err => {
          console.error(`Could not show Adjoe offerwall: ${err}`);
        });
      }
    };
    https://{your_company_name}.playtimeweb.com/play?user_id={user_id}&idfa={IDFA}
    allprojects {
        repositories {
            maven {
                url  "https://releases.adjoe.io/maven"
            }
        }
    }
    npm install
    allprojects {
        repositories {
            maven {
                url  "https://releases.adjoe.io/maven"
            }
        }
    }
    npm install
  • Run the following command to update the CocoaPods repository and install the dependencies:

    pod install --repo-update
  • Once installation is complete, open the .xcworkspace file generated by CocoaPods. The Playtime SDK will now be available for use within your project.

  • Check out our troubleshooting guide if you run into any issues with Cocoapods

    Select your target
  • Go to the General tab

  • Go down to the section Frameworks, Libraries, and Embedded Content

  • Click on add a new file

  • Choose the .xcframework

  • Set Embed & Sign.

  • Manual Integration

    npm install --save https://github.com/adjoeio/adjoe-react-native-sdk-ios#1.9.0

  • yarn add https://github.com/adjoeio/adjoe-react-native-sdk-ios#1.9.0

  • The package doesn't support Expo by default. To install it in an Expo project, see the official documentation on installing third-party packages.

    Install Native dependency

    You need to add the native iOS SDK as a dependency to your native iOS project.

    CocoaPods is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website.

    1. In your project directory, create a Podfile if one doesn’t already exist.

    2. Inside the Podfile, add the following configuration to include the Playtime SDK as a dependency within your app’s target. You can also specify a version or version range—see the CocoaPods Podfile syntax reference for more details.

    3. Open a terminal and navigate to your project’s root directory.

    4. Run the following command to update the CocoaPods repository and install the dependencies:

    5. Once installation is complete, open the .xcworkspace file generated by CocoaPods. The Playtime SDK will now be available for use within your project.

    Check out our troubleshooting guide if you run into any issues with Cocoapods

    Select your target
  • Go to the tab Signing & Capabilities

  • Add the App Attest capability

  • Private Key: Generate a private key that can be used for the AppAttest service and provide the file with the .p8 extension to adjoe. You can find the instruction on how to get the .p8 here. Only the DeviceCheck/AppAttest Service should be added.
    1. Key ID: Generate a key identifier associated with the enabled service.

    2. Team ID: Provide the Team ID, which app ID prefix: Team ID and the Bundle ID separated with a dot, for example: M4CAP9HPPJ.com.my.app. You can learn more about the app ID here.

    Make sure that after you add the capability to your project, you can see the .entitlements file. Specify the production environment.

    Do not use the same credentials, i.e., team ID, with device check for your own app. adjoe should be the only one using the AppAttest / DeviceCheck service with your team ID.

    Find the key: com.apple.developer.devicecheck.appattest-environment

  • Set the value to production (NOT development)

  • Your entitlements file should look like this:

    Search for "Code Signing Entitlements"
  • Ensure both Debug and Release configurations point to the same entitlements file with production set

  • dependencies: [
      .package(url: "https://github.com/adjoeio/adjoe-monetize-spm", from: "1.9.1")
    ]
    .product(name: "PlaytimeMonetize", package: "adjoe-monetize-spm")
    CocoaPods
    Monetize Dashboard
    source 'https://github.com/adjoeio/PlaytimeSpecExternal.git'
    
    platform :ios, '14.0'
    
    target 'MyApp' do
      use_frameworks!
    
      pod 'PlaytimeMonetize', '~> 1.9.1'
    end

    placement

    The placement of the Playtime Catalog inside your app, e.g., "Main Screen", "Shop", "Interstitial", "More Diamonds Screen", etc.

    You can also choose to use adjoe's Apps feature, showing the Playtime catalog with distinct gaming or app experiences.

    Option
    Explanation

    nongaming

    Only shows non-gaming campaigns

    gaming

    Only shows gaming campaigns

    subId1

    An optional identifier that is provided back in the S2S payout URL.

    subId2

    An optional identifier that is provided back in the S2S payout URL.

    subId3

    An optional identifier that is provided back in the S2S payout URL.

    subId4

    An optional identifier that is provided back in the S2S payout URL.

    subId5

    An optional identifier that is provided back in the S2S payout URL.

    application(_:didFinishLaunchingWithOptions:)
    application(_:didFinishLaunchingWithOptions:)
    Task {
        do {
        
            let params = PlaytimeParamsBuilder()
                    .setUANetwork("uaNetwork")
                    .setUAChannel("uaChannel")
                    .setUASubPublisherCleartext("uaSubPublisherCleartext")
                    .setUASubPublisherEncrypted("uaSubPublisherEncrypted")
                    .setPlacement("placement")
                    .build()
            
            let subIds = PlaytimeExtensionsBuilder()
                    .setSubId1("subId1")
                    .setSubId2("subId2")
                    .setSubId3("subId3")
                    .setSubId4("subId4")
                    .setSubId5("subId5")
                    .build()
           
            let options = PlaytimeOptionsBuilder()
                    .setParams(params)
                    .setExtensions(subIds)
                    .build()
            
            try await Playtime.showCatalog(
                     options: options
            )
        } catch {
            // handle errors / retry if needed
        }
    }

    optional

    ua_subpublisher_encrypted

    The encrypted package name or app id of the sub-publisher app within the network from which the user was acquired.

    optional

    gender

    There are three available options you can pass in: "female", "male", "unknown".

    optional

    age

    The current age of the user, e.g., 32.

    optional

    pub_sub_id_1

    These are optional identifiers used in your backend. They will be provided back in the S2S payout.

    optional

    pub_sub_id_2

    See pub_sub_id_1

    optional

    pub_sub_id_3

    See pub_sub_id_1

    optional

    pub_sub_id_4

    See pub_sub_id_1

    optional

    pub_sub_id_5

    See pub_sub_id_1

    optional

    nongaming

    Only shows non-gaming campaigns

    gaming

    Only shows gaming campaigns

    both

    S2S Payouts
    iCloud Private Relay

    Shows both gaming and non-gaming

    source 'https://github.com/adjoeio/PlaytimeSpecExternal.git'
    
    platform :ios, '13.0'
    
    target 'MyApp' do
      use_frameworks!
    
      pod 'PlaytimeMonetize', '~> 1.9.1'
    end
    "dependencies": {
      "react-native-adjoe-sdk-ios": "https://github.com/adjoeio/adjoe-react-native-sdk-ios#1.9.0"
    }
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>com.apple.developer.associated-domains</key>
        <array>
            <string>webcredentials:prod.adjoe.zone</string>
        </array>
        <key>com.apple.developer.devicecheck.appattest-environment</key>
        <string>production</string>
    </dict>
    </plist>
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
    	<key>sdkHash</key>
    	<string>INSERT_YOUR_HASH_HERE</string>
    </dict>
    </plist>
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        Task {
            do {
                try await Playtime.initialize(
                    userID: "userId",
                    sdkHash: "sdkHash",
                    uaNetwork: "uaNetwork",
                    uaChannel: "uaChannel",
                    uaSubPublisherCleartext: "uaSubPublisherCleartext",
                    uaSubPublisherEncrypted: "uaSubPublisherEncrypted",
                    placement: "placement"
                )
            } catch {
                // handle errors / retry if needed
            }
        }
        return true
    }
    - (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions
    {
        [Playtime initializeWithUserID:@"userID"
                               sdkHash:@"sdkHash"
                             uaNetwork:@"uaNetwork"
                             uaChannel:@"uaChannel"
               uaSubPublisherCleartext:@"uaSubPublisherCleartext"
               uaSubPublisherEncrypted:@"uaSubPublisherEncrypted"
                             placement:@"placement"
                     completionHandler:^(NSError * _Nullable error) {
            if (!error) {
                NSLog(@"Playtime initialized successfully");
            } else {
                NSLog(@"Error initializing Playtime: %@", error);
            }
        }];
    }
    Playtime.initialize({
      sdkHash: "sdkHash",
      userId: "userId",
      uaNetwork: "uaNetwork",
      uaChannel: "uaChannel",
      uaSubPublisherCleartext: "uaSubPublisherCleartext",
      uaSubPublisherEncrypted: "uaSubPublisherEncrypted",
      placement: "placement"
    })
    .then(() => {
       console.log('Successful initialization!');
    })
    .catch((err) => {
       console.error(err);
    });
    PlaytimeParams *params = [[[[[[PlaytimeParamsBuilder alloc] init]
                               setUANetwork:@"uaNetwork"]
                              setUAChannel:@"uaChannel"]
                             setUASubPublisherCleartext:@"uaSubPublisherCleartext"]
                            setUASubPublisherEncrypted:@"uaSubPublisherEncrypted"]
                           setPlacement:@"placement"]
                          build];
    
    PlaytimeExtensions *subIds = [[[[[[[[PlaytimeExtensionsBuilder alloc] init]
                                     setSubId1:@"subId1"]
                                    setSubId2:@"subId2"]
                                   setSubId3:@"subId3"]
                                  setSubId4:@"subId4"]
                                 setSubId5:@"subId5"]
                                build];
    
    PlaytimeOptions *options = [[[[PlaytimeOptionsBuilder alloc] init]
                                setParams:params]
                               setExtensions:subIds]
                              build];
    
    [Playtime showCatalogWithOptions:options
                       completionHandler:^(NSError * _Nullable error) {
            if (!error) {
                NSLog(@"Error showing Playtime catalog: %@", error);
            } else {
                NSLog(@"Did show catalog")
            }
        }];
    Playtime.showCatalog({
      uaNetwork: "uaNetwork",
      uaChannel: "uaChannel",
      uaSubPublisherCleartext: "uaSubPublisherCleartext",
      uaSubPublisherEncrypted: "uaSubPublisherEncrypted",
      placement: "placement",
      subId1: "subId1",
      subId2: "subId2",
      subId3: "subId3",
      subId4: "subId4",
      subId5: "subId5"
    })
    .then(() => {
       console.log('Did show catalog!');
    })
    .catch((err) => {
       console.error(err);
    });

    both

    Shows both gaming and non-gaming

    pod install --repo-update

    Changelog

    3.4.0

    Published: November 10th, 2025

    Features and Improvements ✨

    • Add improved error handling

    Bug fixes 🪲

    • Fix issue with back button

    • Critical bug fixes

    3.3.3

    Published: October 16th, 2025

    Bug fixes 🪲

    • Fix bug related to file-picker in chatbot

    3.3.2

    Published: October 8th, 2025

    Bug fixes 🪲

    • Fix bug related to Android 15

    • Fix logging error

    3.3.0

    Published: July 25th, 2025

    Update minSdkVersion to 21+

    Bug fixes 🪲

    • Fix improper TLS check

    Features and Improvements ✨

    • Support Android 15 Window Inset Changes

    • Add enhanced detection of suspicious users

    • Support Capacitor Framework

    3.1.1

    Published: July 15th, 2025

    Bug fixes 🪲

    • Fix issue with attaching WebView

    • Fix out of memory crash with notification icons

    • Fix issue with time-tracking

    3.1.0

    Published: April 28th, 2025

    Bug fixes 🪲

    • Accept empty values in PlaytimeParams.

    • Fix out of memory issue while saving campaign icons.

    • Fix crash related to registering broadcast receivers.

    • Show A server error occurred (HTTP 702)

    Features and Improvements ✨

    • Support 16 KB page size devices.

    • Add ability to remove notification permission.

    • Use WorkManager to collect usage on old Android devices and discontinue use of Alarm Manager and Services.

    3.0.0

    Published: November 11th, 2024

    Breaking Changes 🚨

    • Adjoe has been renamed to Playtime.

    • Offerwall has been renamed to Catalog.

    For React Native Only:

    • user-id has been renamed to userId

    The following APIs have been impacted by these changes. Please update your code so that the correct APIs are used.

    Previous Classes/Methods
    3.0.0 Classes/Methods

    Bug fixes 🪲

    • Fix a crash regarding loading resources in WebView

    • Remove additional = in playtime notification for certain translations

    • Fix not opening installed campaign apps for Android >= 13

    • Stop using

    Features and Improvements ✨

    • Add API Reference

    • Support customisable texts in reward notifications

    • Automatically redirect users to the app after accepting the Android usage permissions

    • Open the usage permission screen within the app rather than the Android Settings

    Migrate to 3.0.0

    1. Update method and class names

    • Update usages of Adjoe to Playtime.

    • Update usages of Offerwall to Catalog.

    For React Native Only:

    • Update usages of user-id to userId

    2. Remove usages of the following API:

    • setProfile - Instead you should use setUserProfile with PlaytimeOptions during the init.

    • canShowOfferwall / canShowCatalog


    2.2.2

    Published: 17 July 2024

    Bug fixes 🪲

    • Fix ANR related to protection library

    2.2.1 Has a known ANR, please do not use this version.

    2.2.1

    Published: 24 May 2024

    Bug fixes 🪲

    • Fix "webpage not available" error which occurred occasionally when returning back to Playtime from the Play Store

    • Fix Proguard issue that occurred while building a release version of the APK

    • Improve the SDK's “notification” logic.

    • Fix exception caused by running multiple processes

    Features and Improvements ✨

    • Deprecate canShowOfferwall API

    • Deprecate setProfile API

    • Improve Korean translations

    • Add enhanced detection of suspicious users

    2.2.0

    Published: 21 March 2024

    Bug fixes 🪲

    • Improve usage permission handling on Android 13+

    Features and Improvements ✨

    • Add enhanced detection of suspicious users

    • Add cash currency support in reward notifications

    2.1.1

    Published at: 19 December 2023

    • Bug fixes

    • Enhance Fraud detection

    • Translation fixes for the notifications

    • Fix for users that were not getting rewarded for the installed apps after they updated an SDK.

    2.1.0

    Published at: 28 July 2023

    • Bug fixes

    • Enhance Fraud detection

    • Usage collection enhancement

    • Fixed an inconsistency in the External User ID

    2.0.7

    Published at: 16 Mar 2023

    • Android 13 support

    • Depreciation of safetynet and replacing it with play integrity

    • New fraud update [VPN-detection]

    2.0.5

    Published at: 11 Nov 2022

    • Fix issues with notification.

    • Enhancements and improvements of workmanager and threading.

    • Bug fixes.

    2.0.4

    Published at: 14 Sep 2022

    • Fix issues with rewards.

    • Allow multiple init without throwing Already Initialised Exception.

    • Error visibility enhancement.

    2.0.2

    Published at: 11 Mar 2022

    • Migrate SDK to Androidx.

    • Add full support to android12.

    • Adding android:export in Manifest.

    • Update

    1.5.1

    Published at: 8 Oct 2021

    • Further enhancements on crash reports

    • Bugfixes

    1.5.0

    Published at: 14.09.2021

    • Additional scenarios to display Advance campaigns.

    • Enhance crash reports.

    • Fix Network issues.

    • Bugfixes.

    1.4.15

    Published at: 08.06.2021

    • Added App Info Fields to Campaigns.

    • Added Support for Advance.

    • Usage/Reward Enhancements.

    • Bugfixes.

    1.4.14

    Published at: 24.03.2021

    • Made the SDK lighter on permissions.

    1.4.13.1

    Published at: 10.03.2021

    • Resolved potential compatibility issues surrounding the WorkManager library.

    1.4.13

    Published at: 03.03.2021

    • Full support for android 11 on targetSDKVersion 30.

    • Improved the Toast Notification to give users immediate feedback.

    • Improved the usage collection reliability.

    • Changed Sub-ID handling with AdjoeParams object to provide further context on the users (such as User Acquisition data and the Playtime placement).

    1.4.12

    Published at: 28.01.2021

    • Improved initialization speed.

    • Improved onboarding user flow on Android 10+.

    • Bugfix for potential crashes.

    1.4.11

    Published at: 30.11.2020

    • Bugfixing for calling the init method outside the main process.

    • Bugfix for accepting the TOS without a network connection.

    1.4.10

    Published at: 04.11.2020

    • Bugfixes for building on Windows.

    1.4.9

    Published at 29.10.2020

    • Bugfixes for apps using minified R8/ProGuard builds.

    1.4.8

    Published at 07.10.2020

    • Bugfixes.

    1.4.7

    Published at 25.09.2020

    • Kotlin: Fixed Adjoe.Options in Kotlin.

    • Unity: Added inline documentation.

    • Unity: The methods now output logs when called inside the Unity Editor.

    1.4.6

    Published at: 16.09.2020‌

    • Added the method Adjoe.isAdjoeProcess to check whether the current process is the :adjoe child process.

    • Added logs to more easily trace and reproduce bugs. See .

    if there’s a connection issue or if the device is offline.

    AdjoeOfferwallListener

    PlaytimeCatalogListener

    AdjoeNotInitializedException

    PlaytimeNotInitializedException

    AdjoeInitialisationListener

    PlaytimeInitialisationListener

    Adjoe.Options

    PlaytimeOptions

    AdjoeParams

    PlaytimeParams

    AdjoeGender

    PlaytimeGender

    AdjoeExtensions

    PlaytimeExtensions

    setDataDirectorySuffix
    of
    WebView
  • Fix crash related to the visibility of AdjoeActivity

  • Add type definitions in React Native Wrapper

  • Add enhanced detection of suspicious users

  • Improve error handling and performance through removal of unused internal classes

  • Improve security of WebView settings

  • Supporting all React Native versions.

    You can now use the Adjoe.setUAParams(adjoeParams : AdjoeParams) to add additional parameters.

  • Added translation for additional 7 languages ( in, it, ja, ko, pl, pt and tr) in addition to en, de, es, fr

  • consistent update cross all wrappers to mark this update as the new base line

  • Bug fixes.
    workManager
    library to 2.7.1.
  • Add flag IMMUTABLE and MUTABLE to pendingIntent.

  • Bug fixes.

  • Further enhancement.

  • Adjoe.init()

    Playtime.init()

    Adjoe.isInitialized()

    Playtime.isInitialized()

    Adjoe.sendUserEvent()

    Playtime.sendUserEvent()

    Adjoe.getOfferwallIntent()

    Playtime.getCatalogIntent()

    Adjoe.setOfferwallListener()

    Playtime.setCatalogListener()

    Adjoe.removeOfferwallListener()

    Playtime.removeCatalogListener()

    Enabling logs

    Examples

    Below is a full example implementation of the Playtime SDK. It uses Jetpack Compose to set up a button that leads the user to the Playtime Offerwall. To run the example:

    1. Add the dependency

    2. Add your SDK hash

    3. If you're using an emulator, add it as a test device.

    In the future we will update this page with examples for our other wrappers. Please check back soon.

    package com.example.sdk_integration_activity
    
    import android.content.Context
    import android.os.Bundle
    import android.util.Log
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.compose.foundation.layout.Arrangement
    import androidx.compose.foundation.layout.Column
    import androidx.compose.foundation.layout.fillMaxSize
    import androidx.compose.foundation.layout.size
    import androidx.compose.material3.Button
    import androidx.compose.material3.MaterialTheme
    import androidx.compose.material3.Surface
    import androidx.compose.material3.Text
    import androidx.compose.runtime.Composable
    import androidx.compose.runtime.rememberCoroutineScope
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.tooling.preview.Preview
    import androidx.compose.ui.unit.dp
    import io.adjoe.sdk.Playtime
    import io.adjoe.sdk.Playtime.getUserId
    import io.adjoe.sdk.PlaytimeException
    import io.adjoe.sdk.PlaytimeExtensions
    import io.adjoe.sdk.PlaytimeGender
    import io.adjoe.sdk.PlaytimeInitialisationListener
    import io.adjoe.sdk.PlaytimeNotInitializedException
    import io.adjoe.sdk.PlaytimeCatalogListener
    import io.adjoe.sdk.PlaytimeParams
    import io.adjoe.sdk.PlaytimeUserProfile
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.launch
    import kotlinx.coroutines.withContext
    import java.util.Calendar
    import java.util.Date
    
    class MainActivity : ComponentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            initPlaytime()
            setupPlaytimeCatalogListener()
        }
    
    // the initPlaytime() function has the playtime params, but if your params have
    // changed, or if you don't yet have a param, it is recommended that you take a
    // different approach. For example, if you don't get the user ID until after the
    // user has signed in. You should then send these values in the OnResume.
    private fun initPlaytime()
    {
        // pass in the user id. These values should come from your backend.
        // If you don't get the userID until after the sign-in, this value can be passed-in onResume.
        val userId = "f0b9d695-405d-4b13-8a79-5fe21dc901e6"
    
        // set up the parameters, these values should also come from your backend.
        val playtimeParams = PlaytimeParams.Builder()
            .setUaNetwork("tiktok")
            .setUaChannel("video")
            .setUaSubPublisherCleartext("Example: Game 2")
            .setUaSubPublisherEncrypted("8bb1e7911818be32449f6726ff7ecd102ba1862b")
            .setPlacement("Main Screen")
            .build()
    
        // these values are additional options that you can pass-in to identify users. These data will be sent back in the S2S URL. Example data below.
        val playtimeExtensions = PlaytimeExtensions.Builder()
            .setSubId1("Target Group 1")
            .setSubId2("Target Group 2")
            .build()
    
        //Set the options
        val options = PlaytimeOptions()
            .setUserId(userId)
            .setParams(playtimeParams)
            .setUserProfile(getUserProfile())
            .setExtensions(playtimeExtensions)
    
        // Initialize the adjoe Playtime SDK, passing in the Playtime Options
        try {
            Playtime.init(this, "your_sdk_hash", options, object : PlaytimeInitialisationListener {
                override fun onInitialisationFinished() {
                    // The adjoe Playtime SDK was initialized successfully
                    //("initialized successfully")
                }
    
                override fun onInitialisationError(exception: Exception?) {
                    // An error occurred while initializing the Playtime SDK.
                    // Note that exception might be null
                }
            })
        } catch (e: PlaytimeNotInitializedException) {
            // Handle initialization exception
        }
        setContent {
            ExampleApp(this)
        }
    }
    
    //Get the user profile information. These data should come from your backend. 
        fun getSampleUserGender(): String {
            return "male"
        }
    
        //Pass in the user's birthday information if it's available
        //and if they've consented to sharing it
        fun getSampleUserBirthday(): Date {
            val birthday = Calendar.getInstance()
            birthday.set(Calendar.YEAR, 1995)
            birthday.set(Calendar.MONTH, Calendar.JANUARY)
            birthday.set(Calendar.DAY_OF_MONTH, 30)
    
            return birthday.time
        }
    
        val birthday = getSampleUserBirthday()
    
        fun getUserProfile(): PlaytimeUserProfile {
            val gender: PlaytimeGender = when (getSampleUserGender()) {
                "male" -> PlaytimeGender.MALE
                "female" -> PlaytimeGender.FEMALE
                else -> PlaytimeGender.UNKNOWN
            }
            return PlaytimeUserProfile(gender, birthday)
        }
    
    //Set up the listener for when the catalog is opened or closed. 
        private fun setupPlaytimeCatalogListener() {
            Playtime.setCatalogListener(object : PlaytimeCatalogListener {
                override fun onCatalogOpened(type: String) {
                    Log.d("ADJOE", "Catalog of type '" + type + "' was opened")
                }
    
                override fun onCatalogClosed(type: String) {
                    Log.d("ADJOE", "Catalog of type '" + type + "' was closed")
                }
            })
        }
    
        //Whenever your app comes back into the foreground,
        //you should init the SDK again and send the updated catalog Params.
        override fun onResume() {
            super.onResume()
            initPlaytime()
        }
    }
    
    //Set up the params to send with the teaser. Use this in case you have multiple catalog
    // placements in your app and want to evaluate the effectiveness of each.
    val teaserPlaytimeParams: PlaytimeParams = PlaytimeParams.Builder()
        .setPlacement("Main Screen")
        .build()
    
    //Set up the additional methods
    private suspend fun additionalChecks(context: Context) {
        withContext(Dispatchers.IO) {
            getVersion()
            getVersionName()
            val tosAccepted = hasAcceptedTOS(context)
            val permissionsAccepted = hasAcceptedUsagePermission(context)
            val userIdRetreived = getUserId(context)
        }
    }
    suspend fun getVersion() {
        val version: Int? = Playtime.getVersion()
        Log.d("ADJOE", "Playtime SDK version is $version")
    }
    
    suspend fun getVersionName() {
        val versionName: String? = Playtime.getVersionName()
        Log.d("ADJOE", "Playtime SDK version name is $versionName")
    }
    
    suspend fun hasAcceptedTOS(context: Context) {
        val accepted: Boolean? = Playtime.hasAcceptedTOS(context)
        Log.d("ADJOE", "TOS accepted: $accepted")
    }
    
    suspend fun hasAcceptedUsagePermission(context: Context) {
        val accepted: Boolean? = Playtime.hasAcceptedUsagePermission(context)
        Log.d("ADJOE", "Usage permission accepted: $accepted")
    }
    
    suspend fun getUserId(context: Context) {
        val userId: String? = Playtime.getUserId(context)
        Log.d("ADJOE", "User ID is \"$userId\"")
    }
    
    // Set up the teaser event function
    private fun sendTeaser(context:Context) {
                Playtime.sendUserEvent(context, Playtime.EVENT_TEASER_SHOWN, null, teaserPlaytimeParams)
            }
    
    @Composable
    fun ExampleApp(activity: ComponentActivity) {
        Surface(color = MaterialTheme.colorScheme.background) {
            Column(
                modifier = Modifier.fillMaxSize(),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                ShowPlaytimeCatalogButton(activity)
            }
        }
    }
    
    @Composable
    fun ShowPlaytimeCatalogButton(activity: ComponentActivity) {
        val coroutineScope = rememberCoroutineScope()
    
        Column(
            modifier = Modifier.fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
    
            // Button to navigate to the catalog screen
            Button(
                onClick = {
                    coroutineScope.launch {
                        try {
                            val catalogCatalogIntent = Playtime.getCatalogIntent(activity)
                            activity.startActivity(playtimeCatalogIntent)
                        } catch(notInitializedException: PlaytimeNotInitializedException) {
                            // Handle not initialized exception
                        } catch(exception: PlaytimeException) {
                            // Handle other exceptions
                        }
                        sendTeaser(context = activity.applicationContext) // send the teaser when the user clicks the Catalog Button. The teaser can also be sent when the user views it.
                        additionalChecks(context = activity.applicationContext) // run the additional checks. 
                    }
                },
                modifier = Modifier.size(width = 250.dp, height = 75.dp)
            ) {
                Text("Show Playtime Catalog")
            }
        }
    }
    
    @Preview(showBackground = true)
    @Composable
    fun DefaultPreview() {
        ExampleApp(ComponentActivity())
    }

    Current version: 1.9.0 . View Changelog.

    Advertisers define ad formats and target countries through campaigns, which are then featured in the Playtime Catalog. These campaigns are set by advertisers and allow for rewards once the completion of the campaign conditions are verified through the MMP.

    The Playtime catalog lists various campaigns that the users can complete to receive a reward in your app's native in-app currency. Our algorithms prioritize displaying games and apps to users based on their potential to generate maximum revenue and the likelihood of being downloaded. Campaign availability depends on user permissions and country.

    Advertisers define ad formats and target countries through campaigns, which are then featured in the Playtime Catalog. These campaigns are set by advertisers and allow for rewards once the completion of the campaign conditions are verified through the MMP.

    The Playtime catalog lists various campaigns that the users can complete to receive a reward in your app's native in-app currency. Our algorithms prioritize displaying games and apps to users based on their potential to generate maximum revenue and the likelihood of being downloaded. Campaign availability depends on user permissions and country.

    Parameter
    Detail
    Format
    Type
    Platform (Android/iOS)

    user_uuid

    Identifies the user eligible for rewards. This ID must align with the one provided during SDK initialization or user-id in the PlaytimeWeb URL.

    String

    Required

    Both

    sid

    The request signature, used to verify request authenticity. Requires a token, which will be shared with you by your Account Manager. Detailed setup instructions .

    String

    Required

    Both

    trans_uuid

    The unique transaction ID.

    UUID v4

    Required

    Both

    coin_amount

    The amount of virtual currency the user should get.

    Integer

    Required

    Both

    currency

    The name of the virtual currency.

    String

    Required

    Both

    sdk_app_id

    App ID of the SDK. Example: com.example.android.gamename

    String

    Optional

    Both

    app_id

    App ID that is used to generate usage.

    String

    Optional

    Both

    device_id

    Optional external device ID.

    String

    Optional

    Both

    app_name

    URL encoded app name in English

    String

    Optional

    Both

    reward_level

    The name of the reward level of the game. For Time-based (Playtime) campaigns, this will correspond to each of the 37 levels. For event-based campaigns (Advance+), this value will always be 0.

    Integer

    Optional

    Android

    reward_type

    The type of reward given. Example: Playtime, AdvancePlus

    String

    Optional

    Both

    ua_network

    The network from which the user was acquired, e.g., adwords, facebook, organic.

    String

    Optional

    Both

    ua_channel

    The channel of the campaign in the network that led to the user's acquisition, e.g., incent, video.

    String

    Optional

    Both

    ua_subpublisher_encrypted

    The encrypted package name or app id of the sub-publisher app within the network from which the user was acquired.

    String

    Optional

    Both

    ua_subpublisher_cleartext

    The cleartext package name of app id of the sub publisher app in the network where the user was acquired, e.g., com.domain.appName

    String

    Optional

    Both

    placement

    The placement of Playtime inside your app, e.g., "Main Screen", "Shop", "Interstitial", "More Diamonds Screen", etc.

    String

    Optional

    Both

    app_category

    Category of the App that user played. Example: Board, Casino, Casual, Entertainment, Puzzle, Other

    String

    Optional

    Both

    usage_seconds

    Amount of seconds the current level requires. Time-based campaigns (Playtime) only.

    Integer

    Optional

    Android

    total_usage_seconds

    Total amount of seconds that user has played this game. Time-based campaigns (Playtime) only.

    Integer

    Optional

    Android

    advanceplus_event_name

    The name of the milestone/event that user completed to receive this reward. Event-based campaigns (Advance+) only.

    String

    Optional

    Both

    advanceplus_event_description

    The description of the milestone/event that user completed to receive this reward. Event-based campaigns (Advance+) only.

    String

    Optional

    Both

    publisher_sub_id1

    Value set in the extension object to be returned in the S2S payout URL.

    String

    Optional

    Both

    publisher_sub_id2

    Value set in the extension object to be returned in the S2S payout URL.

    String

    Optional

    Both

    publisher_sub_id3

    Value set in the extension object to be returned in the S2S payout URL.

    String

    Optional

    Both

    publisher_sub_id4

    Value set in the extension object to be returned in the S2S payout URL.

    String

    Optional

    Both

    publisher_sub_id5

    Value set in the extension object to be returned in the S2S payout URL.

    String

    Optional

    Both

    event_boost_factor

    The boost factor for the current promotion. Example: 2x, returned as 2.

    Integer

    Optional

    Both

    event_boost_start_date

    Date when the boost factor began.

    String

    Optional

    Both

    event_boost_stop_date

    Date when the boost factor stopped.

    String

    Optional

    Both

    install_date

    Date when the game was installed.

    String

    Optional

    Both

    manually_completed

    Returns true if the event was completed manually in the monetize dashboard, false otherwise.

    Boolean

    Optional

    Both

    advanceplus_event_level

    Returns the consecutive order number of the completed milestone. Starts from 0.

    Integer

    Optional

    Both

    event_type

    Returns sequential or bonus , depending on the event.

    String

    Optional

    Both

    timed_coins_amount

    Returns the amount of coins for the time-boosted event.

    Integer

    Optional

    Both

    timed_coins_event_duration

    Returns the duration of the time-boosted event in seconds.

    Integer

    Optional

    Both

    rewarded_at

    Time the reward was sent. Format: Unix epoch with seconds precision.

    Integer

    Optional

    Both

    below

    Advertisers define ad formats and target countries through campaigns, which are then featured in the Playtime Catalog. These campaigns are set by advertisers and allow for rewards once the completion of the campaign conditions are verified through the MMP.

    The Playtime catalog lists various campaigns that the users can complete to receive a reward in your app's native in-app currency. Our algorithms prioritize displaying games and apps to users based on their potential to generate maximum revenue and the likelihood of being downloaded. Campaign availability depends on user permissions and country.

    Use the SDK

    There are 2 parts to implementing and using the Playtime SDK:

    Part 1: Intialize the Playtime SDK

    Part 2: Launch the Catalog

    Part 1: Initialize the SDK

    To make all of the features of the Playtime SDK available, you need to initialize it first. This initialization happens asynchronously in the background, with its completion indicated by a callback method. Make sure to initialize the SDK immediately after the app launches and each time it returns to the foreground.

    Best practices for the SDK initialization:

    • Initialize early: Initialize the SDK as soon as possible after your app starts. This ensures all Playtime SDK features are ready for use right away.

    • Initialize after your app authentication: Once a user signs up or logs in, initialize the SDK again with the latest user details. This updates the userID within the SDK.

    • Initialize regularly: For the best user experience, call the init method every time your app comes into the foreground. Doing so will not affect your app's performance.

    Initialization

    Important: Initialization must happen on the main thread since it's a UI-related action. Calling any of the init methods off the main thread could lead to app disruptions.

    Playtime Options

    Pass the following additional options to the Playtime SDK during initialization:

    Parameter
    Type
    Explanation

    Playtime Parameters

    When initializing the SDK, you should include additional User Acquisition and Playtime placement parameters. These parameters allow you to track and analyze Playtime's performance within your app.

    To make the most of the Playtime SDK, to maximize your revenue and provide the best user experience, use all of these parameters for data-driven optimizations. For instance, you can monitor the performance of different Playtime button placements or determine the effectiveness of different UA sources.

    You can provide these parameters in any combination, both at initialization and at any point in the app's lifecycle—such as when a user launches the Playtime Catalog interacts with a campaign or requests a payout.

    These values must be url-safe as they could potentially be returned as query parameters in the .

    Parameter
    Type
    Explanation

    User Profile Information

    Our game recommendations are personalized through algorithms that consider users' preferences and gender information. If a user consents to share their gender and/or birthday, you can share this information with adjoe to enhance the customization of game suggestions.

    The code below is for demonstration purposes and can be modified to best suit your app's set-up. The important thing is that you get the user's birthday and gender information from your backend and pass in the data to adjoe when initializing.

    The birthday data should be in ISO format. If your data doesn't already look like that, please make sure to format it so that it is. Example: 2000-07-17T22:25:00.000Z

    Playtime Extensions

    These are optional identifiers used in your backend. They will be provided back in the URL.

    Checking Initialization

    We recommend to verify the SDK is correctly initialized. Each wrapper provides an Playtime.isInitialized method for this purpose. When executed, this method returns true if the SDK is initialized, and false otherwise. Use IsInitialized for debugging or to verify SDK initialization before calling other methods.

    Important: Do not call Playtime.init based on the result of isInitialized.The SDK already performs its own initialization checks. Doing so again may degrade the user experience.

    Part 2: Launch the Catalog

    The primary function of the Playtime SDK is showing the catalog to the users. The Playtime lists various campaigns that the users can complete to receive a reward in the app's native in-app currency.

    Add a Teaser Event

    When the placement button for Playtime appears, whether clicked or not, please send us the teaser event. This notifies adjoe when and where the placement is shown, which is crucial for tracking user engagement and enhancing the user experience.

    Use playtimeParams to pass in the placement information, i.e., where in the app the button was shown - for example: "Main Page".

    Launch Activity

    We recommend triggering the catalog via a button or UI element (within the SDK it is housed in a separate activity). Rather than launching the activity directly, we provide an Intent for you to initiate it, enabling, for example, transition animations for a smoother user experience.

    Use the code below to display the adjoe Playtime Offerwall. It is recommended to send any updated PlaytimeParams when launching the Playtime Catalog.

    Set Up a Listener (Native)

    You can set up a listener to be notified when the Playtime Catalog is opened or closed, with the event type always being catalog. To remove the listener, simply call Playtime.removeCatalogListener().

    Additional Useful Methods

    name
    description

    User Experience

    Terms of Service
    Access to Advertising Info
  • Initialization triggers: Call the init method at the app start and resume (when the app comes back into the foreground).

  • Placement

    String

    The placement of the Playtime Catalog inside your app, e.g., "Main Screen", "Shop", "Interstitial", "More Diamonds Screen", etc.

    You can also choose to use adjoe's Apps feature, showing the Playtime catalog with distinct gaming or app experiences.

    Option
    Explanation

    User ID

    String

    A custom identifier you can assign to each user. If you haven't set one, the Playtime SDK generates it automatically. It is accessible through Playtime.getUserId(Context). This ID is necessary for S2S payouts, thus required. Ensure this value is url-safe and that the value is never empty.

    Parameters

    Object

    Values for the User Acquisition and Playtime placement information. These values must be url-safe. More on this below.

    User Profile

    Object

    Birthday and gender data of the app users. This data is used by adjoe to provide the best campaigns for the user. More on this below.

    Extensions

    Object

    These are optional identifiers used in your backend. They will be provided back in the S2S payout.

    UA Network

    String

    User Acquisition Network. The network through which the user was acquired, e.g., Adwords, Facebook, or Organic.

    UA Channel

    String

    User Acquisition Channel. The channel within the network from which the user was acquired, e.g. incentivized or video campaigns.

    User Acquisition Sub Publisher Cleartext

    String

    The cleartext package name of the sub-publisher's app ID within the network from which the user was acquired, e.g., com.domain.appName.

    User Acquisition Sub Publisher Encrypted

    String

    The encrypted package name or app ID of the sub-publisher's app from the network where the user was acquired.

    getVersion

    Returns the internal version code of the Playtime SDK, for example 70.

    getVersionName

    Returns the version name of the Playtime SDK, for example 3.0.0

    hasAcceptedTOS

    Checks whether the user has accepted the adjoe Terms of Service (TOS). Returns true if the user has accepted the TOS and false otherwise.

    getUserId

    Returns the unique ID of the user by which the user is identified.

    hasAcceptedUsagePermission

    Checks whether the user has given access to the usage statistics. Returns true when the user has given access, false otherwise.

    If the user rejects TOS, they will see the following screen. No campaigns will be shown.

    After the user selects "Allow", they will be taken to the usage settings to allow tracking.

    Users need to accept adjoe's Terms of Service (TOS) to use Playtime and access games. Agreeing to the TOS is mandatory before installing any partner apps. After users accept the TOS, we can identify the types of apps/games they have installed, allowing our algorithm to tailor game suggestions on the catalog based on their preferences.

    We also ask the user to accept the App Usage Permissions. This allows us to monitor the time spent in each game or app. Without accepting these permissions, users won't be eligible to participate in Playtime campaigns and will be limited to only Advance or Advance+ campaigns.

    S2S Payout request
    S2S payout
    catalog
    // The initialization will run in the main application process asynchronously in the background and notify you when it is finished by invoking the PlaytimeInitializationListener's onInitialisationFinished or onInitialisationError method.
    
    class MainActivity : AppCompatActivity {
    	
        override fun onCreate(savedInstanceState: Bundle?) {
            Playtime.init(this, "sdkHash", options, object: PlaytimeInitialisationListener {
                
                override fun onInitialisationFinished() {
                    // the Playtime SDK was initialized successfully
                }
                
                override fun onInitialisationError(exception: Exception?) {
                    // an error occurred while initializing the Playtime SDK.
                    // note that exception might be null
                }
            })
        }
    }
    // The initialization will run in the main application process asynchronously in the background and notify you when it is finished by invoking the PlaytimeInitializationListener's onInitialisationFinished or onInitialisationError method.
    
    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate() {
            super.onCreate();
            Playtime.init(this, "sdkHash", options, new PlaytimeInitialisationListener() {
    
                @Override
                public void onInitialisationFinished() {
                    // the Playtime SDK was initialized successfully
                }
    
                @Override
                public void onInitialisationError(Exception exception) {
                    // an error occurred while initializing the Playtime SDK.
                    // note that exception might be null
                }
            });
        }
    }
    // Should be called during the app start, for example in the App.js's componentDidMount() method
    
    Playtime.init('sdkHash', options).then(() => {
          console.log('Initialized Playtime SDK'); 
        }).catch((err) => {
          console.log('Could not initilize Playtime SDK');
          console.error(err);
        });
    // The init method will return a Future<void> which succeeds when the SDK has been initialized successfully and fails with an error when the initialization fails. Make sure to initialize in initState as well as didChangeAppLifecycleState.
    
    import 'package:adjoe/adjoe.dart';
    import 'package:adjoe/gender.dart';
    import 'package:adjoe/options.dart';
    import 'package:adjoe/params.dart';
    import 'package:adjoe/user_profile.dart';
    
    void initializePlaytime() {
      Playtime.init(sdkHash, options).then((_) {
        print('Init finished successful');
      }, onError: (err) {
        print('Init failed: $err');
      });
    }
    using io.adjoe.sdk;
    
    public class Main {
    
    void Start()
    {
        Playtime.Init("sdkHash", options, PlaytimeInitialisationSuccess, PlaytimeInitialisationError);
    }
    
    public void PlaytimeInitialisationSuccess()
    {
        // the Playtime SDK was initialized successfully
    }
    
    public void PlaytimeInitialisationError(Exception exception)
    {
        // an error occurred while initializing the Playtime SDK.
        // note that exception might be null
    }
    }
    Playtime.init(
        {
            apiKey: SDK_HASH //MANDATORY,
            userId: "VALUE" // OPTIONAL,
            applicationProcessName: "VALUE" // OPTIONAL,
            playtimeParams: { // OPTIONAL
                uaNetwork: "VALUE", // OPTIONAL
                uaChannel: "VALUE", // OPTIONAL
                uaSubPublisherCleartext: "VALUE", // OPTIONAL
                uaSubPublisherEncrypted: "VALUE", // OPTIONAL
                placement: "VALUE" // OPTIONAL
            },
            playtimeExtension: { // OPTIONAL
                  subId1: "VALUE", // OPTIONAL
                  subId2: "VALUE", // OPTIONAL
                  subId3: "VALUE", // OPTIONAL
                  subId4: "VALUE", // OPTIONAL
                  subId5: "VALUE" // OPTIONAL
            },
            playtimeUserProfile: { // OPTIONAL
                  gender: "VALUE", // OPTIONAL
                  birthday: "VALUE" // OPTIONAL
            }
        })
        .then(() => //INITIALIZATION FINISHED SUCCESSFULLY )
        .catch((err) => //HANDLE ERROR)
    window.PlaytimePlugin.initialize(
      'sdkHash','options',
      function() {
          // the Playtime plugin was initialized successfully
      },
      function(err) {
          // an error occurred while initializing the Playtime plugin.
      },
    );
    val options = PlaytimeOptions()
        .setUserId(userId)
        .setParams(playtimeParams);
        .setUserProfile(playtimeUserProfile)
        .setExtensions(playtimeExtensions);
    Playtime.Options options = new PlaytimeOptions()
        .setUserId(userId)
        .setParams(playtimeParams);
        .setUserProfile(playtimeUserProfile)
        .setExtensions(playtimeExtensions);
    Playtime.init('sdkHash', 
        { 
          'userId': "06957070-6579-46cd-9665-d8fe7cd286d5",
          'playtimeParams': {
                    'uaNetwork': "uaNetwork_value",
              	'uaChannel': "uaChannel_value",
              	'uaSubPublisherCleartext': "uaSubPublisherCleartext_value",
              	'uaSubPublisherEncrypted': "uaSubPublisherEncrypted_value",
              	'placement': 'placement_value'
            }, 
         'playtimeExtension': {
            'subId1': "RN_subId1",
            'subId2': "RN_subId2",
            'subId3': "RN_subId3",
            'subId4': "RN_subId4",
            'subId5': "RN_subId5"
        }, 
        'playtimeUserProfile': playtimeUserProfile()
        
        }).then(() => {
          console.log('Initialized Playtime SDK');
        })
        .catch((err) => {
          console.log('Could not initilize Playtime SDK');
          console.error(err);
        });
    PlaytimeOptions options = new PlaytimeOptions()
      ..userId = 'user_id'
      ..params = (new PlaytimeParams()
        ..uaNetwork = 'The user acquisition network'
        ..uaChannel = 'The user acquisition channel.'
        ..uaSubPublisherCleartext = 'The user acquisition publisher cleartext'
        ..uaSubPublisherEncrypted = 'The user acquisition publisher encrypted'
        ..placement = 'The catalog placement')
      ..userProfile = playtimeUserProfile
      ..extensions = (new PlaytimeExtensions()
        ..subId1 = 'flutter_subId1'
        ..subId2 = 'flutter_subId2'
        ..subId3 = 'flutter_subId3'
        ..subId4 = 'flutter_subId4'  
        ..subId5 = 'flutter_subId5');
    PlaytimeOptions options = new PlaytimeOptions()
        .SetUserId(userId)
        .SetPlaytimeParams(playtimeParams)
        .SetPlaytimeUserProfile(playtimeUserProfile)
        .SetPlaytimeExtensions(playtimeExtensions);
    // A complete example
    window.PlaytimePlugin.initialize(
      'sdkHash',
      { 
            'user_id': getUserId(),
            'playtime_params': {
                    'uaNetwork': "uaNetwork_value",
              	'uaChannel': "uaChannel_value",
              	'uaSubPublisherCleartext': "uaSubPublisherCleartext_value",
              	'uaSubPublisherEncrypted': "uaSubPublisherEncrypted_value",
              	'placement': 'placement_value'
            }, 
            'playtime_extension': {
                    'subId1': "RN_subId1",
                    'subId2': "RN_subId2",
                    'subId3': "RN_subId3",
                    'subId4': "RN_subId4",
                    'subId5': "RN_subId5"
            }, 
            'user_profile': {
                    'gender': 'MALE',
                    'birthdate': "2023-07-17T22:25:00.000Z"
            }
        },
      function() {
          // the Playtime plugin was initialized successfully
      },
      function(err) {
          // an error occurred while initializing the Playtime plugin.
      },
    );
    val playtimeParams = PlaytimeParams.Builder()
        .setUaNetwork("network")
        .setUaChannel("channel")
        .setUaSubPublisherCleartext("SubPublisherCleartext")
        .setUaSubPublisherEncrypted("SubPublisherEncrypted")
        .setPlacement("placement")
        .build()
    Playtime.setUAParams(context, playtimeParams)
    PlaytimeParams playtimeParams = new PlaytimeParams.Builder()
                    .setUaNetwork("network")
                    .setUaChannel("channel")
                    .setUaSubPublisherCleartext("SubPublisherCleartext")
                    .setUaSubPublisherEncrypted("SubPublisherEncrypted")
                    .setPlacement("placement")
                    .build();
    Playtime.setUAParams(context, playtimeParams);
    playtime.setUAParams({
                'uaNetwork': "RN-uaNetwork",
                'uaChannel': "RN-uaChannel",
                'uaSubPublisherCleartext': "RN-uaSubPublisherCleartext",
                'uaSubPublisherEncrypted': "RN-uaSubPublisherEncrypted",
                'placement': 'RN-placement'
    })
    .then(() => {
                this.logDialog(`Setting UA prameters were sucessful.`);
    })
    .catch((err) => {
                this.logDialog(`Setting UA prameters failed.`);
                console.error(err);
    });
    Playtime.setUAParams(new PlaytimeParams()
            ..uaNetwork = 'uaNetwork'
            ..uaChannel = 'uaChannel'
            ..uaSubPublisherCleartext = 'uaSubPublisherCleartext'
            ..uaSubPublisherEncrypted = 'uaSubPublisherEncrypted'
            ..placement = 'placement');
    PlaytimeParams playtimeParams = new PlaytimeParams();
    playtimeParams.SetUaNetwork("network")
               .SetUaChannel("Channel")
               .SetUaSubPublisherCleartext("SubPublisherCleartext")
               .SetUaSubPublisherEncrypted("SubPublisherEncrypted")
               .SetPlacement("placement");
    Playtime.SetUAParams(playtimeParams);
    // A complete example
    window.PlaytimePlugin.initialize(
      'sdkHash',
      { 
            'user_id': getUserId(),
            'playtime_params': {
                    'uaNetwork': "uaNetwork_value",
              	'uaChannel': "uaChannel_value",
              	'uaSubPublisherCleartext': "uaSubPublisherCleartext_value",
              	'uaSubPublisherEncrypted': "uaSubPublisherEncrypted_value",
              	'placement': 'placement_value'
            }, 
            'playtime_extension': {
                    'subId1': "RN_subId1",
                    'subId2': "RN_subId2",
                    'subId3': "RN_subId3",
                    'subId4': "RN_subId4",
                    'subId5': "RN_subId5"
            }, 
            'user_profile': {
                    'gender': 'male',
                    'birthdate': "2000-07-17T22:25:00.000Z"
            }
        },
      function() {
          // the Playtime plugin was initialized successfully
      },
      function(err) {
          // an error occurred while initializing the Playtime plugin.
      },
    );
    fun playtimeUserProfile(): PlaytimeUserProfile {
        val gender: PlaytimeGender = when (getUserGender()) {
            "male" -> PlaytimeGender.MALE
            "female" -> PlaytimeGender.FEMALE
            else -> PlaytimeGender.UNKNOWN
        }
    
    // if you don't know the exact birthday, the year is enough
        val birthday: Date = if (isExactBirthdayKnown()) {
            getUserBirthday()
        } else {
            val calendar = Calendar.getInstance()
            calendar.set(Calendar.YEAR, getYearOfBirth())
            calendar.set(Calendar.MONTH, 0)
            calendar.set(Calendar.DAY_OF_MONTH, 0)
            calendar.time
        }
    
        return PlaytimeUserProfile(gender, birthday)
    }
    public class UserProfileUtils {
    
        public static PlaytimeUserProfile playtimeUserProfile() {
            PlaytimeGender gender = getPlaytimeGender(getUserGender());
            Date birthday = getBirthday();
            return new PlaytimeUserProfile(gender, birthday);
        }
    
        private static PlaytimeGender getPlaytimeGender(String userGender) {
            switch (userGender) {
                case "male":
                    return PlaytimeGender.MALE;
                case "female":
                    return PlaytimeGender.FEMALE;
                default:
                    return PlaytimeGender.UNKNOWN;
            }
        }
    
        private static String getUserGender() {
            // Implement this method to get the user's gender
            return "male"; // Example placeholder
        }
    
        private static Date getBirthday() {
            if (isExactBirthdayKnown()) {
                return getUserBirthday();
            } else {
                Calendar calendar = Calendar.getInstance();
                calendar.set(Calendar.YEAR, getYearOfBirth());
                calendar.set(Calendar.MONTH, Calendar.JANUARY);
                calendar.set(Calendar.DAY_OF_MONTH, 1);
                return calendar.getTime();
            }
        }
    
        private static Date getUserBirthday() {
            // Implement this method to get the user's birthday
            return new Date(); // Example placeholder
        }
    
        private static boolean isExactBirthdayKnown() {
            // Implement this method to determine if the exact birthday is known
            return true; // Example placeholder
        }
    
        private static int getYearOfBirth() {
            // Implement this method to get the user's year of birth
            return 1990; // Example placeholder
        }
    }
    function playtimeUserProfile() {
        const userGender = getUserGender();
        let gender;
        if (userGender === "male") {
            gender = "MALE";
        } else if (userGender === "female") {
            gender = "FEMALE";
        } else {
            gender = "UNKNOWN";
        }
    
    // if you don't know the month or day, you can set them to 01
        let birthday;
        if (isExactBirthdayKnown()) {
            birthday = new Date(getUserBirthday());
        } else {
            const calendar = new Date();
            calendar.setFullYear(getYearOfBirth());
            calendar.setMonth(0);
            calendar.setDate(1);
            birthday = calendar;
        }
    
        birthday = birthday.toISOString() 
        return { gender, birthday };
    }
    import 'package:adjoe/user_profile.dart';
    import 'package:adjoe/gender.dart';
    
    PlaytimeGender gender;
    switch (getUserGender()) {
      case "male":
        gender = PlaytimeGender.MALE;
        break;
    
      case "female":
        gender = PlaytimeGender.FEMALE;
        break;
    
      default:
        gender = PlaytimeGender.UNKNOWN;
        break;
    }
    
    // set the birthday. If you don't know the user's exact birthday, the year is already enough.
    DateTime birthday = getUserBirthday();
    
    PlaytimeUserProfile playtimeUserProfile = PlaytimeUserProfile()
      ..gender = gender
      ..birthday = birthday;
    PlaytimeGender gender;
    string userGender = GetUserGender();
    if (userGender == "male")
        gender = PlaytimeGender.MALE;
    else if (userGender == "female")
        gender = PlaytimeGender.FEMALE;
    else
        gender = PlaytimeGender.UNKNOWN;
    
    DateTime birthday = new DateTime();
    birthday = DateTime.SpecifyKind(birthday, DateTimeKind.Utc);
    
    if (isExactBirthdayKnown())
        {
            birthday = DateTime.Parse(getUserBirthday());
        }
    else
        {
            birthday = new DateTime(getYearOfBirth(), 1, 1);
        }
    
    string birthdayIso = birthday.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
    PlaytimeUserProfile playtimeUserProfile = new PlaytimeUserProfile(gender, birthdayIso);
    
    //Cordova has a different API that can be used to set the user profile information.
    
    window.PlaytimePlugin.setProfile(
      source,
      gender,
      birthday,
      function() {
        // successfully sent the profile information to adjoe
      },
      function(err) {
        // an error occurred while sending the profile information to adjoe
      },
    );
    val playtimeExtensions = PlaytimeExtensions.Builder()
        .setSubId1("subId 1")
        .setSubId2("subId 2")
        .setSubId3("subId 3")
        .setSubId4("subId 4")
        .setSubId5("subId 5")
        .build()
    PlaytimeExtensions playtimeExtensions = new PlaytimeExtensions.Builder()
        .setSubId1("subId 1")
        .setSubId2("subId 2")
        .setSubId3("subId 3")
        .setSubId4("subId 4")
        .setSubId5("subId 5")
        .build();
    // A complete example
    Playtime.init('sdkHash', 
        { 
          'userId': "06957070-6579-46cd-9665-d8fe7cd286d5",
          'applicationProcessName' : "io.adjoe.anyApp",
          'playtimeParams': {
                    'uaNetwork': "uaNetwork_value",
              	'uaChannel': "uaChannel_value",
              	'uaSubPublisherCleartext': "uaSubPublisherCleartext_value",
              	'uaSubPublisherEncrypted': "uaSubPublisherEncrypted_value",
              	'placement': 'placement_value'
            }, 
            'playtimeExtension': {
                    'subId1': "RN_subId1",
                    'subId2': "RN_subId2",
                    'subId3': "RN_subId3",
                    'subId4': "RN_subId4",
                    'subId5': "RN_subId5"
            }, 
            'playtimeUserProfile': {
                    'gender': 'male',
                    'birthday': "2000-07-17T22:25:00.000Z"
            }
        
        }).then(() => {
          console.log('Initialized Playtime SDK');
        })
        .catch((err) => {
          console.log('Could not initilize Playtime SDK');
          console.error(err);
        });
    PlaytimeOptions options = new PlaytimeOptions()
      ..userId = 'user_id'
      ..applicationProcessName = 'io.adjoe.FlutterTestApp'
      ..params = (new PlaytimeParams()
        ..uaNetwork = 'The user acquisition network'
        ..uaChannel = 'The user acquisition channel.'
        ..uaSubPublisherCleartext = 'The user acquisition publisher cleartext'
        ..uaSubPublisherEncrypted = 'The user acquisition publisher encrypted'
        ..placement = 'The offerwall placement')
      ..userProfile = (new PlaytimeUserProfile()
        ..birthday = birthday
        ..gender = gender)
      ..extensions = (new PlaytimeExtensions()
        ..subId1 = 'flutter_subId1'
        ..subId2 = 'flutter_subId2'
        ..subId3 = 'flutter_subId3'
        ..subId4 = 'flutter_subId4'  
        ..subId5 = 'flutter_subId5');
    PlaytimeExtensions playtimeExtensions = new PlaytimeExtensions();
    playtimeExtensions.setSubId1("subId 1")
        .setSubId2("subId 2")
        .setSubId3("subId 3")
        .setSubId4("subId 4")
        .setSubId5("subId 5");
    // A complete example
    window.PlaytimePlugin.initialize(
      'sdkHash',
      { 
            'user_id': getUserId(),
            'playtime_params': {
                    'uaNetwork': "uaNetwork_value",
              	'uaChannel': "uaChannel_value",
              	'uaSubPublisherCleartext': "uaSubPublisherCleartext_value",
              	'uaSubPublisherEncrypted': "uaSubPublisherEncrypted_value",
              	'placement': 'placement_value'
            }, 
            'playtime_extension': {
                    'subId1': "RN_subId1",
                    'subId2': "RN_subId2",
                    'subId3': "RN_subId3",
                    'subId4': "RN_subId4",
                    'subId5': "RN_subId5"
            }, 
            'user_profile': {
                    'gender': 'male',
                    'birthdate': "2023-07-17T22:25:00.000Z"
            }
        },
      function() {
          // the Playtime plugin was initialized successfully
      },
      function(err) {
          // an error occurred while initializing the Playtime plugin.
      },
    );
    try {
        Playtime.sendUserEvent(context, Playtime.EVENT_TEASER_SHOWN, null, playtimeParams)
    } 
    catch (e: PlaytimeNotInitializedException) {
        // you have to initialize the Playtime SDK
    }
    try {
        Playtime.sendUserEvent(context, Playtime.EVENT_TEASER_SHOWN, null, playtimeParams);
    }
    catch (PlaytimeNotInitializedException e) {
        // you have to initialize the Playtime SDK
    }
    Playtime.sendEvent(Playtime.EVENT_TEASER_SHOWN, null, {'<playtimeParams>'});
    Playtime.sendTeaserShownEvent(params);
    Playtime.sendEvent(Playtime.EVENT_TEASER_SHOWN, null, playtimeParams);
    function() {
        // show teaser button
            window.PlaytimePlugin.sendUserEvent(window.PlaytimePlugin.EVENT_TEASER_SHOWN, '<subId1>', '<subId2>');
        },
    function() {
            //can't show the catalog 
        }
    try {
        val playtimeParams = PlaytimeParams.Builder()
            .setUaNetwork("network")
            .setUaChannel("channel")
            .setUaSubPublisherCleartext("SubPublisherCleartext")
            .setUaSubPublisherEncrypted("SubPublisherEncrypted")
            .setPlacement("placement")
            .build()
        val playtimeCatalogIntent = Playtime.getCatalogIntent(context, playtimeParams)
        context.startActivity(playtimeCatalogIntent)
    } 
        catch(notInitializedException: PlaytimeNotInitializedException) {
        // you have not initialized the Playtime SDK
    } 
        catch(exception: PlaytimeException) {
        // the catalog cannot be displayed for some other reason
    }
    try {
        PlaytimeParams playtimeParams = new PlaytimeParams.Builder()
                .setUaNetwork("network")
                .setUaChannel("channel")
                .setUaSubPublisherCleartext("SubPublisherCleartext")
                .setUaSubPublisherEncrypted("SubPublisherEncrypted")
                .setPlacement("placement")
                .build();
        Intent playtimeCatalogIntent = Playtime.getCatalogIntent(context, playtimeParams);
        context.startActivity(playtimeCatalogIntent);
    } 
            catch (PlaytimeNotInitializedException notInitializedException) {
        // you have not initialized the Playtime SDK
    } 
            catch (PlaytimeException exception) {
        // the catalog cannot be displayed for some other reason
    }
    
    Playtime.showCatalog({
    	'uaNetwork': "uaNetwork_value",
    	'uaChannel': "uaChannel_value",
    	'uaSubPublisherCleartext': "uaSubPublisherCleartext_value",
    	'uaSubPublisherEncrypted': "uaSubPublisherEncrypted_value",
    	'placement': 'placement_value'
    })
        .then(() => {
            console.log('Now showing Playtime offerwall');
        })
        .catch((err) => {
            console.log('Could not show Playtime offerwall');
            console.error(err);
        });
    PlaytimeParams params = new PlaytimeParams()
        ..uaNetwork = 'network'
        ..uaChannel = 'channel'
        ..uaSubPublisherCleartext = 'cleartext'
        ..haSubPublisherEncrypted = 'encrypted'
        ..placement = 'placement';
    Playtime.showPlaytime(params);
    PlaytimeParams playtimeParams = new PlaytimeParams();
    playtimeParams.SetUaNetwork("network")
        .SetUaChannel("Channel")
        .SetUaSubPublisherCleartext("SubPublisherCleartext")
        .SetUaSubPublisherEncrypted("SubPublisherEncrypted")
        .SetPlacement("placement");
    Playtime.ShowCatalog(playtimeParams)
    Playtime.showCatalog({ // PARAMETER IS OPTIONAL
    	'uaNetwork': "uaNetwork_value",
    	'uaChannel': "uaChannel_value",
    	'uaSubPublisherCleartext': "uaSubPublisherCleartext_value",
    	'uaSubPublisherEncrypted': "uaSubPublisherEncrypted_value",
    	'placement': 'placement_value'
    })
        .then(() => {
            console.log('Now showing Playtime catalog');
        })
        .catch((err) => {
            console.log('Could not show Playtime catalog');
            console.error(err);
        });
    window.PlaytimePlugin.showCatalog(
      function() {
        // the catalog was displayed to the user
      },
      function(err) {
        // the catalog cannot be displayed
      },
    );
    
    //You can pass two optional Sub-IDs when you launch Playtime, which will be logged every time a user views or clicks a campaign: 
    window.PlaytimePlugin.showCatalogWithSubIDs('<subId1>', '<subId2>', success, error).
    Playtime.setCatalogListener(object:PlaytimeCatalogListener() {
      fun onCatalogOpened(type:String) {
        Log.d(TAG, "Catalog of type '" + type + "' was opened")
      }
      fun onCatalogClosed(type:String) {
        Log.d(TAG, "Catalog of type '" + type + "' was closed")
      }
    })
    Playtime.setCatalogListener(new PlaytimeCatalogListener() {
    
        @Override
        public void onCatalogOpened(String type) {
            Log.d(TAG, "Catalog of type '" + type + "' was opened");
        }
    
        @Override
        public void onCatalogClosed(String type) {
            Log.d(TAG, "Catalog of type '" + type + "' was closed");
        }
    });

    Only shows gaming campaigns

    both

    Shows both gaming and non-gaming

    nongaming

    Only shows non-gaming campaigns

    gaming

    When initializing the SDK, you should include additional User Acquisition and placement parameters. These parameters allow you to track and analyze Playtime's performance within your app.

    You can provide these parameters in any combination, both at initialization and at any point in the app's lifecycle—such as when a user launches the Playtime Catalog interacts with a campaign or requests a payout.

    These values must be url-safe as they could potentially be returned as query parameters in the S2S Payout request.

    To make the most of the Playtime SDK, to maximize your revenue and provide the best user experience, use all of these parameters for data-driven optimizations. For instance, you can monitor the performance of different Playtime button placements or determine the effectiveness of different UA sources.