# Choicely Gemini CLI Extension Conversation Playbook Source: https://docs.choicely.com/AI/gemini-cli-playbook Use Gemini Cli with the Choicely extension to clone, build, install, and launch the SDK demos via chat. # Choicely Gemini CLI Extension – Conversation Playbook This guide is for anyone who only wants to talk to Gemini and still get the Choicely demo up and running. Every section tells you exactly what to say, what Gemini will do for you, and what to expect back. Follow the prompts and Gemini selects every tool and command on your behalf. ## Before You Start Talking to Gemini * **Install/update Gemini CLI**: Run `npm install -g @google/gemini-cli`. * **Install the extension**: Run `gemini extensions install https://github.com/choicely/choicely-gemini-cli-extension.git`. * You will be prompted to enter variables like `CHOICELY_APP_KEY`, `JAVA_HOME`, and `ANDROID_HOME`. * **Note**: If you leave `CHOICELY_APP_KEY` empty (press Enter to skip), the extension will still install and the app will work fine, but it will show the preconfigured demo app instead of your app. You can provide the key later in the chat (e.g., "Configure the app key to be ``"). * **Note**: If you don't have `JAVA_HOME` or `ANDROID_HOME` set, don't worry! The extension can automatically download the necessary SDKs for you. * **Prepare a workspace**: The extension uses a folder named `choicely-sdk-demo` in your current directory. * **Devices and emulators**: Gemini lists every connected phone/AVD/simulator and pauses for your confirmation before booting or using one. ## Quick Way to Ask * Say: “Clone the demo and build/install/launch on Android,” or “Do the same on iOS.” Gemini handles the steps and tools automatically. ## Workflow 1 – Launch the Android Demo These five steps take you from nothing to a running Choicely demo on an emulator or real device. Keep the asks simple—“fetch, build, install, launch.” The agent handles targets automatically and only asks you to choose if more than one is available. ### Step 1 – Clone (and Auto-Configure) the Demo Project * **Say**: “Download the Choicely SDK demo into my current folder.” * **Expect**: Gemini clones (or refreshes if you’ve allowed overwrite) and returns the repo path. If you haven't set `CHOICELY_APP_KEY`, it will ask you for it or you can provide it now. Android clone screenshot ### Step 2 – Build the APK * **Say**: “Build the Android demo app.” * **Expect**: Gemini builds the APK. On failure, it returns the error log. * **If the build fails due to missing Java/Android SDK**: Say "Install Android dependencies" to have Gemini fix it automatically. Android build screenshot ### Step 3 – Install the Build * **Say**: “Install the application on an emulator.” * **Expect**: Gemini lists the available devices/AVDs, asks if it should start the recommended one, then installs once you confirm. Afterward it repeats the package name and device ID. * **Note**: If you need a new emulator, say "Install dependencies with emulator support" to download the emulator binaries. Android install screenshot ### Step 4 – Launch and Validate * **Say**: “Launch the Choicely demo on the emulator.” * **Expect**: Gemini opens the app and can capture a screenshot or logs if you ask. It can also force-stop or uninstall on request. Android launch screenshot Android validation screenshot ## Workflow 2 – Launch the iOS Demo (macOS) Mirror the Android flow on macOS. Ask for the outcome; the agent handles simulator selection/boot automatically and only asks you to choose if more than one simulator is available. ### Step 1 – Clone (and Auto-Configure) the Demo Project * **Say**: “Download the Choicely SDK demo into the current folder.” * **Expect**: Gemini clones (or refreshes if you’ve allowed overwrite) and returns the repo path. If you haven't set `CHOICELY_APP_KEY`, it will ask you for it or you can provide it now. iOS clone screenshot ### Step 2 – Build the iOS Simulator App * **Say**: “Build the iOS demo app.” * **Expect**: Gemini runs the Xcode build and reports success or the build error log. iOS build screenshot ### Step 3 – Install the Build * **Say**: “Install the demo app on a simulator.” * **Expect**: Gemini shows the simulator options, asks before booting one, then installs and reports the simulator ID/bundle after you confirm. iOS install screenshot ### Step 4 – Launch and Validate * **Say**: “Launch the Choicely demo on that simulator.” * **Expect**: Gemini opens the app and can capture a screenshot or logs if you ask. It can also terminate or uninstall on request. iOS launch screenshot ## Troubleshooting / FAQ ### Where do I find my App Key? In Choicely Studio, go to the "Apps" section, select your application, and copy the App Key (UUID) from the top left corner. ### Can I ask questions about the SDK? Yes! You can ask things like "How do I customize the view factory?" or "How to set up push notifications?" and Gemini will search the Choicely Mobile SDK documentation for answers. # Custom Fragment Source: https://docs.choicely.com/android-advanced/custom-fragment Create custom fragments and embed Choicely content in your Android app The Choicely SDK allows you to create custom Fragment and embed Choicely content (articles, feeds, etc.) alongside your own UI logic. ## Steps to Add a Custom Fragment ### 1. Create a Custom Fragment Create a new fragment. This is where you'll define your fragment's layout and any custom logic. ```java Java theme={null} public class YourCustomFragment extends Fragment { EditText editText; Button button; @Override protected int getLayout() { return R.layout.fragment_custom_fragment; } @Override protected void onLayoutCreated(@NonNull View layout, @Nullable Bundle savedInstanceState) { editText = layout.findViewById(R.id.edittext); button = layout.findViewById(R.id.toastButton); } } ``` ```kotlin Kotlin theme={null} class YourCustomFragment : Fragment() { private var editText: EditText? = null private var button: Button? = null override fun getLayout(): Int { return R.layout.fragment_custom_Fragment } override fun onLayoutCreated(layout: View, savedInstanceState: Bundle?) { editText = layout.findViewById(R.id.edittext) button = layout.findViewById(R.id.toastButton) } } ``` ### 2. Create a Content Factory Create a factory class that implements `ChoicelyContentFragmentFactory`. This factory is responsible for providing an instance of your custom content fragment. ```java Java theme={null} public class YourContentFactory extends ChoicelyContentFragmentFactory { @Nullable @Override protected Fragment makeAppContentFragment(Context context, String type, @Nullable Uri internalUri, Bundle data) { Fragment fragment = null; switch (type) { case "special": final String internalUrl = data.getString(ChoicelyIntentKeys.INTERNAL_URL); if (!TextUtils.isEmpty(internalUrl)) { final Uri uri = Uri.parse(internalUrl); if (uri != null) { final String key = uri.getLastPathSegment(); if (!CTextUtils.isEmpty(key)) { switch (key) { case "custom": fragment = YourCustomFragment(); break; default: break; } } } default: break; } return fragment; } } ``` ```kotlin Kotlin theme={null} class YourContentFactory : ChoicelyContentFragmentFactory() { override fun makeAppContentFragment( context: Context, type: String, internalUri: Uri?, data: Bundle ): Fragment? { var fragment: Fragment? = null when (type) { "special" -> { val internalUrl = data.getString(ChoicelyIntentKeys.INTERNAL_URL) if (!internalUrl.isNullOrEmpty()) { val uri = Uri.parse(internalUrl) val key = uri?.lastPathSegment if (!key.isNullOrEmpty()) { when (key) { "custom" -> fragment = YourCustomFragment() } } } } } return fragment } } ``` ### 3. Register the Factory in Application Class Finally, in your `Application` class (e.g., `YourApplication.java` or `YourApplication.kt`), add a call to `ChoicelySDK.factory().setContentFactory()` to use your custom Fragment. ```java Java theme={null} import android.app.Application; import com.choicely.sdk.ChoicelySDK; public class YourApplication extends Application { @Override public void onCreate() { super.onCreate(); ..... ChoicelySDK.factory().setContentFactory(new YourContentFactory()); } } ``` ```kotlin Kotlin theme={null} import android.app.Application import com.choicely.sdk.ChoicelySDK class YourApplication : Application() { override fun onCreate() { super.onCreate() ..... ChoicelySDK.factory().setContentFactory(YourContentFactory()) } } ``` ### 4. Set the Navigation Path Define the navigation path in the builder for your custom Fragment. How to use navigation ```text theme={null} choicely://special/custom ``` # Custom Splash Fragment Source: https://docs.choicely.com/android-advanced/custom-splash Add a custom splash fragment with branding and animations to your Android app You can add a custom splash Fragment in your application with the Choicely SDK. The splash Fragment can display your branding, animations, or custom logic before the main content loads. ## Steps to Add a Custom Splash Fragment ### 1. Create a Splash Fragment Create a new fragment that extends `AbstractSplashFragment`. This is where you'll define your splash Fragment's layout and any custom logic. ```java Java theme={null} public class YourSplashFragment extends AbstractSplashFragment { @Override protected int getLayout() { return R.layout.fragment_custom_splash; } @Override protected void onLayoutCreated(@NonNull View layout, @Nullable Bundle savedInstanceState) { super.onLayoutCreated(layout, savedInstanceState); // Add your custom logic here, like starting an animation or loading data. } // Set Splash Duration in milliseconds @Override protected long getSplashDuration() { return 1500; // 1.5 seconds } } ``` ```kotlin Kotlin theme={null} class YourSplashFragment : AbstractSplashFragment() { override fun getLayout(): Int { return R.layout.fragment_custom_splash } override fun onLayoutCreated(layout: View, savedInstanceState: Bundle?) { super.onLayoutCreated(layout, savedInstanceState) // Add your custom logic here, like starting an animation or loading data. } // Set Splash Duration in milliseconds override fun getSplashDuration(): Long { return 1500 // 1.5 seconds } } ``` ### 2. Create a Splash Factory Create a factory class that implements `ChoicelySplashFactory`. This factory is responsible for providing an instance of your custom splash fragment. ```java Java theme={null} import com.choicely.sdk.factory.ChoicelySplashFactory; import com.choicely.sdk.fragment.AbstractSplashFragment; public class YourSplashFactory implements ChoicelySplashFactory { @Override public AbstractSplashFragment makeSplashFragment() { return new YourSplashFragment(); } } ``` ```kotlin Kotlin theme={null} import com.choicely.sdk.factory.ChoicelySplashFactory import com.choicely.sdk.fragment.AbstractSplashFragment class YourSplashFactory : ChoicelySplashFactory { override fun makeSplashFragment(): AbstractSplashFragment { return YourSplashFragment() } } ``` ### 3. Add the Factory in Your Application Class Finally, in your `Application` class (e.g., `YourApplication.java` or `YourApplication.kt`), add a call to `ChoicelySDK.factory().setSplashFactory()` to use your custom splash Fragment. ```java Java theme={null} import android.app.Application; import com.choicely.sdk.ChoicelySDK; public class YourApplication extends Application { @Override public void onCreate() { super.onCreate(); .... ChoicelySDK.factory().setSplashFactory(new YourSplashFactory()); } } ``` ```kotlin Kotlin theme={null} import android.app.Application import com.choicely.sdk.ChoicelySDK class YourApplication : Application() { override fun onCreate() { super.onCreate() .... ChoicelySDK.factory().setSplashFactory(YourSplashFactory()) } } ``` Remember to create a layout file named `fragment_custom_splash.xml` in your `res/layout` directory to define the visual elements of your splash Fragment, such as an image, text, or a animation view. # React Native Support Source: https://docs.choicely.com/android-advanced/react-native Integrate a React Native module into an existing native Android project # Choicely RN — Android Integration Guide Integrate the Choicely React Native SDK into your native Android project. This guide targets the published SDK artifacts listed below and is verified against them. It is for teams embedding Choicely into an existing native Android app. If you want to build and publish an app without writing code, use [Choicely Studio](https://www.choicely.com) instead. *** ## Versions | Artifact | Version | | ------------------------------------- | ------- | | `com.choicely.sdk:android-core` | `1.1.2` | | `com.choicely.sdk:android-rn` | `0.0.2` | | `com.choicely.sdk:choicely-rn-gradle` | `0.0.4` | *** ## Prerequisites * Node.js >= 20 * Android SDK with CMake * Git * Java 17 *** ## Project Structure (after setup) ``` Android/Java/ ├── app/ ← your Android app module ├── app-react-native/ ← cloned rn-starter repo │ ├── node_modules/ │ ├── package.json │ └── react-native.config.js ├── build.gradle ├── settings.gradle ├── gradle.properties └── gradle/libs.versions.toml ``` *** ## Setup ### Step 1 — Clone the Choicely RN repo ```bash theme={null} cd Android/Java git clone https://github.com/choicely/rn-starter.git app-react-native cd app-react-native && npm install && cd .. ``` > The folder **must** be named `app-react-native` by default. > To use a different name, set `choicelyRnDir` in `gradle.properties` (see Step 2). *** ### Step 2 — `gradle.properties` ```properties theme={null} newArchEnabled=true hermesEnabled=true # RN module folder name — change this ONE line if you rename the folder choicelyRnDir=app-react-native ``` *** ### Step 3 — `settings.gradle` ```groovy theme={null} pluginManagement { ...... // Load the RN Gradle plugin from your local node_modules includeBuild("${settings.providers.gradleProperty('choicelyRnDir').orElse('app-react-native').get()}/node_modules/@react-native/gradle-plugin") } plugins { id "com.facebook.react.settings" } def rnDir = settings.providers.gradleProperty("choicelyRnDir").orElse("app-react-native").get() reactSettings { autolinkLibrariesFromCommand( [ ["/opt/homebrew/bin/node", "/usr/local/bin/node"].find { new File(it).exists() } ?: "node", "node_modules/react-native/cli.js", "config" ], file(rnDir) ) } ``` > **Why `includeBuild` and `reactSettings` can't be automated by a plugin:** > They run in Gradle's *settings phase* — before any plugin from Maven Central can load. > These will always need to be in your `settings.gradle`. *** ### Step 4 — Root `build.gradle` ```groovy theme={null} buildscript { ext.kotlin_version = '2.2.20' ext { minSdkVersion = 26 compileSdkVersion = 36 buildToolsVersion = "35.0.0" ndkVersion = "27.1.12297006" targetSdkVersion = 36 reactNativeVersion = "0.82.0" REACT_NATIVE_NODE_MODULES_DIR = file("${rootDir}/${choicelyRnDir}/node_modules/react-native").canonicalPath REACT_NATIVE_WORKLETS_NODE_MODULES_DIR = file("${rootDir}/${choicelyRnDir}/node_modules/react-native-worklets").canonicalPath // Required: notifee, netinfo, blur, geolocation and pager-view locate the React // Native android prebuilts by walking UP from rootDir, which fails when // node_modules lives inside the RN module folder. These are their escape hatches. reactNativeAndroidRoot = file("${rootDir}/${choicelyRnDir}/node_modules/react-native") set('react-native', [options: [reactNativeAndroidDir: "../../../react-native"]]) } repositories { google() mavenLocal() mavenCentral() maven { url "$rootDir/${choicelyRnDir}/node_modules/react-native/android" } } dependencies { classpath 'com.android.tools.build:gradle:8.13.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // Choicely RN Gradle plugin — provides com.choicely.rn.setup & com.choicely.react classpath 'com.choicely.sdk:choicely-rn-gradle:0.0.4' } } plugins { alias(libs.plugins.android.application) apply false id "com.facebook.react" apply false } // REQUIRED: must be applied on the root project apply plugin: 'com.choicely.rn.setup' ``` *** ### Step 5 — App `build.gradle` ```groovy theme={null} plugins { alias(libs.plugins.android.application) } // com.facebook.react must be applied directly (Gradle ClassLoaderScope limitation) apply plugin: 'com.choicely.react' apply plugin: 'com.facebook.react' // Vector icons def rnDir = findProperty("choicelyRnDir") ?: "app-react-native" project.ext.vectoricons = [iconFontsDir: "${rootDir}/${rnDir}/node_modules/react-native-vector-icons/Fonts"] apply from: "${rootDir}/${rnDir}/node_modules/react-native-vector-icons/fonts.gradle" android { ...... namespace 'com.your.app' ndkVersion rootProject.ext.ndkVersion // Required: resolve duplicate libworklets.so from reanimated & worklets packagingOptions { pickFirst 'lib/arm64-v8a/libworklets.so' pickFirst 'lib/x86/libworklets.so' pickFirst 'lib/x86_64/libworklets.so' pickFirst 'lib/armeabi-v7a/libworklets.so' } } dependencies { // Choicely SDK implementation("com.choicely.sdk:android-core:1.1.2") implementation("com.choicely.sdk:android-rn:0.0.2") } ``` *** ### Also needed — `react-native.config.js` **File:** `app-react-native/react-native.config.js` > Must use dots in the filename — NOT `react-native-config.js`. ```js theme={null} module.exports = { project: { android: { sourceDir: "../", appName: "app", packageName: "com.your.app", // must match applicationId }, }, }; ``` *** ### Step 5b — Required app colours The Choicely SDK themes reference these. Without them the build fails at `Android resource linking failed … resource color/colorPrimaryDark not found`. **File:** `app/src/main/res/values/colors.xml` ```xml theme={null} #FF6200EE #FF3700B3 #FF03DAC5 ``` *** ### Step 6 — `MyApplication.java` Extend `ChoicelyRNApplication` instead of `Application` and initialise both the RN engine and the Choicely SDK. ```java theme={null} package com.your.app; import com.choicely.rn.ChoicelyRNApplication; import com.choicely.rn.ChoicelyRNHost; import com.choicely.rn.utils.ChoicelyRNConfig; import com.choicely.sdk.ChoicelySDK; import com.facebook.react.PackageList; import com.facebook.react.ReactNativeApplicationEntryPoint; public class MyApplication extends ChoicelyRNApplication { @Override public void onCreate() { super.onCreate(); String appKey = "YOUR_APP_KEY"; initReactNative(appKey); initChoicely(appKey); // Bundle loading: // false = load LOCAL bundle (use this during development) // true = load REMOTE bundle from server // For local: run `npm run bundle` in app-react-native/ first to generate the bundle file. ChoicelyRNConfig.serLoadRemoteBundle(getApplicationContext(), false, appKey); } @Override protected void initReactNative(String appKey) { ChoicelyRNHost rnHost = new ChoicelyRNHost(this, appKey) {}; initRNEngine(rnHost); ReactNativeApplicationEntryPoint.loadReactNative(this); addChoicelyBridge(new PackageList(this).getPackages()); } private void initChoicely(String appKey) { ChoicelySDK.init(this, appKey); // Load the latest bundle from remote (non-blocking, updates on next launch) ChoicelyRNConfig.loadUpdatedBundle(appKey, getApplicationContext(), null); } } ``` #### Bundle loading explained | `serLoadRemoteBundle` second arg | Behaviour | | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `false` | Loads the **local** JS bundle packaged in your APK. Run `npm run bundle` inside `app-react-native/` to rebuild it after JS changes — it writes `app/src/main/assets/index.android.bundle`, which is where the SDK looks. Debug variants do **not** bundle automatically, so this step is required before the first debug build. | | `true` | Loads the **remote** bundle from the Choicely server at runtime. Use this in production. | *** ## Artifacts Overview ### Gradle Plugin: `com.choicely.sdk:choicely-rn-gradle` Distributed via Maven Central Snapshots. Provides two plugin IDs: | Plugin ID | Applied in | Purpose | | ----------------------- | ------------------- | ------------------------------------------------------------------ | | `com.choicely.rn.setup` | Root `build.gradle` | Node binary detection, lib patching, CMake fix, PATH injection | | `com.choicely.react` | App `build.gradle` | Configures `react{}` paths, ext vars, `autolinkLibrariesWithApp()` | ### Runtime Library: `com.choicely.sdk:android-rn` AAR library containing: | Component | Description | | -------------------------------------------- | ---------------------------------------------------------------------------------------------- | | `ChoicelyRNApplication` | Abstract `Application` implementing `ReactApplication`, manages multiple `ReactHost` instances | | `ChoicelyRNHost` | Extends `DefaultReactNativeHost`, handles JS bundle loading (asset or remote) | | `ChoicelyDefaultReactHost` | Factory for creating `ReactHost` instances | | `ChoicelyReactNativeFragment` | Fragment for embedding RN views in native screens | | `RNFragmentWrapper` | Wrapper around the RN fragment | | `ChoicelyDeepLinkScreenActivity` | Activity for deep link handling | | `ChoicelyBridgePackage` / `ChoicelyRNBridge` | Native-to-RN bridge module | | `ChoicelyRNConfig` | Config for current version/app key for remote bundles | | `ChoicelyRemoteBundle` | Handles remote JS bundle downloading | **Transitive dependencies** (pulled automatically): * `com.choicely.sdk:android-core` * `com.facebook.react:react-android:0.82.0` * `com.facebook.react:hermes-android:0.82.0` * `androidx.databinding:*:8.13.0` * `org.jetbrains.kotlin:kotlin-stdlib:2.1.21` *** ## Build Commands ```bash theme={null} # Build JS bundle locally (required when serLoadRemoteBundle = false) # Writes app/src/main/assets/index.android.bundle cd app-react-native && npm run bundle && cd .. # Build APK ./gradlew :app:assembleDebug # Clean + Build ./gradlew clean :app:assembleDebug ``` *** ## Renaming the RN folder Change **one line** in `gradle.properties`: ```properties theme={null} choicelyRnDir=my-custom-folder-name ``` *** ## What the Gradle plugin handles automatically | What | Plugin ID | | ----------------------------------------------------------- | ----------------------- | | Node binary detection (macOS Homebrew / `/usr/local`) | `com.choicely.rn.setup` | | Patching third-party libs that hardcode `"node"` | `com.choicely.rn.setup` | | Injecting Node into PATH for all Exec tasks | `com.choicely.rn.setup` | | CMake cache clean fix | `com.choicely.rn.setup` | | `react{}` block (node, cliFile, reactNativeDir, codegenDir) | `com.choicely.react` | | `autolinkLibrariesWithApp()` | `com.choicely.react` | ## What still requires manual setup (Gradle hard limits) | What | Why | | ---------------------------------------------------------- | ------------------------------------------------------- | | `includeBuild(".../gradle-plugin")` in `settings.gradle` | Settings phase — no plugin can run before this | | `reactSettings { autolinkLibrariesFromCommand(...) }` | Settings phase — same reason | | `apply plugin: 'com.facebook.react'` in `app/build.gradle` | ClassLoaderScope — can't be applied from another plugin | *** ## Troubleshooting | Error | Fix | | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | `Could not find project.android.packageName` | Rename to `react-native.config.js` (dots, not dashes) | | `includeBuild path not found` | Run `npm install` in `app-react-native/` first | | `pluginManagement must appear first` | No code before `pluginManagement {}` in `settings.gradle` | | `Node binary not found` | Plugin finds Node automatically; ensure Homebrew or nvm is installed | | `Cannot find module '@react-native/codegen'` | `choicelyRnDir` may point to wrong folder — check `gradle.properties` | | `Failed to apply plugin 'com.choicely.react'` | Add `apply plugin: 'com.choicely.rn.setup'` in root `build.gradle` | | `2 files found with path 'lib/arm64-v8a/libworklets.so'` | Add `packagingOptions { pickFirst 'lib/*/libworklets.so' }` in app `build.gradle` | | `Included build 'choicely-rn-android/rn-gradle-plugin' does not exist` | Remove the `includeBuild("choicely-rn-android/rn-gradle-plugin")` line — the plugin is now distributed via Maven | # Firebase Source: https://docs.choicely.com/android/firebase Set up Firebase in your project with the Choicely Android SDK Follow these steps to set up Firebase in your project with the Choicely Android SDK. Firebase is required to enable certain features in the Choicely Android SDK. ## Why Firebase? * **Push Notifications** – Firebase Cloud Messaging (FCM) allows the app to send push notifications directly to users, helping you keep them engaged with instant updates, news, or alerts. * **Realtime Updates** – With Firebase Realtime Database, content and configuration changes can be delivered instantly to your users without requiring an app update. This ensures your app always reflects the latest data. * **Social Login** – The Choicely SDK integrates with Firebase Authentication to enable sign-in with popular providers such as Google, Facebook, and Apple. This makes it easy for users to log in without creating a separate account. ## Gradle Setup Add the Firebase dependency and plugin configuration in your `build.gradle` files. ### 1. Add Dependency ```gradle theme={null} implementation(platform("com.choicely.sdk:bom:1.1.0")) implementation("com.choicely.sdk:android-core") implementation("com.choicely.sdk:android-firebase") ``` ### 2. Add Plugin in App-level build.gradle ```gradle theme={null} plugins { id("com.google.gms.google-services") } ``` ### 3. Add Plugin in Project-level build.gradle ```gradle theme={null} plugins { ... id("com.google.gms.google-services") version "4.4.3" apply false } ``` # Maps Source: https://docs.choicely.com/android/map Display locations, markers, and interactive map features in your Android app The Choicely Android SDK provides map functionality for displaying locations, markers, and interactive map features. ## Why Maps? * **Location Features** – Display user or business locations directly on a map. * **Interactive Maps** – Add markers, zoom, and navigation support within your app. * **Google Maps Integration** – Leverages the official Google Maps SDK for reliable map rendering and user-friendly experience. ## Gradle Setup Add the Choicely Maps SDK dependency in your `build.gradle` file. ```gradle theme={null} implementation(platform("com.choicely.sdk:bom:1.1.0")) implementation("com.choicely.sdk:android-core") implementation("com.choicely.sdk:android-map") ``` ## Google Maps API Key Configuration To use Google Maps, you need to obtain an API key from the Google Cloud Console and add it to your `AndroidManifest.xml`. ### 1. Enable Google Maps API The Google Maps API allows your app to display interactive maps, add markers, show user or business locations, and provide navigation features directly inside your application. Without enabling this API, your app cannot render maps or use map-related functionalities. Here is a link how you enable Google Maps API: [Google Maps API for Choicely map](/resources/map-api) in the Google Cloud Console. ### 2. Add API Key in `strings.xml` Store your API key as a string resource in `res/values/strings.xml`: ```xml theme={null} YOUR_API_KEY_HERE ``` ### 3. Reference API Key in Manifest Then, reference the string resource inside the `application` tag of your `AndroidManifest.xml`: ```xml theme={null} ``` # Quick Start Source: https://docs.choicely.com/android/quick-start Quickly integrate the Choicely Android SDK into your project Quickly integrate the Choicely Android SDK into a fresh project by following these steps. This guide covers repository setup, dependencies, and essential configuration for both Java and Kotlin projects. ## Create An Android Project Open Android Studio and click New Project to create a new project. Choose your desired project template, for example Empty Activity, and click Next. Now enter your app name and package name. ## Set Java & Kotlin and AGP Version Make sure to set the Java versions is 17 and Kotlin versions 2.2.10 in your project settings. Also, ensure that you are using the latest version of Android Gradle Plugin (AGP) for optimal compatibility with the Choicely SDK. ## Open Build.gradle (App) In your project, navigate to the `build.gradle` file located in the app module. This is where you'll add the necessary configurations for the Choicely SDK. ## Set Compile Option ```kotlin theme={null} compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = "17" } ``` ## 1. Add Repository Server To access the Choicely SDK, you need to add the Maven Central repository to your project. This allows Gradle to fetch the SDK dependencies correctly. ```groovy Java theme={null} pluginManagement { repositories { ...... mavenCentral() } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { ...... mavenCentral() } } ``` ```kotlin Kotlin theme={null} pluginManagement { repositories { ...... maven(url = "https://jitpack.io") mavenCentral() } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { ...... maven(url = "https://jitpack.io") mavenCentral() } } ``` ## 2. Add the Choicely SDK To add the Choicely SDK for Android as a dependency, add the following to your app-level build.gradle file inside the dependencies block. ```groovy Java theme={null} implementation platform("com.choicely.sdk:bom:1.1.1") implementation "com.choicely.sdk:android-core" ``` ```groovy Kotlin theme={null} implementation(platform("com.choicely.sdk:bom:1.1.1")) implementation("com.choicely.sdk:android-core") ``` ## 3. Create Application Class Create an `Application` class in your project, extend `Application`, and initialize the Choicely SDK inside `onCreate()`. Then, set your application class in the `AndroidManifest.xml` file. ```java Java theme={null} public class YourApplication extends Application { @Override public void onCreate() { super.onCreate(); ChoicelySDK.init(this,APP_KEY); } } ``` ```kotlin Kotlin theme={null} class YourApplication : Application() { override fun onCreate() { super.onCreate() ChoicelySDK.init(this,APP_KEY) } } ``` For testing purposes, you can use the following app key: `Y2hvaWNlbHktZXUvYXBwcy9kS1lHUUtUbWREa1pRb1ltZFRiZQ` Alternatively, you can create a new app using Choicely Builder: [How to create apps with Choicely](https://www.choicely.com/tutorials/how-to-create-apps-with-choicely-using-an-app-template) Choicely App Key Here's how to get the app key from the Choicely builder. ### Add application Class in Manifest ```xml theme={null} ... ``` We use our own splash activity to launch the application, so please avoid using a custom splash activity in your app. 🚀 Boom! You're all set — hit run and watch the magic happen. # Shop Source: https://docs.choicely.com/android/shop Enable in-app purchases, subscriptions, and paid votes in your Android app The Choicely Android SDK provides a Shop module for handling in-app purchases, subscriptions, and paid votes directly inside your application. ## Why Shop? * **In-App Purchases** – Allow users to buy items, upgrades, or digital content inside your app. * **Subscriptions** – Offer recurring subscription plans to give users ongoing access to premium features or content. * **Paid Votes** – Monetize voting by enabling users to purchase voting credits, which can be used in competitions or polls. Shop features must be enabled first in your Choicely project before they can be used in your app. ## Gradle Setup Add the Choicely Shop SDK dependency in your `build.gradle` file. ```gradle theme={null} implementation(platform("com.choicely.sdk:bom:1.1.0")) implementation("com.choicely.sdk:android-core") implementation("com.choicely.sdk:android-shop") ``` ## Shop Configuration Initialize the Shop module in your `Application` class after setting up the Choicely SDK. # App Source: https://docs.choicely.com/api-reference/app Create, read, update, and delete Choicely apps The App resource represents a Choicely app and its configuration — store settings, build and splash options, auth methods, toolbar, and more. ## Sample payload ```json theme={null} { "app_store": { "bundle_id": "", "app_store_id": "", "access_key": "" }, "build": { "splash": { "splash_image": {}, "background": {}, "background_style": {}, "foreground": {}, "foreground_style": {}, "style": {} }, "firebase_project": {}, "android": {}, "ios": {}, "general": { "website": "", "support_email": "", "support_url": "", "terms_url": "", "privacy_policy_url": "" } }, "config": { "app_wide_topic": {}, "is_contest_firebase_connection": true, "is_data_service_enabled": true, "is_login_required_at_startup": true, "cloud_settings": { "region": "eu", "updated": "1970-01-01T00:00:00Z" } }, "consent_setup": "", "created": "1970-01-01T00:00:00Z", "custom_data": {}, "default_nav_item": {}, "google_play": {}, "image": {}, "key": "", "master_shop_key": "", "screens": [], "studio_app_profile": { "auth_methods": { "is_apple": true, "is_email": true, "is_facebook": true, "is_google": true, "is_sms": true }, "firebase_project_key": "", "is_age_enabled": true, "is_auto_profile_image_enabled": true, "is_city_enabled": true, "is_email_enabled": false, "is_forgot_your_password_enabled": true, "is_gender_enabled": true, "is_logout_enabled": true, "is_name_enabled": false, "is_profile_enabled": true, "is_profile_image_enabled": true, "login": {}, "profile": {}, "provider": {}, "provider_key": "", "register": {} }, "tags": [], "title": "App title", "toolbar": { "image": {}, "style": {}, "subtitle": "", "title": "" }, "updated": "1970-01-01T00:00:00Z" } ``` ## Endpoints ### Create App `POST /apps` Create a new app. Use the [sample payload](#sample-payload) as a guide for the request body. ```bash cURL theme={null} curl -X POST "https://backend.choicely.com/apps" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "App title" }' ``` ### Get all Apps `GET /apps` Retrieve all apps. ```bash cURL theme={null} curl "https://backend.choicely.com/apps" \ -H "Authorization: Bearer " ``` ### Get a single App `GET /apps/` Retrieve a single app by its key. Unique key of the app. ```bash cURL theme={null} curl "https://backend.choicely.com/apps/" \ -H "Authorization: Bearer " ``` ### Update App `PATCH /apps/` Update an existing app. Include only the fields you want to change. Unique key of the app. ```bash cURL theme={null} curl -X PATCH "https://backend.choicely.com/apps/" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Updated title" }' ``` ### Delete App `DELETE /apps/` Delete an app. Unique key of the app. ```bash cURL theme={null} curl -X DELETE "https://backend.choicely.com/apps/" \ -H "Authorization: Bearer " ``` # Article Source: https://docs.choicely.com/api-reference/article Create, read, update, and delete articles The Article resource represents a piece of content — its title, description, content blocks, thumbnail, and styling. ## Sample payload ```json theme={null} { "content": [], "created": "1970-01-01T00:00:00Z", "custom_data": {}, "description": "Optional description for this content", "image": {}, "key": "", "navigation": {}, "origin_template": "", "style": {}, "tags": [], "thumbnail": [], "thumbnail_style": {}, "title": "Content title", "updated": "1970-01-01T00:00:00Z" } ``` ## Endpoints ### Create Article `POST /articles` Create a new article. Use the [sample payload](#sample-payload) as a guide for the request body. ```bash cURL theme={null} curl -X POST "https://backend.choicely.com/articles" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Content title", "description": "Optional description for this content" }' ``` ### Get all Articles `GET /articles` Retrieve all articles. ```bash cURL theme={null} curl "https://backend.choicely.com/articles" \ -H "Authorization: Bearer " ``` ### Get a single Article `GET /articles/` Retrieve a single article by its key. Unique key of the article. ```bash cURL theme={null} curl "https://backend.choicely.com/articles/" \ -H "Authorization: Bearer " ``` ### Update Article `PATCH /articles/` Update an existing article. Include only the fields you want to change. Unique key of the article. ```bash cURL theme={null} curl -X PATCH "https://backend.choicely.com/articles/" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Updated title" }' ``` ### Delete Article `DELETE /articles/` Delete an article. Unique key of the article. ```bash cURL theme={null} curl -X DELETE "https://backend.choicely.com/articles/" \ -H "Authorization: Bearer " ``` # Authentication Source: https://docs.choicely.com/api-reference/authentication Authenticate Developer API requests with a Bearer token The Choicely Developer API authenticates every request with an API key sent as a **Bearer token** in the HTTP `Authorization` header. ## Create a Developer API key 1. Go to your account's **Manage Account**. 2. In **Linked accounts**, select **Developer API** and create your API key. 3. Choose the permissions your API key should have based on your integration needs. 4. Copy the generated API key and store it securely. Permissions are configured per key. Grant a key only the permissions its integration needs. ## Authorize your requests Include your API key as a Bearer token in the `Authorization` header of every request: ```bash theme={null} Authorization: Bearer ``` For example: ```bash theme={null} curl "https://backend.choicely.com/apps" \ -H "Authorization: Bearer " ``` Never expose your API key in client-side code or public repositories. If a key is leaked, revoke it and create a new one from **Manage Account → Linked accounts → Developer API**. # Contest Source: https://docs.choicely.com/api-reference/contest Create, read, update, and delete contests The Contest resource represents a voting contest, including its voting configuration, rating and ordering rules, schedule, and associated shop. ## Sample payload ```json theme={null} { "contest_config": { "end": "2024-10-05T09:45:12Z", "free": { "is_mobile_only": false, "max_contest": -1, "max_participant": 1 }, "paid": { "is_anonymous": false, "max_contest": -1, "max_participant": -1 }, "rating": { "max_per_participant": 100, "max_rating": 5, "step_size": "fraction", "style": {}, "sub_rating_configs": [ { "sub_rating_id": "", "title": "title", "max_votes": 100, "style": {} } ] }, "ordering": { "skin": "list", "position_style": {}, "position_icon": {}, "positions": [ { "id": "", "index": 0, "votes": 12, "title": "", "style": {}, "icon": {} } ] }, "ip_config": "default", "ip_restriction_per_participant": -1, "is_anonymous_voting_enabled": false, "is_contest_time_shown": true, "is_grid_hidden": false, "is_randomize": false, "is_vote_removal_allowed": false, "limit_result_amount": -1, "participant_order": "running_number", "participant_style": {}, "participant_visibility": "shown", "renew_free_vote": { "cooldown": -1, "days_between": -1 }, "share_top_x": -1, "start": "2023-10-05T09:45:12Z", "vote_visibility": "hidden_until_end" }, "contest_type": "voteonly", "created": "1970-01-01T00:00:00Z", "custom_data": {}, "description": "", "image": {}, "key": "", "publish": "1970-01-01T00:00:00Z", "share": {}, "shop": {}, "style": {}, "tags": [], "thumbnail": [], "title": "Contest title", "updated": "1970-01-01T00:00:00Z", "video": {} } ``` ## Endpoints ### Create Contest `POST /contests` Create a new contest. Use the [sample payload](#sample-payload) as a guide for the request body. ```bash cURL theme={null} curl -X POST "https://backend.choicely.com/contests" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Contest title", "contest_type": "voteonly" }' ``` ### Get all Contests `GET /contests` Retrieve all contests. ```bash cURL theme={null} curl "https://backend.choicely.com/contests" \ -H "Authorization: Bearer " ``` ### Get a single Contest `GET /contests/` Retrieve a single contest by its key. Unique key of the contest. ```bash cURL theme={null} curl "https://backend.choicely.com/contests/" \ -H "Authorization: Bearer " ``` ### Update Contest `PATCH /contests/` Update an existing contest. Include only the fields you want to change. Unique key of the contest. ```bash cURL theme={null} curl -X PATCH "https://backend.choicely.com/contests/" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Updated title" }' ``` ### Delete Contest `DELETE /contests/` Delete a contest. Unique key of the contest. ```bash cURL theme={null} curl -X DELETE "https://backend.choicely.com/contests/" \ -H "Authorization: Bearer " ``` # Feed Source: https://docs.choicely.com/api-reference/feed Create, read, update, and delete feeds The Feed resource represents a scrollable feed of content, including its header, footer, inner navigation, and styling. ## Sample payload ```json theme={null} { "created": "1970-01-01T00:00:00Z", "custom_data": {}, "divider_height": 8, "footer": {}, "header": {}, "inner_navigation": { "is_swipe_enabled": true, "location": "top", "navigation_block": { "is_scroll": true, "nav_list": [] } }, "key": "", "style": {}, "tags": [], "title": "Feed title", "updated": "1970-01-01T00:00:00Z" } ``` ## Endpoints ### Create Feed `POST /feeds` Create a new feed. Use the [sample payload](#sample-payload) as a guide for the request body. ```bash cURL theme={null} curl -X POST "https://backend.choicely.com/feeds" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Feed title" }' ``` ### Get all Feeds `GET /feeds` Retrieve all feeds. ```bash cURL theme={null} curl "https://backend.choicely.com/feeds" \ -H "Authorization: Bearer " ``` ### Get a single Feed `GET /feeds/` Retrieve a single feed by its key. Unique key of the feed. ```bash cURL theme={null} curl "https://backend.choicely.com/feeds/" \ -H "Authorization: Bearer " ``` ### Update Feed `PATCH /feeds/` Update an existing feed. Include only the fields you want to change. Unique key of the feed. ```bash cURL theme={null} curl -X PATCH "https://backend.choicely.com/feeds/" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Updated title" }' ``` ### Delete Feed `DELETE /feeds/` Delete a feed. Unique key of the feed. ```bash cURL theme={null} curl -X DELETE "https://backend.choicely.com/feeds/" \ -H "Authorization: Bearer " ``` # File Source: https://docs.choicely.com/api-reference/file Manage file metadata and upload, download, and delete file data The File resource is split into two sets of endpoints: * **Metadata APIs** — create and manage the file's metadata record (filename, type, access, and more). * **File data APIs** — upload, download, and delete the actual file bytes at the `upload_url` returned when you create the metadata. ## Sample payload ```json theme={null} { "access": { "access": "private", "updated": "1970-01-01T00:00:00Z" }, "created": "1970-01-01T00:00:00Z", "custom_data": {}, "file_extension": "jpg", "file_type": "image/jpeg", "filename": "file_name.jpg", "is_scanned": false, "description": "this is a description", "key": "", "updated": "1970-01-01T00:00:00Z", "upload_url": "" } ``` ## Metadata APIs ### Create file metadata `POST /files` Create a file metadata record. The response includes an `upload_url` you use with the File data APIs to upload the file bytes. ```bash cURL theme={null} curl -X POST "https://backend.choicely.com/files" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "filename": "file_name.jpg", "file_type": "image/jpeg", "file_extension": "jpg" }' ``` ### Get file metadata `GET /files/` Retrieve a file's metadata by its key. Unique key of the file. ```bash cURL theme={null} curl "https://backend.choicely.com/files/" \ -H "Authorization: Bearer " ``` ### Update file metadata `PATCH /files/` Update a file's metadata. Include only the fields you want to change. Unique key of the file. ```bash cURL theme={null} curl -X PATCH "https://backend.choicely.com/files/" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "description": "this is a description" }' ``` ### Delete file metadata `DELETE /files/` Delete a file's metadata. Unique key of the file. ```bash cURL theme={null} curl -X DELETE "https://backend.choicely.com/files/" \ -H "Authorization: Bearer " ``` ## File data APIs These endpoints operate on the `upload_url` returned by **Create file metadata** (the `upload_url` field of the file record). Use that URL directly in place of `` below. ### Upload file `PUT ` Upload the file's bytes to the `upload_url`. ```bash cURL theme={null} curl -X PUT "" \ --upload-file ./file_name.jpg ``` ### Download file `GET ` Download the file's bytes from the `upload_url`. ```bash cURL theme={null} curl "" --output file_name.jpg ``` ### Delete file `DELETE ` Delete the file's bytes at the `upload_url`. ```bash cURL theme={null} curl -X DELETE "" ``` # Image Source: https://docs.choicely.com/api-reference/image Upload, read, and delete images The Image resource lets you upload images — either by uploading a file directly or from a URL — and manage them. ## Sample payload ```json theme={null} { "access": { "access": "public", "updated": "1970-01-01T00:00:00Z" }, "created": "1970-01-01T00:00:00Z", "custom_data": {}, "format": "webp", "key": "", "title": "image_name", "updated": "1970-01-01T00:00:00Z" } ``` ## Endpoints ### Upload an image file `POST /images` Upload an image by sending the file as multipart form data. ```bash cURL theme={null} curl -X POST "https://backend.choicely.com/images" \ -H "Authorization: Bearer " \ -F "file=@./image_name.webp" ``` ### Upload an image from a URL `POST /images/upload_from_url` Upload an image by referencing a remote URL. ```bash cURL theme={null} curl -X POST "https://backend.choicely.com/images/upload_from_url" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/image.webp" }' ``` ### Get image `GET /images/` Retrieve an image by its key. Unique key of the image. ```bash cURL theme={null} curl "https://backend.choicely.com/images/" \ -H "Authorization: Bearer " ``` ### Delete image `DELETE /images/` Delete an image. Unique key of the image. ```bash cURL theme={null} curl -X DELETE "https://backend.choicely.com/images/" \ -H "Authorization: Bearer " ``` # Introduction Source: https://docs.choicely.com/api-reference/introduction Integrate with Choicely using the Developer REST API The Choicely Developer API is a REST API that lets you programmatically manage the same resources you build in [Choicely Studio](https://studio.choicely.com) — apps, articles, contests, feeds, files, images, purchases, schedules, shops, surveys, and vote counts. Requests and responses use JSON, and every request is authenticated with a Bearer token. Create your key on the [Authentication](/api-reference/authentication) page, then follow the [Quickstart](/api-reference/quickstart) to make your first call. ## Base URL All endpoints are served from the following base URL. Combine it with the paths shown on each resource page. ```bash theme={null} https://backend.choicely.com ``` For example, the Apps collection is available at `https://backend.choicely.com/apps`. ## Resources Create and manage apps Create and manage articles Create and manage contests Create and manage feeds Manage file metadata and data Upload and manage images Read user purchases Manage schedules and timeslots Manage shops and master shops Create and manage surveys Read contest vote counts # Purchase Source: https://docs.choicely.com/api-reference/purchase Read user purchases The Purchase resource represents a purchase made by a user — subscriptions and vote purchases across payment platforms. These endpoints are **read-only**. Purchase endpoints are served under an `/api` prefix, unlike the other resources in this reference. ## Sample payload ```json theme={null} { "created": "1970-01-01T00:00:00Z", "key": "", "package_key": "", "price": 100, "purchase_platform": "google_play", "purchase_token": "", "shop_key": "", "subscription": { "is_auto_renew": true, "grace_end": "1970-01-01T00:00:00Z", "expiration": "1970-01-01T00:00:00Z", "access": true }, "type": "vote", "updated": "1970-01-01T00:00:00Z", "vote": { "contest_key": "", "participant_key": "", "count": 100 } } ``` ## Endpoints ### Get all Purchases for a User `GET /api/users//purchases` Retrieve all purchases for a user. Unique identifier of the user. ```bash cURL theme={null} curl "https://backend.choicely.com/api/users//purchases" \ -H "Authorization: Bearer " ``` ### Get a single Purchase `GET /api/purchases/` Retrieve a single purchase by its key. Unique key of the purchase. ```bash cURL theme={null} curl "https://backend.choicely.com/api/purchases/" \ -H "Authorization: Bearer " ``` # Quickstart Source: https://docs.choicely.com/api-reference/quickstart Set up your API key, configure requests, and call your first endpoint This guide walks you through using the Choicely Developer API. By following these steps, you'll set up your API key, configure your requests, and interact with the various API endpoints to integrate seamlessly with the platform. ## Step 1: Generate your Developer API key To begin using the Developer API, generate an API key with the necessary permissions: 1. Go to your account's **Manage Account**. 2. In **Linked accounts**, select **Developer API** and create your API key. 3. Choose the permissions your API key should have based on your integration needs. 4. Copy the generated API key and store it securely — you'll need it for configuring requests. Store your API key securely and never expose it in client-side code. Treat it like a password. ## Step 2: Configure your request with the API key Use your API key as a Bearer token in the request header to authenticate your calls: ```bash theme={null} Authorization: Bearer ``` This header ensures that each API request is authorized and processed securely. See [Authentication](/api-reference/authentication) for details. ## Step 3: Browse the documentation Explore the [resource pages](/api-reference/introduction) to find the available endpoints. Each endpoint includes: * A description of the endpoint's functionality * Required headers, parameters, and permissions * Sample payloads for reference ## Step 4: Choose your endpoint and HTTP method Find the endpoint you want to use based on your integration requirements: * **Available endpoints:** `/apps`, `/articles`, `/contests`, and others. * **HTTP methods:** Identify the method required for each endpoint, such as `GET`, `POST`, `PATCH`, etc. For example: ```http theme={null} POST /apps GET /apps/ PATCH /apps/ ``` ## Step 5: Experiment with sample payloads Each resource page provides a sample payload to help you format your data: * **Request body:** Use the sample payload as a guide for structuring your request. * **Customization:** Adjust fields within the payload to meet your requirements. * **Testing:** Try sending requests with sample payloads in Postman or any HTTP client. Now you're ready to start integrating with the Choicely Developer API. For any questions, refer to the resource pages or reach out to [support@choicely.com](mailto:support@choicely.com) for further assistance. # Schedule Source: https://docs.choicely.com/api-reference/schedule Manage schedules and their timeslots The Schedule resource represents an event schedule with days and venues. Each schedule can contain **timeslots** — the individual sessions placed on a day and venue. Schedules use the `/conventions` path. Timeslots are nested under a schedule at `/conventions//timeslots`. ## Schedule ### Sample payload ```json theme={null} { "created": "1970-01-01T00:00:00Z", "custom_data": { "is_list_open_default": false, "is_top_controls_enabled": true, "list_default_mode": "order_venue" }, "days": [ { "bottom_text": "00:00 - 23:59", "end": "1970-01-01T23:59:00Z", "id": "", "start": "1970-01-01T00:00:00Z", "title": "Thu" } ], "end": "1970-01-01T23:59:00Z", "key": "", "search_help": [], "start": "1970-01-01T00:00:00Z", "style": { "alpha": 1, "letter_spacing": 0, "max_lines": 0, "sub_styles": [] }, "title": "Schedule Title", "updated": "1970-01-01T00:00:00Z", "venues": [ { "description": "", "id": "", "position": 1, "style": {}, "title": "Venue title" } ] } ``` ### Create Schedule `POST /conventions` Create a new schedule. Use the [sample payload](#sample-payload) as a guide for the request body. ```bash cURL theme={null} curl -X POST "https://backend.choicely.com/conventions" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Schedule Title" }' ``` ### Get all Schedules `GET /conventions` Retrieve all schedules. ```bash cURL theme={null} curl "https://backend.choicely.com/conventions" \ -H "Authorization: Bearer " ``` ### Get a single Schedule `GET /conventions/` Retrieve a single schedule by its key. Unique key of the schedule. ```bash cURL theme={null} curl "https://backend.choicely.com/conventions/" \ -H "Authorization: Bearer " ``` ### Update Schedule `PATCH /conventions/` Update an existing schedule. Include only the fields you want to change. Unique key of the schedule. ```bash cURL theme={null} curl -X PATCH "https://backend.choicely.com/conventions/" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Updated title" }' ``` ### Delete Schedule `DELETE /conventions/` Delete a schedule. Unique key of the schedule. ```bash cURL theme={null} curl -X DELETE "https://backend.choicely.com/conventions/" \ -H "Authorization: Bearer " ``` ## Schedule Timeslot ### Sample payload ```json theme={null} { "article": "", "custom_data": {}, "day_id": "", "description": "", "end": "1970-01-01T23:59:00Z", "image": "", "navigation": {}, "search_terms": [ "search_term", "search_term_2" ], "start": "1970-01-01T00:00:00Z", "style": {}, "time_text": "00-23", "title": "Timeslot Title", "venue_id": "" } ``` ### Create Timeslot `POST /conventions//timeslots` Create a new timeslot within a schedule. Unique key of the schedule the timeslot belongs to. ```bash cURL theme={null} curl -X POST "https://backend.choicely.com/conventions//timeslots" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Timeslot Title" }' ``` ### List Timeslots of Schedule `GET /conventions//timeslots` Retrieve all timeslots for a schedule. Unique key of the schedule. ```bash cURL theme={null} curl "https://backend.choicely.com/conventions//timeslots" \ -H "Authorization: Bearer " ``` ### Get a single Timeslot `GET /timeslots/` Retrieve a single timeslot by its key. Unique key of the timeslot. ```bash cURL theme={null} curl "https://backend.choicely.com/timeslots/" \ -H "Authorization: Bearer " ``` ### Update Timeslot `PATCH /timeslots/` Update an existing timeslot. Include only the fields you want to change. Unique key of the timeslot. ```bash cURL theme={null} curl -X PATCH "https://backend.choicely.com/timeslots/" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Updated title" }' ``` ### Delete Timeslot `DELETE /timeslots/` Delete a timeslot. Unique key of the timeslot. ```bash cURL theme={null} curl -X DELETE "https://backend.choicely.com/timeslots/" \ -H "Authorization: Bearer " ``` # Shop Source: https://docs.choicely.com/api-reference/shop Manage shops and master shops The Shop resource represents a store where users can purchase votes or subscriptions — including its packages, payment methods, currency, and button/template configuration. **Master Shops** share the same payload shape and provide reusable shop configuration across apps. ## Sample payload ```json theme={null} { "article": "", "cancel_button": { "text": "NO THANKS", "icon": {}, "style": {} }, "created": "1970-01-01T00:00:00Z", "currency": "EUR", "currency_symbol": "€", "currency_template": "{currency_symbol}{price}", "custom_data": {}, "description_template": "Get more votes, {user_name}!", "image": {}, "is_login_required": false, "key": "", "ok_button": { "text": "GET VOTES", "icon": {}, "style": {} }, "packages": [ { "created": "1970-01-01T00:00:00Z", "key": "", "package_type": "permanent", "price": 100, "title": "Package title", "updated": "1970-01-01T00:00:00Z", "vote_count": 1, "sub_months": 1, "after_purchase_navigation": {} } ], "payment_methods": [ "google_play", "app_store", "stripe" ], "receipt_description_template": "Shop - bought {get_votes_count} votes", "shop_type": "participant_vote", "stripe_statement_descriptor_suffix": "", "style": {}, "title": "Shop title", "title_template": "Support {participant_title}", "updated": "1970-01-01T00:00:00Z" } ``` ## Shop APIs ### Create Shop `POST /shops` Create a new shop. Use the [sample payload](#sample-payload) as a guide for the request body. ```bash cURL theme={null} curl -X POST "https://backend.choicely.com/shops" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Shop title", "currency": "EUR" }' ``` ### Get all Shops `GET /shops` Retrieve all shops. ```bash cURL theme={null} curl "https://backend.choicely.com/shops" \ -H "Authorization: Bearer " ``` ### Get a single Shop `GET /shops/` Retrieve a single shop by its key. Unique key of the shop. ```bash cURL theme={null} curl "https://backend.choicely.com/shops/" \ -H "Authorization: Bearer " ``` ### Update Shop `PATCH /shops/` Update an existing shop. Include only the fields you want to change. Unique key of the shop. ```bash cURL theme={null} curl -X PATCH "https://backend.choicely.com/shops/" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Updated title" }' ``` ### Delete Shop `DELETE /shops/` Delete a shop. Unique key of the shop. ```bash cURL theme={null} curl -X DELETE "https://backend.choicely.com/shops/" \ -H "Authorization: Bearer " ``` ## Master Shop APIs ### Create Master Shop `POST /master_shops` Create a new master shop. Use the [sample payload](#sample-payload) as a guide for the request body. ```bash cURL theme={null} curl -X POST "https://backend.choicely.com/master_shops" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Shop title" }' ``` ### Get all Master Shops `GET /master_shops` Retrieve all master shops. ```bash cURL theme={null} curl "https://backend.choicely.com/master_shops" \ -H "Authorization: Bearer " ``` ### Get a single Master Shop `GET /master_shops/` Retrieve a single master shop by its key. Unique key of the master shop. ```bash cURL theme={null} curl "https://backend.choicely.com/master_shops/" \ -H "Authorization: Bearer " ``` ### Update Master Shop `PATCH /master_shops/` Update an existing master shop. Include only the fields you want to change. Unique key of the master shop. ```bash cURL theme={null} curl -X PATCH "https://backend.choicely.com/master_shops/" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Updated title" }' ``` ### Delete Master Shop `DELETE /master_shops/` Delete a master shop. Unique key of the master shop. ```bash cURL theme={null} curl -X DELETE "https://backend.choicely.com/master_shops/" \ -H "Authorization: Bearer " ``` # Survey Source: https://docs.choicely.com/api-reference/survey Create, read, update, and delete surveys The Survey resource represents a survey with its fields, groups, schedule, and answer settings. Creating a survey uses `POST /surveys/` (with the key in the path), not `POST /surveys`. ## Sample payload ```json theme={null} { "created": "1970-01-01T00:00:00Z", "custom_data": {}, "end": "2025-05-20T22:45:00Z", "fields": [], "groups": [], "image": {}, "key": "", "settings": { "answer_mode": "normal", "auto_delete_answers_after_days": -1, "is_anonymous_enabled": true }, "start": "2024-09-19T21:00:00Z", "style": {}, "tags": [], "title": "Survey title", "updated": "1970-01-01T00:00:00Z" } ``` ## Endpoints ### Get all Surveys `GET /surveys` Retrieve all surveys. ```bash cURL theme={null} curl "https://backend.choicely.com/surveys" \ -H "Authorization: Bearer " ``` ### Get a single Survey `GET /surveys/` Retrieve a single survey by its key. Unique key of the survey. ```bash cURL theme={null} curl "https://backend.choicely.com/surveys/" \ -H "Authorization: Bearer " ``` ### Create Survey `POST /surveys/` Create a survey. Use the [sample payload](#sample-payload) as a guide for the request body. Unique key of the survey. ```bash cURL theme={null} curl -X POST "https://backend.choicely.com/surveys/" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Survey title" }' ``` ### Update Survey `PATCH /surveys/` Update an existing survey. Include only the fields you want to change. Unique key of the survey. ```bash cURL theme={null} curl -X PATCH "https://backend.choicely.com/surveys/" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "Updated title" }' ``` ### Delete Survey `DELETE /surveys/` Delete a survey. Unique key of the survey. ```bash cURL theme={null} curl -X DELETE "https://backend.choicely.com/surveys/" \ -H "Authorization: Bearer " ``` # Vote count Source: https://docs.choicely.com/api-reference/vote-count Read aggregated vote counts for a contest The Vote count resource returns aggregated voting data for a contest — totals per participant and per vote source, including internal sources such as free (`c-free`) and paid (`c-paid`) votes. This endpoint is **read-only**. ## Sample payload ```json theme={null} { "anonymous_voter_count": 0, "contest_key": "", "contest_type": "voteonly", "count": 0, "created": "1970-01-01T00:00:00Z", "participants": { "": { "anonymous_voter_count": 0, "count": 0, "unique_voter_count": 0, "vote_sources": [ { "anonymous_voter_count": 0, "count": 0, "mapped_id": "", "source": "", "unique_voter_count": 0 }, { "anonymous_voter_count": 0, "count": 0, "source": "c-free", "unique_voter_count": 0 }, { "anonymous_voter_count": 0, "count": 0, "source": "c-paid", "unique_voter_count": 0 } ] } }, "total_vote_sources": [ { "anonymous_voter_count": 0, "count": 0, "is_in_total": true, "is_internal": false, "source": "", "title": "Valid source", "unique_voter_count": 0, "updated": "1970-01-01T00:00:00Z" }, { "anonymous_voter_count": 0, "count": 0, "is_in_total": true, "is_internal": true, "source": "c-free", "title": "Free Votes", "unique_voter_count": 0, "updated": "1970-01-01T00:00:00Z" }, { "anonymous_voter_count": 0, "count": 0, "is_in_total": true, "is_internal": true, "source": "c-paid", "title": "Paid Votes", "unique_voter_count": 0, "updated": "1970-01-01T00:00:00Z" } ], "unique_voter_count": 0, "updated": "1970-01-01T00:00:00Z" } ``` ## Endpoints ### Get vote count `GET /contests//vote_updates/?sources=true` Retrieve aggregated vote counts for a contest. Unique key of the contest. When `true`, include a per-source breakdown of the vote counts. ```bash cURL theme={null} curl "https://backend.choicely.com/contests//vote_updates/?sources=true" \ -H "Authorization: Bearer " ``` # Custom View Source: https://docs.choicely.com/ios-advanced/custom-view Integrate your own custom views into Choicely SDK for iOS ## Add Custom view to your project 1. Create a class that conforms to `ChoicelyExternalViewControllerFactory` protocol: ```swift theme={null} class YourCustomViewControllerFactory: ChoicelyExternalViewControllerFactory { func createViewController(choicelyNavigationitem: ChoicelyNavigationItem?) -> ChoicelyController? { return nil } } ``` 2. Declare all your custom views usage inside it's `createViewController()` method: ```swift UIKit theme={null} let yourCustomUrl = "choicely://special/your_custom_url" let internalUrl = choicelyNavigationitem?.internalUrl if internalUrl.contains(yourCustomUrl) == true { return YourCustomViewController() } // In order to return YourCustomViewController make it a subclass of ChoicelyViewController. ``` ```swift SwiftUI theme={null} let yourCustomUrl = "choicely://special/your_custom_url" let internalUrl = choicelyNavigationitem?.internalUrl if internalUrl.contains(yourCustomUrl) == true { return ChoicelyView { YourCustomView() } } // ChoicelyView is a wrapper that will return ChoicelyViewController from any SwiftUI view. ``` Custom navigation URLs like `"choicely://special/your_custom_url"` can be set and configured in Choicely Studio. This is how you can create custom navigation URLs: How to use navigation iOS 3. Set the custom url, click "Add Navigation" and save the changes by clicking "Update" button in the top right corner. 4. Here is the example of fully configured `ChoicelyExternalViewControllerFactory`: ```swift UIKit theme={null} import ChoicelyCore class YourCustomViewControllerFactory: ChoicelyExternalViewControllerFactory { func createViewController(choicelyNavigationitem: ChoicelyNavigationItem?) -> ChoicelyController? { let yourCustomUrl = "choicely://special/your_custom_url" let internalUrl = choicelyNavigationitem?.internalUrl if internalUrl.contains(yourCustomUrl) == true { return YourCustomViewController() } return nil } } ``` ```swift SwiftUI theme={null} import ChoicelyCore class YourCustomViewControllerFactory: ChoicelyExternalViewControllerFactory { func createViewController(choicelyNavigationitem: ChoicelyNavigationItem?) -> ChoicelyController? { let yourCustomUrl = "choicely://special/your_custom_url" let internalUrl = choicelyNavigationitem?.internalUrl if internalUrl.contains(yourCustomUrl) == true { return ChoicelyView { YourCustomView() } } return nil } } ``` 5. Create an instance of `YourCustomViewControllerFactory` class and just before `ChoicelySDK.initialize(...)` assign it to `ChoicelySDK.settings.externalViewControllerFactory`: ```swift theme={null} ChoicelySDK.settings.externalViewControllerFactory = YourCustomViewControllerFactory() ``` To learn more about other ChoicelySDK settings explore them in XCode with `(⌥)+click`. 6. That's it! You're ready to use your custom views. You can mix Choicely content like `Articles` and `Surveys` with your own UI logic inside one custom view. # React Native Support Source: https://docs.choicely.com/ios-advanced/react-native Integrate React Native content and enable URL navigation from JavaScript ## Add React Native Module To display React Native content within your Choicely app, you need to add the `ChoicelyReactNative` module. 1. In Xcode, with your app project open, navigate to **File > Add Packages**. 2. Add the Choicely SDK repository if you haven't already: ``` https://github.com/choicely/choicely-sdk-ios.git ``` 3. Select the `ChoicelyReactNative` module in addition to `ChoicelyCore`. ## Enable URL Navigation from React Native To allow React Native JavaScript code to open Choicely content using `Linking.openURL()`, you need to register a custom URL scheme and forward URLs to the SDK. ### 1. Register URL Scheme Add the `choicely` URL scheme to your app's Info.plist: ```xml theme={null} CFBundleURLTypes CFBundleURLSchemes choicely ``` ### 2. Handle URLs in AppDelegate Forward incoming URLs to the SDK by implementing the URL handler in your AppDelegate: ```swift UIKit theme={null} import ChoicelyCore @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { ChoicelySDK.initialize( application: application, appKey: "YOUR_APP_KEY" ) return true } func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { return ChoicelySDK.handleOpenURL(url) } } ``` ```swift SwiftUI theme={null} import SwiftUI import ChoicelyCore @main struct YourApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ChoicelySplashView() } } } class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { ChoicelySDK.initialize( application: application, appKey: "YOUR_APP_KEY" ) return true } func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { return ChoicelySDK.handleOpenURL(url) } } ``` ## Using Linking.openURL in React Native Once configured, your React Native JavaScript code can open any Choicely content using the `Linking` API: ```javascript theme={null} import { Linking } from 'react-native' // Open a React Native module (module name configured in Choicely Studio) await Linking.openURL('choicely://special/rn/your-module-name?message=testing') // Open an article (using the article's content key) await Linking.openURL('choicely://article/your-article-key') // Open a contest (using the contest's content key) await Linking.openURL('choicely://contest/your-contest-key') // Open a feed (using the feed's content key) await Linking.openURL('choicely://feed/your-feed-key') ``` Replace `your-article-key`, `your-contest-key`, and `your-feed-key` with the actual content keys from Choicely Studio. You can find these keys in the content settings for each article, contest, or feed. ### Passing Parameters to React Native Modules You can pass data to your React Native modules using query parameters. These parameters will be automatically converted to props and passed to your component. ```javascript theme={null} // Pass a single parameter await Linking.openURL('choicely://special/rn/news-article?articleId=12345') // Pass multiple parameters await Linking.openURL('choicely://special/rn/news-article?articleId=12345&highlight=true§ion=sports') ``` Your React Native component will receive these as props: ```javascript theme={null} export default function NewsArticle(props) { console.log(props.articleId) // "12345" console.log(props.highlight) // "true" console.log(props.section) // "sports" return ( Article ID: {props.articleId} ) } ``` Query parameter values are always passed as strings. Convert them to the appropriate type in your component if needed (e.g., `const id = Number(props.articleId)`). ### Check if URL can be opened Before opening a URL, you can check if the URL scheme is supported: ```javascript theme={null} const canOpen = await Linking.canOpenURL('choicely://special/rn/your-module-name') if (canOpen) { await Linking.openURL('choicely://special/rn/your-module-name') } else { console.log('URL scheme not supported') } ``` ## Supported URL Formats The SDK supports the following internal URL formats: * `choicely://special/rn/` - Opens a React Native module * `choicely://article/` - Opens an article * `choicely://contest/` - Opens a contest * `choicely://feed/` - Opens a feed * And other Choicely content types All content keys (``) are unique identifiers created in Choicely Studio. You can find the key for each piece of content in its settings page. ## Example: Navigation from React Native Here's a complete example of navigating between different Choicely content types from React Native: ```javascript theme={null} import React from 'react' import { View, Button, Linking } from 'react-native' export default function NavigationExample() { const openArticle = async () => { // Replace with your actual article key from Choicely Studio await Linking.openURL('choicely://article/your-article-key') } const openContest = async () => { // Replace with your actual contest key from Choicely Studio await Linking.openURL('choicely://contest/your-contest-key') } const openFeed = async () => { // Replace with your actual feed key from Choicely Studio await Linking.openURL('choicely://feed/your-feed-key') } const openNewsWithParams = async () => { // Open another RN module with parameters await Linking.openURL('choicely://special/rn/news-detail?articleId=12345&source=home') } return (