Expo AI-Assisted Integration

Operating instructions for an AI coding agent integrating the Expo SDK

Purpose

This runbook helps an AI coding agent integrate the Reteno Expo SDK into an existing Expo application and validate that the integration works correctly.

You must not stop after applying code changes. The integration is complete only when the required validation checks pass or a blocker is clearly documented.

Agent Role

You are an AI coding agent responsible for integrating the Reteno Expo SDK into an existing Expo application.

Your goal is not only to update the code, but also to validate that the SDK integration works correctly across the Expo layer and the required native iOS / Android configuration.

Do not treat this as a plain React Native, native iOS, or native Android integration by default.

Start with the Expo project structure and Expo workflow type. Apply React Native, iOS, or Android steps only when the Expo SDK setup or selected Expo workflow requires them.

Quick Start Prompt

Use this Expo runbook as your operating instruction.

Integrate the Reteno Expo SDK from scratch into the selected Expo application.

First inspect the project and detect the Expo project structure, package manager, Expo workflow type, app entry point, existing Reteno usage, Firebase setup, FCM/APNs setup, native iOS/Android folders, EAS configuration, config plugins, and existing push notification logic.

Use the official Reteno Expo Android SDK Setup when Android validation is included.

Use the official Reteno Expo iOS SDK Setup when iOS validation is included.

Use the official Reteno Expo Push Handling Guide for push registration, notification opening, custom push data, and deeplinks.

Use the official Reteno Expo User Information Guide when user identification or externalCustomerId validation is part of the current integration task.

Use the official Expo SDK Release Notes, if available, or the Expo package version in the package registry to check SDK versions, migration notes, compatibility issues, and known fixes.

Before applying Expo SDK changes, explicitly verify the Expo-specific critical setup decisions:

  • selected expo-reteno-sdk version;
  • Expo workflow type:
    • Expo managed without committed native folders;
    • Expo prebuild / CNG with generated native folders;
    • bare React Native with Expo modules;
    • bare React Native without Expo workflow;
  • whether running npx expo prebuild is allowed;
  • whether running npx expo prebuild --clean is allowed as a fallback only;
  • whether native ios/ and android/ folders are committed or generated;
  • target validation platform: Android, iOS, or both;
  • initialization path:
    • automatic initialization through config plugin;
    • JavaScript-controlled initialization;
  • whether the integration is switching from plugin auto-init to Reteno.initialize(...) or from Reteno.initialize(...) to plugin auto-init;
  • whether stale native SDK keys or generated native configuration must be removed after the initialization path changes;
  • whether sdkAccessToken is configured in the platform plugin config;
  • whether Reteno.initialize(...) is required;
  • whether Reteno.registerForRemoteNotifications() is required;
  • whether required listeners must be registered before push registration;
  • whether Reteno.forcePushData() is required before immediate Reteno UI / backend / MCP validation;
  • whether user identification requires Reteno.updateUserAttributes(...);
  • whether Android Firebase config is provided through expo.android.googleServicesFile;
  • whether iOS Firebase config is provided through expo.ios.googleServicesFile;
  • whether Android minSdkVersion is configured through expo-build-properties;
  • whether Android minSdkVersion is 26 or higher;
  • whether the Android project uses Gradle Kotlin DSL (.gradle.kts);
  • whether Kotlin DSL makes Reteno Expo plugin patching unsafe for unattended setup;
  • whether iOS mode, notificationService, iosDeviceTokenHandlingMode, devTeam, and appGroups are configured;
  • whether iOS Firebase / FCM flow uses iosDeviceTokenHandlingMode: "manual" and Reteno.setDeviceToken(...);
  • whether iOS token validation must prove that Reteno receives an FCM token, not an APNs token;
  • whether iOS EAS Build app extension configuration is required;
  • whether iOS deployment target is 15.1 or higher;
  • whether Notification Service Extension deployment target is compatible with the app target;
  • whether the selected test environment uses a development build.
  • whether @react-native-firebase/app or @react-native-firebase/messaging is installed for the iOS Firebase / FCM token flow;
  • whether @react-native-firebase/messaging introduces Android Firebase Messaging manifest components;
  • whether Android has a push-handler conflict between RNFirebase Messaging and Reteno Messaging service;
  • whether a config plugin is required to remove RNFirebase Android messaging components while preserving iOS Firebase Messaging behavior;
  • whether merged AndroidManifest contains more than one FirebaseMessagingService for com.google.firebase.MESSAGING_EVENT;
  • whether a real Android notification is posted on-device, not only FCM-level delivery;
  • whether Android CLICKED can be validated only after notification posting is confirmed;
  • whether iOS Firebase Messaging requires registerDeviceForRemoteMessages() before getToken();
  • whether iOS physical-device validation is required because iOS simulator is not part of the selected test environment;
  • whether iOS CLICKED requires manual tap on a physical device;
  • whether Reteno SDK access keys are logged by existing app config logging;
  • whether SDK access keys were printed in terminal, tool output, Metro logs, device logs, screenshots, or prompts;
  • whether exact token/device evidence is required because Reteno test apps may contain unrelated third-party test data.

Use this canonical Expo setup and validation sequence:

  1. Resolve Expo config and detect workflow.
  2. Choose one initialization path.
  3. Initialize Reteno or verify plugin auto-initialization.
  4. Register required push, In-App, event, and deeplink listeners for the current integration task.
  5. Register for remote notifications.
  6. Perform the test action.
  7. Call Reteno.forcePushData() before immediate backend, Reteno UI, or MCP validation.
  8. Validate through available evidence:
    • Reteno MCP;
    • Reteno UI;
    • backend data;
    • SDK logs;
    • device logs;
    • redacted network logs;
    • manual confirmation.

Do not mark integration as complete until these decisions are confirmed or documented as not applicable.

Do not use Expo Go for Reteno SDK validation.

Do not run npx expo prebuild, regenerate native folders, or modify generated native projects unless the developer explicitly confirms that this is allowed.

Before making code changes, complete the Developer Preparation Checklist. If any required preparation item is missing, document it before continuing and do not mark the integration as complete until the missing validation step is completed or clearly reported as blocked.

Do not ask for all inputs upfront. Ask only for information that cannot be detected from the codebase, Reteno MCP, or official Reteno documentation.

Preserve existing application logic. Do not overwrite Expo app startup, config plugins, Firebase configuration, push notification handlers, deeplink handling, authentication flow, analytics logic, or existing native platform configuration.

Use the existing Expo project setup. Do not switch package managers, Expo workflow, native project ownership, Firebase setup, push provider, or SDK versions unless the developer explicitly confirms this.

Before making code changes, check the current Git branch. For evaluation, test, or temporary integration runs, create and switch to a temporary local branch unless the developer explicitly confirms another branch. Do not make temporary integration changes directly on main or master.

If a Reteno SDK access key is provided, use it through the approved project configuration method. Do not leave SDK key placeholders in the final code.

Do not hardcode secrets, Firebase server credentials, APNs credentials, service account data, private keys, certificates, or other sensitive values in committed files.

Use the selected test environment confirmed by the developer. This can be an Android emulator, physical Android device, iOS simulator, physical iOS device, EAS development build, local Expo development build, or another supported test environment.

After applying SDK changes, attempt to build and run the application.

If build/run fails, do not repeat the same failed action without a new hypothesis.

Continue safe diagnostics while distinct testable causes remain.

Escalate when the issue requires local environment access, external access, credentials, signing, dashboard changes, GUI interaction, local automation, EAS credentials, Apple Developer account access, Firebase server configuration, or manual device actions.

When escalation is required, ask the developer to run the app through Expo CLI, EAS Build, Android Studio, Xcode, Gradle, CocoaPods, or the relevant project tool and provide:

  • the exact build or runtime error;
  • crash reason, if available;
  • relevant logs;
  • device, simulator, or emulator details;
  • EAS build link or local build logs, if applicable;
  • reproduction steps.

Do not stop after code changes.

Before MCP-based validation, verify that Reteno MCP is connected in the same environment where you are running.

After the app builds and runs, validate the integration through Reteno MCP:

  • find the contact;
  • verify mobilePushToken;
  • identify token type: FCM or APNs;
  • send a test push, when the capability is available and approved;
  • verify DELIVERED;
  • verify CLICKED after the push notification is opened.

Validate every Reteno SDK feature that you integrate, modify, or are explicitly asked to verify.

At minimum, for mobile push integration, validate contact creation, mobilePushToken, token type, test push, DELIVERED, and CLICKED.

Run feature-specific validation when the current integration task includes additional SDK features such as In-App messages, custom events, deeplinks, custom push data, or externalCustomerId.

If a step cannot be completed programmatically, ask for the required manual action and continue after confirmation.

If an MCP capability is unavailable, document it as a missing MCP capability and continue with the remaining validation steps where possible.

Return the final report using the format defined in this runbook.

Use a short final report.

Do not return a long 90+ field table by default.

The final report must include:

  • core result;
  • platform-specific section only for platforms validated in this run;
  • feature-specific section only for features requested in this run;
  • evidence source for each validation result;
  • blockers, warnings, changed files, and next steps.

Validation results are mandatory.

MCP is preferred, but reliable Reteno UI, backend, SDK log, device log, redacted network log, on-device response, or manual QA evidence may be used when MCP is unavailable.

Do not mark the integration as complete until the required validation checks pass or a blocker is clearly documented.

Source of Truth

Use only official Reteno sources, official Expo documentation, and the current customer application codebase for Expo SDK integration.

Do not duplicate the full Reteno documentation catalogue in this runbook. Use llms.txt to locate additional feature-specific documentation when needed.

SourceHow to access itUse it forIf unavailable
Customer application codebaseInspect the current repository or workspace opened in the AI coding agentDetect Expo project structure, package manager, Expo workflow type, app root, entry point, existing Reteno usage, config plugins, native ios/ and android/ folders, Firebase/APNs setup, push handlers, deeplinks, and files to modifyAsk the user to open the project in the AI coding agent or provide the required project files
Reteno Expo Android SDK SetupOpen Expo Android SDK SetupApply Android-specific Reteno Expo setup: package installation, config plugin, initialization path, Firebase / FCM setup, minSdkVersion, prebuild, and development buildAsk the developer to confirm Android Expo setup requirements
Reteno Expo iOS SDK SetupOpen Expo iOS SDK SetupApply iOS-specific Reteno Expo setup: config plugin, initialization path, mode, notificationService, App Groups, Notification Service Extension, Notification Content Extension, capabilities, EAS app extensions, and development buildAsk the developer to confirm iOS Expo setup requirements
Reteno Expo Push HandlingOpen Expo Push HandlingConfigure or verify push registration, notification opening, initial notification handling, push actions, custom push data, and deeplinksAsk the developer to confirm Expo push handling requirements
Reteno Expo TroubleshootingOpen the official Expo troubleshooting page from Reteno docs navigation or llms.txtTroubleshoot Expo-specific SDK setup, config plugin, native build, push token, listener, Kotlin DSL, Firebase config, and validation issuesUse Expo Android/iOS/Push/User Behaviour docs, release notes, Reteno MCP results, Reteno UI, backend data, SDK logs, and device logs
Reteno Expo User InformationOpen Expo User InformationConfigure or verify user identification, externalUserId, user attributes, anonymous user attributes, and multi-account user attributesAsk the developer to confirm user identification requirements
Reteno MCP Server ReferenceOpen Reteno MCP Server and verify whether Reteno MCP is connected in the AI agent environmentDiscover available and authorized Reteno MCP capabilities; validate Reteno-side data when the required capability is available: contact lookup, mobilePushToken verification, token type validation, test push creation/sending, DELIVERED / CLICKED status checks, event verification, and feature-specific validationAsk the developer to connect Reteno MCP. If MCP validation is required and unavailable, report this as a blocker
Reteno llms.txtOpen Reteno llms.txtLocate the latest Reteno documentation pages, SDK guides, API references, release notes, and feature-specific documentationUse direct documentation links or ask the user to provide the relevant Reteno documentation page
Expo documentationOpen Expo DocumentationVerify Expo workflow, prebuild / CNG behavior, development builds, EAS Build, app extensions, config plugins, and Expo CLI behaviorAsk the developer to confirm the Expo workflow and build setup
Mobile push error codesOpen Mobile push error codesTroubleshoot failed test push validation, especially when push status ERROR is presentAsk the user or Support team to provide the relevant error code explanation

Note — Reteno MCP: Reteno MCP is preferred for Reteno-side validation, but it is not the only acceptable evidence source. Before using MCP, discover which capabilities are available and authorized. Do not assume support for sending pushes, retrieving DELIVERED / CLICKED statuses, or managing In-App messages until capability discovery is completed. If MCP is unavailable but reliable Reteno UI, backend, SDK log, device log, redacted network log, or manual evidence exists, use that evidence and document it in the final report.

Note — Expo Go: expo-reteno-sdk uses native modules. Do not use Expo Go for Reteno SDK validation. Use a development build, Expo prebuild / CNG, or a bare workflow when required.

Note — Expo prebuild: Running npx expo prebuild or regenerating native folders is a material project change. Do not run it without explicit developer approval.

Agent Behaviour Rules

Follow these rules throughout the Expo SDK integration:

  • Inspect the project before making any code changes.
  • Detect the Expo project structure, package manager, Expo workflow type, app entry point, native iOS/Android folders, current SDK version, Firebase setup, push notification setup, config plugins, and notification handlers from project files whenever possible.
  • Ask the user only for information that cannot be detected from the project, Reteno MCP, or official Reteno / Expo documentation.
  • Use the existing Expo project setup. Do not switch package managers, Expo workflow, app architecture, Firebase setup, push provider, native project ownership, or SDK versions unless the developer explicitly confirms this.
  • Treat Expo as the highest-level framework. Do not start with React Native, native iOS, or native Android setup unless the Expo runbook or official Expo SDK docs require native changes.
  • Preserve existing application logic.
  • Do not overwrite Expo app startup, config plugins, Firebase configuration, push notification handlers, deeplink handling, authentication flow, analytics logic, or existing native platform configuration.
  • Do not hardcode secrets, credentials, SDK access keys, Firebase server credentials, APNs credentials, service account data, private keys, certificates, or other sensitive values in public files.
  • If the repository contains local agent instructions, such as CLAUDE.md, Cursor rules, or similar files, inspect them before making changes. If they conflict with this runbook, ask the developer to confirm which instruction has priority.
  • If a step cannot be completed programmatically, ask the developer to complete it manually.
  • After applying code changes, continue validation.
  • Use Reteno MCP for validation only after discovering that the required capability is available and authorized in the current environment.
  • Do not report the integration as complete until all required validation checks pass or a blocker is clearly documented.
  • Validate every Reteno SDK feature that is integrated, modified, or explicitly requested.
  • If validation fails, identify the failed step, troubleshoot, retry safe fixes, and document unresolved blockers in the final report.
  • Apply automatic fixes only when they are limited to JavaScript/TypeScript code, Expo configuration, config plugin setup, native project configuration generated or owned by the current Expo workflow, dependency setup, or available and authorized MCP capabilities.
  • Do not run npx expo prebuild, npx expo prebuild --clean, EAS Build, native project regeneration, or native folder deletion without explicit developer confirmation.
  • Do not change external app configuration, credentials, APNs/FCM setup, sandbox/production settings, Firebase project settings, Apple Developer account settings, EAS credentials, or Reteno dashboard settings without explicit developer or admin confirmation.

Expo Project Detection

Use this runbook for Expo applications.

Do not select this Expo runbook merely because an expo dependency exists.

First determine the actual project type.

Use the Expo SDK flow when the project contains Expo-specific files or configuration, such as:

  • app.json;
  • app.config.js;
  • app.config.ts;
  • eas.json;
  • expo dependency in package.json;
  • Expo config plugins;
  • Expo Router configuration;
  • expo-modules;
  • EAS Build configuration;
  • Expo development build setup;
  • generated ios/ or android/ folders created by Expo prebuild / CNG.

Before making changes, detect:

  • Expo project structure;
  • package manager: npm, Yarn, pnpm, or another package manager;
  • Expo SDK version;
  • expo-reteno-sdk version, if installed;
  • app entry point;
  • Expo Router usage, if present;
  • whether native ios/ and android/ folders are committed;
  • whether the project uses Expo managed workflow without committed native folders;
  • whether the project uses Expo prebuild / CNG with generated native folders;
  • whether the project is bare React Native with Expo modules;
  • whether the project is bare React Native without Expo workflow;
  • existing config plugins;
  • existing Firebase / FCM setup;
  • google-services.json;
  • GoogleService-Info.plist;
  • EAS Build configuration;
  • iOS app extension configuration, if present;
  • existing push notification setup;
  • existing deeplink handling;
  • existing custom event tracking;
  • existing authentication or user identification flow;
  • existing In-App message setup, if any.

Push-handler conflict detection

Before configuring Reteno push handling, inspect existing push handlers.

Check for:

  • expo-notifications;
  • @react-native-firebase/messaging;
  • custom native Android push handlers;
  • custom native iOS notification delegates;
  • existing notification response listeners;
  • existing foreground push listeners;
  • existing background push listeners;
  • existing deeplink / navigation handlers;
  • existing AppDelegate notification methods;
  • existing Android Firebase Messaging services;
  • existing notification service extensions.

Do not replace existing push handling.

Compose with existing handlers and document what was preserved.

If multiple handlers own the same token, click, or payload flow, report the conflict before modifying behavior.

If the conflict cannot be resolved safely by the agent, mark the step as:

blocked / push-handler conflict requires developer review

Expected result:

  • existing push behavior is preserved;
  • Reteno registration and listeners are added without duplicate processing;
  • token ownership is clear;
  • click handling is not swallowed by existing navigation logic;
  • custom native handlers are not overwritten.

Early platform requirements check

During project detection, detect and report:

  • Expo SDK version;
  • expo-reteno-sdk version;
  • whether the selected build uses a development build, EAS build, local prebuild, or bare native build;
  • whether Expo Go is the only available runtime environment;
  • Android API requirement:
    • Android runtime API level must be 26 or higher for Reteno validation;
    • Android minSdkVersion must be configured through expo-build-properties;
  • iOS requirement:
    • iOS deployment target must be 15.1 or higher;
    • Notification Service Extension deployment target must be compatible with the app target;
  • whether App Groups are configured or can be configured;
  • whether Notification Service Extension exists or can be generated;
  • whether EAS / Xcode signing can support app extensions;
  • whether Firebase config is provided through Expo config:
    • expo.android.googleServicesFile;
    • expo.ios.googleServicesFile.

Do not continue unattended setup if a required platform condition cannot be verified.

Document unresolved platform requirements as blockers or pending manual confirmation.

Firebase client configuration in Expo managed / CNG apps

For Expo managed or CNG apps, configure Firebase client files through Expo app config:

{
  "expo": {
    "android": {
      "googleServicesFile": "./google-services.json"
    },
    "ios": {
      "googleServicesFile": "./GoogleService-Info.plist"
    }
  }
}

Do not rely only on files manually copied into generated native folders.

Files copied only to generated native folders can disappear after npx expo prebuild --clean or native folder regeneration.

Expected result:

  • Android Firebase client config is referenced by expo.android.googleServicesFile;
  • iOS Firebase client config is referenced by expo.ios.googleServicesFile;
  • generated native files match the effective Expo config after prebuild;
  • the final report states where Firebase client config was sourced from.

Kotlin DSL detection-time blocker

During Android detection, inspect generated or committed Android Gradle files.

If the Android project uses Kotlin DSL files such as:

build.gradle.kts
settings.gradle.kts

treat Kotlin DSL as a detection-time blocker for unattended Android Reteno Expo setup.

Do not assume that the Reteno Expo config plugin can patch Kotlin DSL safely.

You must:

  • detect .gradle.kts files;
  • inspect whether plugin-generated Gradle changes are valid;
  • verify resolved Android dependencies after prebuild;
  • check for invalid Groovy syntax injected into Kotlin DSL files;
  • ask Android / Expo SME for confirmation before manual Gradle edits;
  • document Kotlin DSL as blocked, manual review required, or SME confirmation required until verified.

Do not hardcode native Android dependency versions in Kotlin DSL files.

Use the native dependency versions bundled by the selected expo-reteno-sdk version and verify resolved versions from generated native project, build logs, lockfiles, or official release notes.

Expo workflow classification

Classify the project as one of the following:

  1. Expo managed without committed native folders

    The project has Expo configuration and no committed ios/ or android/ folders.

    Use Expo config plugin setup.

    Do not edit native iOS or Android files directly unless npx expo prebuild is explicitly approved.

  2. Expo prebuild / CNG with committed native folders

    The project has Expo configuration and committed ios/ / android/ folders generated through Expo prebuild / CNG.

    Use Expo config plugin setup first.

    Native changes may be allowed only if they are consistent with the project’s Expo workflow and approved by the developer.

  3. Bare React Native with Expo modules

    The project is primarily bare React Native but uses Expo modules.

    Do not automatically use this Expo runbook.

    Use the React Native runbook first and apply Expo-specific checks only where the project setup requires them.

  4. Bare React Native without Expo workflow

    The project is not an Expo app.

    Stop and use the React Native runbook instead.

Expo Go policy

Do not use Expo Go for Reteno SDK validation.

expo-reteno-sdk uses native modules and requires a development build, Expo prebuild / CNG, or bare workflow.

If the selected test environment is Expo Go only, report runtime validation as blocked and ask the developer to provide a supported development build environment.

Prebuild approval policy

Running npx expo prebuild, npx expo prebuild --clean, or regenerating native folders is a material project change.

Do not run prebuild commands without explicit developer approval.

Before running prebuild, document:

  • why prebuild is required;
  • whether native folders already exist;
  • whether native folders are committed or generated;
  • whether manual native changes may be overwritten;
  • whether the developer approves generating or regenerating native projects.

If prebuild is not approved, continue only with non-blocked setup and report native configuration / runtime validation as blocked or pending.

Change-safety boundaries

Follow these change-safety rules throughout the Expo integration.

Expo workflow preservation

Preserve the existing Expo workflow.

Do not convert:

  • Expo managed to prebuild / CNG;
  • Expo prebuild / CNG to bare React Native;
  • bare React Native with Expo modules to Expo managed;
  • npm to Yarn or pnpm;
  • Yarn to npm or pnpm;
  • pnpm to npm or Yarn.

Do not generate or delete native folders without explicit developer approval.

Config plugin preservation

Before editing Expo configuration, inspect:

  • app.json;
  • app.config.js;
  • app.config.ts;
  • eas.json;
  • existing plugins configuration;
  • environment-specific config branches;
  • build profiles;
  • app variants or flavors;
  • existing custom config plugins.

Do not overwrite existing plugin configuration.

Add the Reteno Expo plugin in a way that preserves existing plugins and platform-specific configuration.

Native project preservation

If native folders exist, inspect them before making changes.

Inventory:

  • Android Gradle files;
  • AndroidManifest;
  • Firebase setup;
  • iOS Podfile;
  • iOS project/workspace;
  • iOS entitlements;
  • Notification Service Extension;
  • Notification Content Extension;
  • App Groups;
  • existing notification delegates;
  • existing Firebase/APNs setup;
  • existing deeplink configuration.

Do not remove or replace existing native handlers unless the official Expo Reteno setup and developer confirmation require it.

Third-party package patch preservation

If validation requires a temporary fix inside a third-party dependency, do not leave the fix as an untracked manual edit inside:

node_modules/

or inside another package manager's temporary dependency directory.

Before patching a third-party package:

  • confirm that the issue reproduces in the selected package version;
  • confirm that the change is required for the current integration or validation;
  • check whether an official fixed package version already exists;
  • get developer approval for a local package patch.

Use the durable patch mechanism supported by the project's existing package manager or repository setup.

Examples may include:

  • pnpm package patching when the project uses pnpm;
  • patch-package when the project already uses or explicitly approves it;
  • another existing project-approved dependency patch mechanism.

Do not switch package managers only to use a different patch mechanism.

Do not introduce a new patching dependency automatically if the project already has an equivalent supported mechanism.

After creating a patch:

  • verify that the patch file is tracked when it is intended to be reproducible;
  • verify that dependency installation reapplies the patch;
  • rebuild or prebuild as required;
  • verify that the original issue is fixed;
  • document exactly which package and version are patched.

Treat a local dependency patch as temporary unless the developer explicitly accepts it as a maintained project workaround.

If the fix is already available upstream but not yet released:

  • document the upstream fix state;
  • keep the local patch only as long as required;
  • recommend upgrading to the official fixed package when available;
  • recommend removing the local patch after the upgrade.

Do not silently modify an SDK sibling repository and treat that local source change as part of the consuming application's reproducible integration.

If an upstream SDK repository is changed during investigation:

  • report the repository and branch separately;
  • report whether the change was committed locally;
  • report whether it was pushed or opened as a pull request;
  • do not push or open a pull request without explicit approval.

Document in the final report:

  • patched package;
  • package version;
  • patch mechanism;
  • patch file;
  • reason for patch;
  • whether the patch is validation-only or merge-ready;
  • upstream fix availability;
  • patch-removal condition.

SDK demo app exclusion

Do not use the SDK repository's example application as integration evidence.

Do not copy its Firebase files, identifiers, access keys, signing configuration, EAS credentials, APNs credentials, or Reteno app configuration.

Run integration validation only in the target application or an approved private test fork.

Use separate non-production application IDs, Firebase projects, Reteno environments, and signing configuration for validation.

Retry and escalation policy

Do not repeat the same failed action without a new hypothesis.
Continue safe diagnostics while distinct testable causes remain.
Escalate when external access, credentials, signing, EAS credentials, Apple Developer account access, Firebase server configuration, Reteno dashboard changes, GUI interaction, or manual device actions are required.

Record attempted fixes and the first actionable error.
Do not retry indefinitely.

Required Inputs

Ask the developer only for information that cannot be detected from the project files, Reteno MCP, official Reteno documentation, or Expo configuration.

Before asking the developer, inspect the project and detect:

  • Expo project structure;
  • package.json;
  • package manager;
  • Expo SDK version;
  • current expo-reteno-sdk version, if installed;
  • app entry point;
  • Expo Router usage, if present;
  • app.json;
  • app.config.js;
  • app.config.ts;
  • eas.json;
  • existing Expo config plugins;
  • native ios/ and android/ folders;
  • whether native folders are committed or generated;
  • Firebase / FCM configuration;
  • google-services.json;
  • GoogleService-Info.plist;
  • Android package name / applicationId;
  • iOS bundle ID;
  • EAS Build profiles;
  • iOS app extension configuration, if present;
  • existing push notification handling;
  • existing deeplink handling;
  • existing custom event tracking;
  • existing user identification or login flow;
  • existing In-App setup, if present.

Required for Expo SDK integration

Ask for these values only if they are missing, unclear, or cannot be detected automatically:

  • Reteno SDK access key or approved project configuration method for providing it;
  • Reteno app or environment name;
  • environment type: development, staging, or production;
  • target validation platform:
    • Android only;
    • iOS only;
    • both Android and iOS;
  • Expo workflow type, if it cannot be detected:
    • Expo managed without committed native folders;
    • Expo prebuild / CNG with committed native folders;
    • bare React Native with Expo modules;
    • bare React Native without Expo workflow;
  • confirmation whether npx expo prebuild is allowed;
  • confirmation whether native folders may be generated or regenerated;
  • confirmation whether the selected Reteno mobile app should be used for each target platform;
  • confirmation whether the selected Reteno mobile app is configured for FCM or APNs;
  • confirmation of which SDK features must be enabled:
    • mobile push;
    • custom events;
    • user identification;
    • In-App messages;
    • deeplinks;
    • custom push data.

Required for Expo push setup and validation

Ask for these values only when they are missing, unclear, or cannot be detected automatically:

  • confirmation that Firebase / FCM is configured for Android, if Android validation is included;
  • confirmation that google-services.json belongs to the correct Firebase project;
  • confirmation that Android minSdkVersion can be set to 26 or higher;
  • confirmation that iOS push provider is configured as APNs or Firebase, if iOS validation is included;
  • confirmation whether iOS notificationService should be apns or firebase;
  • confirmation whether iOS mode should be development or production;
  • confirmation that GoogleService-Info.plist belongs to the correct Firebase project, if iOS uses Firebase / FCM;
  • confirmation that iOS devTeam is available when required;
  • confirmation that iOS App Groups can be configured;
  • confirmation that iOS app extensions can be configured, if EAS Build is used;
  • confirmation that the selected Reteno mobile app is configured with the correct token type;
  • confirmation that Reteno MCP is connected and available in the AI coding agent environment;
  • confirmation that the signed-in Reteno account has access to the selected Reteno app/environment;
  • access to the selected test environment for push validation:
    • Android emulator;
    • physical Android device;
    • iOS simulator, if supported and confirmed by the developer;
    • physical iOS device;
    • Expo development build;
    • EAS development build;
    • another supported environment;
  • confirmation from the developer that push validation is supported in the selected test environment;
  • confirmation that push notifications can be enabled in the selected test environment;
  • confirmation that the developer or QA engineer can open the test push notification if you cannot interact with the environment directly.

Required for EAS Build / app extension setup

Ask for these values only if iOS validation is included and the project uses EAS Build or managed credentials:

  • confirmation whether EAS Build is used;
  • eas.json build profile to use;
  • Apple Developer Team ID, if required;
  • iOS bundle identifier;
  • Notification Service Extension bundle identifier;
  • Notification Content Extension bundle identifier, if rich push UI is enabled;
  • App Group identifier;
  • confirmation that iOS app extensions can be defined in Expo config;
  • confirmation that EAS credentials / provisioning can be updated by the developer or admin.

Do not change EAS credentials, Apple Developer account settings, signing, provisioning, or app extension identifiers without explicit developer or admin confirmation.

Required for feature-specific custom event validation

Ask for these inputs when custom event validation is part of the current integration task:

  • confirmation that a test custom event can be added to the Expo app code;
  • confirmed event name for validation;
  • access to Reteno MCP, Reteno UI, backend data, event log, SDK logs, device logs, or API response to verify that the event was received and assigned to the correct contact.

Do not hardcode a required event name unless the developer confirms it.

Required for feature-specific In-App validation

Ask for these inputs only when In-App validation is part of the current integration task:

  • confirmation whether an In-App message already exists or should be created for validation;
  • existing In-App message name or ID, if an existing In-App message should be used;
  • confirmation whether the default In-App message is available in the selected Reteno account;
  • confirmed trigger type:
    • app open;
    • uniquely named event;
    • mobile-push + In-App;
    • another approved trigger;
  • confirmed trigger event name, if event-triggered In-App validation is part of the current integration task;
  • access to the selected test environment where the In-App message should appear;
  • confirmation that the developer, admin, or QA engineer can create, configure, publish, open, or click the In-App message manually if MCP cannot do it.

Do not create, update, publish, configure, attach, or send In-App messages without explicit developer or admin approval.

Required for feature-specific externalCustomerId validation

Ask for these values when the current integration task includes login, registration, user identification, or explicitly asks to validate externalCustomerId.

For externalCustomerId validation, ask for:

  • confirmation that login or registration flow should be tested;
  • test login or registration credentials, if required;
  • expected stable customer ID format;
  • confirmation that the agent, developer, user, or QA engineer can complete the login or registration flow.

Do not validate externalCustomerId unless this check is part of the current integration task.

Required for deeplink or custom push data validation

Ask for these values only when deeplink or custom push data validation is part of the current integration task:

  • expected deeplink format;
  • expected Expo Router / navigation behavior;
  • expected custom push payload fields;
  • expected destination screen or app behavior;
  • whether auto-open links should be enabled or disabled;
  • whether the test should cover foreground, background, and terminated app states.

Security rules

Do not include credentials, Firebase server credentials, service account data, APNs private keys, certificates, private keys, Reteno REST API keys, or other sensitive values in the prompt, public documentation, final report, or committed source files.

If a Reteno SDK access key is required, ask the developer to provide it through the approved project configuration method.

The Reteno SDK access key is a client-side SDK key, but it must still be handled according to the project policy.

Do not print, grep, cat, log, or otherwise expose SDK access keys, Firebase server credentials, APNs credentials, private keys, certificates, service account data, or other secrets in tool output.

When checking that a local config file exists or is loaded correctly:

  • verify file presence without printing its contents;
  • verify variable names without printing secret values;
  • use redacted output only;
  • never include full key values in logs, screenshots, terminal output, prompts, or final reports.

Do not run broad commands that may expose secrets, such as cat <secret-config-file>, grep -R RETENO ., grep -R API_KEY ., grep -R SERVICE_ACCOUNT ., or grep -R PRIVATE_KEY ., unless the command is scoped to show only filenames or redacted output.

If a secret is accidentally printed in your session output:

  • report the exposure immediately;
  • mark it as a security warning / action required;
  • recommend rotating the exposed key or credential in the relevant provider dashboard;
  • do not repeat or copy the exposed value in the final report.

Secret exposure through app config logs and diagnostics

Before and after adding Reteno configuration, inspect whether the app logs configuration objects that may now contain Reteno SDK access keys.

Existing app logging may accidentally expose SDK keys after Reteno values are added to app config.

Check for logging patterns such as:

  • console.log(appConfig);

  • console.warn(appConfig);

  • logging full environment objects;

  • logging full config objects;

  • logging SDK access keys directly;

  • shell commands that print local config files;

  • broad grep/cat commands that expose secret values.

If Reteno SDK access keys are logged:

  • redact the keys before logging;
  • do not remove unrelated app logging without developer approval;
  • do not repeat or copy the exposed value in the final report;
  • document that secret exposure happened;
  • recommend rotating the exposed keys in Reteno;
  • move production values into the approved secret/config store.

If a secret was printed in terminal output, tool output, Metro logs, device logs, screenshots, or prompts, treat it as exposed even if it was never committed to git.

Expected final report wording:

Secret exposure detected: Yes
Secret rotation recommended: Yes

Document:

  • which secret type was exposed;
  • where it was exposed:
    • app logs;
    • Metro logs;
    • device logs;
    • terminal output;
    • tool output;
    • screenshot;
    • prompt;
  • whether redaction was added;
  • whether rotation is still pending.

Firebase and APNs security distinction

Distinguish between client configuration and server credentials.

Client configuration may include:

  • Android Firebase client configuration;
  • Apple-platform Firebase client configuration;
  • Firebase client API keys.

Client configuration may be included in the repository only when this matches the project policy.

Server credentials must never be committed, printed, copied into prompts, or included in the final report.

Server credentials include:

  • Firebase Admin service account JSON;
  • APNs authentication keys;
  • APNs certificates;
  • private keys;
  • Reteno REST API credentials.

If Reteno server-side Firebase or APNs configuration is missing, ask the developer or admin to configure it in the Reteno dashboard. Do not ask the developer to paste server credentials into the chat.

Developer Preparation Checklist

Before starting Expo SDK integration, ask the developer to prepare or confirm the items below.

You must not ask for all values upfront if they can be detected from the project. However, the developer should make sure that the required project access, credentials, environment, and validation tools are available before the integration run starts.

1. Project access

The developer should provide or confirm:

  • access to the Expo project repository;
  • the correct branch for the integration run;
  • whether the current branch can be modified;
  • whether a temporary local branch should be created;
  • whether the working tree is clean or contains unrelated changes;
  • whether you are allowed to install dependencies;
  • whether you are allowed to run Expo CLI commands;
  • whether you are allowed to run npx expo prebuild.

Do not continue with file modifications if branch or working tree safety is unclear.

2. Expo workflow and native ownership

The developer should confirm:

  • Expo workflow type:
    • Expo managed without committed native folders;
    • Expo prebuild / CNG with committed native folders;
    • bare React Native with Expo modules;
    • bare React Native without Expo workflow;
  • whether native ios/ and android/ folders are committed;
  • whether native folders are generated and should not be edited manually;
  • whether npx expo prebuild is allowed;
  • whether npx expo prebuild --clean is allowed;
  • whether manual native changes may be overwritten by prebuild;
  • which Expo build profile should be used.

Do not run prebuild or regenerate native folders without explicit developer approval.

3. Reteno app and SDK access key

The developer should confirm:

  • selected Reteno organization / workspace;
  • selected Reteno app/environment;
  • whether the Reteno app is for Android, iOS, or both;
  • token type configured for each selected Reteno app:
    • Android: FCM;
    • iOS: APNs or FCM;
  • whether development and production apps are separated;
  • approved method for providing the Reteno SDK access key;
  • whether debug mode may be enabled during the integration run.

Do not hardcode the SDK access key in committed files.

4. Firebase / FCM / APNs setup

For Android validation, the developer should confirm:

  • Firebase project;
  • Android package name / applicationId;
  • google-services.json;
  • Reteno server-side Firebase configuration;
  • Android test device or emulator;
  • Android minSdkVersion can be set to 26 or higher.

For iOS validation, the developer should confirm:

  • iOS bundle ID;
  • push route:
    • direct APNs;
    • Firebase / FCM with APNs transport;
  • mode: development or production;
  • notificationService: apns or firebase;
  • GoogleService-Info.plist, if Firebase / FCM is used;
  • Firebase APNs credentials, if Firebase / FCM is used;
  • APNs sandbox / production route, if direct APNs is used;
  • Apple Developer Team ID, if required;
  • Push Notifications capability;
  • App Groups capability;
  • Notification Service Extension setup;
  • Notification Content Extension setup, if rich push UI is enabled.

5. EAS Build and iOS app extensions

If EAS Build is used, the developer should confirm:

  • EAS project ID;
  • eas.json build profile;
  • whether managed credentials are used;
  • whether iOS app extensions must be defined in Expo config;
  • Notification Service Extension bundle identifier;
  • Notification Content Extension bundle identifier, if used;
  • App Group identifier;
  • whether signing/provisioning for app extensions can be updated.

Do not change EAS credentials or Apple Developer account settings automatically.

6. Test environment

The developer should confirm the selected test environment:

  • Android emulator;
  • physical Android device;
  • iOS simulator, if supported and confirmed by the developer;
  • physical iOS device;
  • local Expo development build;
  • EAS development build;
  • another supported environment.

Expo Go is not a valid Reteno SDK validation environment.

The developer should confirm that:

  • the app can be built and installed;
  • the app can request push permission;
  • push notifications can be enabled;
  • the developer or QA engineer can open the delivered test push notification if you cannot interact with the device directly;
  • the developer or QA engineer can complete login or registration if user identification validation is required.

7. Reteno MCP

The developer should confirm:

  • Reteno MCP is connected in the same environment where the AI coding agent runs;
  • the signed-in Reteno account has access to the selected organization / workspace;
  • the signed-in Reteno account has access to the selected Reteno app;
  • MCP capabilities can be discovered before validation;
  • manual Reteno UI fallback is available if a required MCP capability is missing.

Do not mark the integration as complete if Reteno-side validation is blocked by missing MCP access.

8. Build and runtime logs

If you cannot build or run the app after safe diagnostics, or if the issue requires local environment access, the developer should provide:

  • exact Expo CLI / EAS / native build error;
  • Metro logs;
  • device, simulator, or emulator details;
  • EAS build link or local build logs, if applicable;
  • Xcode logs, if iOS build fails;
  • Gradle logs, if Android build fails;
  • reproduction steps;
  • confirmation whether the issue is pre-existing or introduced by the integration.

Expo SDK Integration Workflow

Follow this workflow for Expo SDK integration.

1. Inspect the project

Before making changes, inspect the Expo project.

Identify:

  • project structure;
  • package manager;
  • Expo SDK version;
  • app entry point;
  • Expo Router usage;
  • app config file:
    • app.json;
    • app.config.js;
    • app.config.ts;
  • existing Expo config plugins;
  • eas.json;
  • native ios/ and android/ folders;
  • whether native folders are committed;
  • existing Firebase / FCM setup;
  • existing google-services.json;
  • existing GoogleService-Info.plist;
  • existing push notification setup;
  • existing deeplink handling;
  • existing authentication or user identification flow;
  • existing In-App setup, if any;
  • files that may need to be updated.

Do not ask the developer for information that can be detected from project files.

2. Confirm Expo workflow type

Classify the project before installing or configuring the SDK.

Use one of these classifications:

  • Expo managed without committed native folders;
  • Expo prebuild / CNG with committed native folders;
  • bare React Native with Expo modules;
  • bare React Native without Expo workflow.

If the project is bare React Native with Expo modules, do not automatically use the Expo runbook. Ask whether to continue with React Native runbook and apply Expo-specific checks only where needed.

If the project is Expo managed without committed native folders, use config plugin setup and do not edit native files directly unless prebuild is explicitly approved.

If the project uses Expo prebuild / CNG with committed native folders, use config plugin setup first and modify native files only when the official setup and developer approval require it.

3. Check whether Reteno Expo SDK is already installed

If expo-reteno-sdk is already installed, do not reinstall it from scratch.

Instead:

  • detect the current SDK version from package.json, lockfile, or installed package metadata;
  • check the current config plugin setup;
  • check whether Android setup is complete;
  • check whether iOS setup is complete;
  • check whether initialization is automatic or JavaScript-controlled;
  • check whether push handling is configured;
  • check whether current integration is incomplete, outdated, or misconfigured.

If expo-reteno-sdk is not installed, continue with a new Expo SDK integration flow.

4. Check SDK version and release notes

Before installing, updating, or troubleshooting the Reteno Expo SDK:

  • detect the currently installed expo-reteno-sdk version from package.json and the active lockfile;
  • check official Expo SDK setup documentation;
  • check Expo SDK Release Notes, if available;
  • check the package registry;
  • check package metadata;
  • inspect the upstream Reteno Expo SDK repository only when repository access is available and useful for the current issue;
  • check whether the current problem may already be fixed upstream but not yet published.

Distinguish between:

  • installed package version;
  • latest published stable package version;
  • version declared in official documentation;
  • current upstream repository version or unreleased source state.

Do not assume that the version present in the upstream SDK repository is already published to the package registry.

Do not call an installed package version latest unless the package registry or another authoritative published-package source confirms it.

If the installed package, package registry, documentation, release notes, and upstream repository show different versions:

  • document each version separately;
  • do not automatically upgrade or downgrade the application;
  • determine whether the newer upstream change is published or unreleased;
  • ask the developer or Expo platform owner before changing the selected SDK version.

If a required fix exists upstream but has not yet been published:

  • document the upstream fix;
  • do not switch the project to an unpublished SDK source automatically;
  • use a developer-approved temporary patch only when validation requires it;
  • plan to remove the temporary patch after the official fixed package is released and adopted.

Use the latest stable published expo-reteno-sdk version approved for the current project.

Do not use pre-release, beta, alpha, release-candidate, local repository, Git branch, or unpublished package versions unless the developer explicitly approves them.

Document in the final report:

  • installed expo-reteno-sdk version;
  • latest published package version, when verified;
  • upstream repository version or fix state, when inspected;
  • whether a published/upstream mismatch exists;
  • whether the integration uses a temporary patch;
  • recommended upgrade or patch-removal follow-up.

5. Install the Expo Reteno SDK

Install expo-reteno-sdk using the existing project package manager.

Use the detected package manager.

Do not switch package managers.

Do not generate another package manager's lockfile.

Examples:

npm install expo-reteno-sdk
yarn add expo-reteno-sdk

After installation:

  • verify package.json;
  • verify the relevant lockfile;
  • verify that no unrelated package manager lockfile was created;
  • run the project’s package install command only when required.

6. Configure the Expo plugin

Configure expo-reteno-sdk in the Expo config file used by the project:

  • app.json;
  • app.config.js;
  • app.config.ts.

Preserve existing plugins.

Do not overwrite existing config plugin entries.

Add Reteno plugin configuration in the existing plugins array.

Before editing, inspect whether the project uses:

  • static config;
  • dynamic config;
  • environment-specific config;
  • app variants;
  • EAS build profiles;
  • platform-specific config branches.

6.1. Configure Firebase client files through Expo config

For Expo managed / CNG apps, configure Firebase client files in Expo app config.

Do not rely only on files copied into generated native folders.

Use:

{
  "expo": {
    "android": {
      "googleServicesFile": "./google-services.json"
    },
    "ios": {
      "googleServicesFile": "./GoogleService-Info.plist"
    }
  }
}

Use only the platform entries required by the current integration task.

For Android validation:

  • verify expo.android.googleServicesFile;
  • verify that the file exists;
  • verify that it matches the effective Android application ID;
  • verify that generated native Android config matches the resolved Expo config after prebuild.

For iOS validation with Firebase / FCM:

  • verify expo.ios.googleServicesFile;
  • verify that the file exists;
  • verify that it matches the effective iOS bundle ID;
  • verify that Firebase has valid APNs credentials;
  • verify that Reteno uses the corresponding Firebase project.

If native folders already contain Firebase files but Expo config does not reference them, document this as a configuration risk.

If prebuild --clean or native folder regeneration is used, verify Firebase files again after regeneration.

7. Choose initialization path

Choose one initialization path per platform.

Do not configure both automatic initialization and JavaScript-controlled initialization for the same platform unless official documentation and developer confirmation require it.

7.1. Initialization path switching

Switching between plugin automatic initialization and JavaScript-controlled initialization is not a JavaScript-only change.

If switching from Path A to Path B, or from Path B to Path A:

  • get explicit developer approval;
  • run an approved prebuild;
  • inspect generated native files;
  • verify that stale native SDK access keys were removed;
  • verify that stale native initialization config was removed;
  • verify that Reteno initializes exactly once;
  • verify that sdkAccessToken is not present in generated native config when JavaScript-controlled initialization is selected;
  • verify that Reteno.initialize(...) is not used as the active initialization path when plugin auto-init is selected.

Use npx expo prebuild --clean only as a fallback when normal prebuild does not remove stale generated configuration.

Do not run npx expo prebuild --clean by default.

Document the previous initialization path, new initialization path, prebuild command, and stale native config check in the final report.

7.2. Canonical setup and validation sequence

Use this canonical sequence for Expo setup and validation:

  1. Resolve Expo config and detect workflow.
  2. Choose one initialization path.
  3. Initialize Reteno or verify plugin auto-initialization.
  4. Register required listeners for the current integration task.
  5. Register for remote notifications.
  6. Perform the test action.
  7. Call Reteno.forcePushData() before immediate backend, Reteno UI, or MCP validation.
  8. Validate through reliable evidence:
    • Reteno MCP;
    • Reteno UI;
    • backend data;
    • SDK logs;
    • device logs;
    • redacted network logs;
    • manual confirmation.

Do not validate immediately after a test action without flushing queued SDK data when immediate evidence is required.

Path A: Automatic initialization through config plugin

Use this path when:

  • SDK access key can be placed in platform plugin configuration according to project policy;
  • default initialization options are acceptable;
  • the developer approves plugin-based initialization.

Expected result:

  • Reteno plugin receives the platform sdkAccessToken;
  • plugin writes required native configuration during prebuild;
  • Reteno initializes automatically with default options.

Path B: JavaScript-controlled initialization

Use this path when:

  • runtime initialization options are required;
  • SDK access key must come from app runtime config;
  • the app needs to control isDebugMode, lifecycle tracking, session duration, or platform-specific token mode;
  • the developer does not want the access key in platform plugin config.

Expected result:

  • platform plugin config omits sdkAccessToken;
  • the app calls Reteno.initialize(...) once from a stable app startup path;
  • initialization happens before Reteno feature usage;
  • initialization does not duplicate automatic plugin initialization.

If sdkAccessToken is set in plugin config, do not also rely on a later Reteno.initialize(...) call for initialization options unless official docs confirm the intended behavior.

8. Configure Android, if Android validation is included

Follow Android-specific Expo setup.

9. Configure iOS, if iOS validation is included

Follow iOS-specific Expo setup.

10. Configure push handling

Follow Expo push handling.

11. Configure user identification, if required

Follow Expo user identification.

12. Run prebuild only when approved

Run npx expo prebuild only when the developer explicitly approves it.

Before running prebuild, report:

  • whether native folders already exist;
  • whether native folders are committed;
  • whether prebuild may overwrite manual native changes;
  • which platform will be prebuilt:
    • Android;
    • iOS;
    • both;
  • whether --clean is needed;
  • why prebuild is required.

Use the narrowest platform prebuild command possible:

npx expo prebuild --platform android
npx expo prebuild --platform ios

Do not run:

npx expo prebuild --clean

unless the developer explicitly approves it.

12.1. Preserve required fixes across Expo prebuild

For Expo managed / CNG projects, treat generated ios/ and android/ folders as reproducible build output unless the repository explicitly commits and owns those native folders.

Do not assume that a successful manual edit inside generated native files is a durable integration fix.

Before modifying generated native files, determine whether the change must survive the next:

npx expo prebuild

or:

npx expo prebuild --clean

Examples of generated native changes that may be overwritten include:

  • AndroidManifest changes;
  • Gradle changes;
  • iOS Podfile changes;
  • Xcode project settings;
  • signing configuration;
  • provisioning-profile assignment;
  • App Group configuration;
  • Notification Service Extension configuration;
  • Notification Content Extension configuration;
  • native Reteno plugin workarounds.

If a required change must survive prebuild, prefer one of these project-approved durable mechanisms:

  • Expo config;
  • Expo config plugin;
  • existing project generation script;
  • package-manager-supported package patch;
  • another existing reproducible project configuration mechanism.

Do not convert a temporary generated-native validation change into a permanent project change without developer approval.

If a required native change cannot be represented durably during the current integration run:

  • document the native file changed;
  • document that the file is generated;
  • document that the change will be lost after prebuild;
  • document the exact regeneration condition;
  • mark the fix as temporary / validation-only;
  • report whether it must be reapplied manually;
  • do not report the branch as fully merge-ready unless the developer accepts the non-durable setup.

After every approved prebuild:

  1. re-check generated Android and iOS configuration;
  2. verify that required Reteno configuration is still present;
  3. verify signing and extension configuration when applicable;
  4. verify push-handler conflict fixes;
  5. verify Firebase configuration;
  6. verify that any approved package/config-plugin fix was applied again.

Expected result:

  • required Reteno integration changes are reproducible after prebuild;
  • generated-only manual edits are clearly identified;
  • future prebuild does not silently remove a required Reteno fix.

13. Offline generated-config verification

After approved prebuild or native generation, verify generated configuration before runtime validation.

Do this without printing secrets.

Check Android generated configuration:

  • resolved Expo config;
  • generated android/ folder state;
  • android/app/google-services.json;
  • Android package name / applicationId;
  • AndroidManifest metadata;
  • Reteno SDK access key metadata, redacted;
  • Reteno debug metadata;
  • Firebase / FCM service configuration;
  • Gradle dependencies;
  • Gradle Kotlin DSL files, if present;
  • invalid Groovy syntax in .gradle.kts, if Kotlin DSL is used.

Check iOS generated configuration:

  • resolved Expo config;
  • generated ios/ folder state;
  • GoogleService-Info.plist;
  • iOS bundle ID;
  • Info.plist Reteno metadata, redacted;
  • mode;
  • notificationService;
  • iosDeviceTokenHandlingMode;
  • App Groups;
  • Notification Service Extension;
  • Notification Content Extension, if configured;
  • extension deployment target;
  • Podfile;
  • CocoaPods resolution;
  • EAS app extension configuration, if EAS Build is used.

Check push-handler conflicts:

  • expo-notifications;
  • Firebase Messaging;
  • custom native push handlers;
  • existing foreground/background listeners;
  • existing notification response handlers;
  • existing deeplink/navigation handlers.

Do not proceed to runtime validation until generated configuration matches the selected setup path or the mismatch is documented as a blocker.

14. Build and run the application

Use the selected supported test environment.

Do not use Expo Go.

Possible build/run commands may include:

npx expo run:android
npx expo run:ios
eas build --profile development --platform android
eas build --profile development --platform ios

Use the project’s existing scripts when available.

If build/run fails, do not repeat the same failed action without a new hypothesis.

Continue safe diagnostics while distinct testable causes remain.

Escalate when the issue requires local environment access, external access, credentials, signing, EAS credentials, Apple Developer account access, Firebase server configuration, Reteno dashboard changes, GUI interaction, local automation, or manual device actions.

When escalation is required, ask the developer to run the app through Expo CLI, EAS Build, Android Studio, Xcode, Gradle, CocoaPods, or the relevant project tool and provide:

  • exact build or runtime error;
  • crash reason, if available;
  • Metro logs;
  • EAS build link or local build logs, if applicable;
  • relevant native logs;
  • device, simulator, or emulator details;
  • reproduction steps.

15. Continue validation

After the app builds and runs, continue with MCP Capability Discovery and Validation.

Do not mark the integration as complete until required validation checks pass or a blocker is clearly documented.

Android-specific Expo setup

Use this section when Android validation is included.

Follow the official Reteno Expo Android SDK setup.

1. Verify Android prerequisites

Before Android setup, verify:

  • Android package name / applicationId;
  • google-services.json;
  • Firebase / FCM setup;
  • Reteno app configured for Android / FCM;
  • android.minSdkVersion;
  • whether native android/ folder exists;
  • whether native android/ folder is committed;
  • whether prebuild is approved;
  • selected Android test environment.

1.1. Kotlin DSL blocker check

Before applying or trusting generated Android plugin configuration, detect whether the Android project uses Kotlin DSL:

build.gradle.kts
settings.gradle.kts

If Kotlin DSL is detected, treat Android Reteno Expo setup as requiring manual / SME review before unattended changes.

Reason:

  • Expo config plugins may generate Groovy-style Gradle snippets;
  • Kotlin DSL files may reject Groovy syntax;
  • dependencies may be skipped or injected incorrectly;
  • generated Android build may fail even if Expo config looks correct.

You must:

  • stop before manual Gradle edits;
  • document Kotlin DSL detection;
  • ask Android / Expo SME to confirm the safe setup path;
  • verify generated Gradle files after approved prebuild;
  • verify resolved dependencies from build logs or generated native project.

Do not hardcode stale native dependency versions.

Use dependency versions bundled by the selected expo-reteno-sdk version.

1.2. Native dependency version resolution

Do not hardcode native Android or iOS dependency versions from older examples.

Do not use stale bundled native SDK versions in this Expo runbook.

Use the native dependency versions bundled by the selected expo-reteno-sdk version.

Verify resolved native dependency versions through one of these sources:

  • generated native project;
  • build logs;
  • lockfiles;
  • official release notes;
  • package metadata;
  • developer / SME confirmation.

If the generated native project uses a different native dependency version than expected, document the resolved version in the final report and ask the Expo SME to confirm whether it is valid.

2. Android minSdkVersion

Reteno Expo Android validation requires Android API level 26 or higher.

Do not use this invalid Expo config shape:

{
  "expo": {
    "android": {
      "minSdkVersion": 26
    }
  }
}

For Expo managed / CNG apps, configure Android minSdkVersion through expo-build-properties.

Install expo-build-properties only when it is missing and the developer approves dependency changes:

npx expo install expo-build-properties

Example Expo config:

{
  "expo": {
    "plugins": [
      [
        "expo-build-properties",
        {
          "android": {
            "minSdkVersion": 26
          }
        }
      ],
      [
        "expo-reteno-sdk",
        {
          "android": {}
        }
      ]
    ]
  }
}

Expected result:

  • expo-build-properties is present when Android minSdkVersion must be controlled by Expo config;
  • Android minSdkVersion resolves to 26 or higher after prebuild;
  • generated native Android project reflects the expected minSdkVersion;
  • the final report documents the detected and resolved minSdkVersion.

Do not raise minSdkVersion without explicit developer approval.

Treat a minSdkVersion increase as a product compatibility decision.

If the app must support Android versions below API 26, report that Reteno SDK validation cannot pass on those runtimes.

3. Android config plugin setup

Configure the Reteno Expo plugin in the project’s Expo config.

Use Path A or Path B.

Path A: Automatic initialization

Use this path only if the developer approves plugin-based SDK access key configuration.

Example structure:

{
  "expo": {
    "plugins": [
      [
        "expo-reteno-sdk",
        {
          "android": {
            "sdkAccessToken": "YOUR_SDK_ACCESS_KEY",
            "config": {
              "isDebugMode": false
            }
          }
        }
      ]
    ],
    "android": {
      "minSdkVersion": 26
    }
  }
}

Do not commit a real SDK access key unless this matches the project’s approved policy.

If the SDK access key cannot be committed, use a dynamic Expo config or JavaScript-controlled initialization.

Path B: JavaScript-controlled initialization

Use this path when the SDK access key should be provided at runtime or through the app’s approved config method.

Example structure:

{
  "expo": {
    "plugins": [
      [
        "expo-reteno-sdk",
        {
          "android": {}
        }
      ]
    ],
    "android": {
      "minSdkVersion": 26
    }
  }
}

Then initialize Reteno once from a stable app startup path:

import Reteno from 'expo-reteno-sdk';

await Reteno.initialize({
  apiKey: 'YOUR_SDK_ACCESS_KEY',
  isDebugMode: false,
  lifecycleTrackingOptions: 'ALL'
});

Do not initialize Reteno multiple times.

Do not leave SDK key placeholders in final code.

4. Android Firebase config

Verify that google-services.json exists and belongs to the correct Firebase project and Android application ID.

Expected path after prebuild:

android/app/google-services.json

If the file is missing or mismatched:

  • do not invent one;
  • ask the developer to provide the correct Firebase client config;
  • document the blocker in the final report.

Do not ask the developer to paste Firebase Admin service account JSON into the repository or prompt.

5. Android prebuild

Run Android prebuild only after developer approval:

npx expo prebuild --platform android

Expected result:

  • native Android project exists or is updated;
  • Gradle dependencies are configured by the plugin;
  • Reteno initialization is configured according to selected path;
  • Android broadcast receivers / FCM service configuration are generated by the plugin where applicable.

If the project uses Kotlin DSL and plugin patches do not fully apply, document the issue and ask the developer or Android SME to confirm the required manual Gradle setup.

Do not manually duplicate dependencies unless official docs, generated config, and developer/SME confirmation require it.

6. Android development build

Build and run with:

npx expo run:android

or with the project’s existing development build command.

Expo Go is not supported for Reteno SDK validation.

Expected result:

  • app builds;
  • app installs;
  • app launches;
  • SDK initializes;
  • contact is created;
  • mobilePushToken is assigned;
  • token type is FCM;
  • test push can be sent and validated.

iOS-specific Expo setup

Use this section when iOS validation is included.

Follow the official Reteno Expo iOS SDK setup.

1. Verify iOS prerequisites

Before iOS setup, verify:

  • iOS bundle identifier;
  • push route:
    • direct APNs;
    • Firebase / FCM with APNs transport;
  • mode: development or production;
  • notificationService: apns or firebase;
  • Apple Developer Team ID, if required;
  • App Group identifier;
  • Push Notifications capability;
  • App Groups capability;
  • Notification Service Extension setup;
  • Notification Content Extension setup, if rich push UI is enabled;
  • GoogleService-Info.plist, if Firebase / FCM is used;
  • whether EAS Build is used;
  • whether app extensions must be defined in Expo config.

2. iOS config plugin setup

Configure the Reteno Expo plugin in the project’s Expo config.

Use Path A or Path B.

Path A: Automatic initialization

Use this path only if the developer approves plugin-based SDK access key configuration.

Example structure:

{
  "expo": {
    "plugins": [
      [
        "expo-reteno-sdk",
        {
          "ios": {
            "sdkAccessToken": "YOUR_SDK_ACCESS_KEY",
            "mode": "development",
            "notificationService": "apns",
            "devTeam": "XXXXXXXXXX",
            "appGroups": ["group.com.your.bundleid.reteno-local-storage"],
            "config": {
              "isDebugMode": false
            }
          }
        }
      ]
    ]
  }
}

Use mode: "development" for debug, simulator, and development builds.

Use mode: "production" for TestFlight and App Store builds.

Do not commit a real SDK access key unless this matches the project’s approved policy.

Path B: JavaScript-controlled initialization

Use this path when runtime initialization options are required or when the SDK access key should come from an approved runtime config method.

Example structure:

{
  "expo": {
    "plugins": [
      [
        "expo-reteno-sdk",
        {
          "ios": {
            "mode": "development",
            "notificationService": "apns",
            "devTeam": "XXXXXXXXXX",
            "appGroups": ["group.com.your.bundleid.reteno-local-storage"]
          }
        }
      ]
    ]
  }
}

Then initialize Reteno once from a stable app startup path:

import Reteno from 'expo-reteno-sdk';

await Reteno.initialize({
  apiKey: 'YOUR_SDK_ACCESS_KEY',
  isDebugMode: false,
  lifecycleTrackingOptions: 'ALL',
  sessionDurationSeconds: 30,
  pauseInAppMessages: false,
  iosDeviceTokenHandlingMode: 'automatic'
});

If ios.sdkAccessToken is set in plugin config, do not rely on a later Reteno.initialize(...) call for runtime initialization options unless official documentation confirms this behavior.

Path B: JavaScript-controlled initialization for iOS Firebase / FCM

Use this path when iOS uses Firebase / FCM and the SDK access key or initialization options must be controlled from JavaScript.

For iOS Firebase / FCM, use:

{
  "expo": {
    "ios": {
      "googleServicesFile": "./GoogleService-Info.plist"
    },
    "plugins": [
      [
        "expo-reteno-sdk",
        {
          "ios": {
            "mode": "development",
            "notificationService": "firebase",
            "appGroups": ["group.com.your.bundleid.reteno-local-storage"]
          }
        }
      ]
    ]
  }
}

Then initialize Reteno once from a stable app startup path:

import Reteno from 'expo-reteno-sdk';

await Reteno.initialize({
  apiKey: 'YOUR_SDK_ACCESS_KEY',
  isDebugMode: false,
  lifecycleTrackingOptions: 'ALL',
  sessionDurationSeconds: 30,
  pauseInAppMessages: false,
  iosDeviceTokenHandlingMode: 'manual'
});

For iOS Firebase / FCM, iosDeviceTokenHandlingMode must be "manual" unless official documentation and the developer explicitly confirm another mode.

After Firebase Messaging obtains an FCM token, pass it to Reteno:

import { useEffect } from 'react';
import { Platform } from 'react-native';
import messaging from '@react-native-firebase/messaging';
import Reteno from 'expo-reteno-sdk';

useEffect(() => {
  if (Platform.OS !== 'ios') return;

  messaging().getToken().then((token) => {
    Reteno.setDeviceToken(token);
  });

  return messaging().onTokenRefresh((token) => {
    Reteno.setDeviceToken(token);
  });
}, []);

iOS Firebase Messaging registration before getToken()

For iOS Firebase / FCM token flow, verify whether Firebase Messaging must register for remote messages before reading the FCM token.

On a physical iOS device, FCM token retrieval may fail with:

messaging/unregistered: You must be registered for remote messages before calling getToken

If this error occurs, register the device for remote messages before calling getToken(...).

Use the API style that matches the installed React Native Firebase version and the existing project code.

Before selecting the Firebase Messaging API shape, detect the installed @react-native-firebase/messaging version.

Do not copy the namespaced/default API example without checking whether that API is supported by the installed version.

For React Native Firebase versions where the namespaced/default Messaging API has been removed, use the modular API.

Example modular flow:

import {
  getMessaging,
  getToken,
  onTokenRefresh,
  registerDeviceForRemoteMessages,
} from '@react-native-firebase/messaging';

import Reteno from 'expo-reteno-sdk';

const messagingInstance = getMessaging();

await registerDeviceForRemoteMessages(messagingInstance);

const token = await getToken(messagingInstance);

await Reteno.setDeviceToken(token);

const unsubscribe = onTokenRefresh(messagingInstance, async (newToken) => {
  await Reteno.setDeviceToken(newToken);
});

Use the namespaced API only when the installed React Native Firebase version still supports it and the existing application uses that API style.

Do not migrate unrelated Firebase application code between namespaced and modular APIs only for stylistic consistency.

If the installed React Native Firebase version requires modular APIs:

  • use modular getMessaging(...);
  • use modular registerDeviceForRemoteMessages(...);
  • use modular getToken(...);
  • use modular onTokenRefresh(...) when token refresh handling is required.

Document the installed @react-native-firebase/messaging version and selected API style when the iOS Firebase / FCM flow is part of the integration.

Example — namespace API shape:

import messaging from '@react-native-firebase/messaging';
import Reteno from 'expo-reteno-sdk';

await messaging().registerDeviceForRemoteMessages();

const token = await messaging().getToken();

await Reteno.setDeviceToken(token);

Example — modular API shape:

import {
  getMessaging,
  getToken,
  registerDeviceForRemoteMessages
} from '@react-native-firebase/messaging';
import Reteno from 'expo-reteno-sdk';

const messagingInstance = getMessaging();

await registerDeviceForRemoteMessages(messagingInstance);

const token = await getToken(messagingInstance);

await Reteno.setDeviceToken(token);

Expected iOS Firebase / FCM sequence:

  1. Firebase is configured.
  2. Firebase Messaging registers the device for remote messages, when required.
  3. Firebase Messaging obtains an FCM token.
  4. The app passes the FCM token to Reteno through Reteno.setDeviceToken(...).
  5. Reteno receives an FCM token, not an APNs token.

Use this only for the iOS Firebase / FCM route.

Do not use this as a reason to switch the selected push route without developer confirmation.

Document in the final report:

  • whether registerDeviceForRemoteMessages(...) was required;
  • whether it was called before getToken(...);
  • whether FCM token retrieval succeeded;
  • whether Reteno.setDeviceToken(...) was called;
  • whether Reteno received FCM, not APNs.

Expected result:

  • notificationService is firebase;
  • iosDeviceTokenHandlingMode is manual;
  • GoogleService-Info.plist is configured through expo.ios.googleServicesFile;
  • Firebase obtains an FCM token;
  • the FCM token is passed to Reteno through Reteno.setDeviceToken(...);
  • Reteno contact receives token type FCM, not APNs.

Do not mark iOS Firebase / FCM push setup as complete until Reteno-side evidence confirms that the assigned mobilePushToken token type is FCM.

If Reteno receives an APNs token instead of an FCM token, report the iOS FCM path as failed or blocked and inspect token handling mode, Firebase setup, and Reteno.setDeviceToken(...).

2.1 iOS devTeam requirement

devTeam is conditionally required.

Require or verify devTeam only when the effective Xcode / EAS signing setup requires it.

If signing is handled by EAS managed credentials or existing Xcode project settings, do not force a new devTeam value unless:

  • the build fails because the team is missing;
  • the selected config plugin setup requires it;
  • the developer confirms that it is needed.

Document whether devTeam was required, detected, omitted, or confirmed through signing configuration.

3. iOS App Group naming

Use the required Reteno shared-storage App Group format:

group.<main-app-bundle-id>.reteno-local-storage

Use the main app bundle ID.

Do not use the Notification Service Extension bundle ID.

Do not add an extension suffix.

Example:

Main app bundle ID:
com.example.app

Required Reteno App Group:
group.com.example.app.reteno-local-storage

Apply the same App Group to:

  • the main app target;
  • Notification Service Extension;
  • Notification Content Extension, if created.

Expected result:

  • all required targets share the same Reteno App Group;
  • the App Group uses the main app bundle ID;
  • the App Group does not use the extension bundle ID;
  • Reteno shared storage works between app and extensions.

4. iOS Notification Service Extension and Content Extension

The Expo Reteno plugin may create or configure:

  • Notification Service Extension;
  • Notification Content Extension for rich push UI.

Treat Notification Service Extension and Notification Content Extension as separate validation requirements.

For core mobile push validation:

  • Notification Service Extension may be required for Reteno delivery/rich-notification processing;
  • Notification Content Extension is required only when the current integration task includes rich push UI that depends on it.

If Notification Content Extension exists but cannot be signed or provisioned in the current validation environment:

  • do not automatically fail core mobile push validation;
  • determine whether rich push UI is part of the requested validation scope;
  • preserve or temporarily exclude the Content Extension only with developer approval;
  • verify that Notification Service Extension remains correctly configured;
  • continue core DELIVERED and CLICKED validation when the missing Content Extension does not affect that flow;
  • report the Content Extension provisioning issue as a warning.

If rich push UI validation is requested:

  • Notification Content Extension must build, sign, install, and function correctly;
  • missing provisioning or signing for the Content Extension is a blocker for rich push UI validation.

Do not report a missing Notification Content Extension provisioning profile as proof that the complete Reteno push integration failed when:

  • Notification Service Extension is valid;
  • the app builds and installs;
  • the push notification is delivered;
  • the notification can be opened;
  • DELIVERED and CLICKED are validated.

Document separately:

  • Notification Service Extension status;
  • Notification Content Extension status;
  • whether Content Extension was included, detached, or excluded from the validation build;
  • reason for exclusion;
  • whether rich push UI was requested;
  • impact on core push validation;
  • required production follow-up.

Do not create or modify app extensions manually unless:

  • official setup requires it;
  • prebuild has generated native folders;
  • developer approves native changes;
  • EAS Build configuration is updated when required.

If the project uses EAS Build with managed credentials, define app extensions in Expo config so extension signing and provisioning can succeed.

Document:

  • extension target names;
  • extension bundle identifiers;
  • App Group entitlements;
  • whether EAS app extension config was added;
  • whether extension signing/provisioning was verified.

5. Direct APNs setup

For direct APNs, use:

{
  "notificationService": "apns"
}

Verify:

  • selected Reteno app is configured for APNs;
  • token type is APNs;
  • mode matches the build:
    • development for debug/simulator/development builds;
    • production for TestFlight/App Store builds;
  • APNs sandbox / production route is confirmed.

Do not treat missing DELIVERED as an SDK integration issue until APNs sandbox/production routing is confirmed.

6. Firebase / FCM setup on iOS

For Firebase / FCM on iOS, use:

{
  "notificationService": "firebase"
}

Verify:

  • Firebase package is installed if required by the selected setup;
  • GoogleService-Info.plist belongs to the correct Firebase project;
  • Firebase has valid APNs credentials;
  • Reteno uses the corresponding Firebase project;
  • APNs and FCM tokens are not mixed.

If Firebase / FCM is used on iOS, pass the FCM token to Reteno when required by the official setup:

import { useEffect } from 'react';
import { Platform } from 'react-native';
import messaging from '@react-native-firebase/messaging';
import Reteno from 'expo-reteno-sdk';

useEffect(() => {
  if (Platform.OS !== 'ios') return;

  messaging().getToken().then((token) => {
    Reteno.setDeviceToken(token);
  });

  return messaging().onTokenRefresh((token) => {
    Reteno.setDeviceToken(token);
  });
}, []);

Do not mark iOS push setup as complete until the selected token path is verified.

6.1. Verify that the Firebase route does not overwrite the FCM token with a raw APNs token

For iOS configurations that use:

notificationService: firebase

the final Reteno mobilePushToken must remain an FCM token.

Do not assume that a successful Firebase getToken(...) call or an application-level Reteno.setDeviceToken(...) call proves that the final token stored by Reteno remains FCM.

For the selected expo-reteno-sdk version, inspect the generated iOS notification-registration path when:

  • Firebase returns a valid FCM token;
  • Reteno.setDeviceToken(...) is called with that FCM token;
  • Reteno contact later contains an APNs-format token;
  • contact creation and event tracking work, but mobile push does not deliver.

Inspect the generated AppDelegate or plugin-generated iOS code for remote-notification token forwarding.

For affected SDK versions, check whether:

application(
  _:didRegisterForRemoteNotificationsWithDeviceToken:
)

passes the raw APNs device token directly to Reteno through a call such as:

Reteno.userNotificationService.processRemoteNotificationsToken(...)

while the selected route is Firebase / FCM.

Also verify whether Firebase Messaging separately obtains the FCM token through its registration-token callback.

If the Firebase route sends both:

  • the FCM token to Reteno; and
  • the raw APNs token directly to Reteno,

the later APNs update may overwrite the correct FCM token.

Expected result for the Firebase route:

  • Firebase receives the APNs token for APNs-to-FCM mapping;
  • Firebase Messaging obtains an FCM token;
  • Reteno receives the FCM token;
  • the final Reteno mobilePushToken is FCM format;
  • the raw APNs token does not overwrite the Reteno FCM token;
  • test push delivery succeeds.

Do not apply a package or native-code patch only because this runbook describes this historical issue.

First verify:

  • selected expo-reteno-sdk version;
  • generated iOS code;
  • final Reteno token type;
  • whether the issue reproduces in the current project.

If the issue is confirmed for the selected SDK version:

  • document it as an SDK-version-specific defect;
  • prefer an official fixed SDK version when available and approved;
  • if no fixed version is available, use a developer-approved durable package patch;
  • do not rely on a manual edit inside generated ios/ files as the permanent fix.

Document in the final report:

  • selected expo-reteno-sdk version;
  • expected token type;
  • token type before the fix;
  • token type after the fix;
  • whether generated native code forwarded the raw APNs token to Reteno;
  • whether a local package patch was required;
  • whether an upstream fix is available;
  • whether the local patch should be removed after upgrading.

7. iOS prebuild and pods

Run iOS prebuild only after developer approval:

npx expo prebuild --platform ios

Then install pods when required:

npx pod-install

Expected result:

  • native iOS project exists or is updated;
  • Notification Service Extension is configured;
  • entitlements are configured;
  • App Groups are configured;
  • plugin registration is applied;
  • CocoaPods installation succeeds.

8. iOS development build

Build and run with:

npx expo run:ios

or with the project’s existing development build command.

Expo Go is not supported for Reteno SDK validation.

Expected result:

  • app builds;
  • app installs;
  • app launches;
  • SDK initializes;
  • contact is created;
  • mobilePushToken is assigned;
  • token type is APNs or FCM, depending on selected route;
  • test push can be sent and validated.

Expo push handling

Use this section when mobile push validation is included.

Follow the official Reteno Expo push handling documentation and the selected platform setup.

1. Push registration

After SDK integration, verify that the app registers for remote notifications through the selected Expo / native setup.

Use the Expo SDK method:

import { useEffect } from 'react';
import Reteno from 'expo-reteno-sdk';

useEffect(() => {
  Reteno.registerForRemoteNotifications();
}, []);

Call Reteno.registerForRemoteNotifications() once at app startup when mobile push validation is part of the current integration task.

Use this order:

  1. Initialize Reteno or verify plugin auto-initialization.
  2. Register required listeners for the current integration task.
  3. Register for remote notifications.
  4. Perform the test action.
  5. Call Reteno.forcePushData() before immediate backend, Reteno UI, or MCP validation.
  6. Validate the result.

If JavaScript-controlled initialization is used, call Reteno.initialize(...) before registering listeners or requesting push notifications.

If sdkAccessToken is set in the platform plugin config, automatic initialization is enabled. In that case, skip duplicate Reteno.initialize(...) calls, but still register for remote notifications and verify push registration.

Do not mark mobile push setup as complete until notification permission, token registration, and mobilePushToken assignment are verified.

Expected result:

  • notification permission can be requested;
  • push token is obtained;
  • push token is passed to Reteno through the selected platform route;
  • Reteno contact has mobilePushToken;
  • token type matches the selected Reteno app configuration.

2. Android push handling

For Android, expected token type is:

FCM

Verify:

  • Android Firebase setup is correct;
  • google-services.json matches the effective Android application ID;
  • Android runtime API level is 26 or higher;
  • the app is built as a development/native build, not Expo Go;
  • notification permission is handled on Android 13+;
  • notification is shown on-device;
  • DELIVERED and CLICKED evidence can be verified.

2.1. Android notification permission APIs

When Android mobile push validation is part of the current integration task, verify notification permission through the Expo Reteno permission APIs supported by the selected expo-reteno-sdk version.

Use:

import Reteno from 'expo-reteno-sdk';

const permissionStatus = await Reteno.getNotificationPermissionStatus();

const permissionResult = await Reteno.requestNotificationPermission();

Use Reteno.requestNotificationPermission() when the app needs to request notification permission.

Use Reteno.getNotificationPermissionStatus() to verify the current permission state.

Do not infer permission state only from UI.

For Android 13+ validation, report:

  • whether notification permission was requested;
  • permission request result;
  • current permission status;
  • whether mobilePushToken is assigned after permission and token setup;
  • whether push can be displayed on-device.

If these APIs are unavailable in the selected expo-reteno-sdk version, document this as a version or capability limitation and use approved fallback evidence such as Reteno UI, SDK logs, device logs, or manual QA confirmation.

2.2. Android push-handler conflict with @react-native-firebase/messaging

When @react-native-firebase/messaging is installed for an iOS Firebase / FCM token flow, verify that it does not introduce an Android push-handler conflict.

Inspect the generated or merged Android manifest after prebuild/build.

Check whether more than one FirebaseMessagingService handles:

com.google.firebase.MESSAGING_EVENT

Possible conflicting services may include:

io.invertase.firebase.messaging.ReactNativeFirebaseMessagingService
expo.modules.retenosdk.RetenoMessagingService

If RNFirebase Messaging wins the Android service race:

  • FCM-level delivery may still be recorded;
  • Reteno may receive backend delivery evidence;
  • no system notification may be posted on-device;
  • CLICKED cannot be validated because there is no notification to open.

Before marking Android push as complete, verify that a real notification is posted on-device.

Use reliable evidence such as:

  • merged AndroidManifest inspection;
  • adb logcat;
  • adb shell dumpsys notification;
  • screenshot or manual visual confirmation;
  • Reteno MCP / Reteno UI / backend evidence.

If Android conflict is confirmed, use an Expo config plugin to remove RNFirebase Android manifest components only when this is approved and required for the selected Expo setup.

The config plugin must be Android-only.

Do not remove or change iOS Firebase Messaging behavior when the iOS FCM token flow depends on it.

Expected result:

  • RNFirebase Android Messaging service is removed from the merged Android manifest;
  • Reteno Android Messaging service remains active;
  • a real notification is posted on-device;
  • tapping the notification opens the app;
  • CLICKED is confirmed for the same contact/message.

Document in the final report:

  • whether @react-native-firebase/messaging was installed;
  • whether RNFirebase Android manifest components were detected;
  • whether a config plugin removed them;
  • whether the merged manifest was verified after rebuild;
  • whether real notification posting was verified;
  • whether Android DELIVERED and CLICKED were confirmed.

3. iOS push handling

For iOS, expected token type is:

APNs

or:

FCM

depending on selected notificationService.

Verify:

  • mode is development or production and matches the test build;
  • notificationService is apns or firebase;
  • App Groups are configured;
  • Notification Service Extension is configured;
  • Notification Content Extension is configured if rich push UI is enabled;
  • Firebase/APNs route is configured correctly;
  • token is assigned to the Reteno contact;
  • DELIVERED and CLICKED evidence can be verified.

4. Push opening and CLICKED

After DELIVERED status is available, verify that push interaction is tracked.

The test push notification must be opened in the selected test environment.

If you can interact with the device/simulator/emulator:

  1. Open the delivered notification.
  2. Confirm that the app opens.
  3. Continue Reteno MCP validation.
  4. Verify CLICKED.

If you cannot interact with the environment:

  1. Ask the developer or QA engineer to open the delivered test push manually.
  2. Wait for confirmation.
  3. Continue Reteno MCP validation.
  4. Verify CLICKED.

Do not treat inability to tap a notification automatically as an SDK integration failure.

Document it as a local automation limitation or manual fallback.

4.1. Platform-specific push listeners

Register only the listeners required for the selected platform and the current integration task.

4.2. Foreground push listener

Use this listener when foreground push handling must be verified:

import { useEffect } from 'react';
import Reteno from 'expo-reteno-sdk';

useEffect(() => {
  const pushListener = Reteno.setOnRetenoPushReceivedListener((event) => {
    console.log('onRetenoPushReceived', event);
  });

  return () => pushListener.remove();
}, []);

4.3 Push click listener

Use this listener for push notification click handling:

import { useEffect } from 'react';
import Reteno from 'expo-reteno-sdk';

useEffect(() => {
  const pushClickListener = Reteno.setOnRetenoPushClickedListener((event) => {
    console.log('onRetenoPushClicked', event);
  });

  return () => pushClickListener.remove();
}, []);

4.4 iOS action button listener

For iOS push action buttons, use:

import { useEffect } from 'react';
import { Platform } from 'react-native';
import Reteno from 'expo-reteno-sdk';

useEffect(() => {
  if (Platform.OS !== 'ios') return;

  const listener = Reteno.setOnRetenoPushButtonClickedListener((event) => {
    console.log('onRetenoPushButtonClicked', event);
  });

  return () => listener.remove();
}, []);

Do not use the iOS action-button listener as proof that Android click handling is configured.

Do not use the Android click listener as proof that iOS action-button handling is configured.

Document which listeners were registered and which platform they apply to.

4.6. Android-only push listeners

Some push listeners are Android-only.

Use Android-only listeners only when Android validation is part of the current integration task and the selected expo-reteno-sdk version supports them.

Android-only listeners may include:

  • custom push data listener;
  • push dismissed listener.

Do not configure Android-only listeners for iOS validation.

Do not use Android-only listener results as evidence for iOS behavior.

When Android-only listeners are used, document:

  • listener name;
  • platform;
  • expected payload;
  • actual payload;
  • foreground/background/terminated state, if applicable;
  • validation evidence.

If the selected SDK version does not expose these listeners, document this as not available in selected SDK version instead of treating validation as failed.

5. Auto-open links

If deeplink or custom push data validation is part of the current integration task, verify whether auto-open links should be enabled or disabled.

Use the JavaScript-side auto-open links API only when official Reteno Expo Push Handling documentation and the developer confirm that this behavior is expected.

Document:

  • whether auto-open links are enabled;
  • expected destination;
  • actual destination;
  • foreground behavior;
  • background behavior;
  • terminated-state behavior.

Do not treat a successful warm-start link opening as proof that cold-start link handling works.

Auto-open links behavior

If deeplink or custom push data validation is part of the current integration task, verify whether auto-open links should be enabled or disabled.

Use:

import Reteno from 'expo-reteno-sdk';

await Reteno.setAutoOpenLinks(true);

const isEnabled = await Reteno.getAutoOpenLinks();
console.log('Auto-open links:', isEnabled);

Default value differs by platform:

  • iOS: enabled by default;
  • Android: disabled by default.

Document:

  • whether auto-open links are enabled;
  • platform;
  • expected destination;
  • actual destination;
  • foreground behavior;
  • background behavior;
  • terminated-state behavior.

Do not treat a successful warm-start link opening as proof that cold-start link handling works.

Do not use Android result as proof of iOS behavior.

Do not use iOS result as proof of Android behavior.

Expo user identification

Use this section when user identification or externalCustomerId validation is part of the current integration task.

Do not validate externalCustomerId unless this check is requested or the integration modifies user identification behavior.

1. Anonymous contact

Before login or registration, SDK initialization should create or update an anonymous contact when the app runs.

Expected result:

  • contact exists in Reteno;
  • contactId is available;
  • deviceId is available when this data can be retrieved;
  • platform / OS information is available;
  • externalCustomerId is not required at this stage;
  • mobilePushToken may be assigned if push permission and token setup are complete.

2. Identified user

When the user logs in, signs up, or updates their profile, identify the user through Expo user information API.

Use:

import Reteno from 'expo-reteno-sdk';

Reteno.updateUserAttributes({
  externalUserId: 'USER_ID',
  user: {}
});

Use a stable backend user ID as externalUserId whenever available.

Do not use unstable identifiers such as:

  • device ID;
  • mobile push token;
  • temporary session ID;
  • random UUID generated on the device.

Use a stable backend user ID as externalUserId.

The value may be numeric or non-numeric, as long as it is stable and approved by the product/backend team.

Do not use unstable identifiers such as:

  • device ID;
  • mobile push token;
  • temporary session ID;
  • random UUID generated on the device.

Expected result:

  • before login or registration, the contact is anonymous;
  • after login or registration, externalUserId is sent through Reteno.updateUserAttributes(...);
  • Reteno-side externalCustomerId is assigned to the same contact/device;
  • mobilePushToken remains assigned after identification.

3. User attributes

If user attribute validation is part of the current integration task, use Reteno.updateUserAttributes(...) with the approved user attributes.

Example structure:

Reteno.updateUserAttributes({
  externalUserId: 'USER_ID',
  user: {
    userAttributes: {
      email: '[email protected]',
      firstName: 'John',
      lastName: 'Doe',
      languageCode: 'en',
      timeZone: 'Europe/Kyiv'
    },
    subscriptionKeys: ['news'],
    groupNamesInclude: ['vip'],
    groupNamesExclude: ['inactive']
  }
});

Use valid formats:

  • languageCode: RFC 5646 format;
  • timeZone: TZ database format.

Do not send real personal data in test integrations unless the developer explicitly confirms that the test account is approved.

4. Anonymous user attributes

If anonymous user attributes are part of the current integration task, use:

Reteno.updateAnonymousUserAttributes({
  firstName: 'Guest',
  lastName: 'User',
  languageCode: 'en',
  timeZone: 'Europe/Kyiv'
});

Do not use anonymous attributes for email or phone. Use identified user flow with externalUserId when email or phone is involved.

5. Multi-account user attributes

If the app uses multiple Reteno accounts, use the official multi-account user attribute method only when the developer confirms the account suffix and expected account mapping.

Document:

  • account suffix;
  • external user ID;
  • which Reteno account received the update;
  • whether the update was verified.

MCP Capability Discovery and Validation

After applying Expo SDK integration changes, validate the integration through Reteno MCP.

Do not mark the integration as complete until all required validation checks pass or a blocker is clearly documented.

MCP capability discovery

Before using Reteno MCP for validation, discover which capabilities are actually available and authorized in the current agent environment.

Verify that Reteno MCP is connected in the same environment where the AI coding agent runs.

For Expo core mobile push validation, possible capabilities may include:

  • finding a contact by contactId, if available;
  • finding a contact by deviceId, if available;
  • displaying the assigned mobilePushToken;
  • identifying token type: FCM or APNs;
  • creating or selecting a test push notification;
  • sending a test push notification to one approved test contact;
  • checking DELIVERED status;
  • checking CLICKED status after the push notification is opened.

Do not assume that all MCP capabilities are available.

If a required MCP capability is unavailable, use an approved manual fallback, Reteno UI, backend data, SDK logs, device logs, redacted network logs, or mark the step as blocked, manual fallback required, or missing MCP capability.

Do not mark the integration as complete if a required validation step could not be verified.

Evidence priority

Reteno MCP is the preferred Reteno-side validation path, but MCP itself is not the validation result.

Validation evidence is mandatory.

If MCP is unavailable or a required MCP capability is missing, use reliable fallback evidence when available:

  • Reteno UI;
  • backend data;
  • SDK logs;
  • device logs;
  • redacted network logs;
  • on-device API response;
  • developer / QA confirmation.

When fallback evidence is used:

  • state why MCP was unavailable or insufficient;
  • state which evidence source was used;
  • correlate only through verified identifiers;
  • do not include full push tokens or secrets;
  • report the result as Passed with warnings only if required validation is actually verified;
  • report the result as Blocked or Partial when required validation cannot be verified.

Do not mark a validation step as passed only because code was added.

Reteno MCP and local automation are separate

Reteno MCP validates Reteno-side data and statuses.

Reteno MCP is used for:

  • contact lookup;
  • mobilePushToken verification;
  • token type validation;
  • test push creation and sending when the capability is available and approved;
  • DELIVERED / CLICKED status checks when the capability is available;
  • feature-specific validation when the required MCP capabilities are available.

Local device, simulator, emulator, Expo, or EAS tooling is used for runtime interaction:

  • building the app;
  • installing the development build;
  • running the app;
  • granting notification permission;
  • opening the delivered test push notification;
  • completing login or registration steps;
  • tapping an In-App message button, if required.

If local automation is unavailable, ask the developer or QA engineer to perform the runtime action manually and continue validation after confirmation.

If Reteno MCP is unavailable, Reteno-side validation cannot be completed and must be reported as blocked or pending.

Do not treat local automation failure as an SDK integration failure.

Do not treat Reteno MCP failure as a local device automation failure.

Validation flow

1. Check selected Reteno mobile app configuration

Verify that the selected Reteno mobile app matches the Expo target platform.

Expected configuration:

  • Expo Android: FCM;
  • Expo iOS with direct APNs: APNs;
  • Expo iOS with Firebase / FCM: FCM.

Ask the developer or admin to confirm:

  • selected Reteno mobile app;
  • target validation platform: Android, iOS, or both;
  • token type configured for the selected app: FCM or APNs;
  • Firebase / FCM setup, if FCM is used;
  • APNs sandbox / production setup, if direct APNs is used;
  • whether the selected Reteno app matches the selected test environment.

If MCP cannot read app-level push configuration, ask the developer or admin to confirm it manually.

Do not change app-level Reteno configuration automatically unless this action is explicitly supported and confirmed by the developer or admin.

2. Create or initialize an anonymous contact

Before login or registration, initialize the SDK and create an anonymous contact.

At this stage, the contact must be anonymous.

To verify that the contact was created, use Reteno MCP, backend data, logs, or Reteno UI.

Expected result:

  • contactId is available;
  • deviceId is available, if this data can be retrieved;
  • platform / OS information is available;
  • externalCustomerId is not required at this stage;
  • mobilePushToken may or may not be available, depending on push permission and token setup.

Use contactId as the primary identifier.

If Reteno MCP supports deviceId lookup, also verify that the same contact can be found by deviceId.

2.1. Anonymous contact correlation in Expo

The Expo SDK may not expose contactId or deviceId directly to JavaScript.

Do not require direct contactId or deviceId access from the Expo app.

When validating an anonymous contact, correlate Reteno data using available evidence:

  • redacted mobilePushToken;
  • token type: FCM or APNs;
  • platform: Android or iOS;
  • app version / build version;
  • OS version;
  • device model, if available;
  • Reteno contact creation or update timestamp;
  • SDK logs;
  • device logs;
  • Reteno UI;
  • backend data;
  • test push message ID, name, or messageTag;
  • external user ID, after login, if user identification is part of the current integration task.Use a stable backend user ID as externalUserId.

Use contactId as the primary identifier only when Reteno MCP, Reteno UI, backend data, or another reliable source provides it.
Do not include full push tokens in the final report. Use redacted values only.

2.2. Shared Reteno test app data correlation

Non-production Reteno mobile apps may contain unrelated test data from other teams, devices, app versions, or validation runs.

Do not trust app-level labels alone when correlating a test contact.

If the selected Reteno app contains unrelated data, correlate the validation contact through exact evidence.

Use one or more of:

  • exact mobilePushToken;
  • redacted token prefix/suffix;
  • local SDK storage;
  • device ID;
  • app version;
  • platform;
  • OS version;
  • device model;
  • message ID;
  • message instance ID;
  • messageTag;
  • timestamp of device upload or push send;
  • Reteno MCP result;
  • Reteno UI / backend evidence;
  • device logs.

Do not include full push tokens or secrets in the final report.

If app-level labels are stale or misleading, document the mismatch as a warning.

Expected final report wording:

Reteno app label was not used as the only correlation source. Validation was correlated through exact token/device/message evidence.

If contact correlation cannot be established reliably, report the validation as Blocked or Partial.

3. Verify mobilePushToken

After the app is installed, launched, initialized, and push permission / token setup is complete, verify that a mobilePushToken is assigned to the test contact.

Expected result:

  • mobilePushToken is present;
  • token type is correct:
    • Android: FCM;
    • iOS direct APNs: APNs;
    • iOS Firebase / FCM: FCM;
  • token belongs to the same contact/device used in the validation run;
  • token matches the selected Reteno mobile app configuration.

Do not include full push token values in the final report.

Use redacted values only when needed for troubleshooting.

4. Send a test push

Send a test push only when the required Reteno MCP capability is available and explicitly approved.

Use only one approved test contact.

Do not create or send a broad campaign during validation.

Create and send the test push only to the selected test contact.

Scope the test push explicitly to the current contactId, deviceId, or mobilePushToken, depending on the available MCP capability.

Use a dedicated messageTag, for example test, to make push statuses easier to find.

If the MCP permission classifier blocks the push capability, retry only with an explicitly scoped single-contact payload.

If MCP cannot send the test push, ask the developer or admin to send it manually through Reteno UI and provide the message name / ID / tag for validation.

5. Verify DELIVERED

After the test push is sent, verify delivery.

Expected result:

  • DELIVERED status is available for the test push.

If DELIVERED is not available and ERROR is present:

  1. Check the error code.
  2. Match the error code with Reteno mobile push error documentation.
  3. Explain the likely reason.
  4. Provide troubleshooting steps.

Possible causes may include:

  • missing or invalid mobilePushToken;
  • token type mismatch;
  • incorrect Reteno mobile app configuration;
  • wrong Firebase project;
  • wrong google-services.json;
  • wrong GoogleService-Info.plist;
  • APNs sandbox / production mismatch;
  • missing iOS App Group;
  • missing iOS Notification Service Extension;
  • Expo Go used instead of a development build;
  • Android API level below 26;
  • notification permission disabled;
  • invalid or outdated token;
  • unsupported test environment.

If no DELIVERED or ERROR status is available, use available MCP capabilities, Reteno UI, backend data, SDK logs, device logs, redacted network logs, or manual confirmation to determine whether validation is blocked or pending.

6. Open the delivered push and verify CLICKED

After DELIVERED status is available, verify that push interaction is tracked.

The delivered test push notification must be opened in the selected test environment.

If you can interact with the device/simulator/emulator:

  1. Open the delivered notification.
  2. Confirm that the app opens.
  3. Continue Reteno MCP validation.
  4. Verify CLICKED.

If you cannot interact with the environment:

  1. Ask the developer or QA engineer to open the delivered test push manually.
  2. Wait for confirmation.
  3. Continue Reteno MCP validation.
  4. Verify CLICKED.

Expected result:

  • app opens after the push notification is opened;
  • CLICKED status is available;
  • CLICKED belongs to the same contact/message as DELIVERED.

Do not treat inability to tap a notification automatically as an SDK integration failure.

Document it as a local automation limitation or manual fallback.

7. Validate feature-specific flows

Run feature-specific validation only when the current integration task includes the related feature.

Feature-specific validation may include:

  • custom events;
  • In-App messages;
  • mobile-push + In-App;
  • externalCustomerId;
  • deeplinks;
  • custom push data;
  • READ status;
  • action buttons.

If a feature is not part of the current integration task, report it as:

not requested for this integration

Do not use MVP, extended validation, optional, out of scope, out of MVP scope, included in scope, or selected validation scope wording in the final report.

Feature-specific validation: custom event

Use this check only when the current integration task includes custom event validation or explicitly asks to validate a custom event.

Do not hardcode a required event name.

Ask the developer to confirm the event name.

When custom event validation is part of the current integration task, use the required logEvent payload shape:

import Reteno from 'expo-reteno-sdk';

await Reteno.logEvent({
  eventName: 'EVENT_NAME',
  date: new Date().toISOString(),
  parameters: [
    {
      name: 'parameterName',
      value: 'parameterValue'
    }
  ]
});

Required fields:

  • eventName;
  • date in ISO-8601 format;
  • parameters, when event parameters are part of validation.

After logging the event, call:

await Reteno.forcePushData();

before immediate backend, Reteno UI, or MCP validation.

Expected result:

  • event is sent from the Expo app;
  • queued SDK data is flushed when immediate validation is required;
  • event appears in Reteno through MCP, Reteno UI, backend data, SDK logs, device logs, or another approved evidence source;
  • event is correlated with the correct contact using available evidence.

If the event cannot be verified immediately and forcePushData() was not called, do not mark validation as failed until queued SDK data behavior is considered.

If the event still cannot be verified after forcePushData(), report the result as blocked, pending, or failed, depending on available evidence.

Local Device, Simulator, Emulator, and Expo Automation

Reteno MCP and local runtime automation are separate.

Reteno MCP validates Reteno-side data and statuses.

Local runtime automation is used to build, install, launch, and interact with the Expo application.

Supported local automation methods

For Expo validation, local automation may include:

  • mobile-mcp;
  • adb;
  • uiautomator;
  • Android Studio tools;
  • Xcode tools;
  • Expo CLI;
  • EAS Build;
  • simulator controls;
  • emulator controls;
  • manual developer / QA interaction.

If mobile-mcp is unavailable but another approved local automation method works, such as adb, uiautomator, Expo CLI, Android Studio, Xcode, or manual interaction, use it and document the method in the final report.

This is not an Expo SDK integration failure.

Expo Go is not supported

Do not use Expo Go for Reteno SDK validation.

expo-reteno-sdk uses native modules and requires a development build, Expo prebuild / CNG, or bare workflow.

If the only available test environment is Expo Go:

  • continue only with non-runtime code/config review;
  • do not mark validation as complete;
  • report runtime validation as blocked;
  • ask the developer to provide a supported development build environment.

Android automation

For Android validation, you may use:

  • npx expo run:android;
  • Android emulator;
  • physical Android device;
  • adb;
  • uiautomator;
  • Android Studio tools;
  • manual developer / QA interaction.

Runtime actions may include:

  • installing the app;
  • launching the app;
  • granting notification permission;
  • confirming Android API level 26 or higher;
  • opening the delivered push notification;
  • confirming app open / navigation after notification tap;
  • collecting device logs.

If you cannot open the delivered push automatically, ask the developer or QA engineer to open it manually and continue Reteno MCP validation after confirmation.

iOS automation

For iOS validation, you may use:

  • npx expo run:ios;
  • Xcode;
  • iOS simulator, if supported and confirmed by the developer;
  • physical iOS device;
  • EAS development build;
  • manual developer / QA interaction.

Runtime actions may include:

  • installing the development build;
  • launching the app;
  • granting push notification permission;
  • confirming the selected build mode:
    • development;
    • production;
  • opening the delivered push notification;
  • confirming app open / navigation after notification tap;
  • collecting device logs.

For push validation, physical iOS device is the safest acceptance environment unless the developer confirms that the selected simulator and Xcode runtime support remote notifications and token registration.

If the selected iOS simulator cannot obtain a push token, use it only for build and launch validation and report end-to-end push validation as blocked.

iOS physical-device manual tap fallback

When iOS validation runs on a physical device, UI automation may be unavailable.

Do not treat unavailable iOS physical-device automation as an Expo SDK integration failure.

If the agent cannot tap the delivered notification automatically:

  1. Ask the developer or QA engineer to open the delivered test push manually.
  2. Wait for confirmation.
  3. Continue Reteno-side validation.
  4. Verify CLICKED.

Document:

  • who performed the manual tap, if known;
  • whether the push was visible on the physical device;
  • whether the app opened after tap;
  • whether CLICKED was confirmed through MCP, Reteno UI, backend data, SDK logs, device logs, or another reliable evidence source.

Expected result:

  • iOS DELIVERED is confirmed before or after manual interaction;
  • manual tap opens the app;
  • CLICKED is confirmed for the same contact/message.

EAS Build automation

When EAS Build is used, you may inspect:

  • eas.json;
  • build profiles;
  • Expo config;
  • app extension configuration;
  • EAS build logs, if provided;
  • EAS build link, if provided.

You must not change EAS credentials, Apple Developer account settings, signing, provisioning, or app extension identifiers without explicit developer or admin confirmation.

If EAS Build fails because of signing, provisioning, app extensions, or credentials, report this as an environment / credentials blocker and ask the developer or admin to resolve it.

Manual fallback

Use manual fallback when:

  • local automation is unavailable;
  • GUI interaction is required;
  • push notification must be opened manually;
  • login or registration is required;
  • In-App message must be clicked manually;
  • EAS credentials or Apple Developer account access is required;
  • simulator/emulator/device access is unavailable to you.

When manual fallback is used, the final report must document:

  • what action was requested;
  • who completed the action;
  • when it was completed, if known;
  • what evidence was provided;
  • which validation step resumed after the manual action.

Validation Requirements

You must validate every Reteno SDK feature that it integrates, modifies, or is explicitly asked to verify.

Do not treat feature-specific validation as optional when the feature is part of the current integration task.

Core mobile push validation

When mobile push integration is included, the following checks are required:

  • SDK initialization;
  • contact creation;
  • mobilePushToken assignment;
  • token type validation;
  • test push sending;
  • DELIVERED status;
  • push notification opening;
  • CLICKED status;
  • final report.

Do not mark the integration as complete until core mobile push validation passes or a blocker is clearly documented.

Platform-specific token expectations

Expected token type depends on platform and selected Reteno app configuration:

PlatformExpected token type
Expo AndroidFCM
Expo iOS with direct APNsAPNs
Expo iOS with Firebase / FCMFCM

The token assigned to the contact must match the token type configured for the selected Reteno mobile app.

Android validation success criteria

For Expo Android validation, success requires:

  • Expo app root detected;
  • Expo workflow type detected;
  • expo-reteno-sdk installed or confirmed;
  • Reteno Expo plugin configured;
  • Android minSdkVersion is 26 or higher;
  • google-services.json present and matching the effective Android application ID;
  • development build is used, not Expo Go;
  • app builds and runs on the selected Android test environment;
  • contact is created or found;
  • mobilePushToken is assigned;
  • token type is FCM;
  • test push is sent to one approved test contact when the capability is available and approved;
  • delivery evidence is verified;
  • click / interaction evidence is verified after opening the notification;
  • final report documents any fallback evidence or warnings.

iOS validation success criteria

For Expo iOS validation, success requires:

  • Expo app root detected;
  • Expo workflow type detected;
  • expo-reteno-sdk installed or confirmed;
  • Reteno Expo plugin configured;
  • mode configured as development or production;
  • notificationService configured as apns or firebase;
  • App Group configured in the required format;
  • Notification Service Extension configured when required;
  • EAS app extension configuration completed when EAS Build / managed credentials require it;
  • development/native build is used, not Expo Go;
  • app builds and runs on the selected iOS test environment;
  • contact is created or found;
  • mobilePushToken is assigned;
  • token type is APNs or FCM, depending on selected route;
  • test push is sent to one approved test contact when the capability is available and approved;
  • delivery evidence is verified;
  • click / interaction evidence is verified after opening the notification;
  • final report documents any fallback evidence or warnings.

Feature-specific validation

Run feature-specific validation only when the feature is part of the current integration task.

Feature-specific validation may include:

  • custom events;
  • user identification / externalCustomerId;
  • user attributes;
  • anonymous user attributes;
  • multi-account user attributes;
  • deeplinks;
  • custom push data;
  • In-App messages;
  • mobile-push + In-App;
  • action buttons;
  • READ status.

If a feature was not part of the current integration task, report it as:

not requested for this integration

If a feature was part of the current integration task but could not be validated, report it as one of the following:

blocked
manual fallback required
missing MCP capability
unavailable test environment
unavailable Reteno UI / admin setup

Evidence rules

The final report must state how each result was verified.

Valid evidence may include:

  • Reteno MCP result;
  • Reteno UI confirmation;
  • backend data;
  • SDK logs;
  • device logs;
  • redacted network logs;
  • on-device API response;
  • screenshot or screen recording;
  • developer / QA confirmation.

Evidence collected on one platform must not be treated as evidence for another platform.

Android result does not validate iOS.

iOS result does not validate Android.

Complete vs Partial report

Use Complete only when all required validation checks for the current integration task pass.

Use Partial when any required step is skipped, blocked, or not verified.

Required steps may include:

  • build;
  • run;
  • development build availability;
  • runtime interaction;
  • Reteno-side validation;
  • contact correlation;
  • token verification;
  • test push;
  • DELIVERED;
  • CLICKED;
  • requested feature-specific validation.

MCP is preferred, but MCP itself is not mandatory.

Validation evidence is mandatory.

If MCP is unavailable or a required MCP capability is missing, use reliable fallback evidence when available:

  • Reteno UI;
  • backend data;
  • SDK logs;
  • device logs;
  • redacted network logs;
  • on-device API response;
  • developer / QA confirmation.

Use Passed with warnings only when required validation passes through reliable evidence but non-blocking warnings remain.

Use Blocked when validation cannot continue because of missing access, missing environment, missing credentials, Expo Go-only runtime, Firebase/APNs setup issue, Kotlin DSL blocker, EAS signing issue, app extension signing issue, or incomplete manual action.

Use Failed when validation was attempted and a required check failed with evidence.

Do not mark a validation step as passed only because code was added.

Expo Troubleshooting

If validation fails, identify the failed step and troubleshoot before escalating.

Do not stop immediately after a failed validation check.

Check the likely cause, document the result, retry safe fixes, and continue with the remaining validation steps where possible.

Expo Go is used

If the app is running in Expo Go, Reteno SDK validation cannot be completed.

Expected behavior:

  • native module may be unavailable;
  • SDK initialization may fail;
  • contact may not be created;
  • push token may not be assigned;
  • push validation cannot pass.

Resolution:

  • use a local development build;
  • use EAS development build;
  • use Expo prebuild / CNG when approved;
  • use bare workflow if the project already uses it.

Do not mark validation as complete when the only runtime environment is Expo Go.

Expo workflow is unclear

If the Expo workflow cannot be classified, inspect:

  • app.json;
  • app.config.js;
  • app.config.ts;
  • eas.json;
  • native ios/ and android/ folders;
  • package.json;
  • Expo dependencies;
  • existing config plugins;
  • repository instructions.

Do not run prebuild or edit native folders until the workflow and native ownership are confirmed.

If still unclear, ask the developer whether the project is:

  • Expo managed without committed native folders;
  • Expo prebuild / CNG with committed native folders;
  • bare React Native with Expo modules;
  • bare React Native without Expo workflow.

Prebuild is required but not approved

If the Reteno Expo plugin requires native changes but prebuild is not approved:

  • complete only non-blocked JavaScript / TypeScript and Expo config steps;
  • do not modify generated native projects directly;
  • document native setup as blocked or pending;
  • ask the developer to approve npx expo prebuild or provide a prebuilt native project.

Do not mark runtime validation as complete until the native build is available.

Prebuild overwrote or may overwrite manual native changes

Before running prebuild, check whether native folders contain manual changes.

If prebuild may overwrite manual native changes:

  • stop before running prebuild;
  • document the risk;
  • ask the developer for approval;
  • ask whether manual native changes should be preserved, regenerated, or migrated to config plugins.

Do not run npx expo prebuild --clean without explicit approval.

Config plugin is missing or not applied

If expected native configuration is missing after prebuild, check:

  • Reteno plugin entry in Expo config;
  • platform-specific plugin config;
  • whether the correct config file is used;
  • whether dynamic config branches hide plugin configuration;
  • whether the correct EAS profile is used;
  • whether prebuild was run after plugin changes;
  • whether Kotlin DSL or custom Gradle setup prevented automatic patching.

If plugin changes are not applied:

  • rerun prebuild only if approved;
  • inspect generated native files;
  • ask developer/SME before applying manual native patches.

SDK initialization is missing or duplicated

If SDK initialization does not work, check:

  • whether Path A or Path B was selected;
  • whether sdkAccessToken is set in platform plugin config;
  • whether Reteno.initialize(...) is called when required;
  • whether Reteno.initialize(...) is accidentally called when automatic plugin initialization already applies;
  • whether initialization happens once from a stable app startup path;
  • whether initialization happens before Reteno feature usage;
  • whether the SDK access key is missing, placeholder, or loaded too late.

Do not configure both automatic initialization and JavaScript-controlled initialization for the same platform unless official documentation and developer confirmation require it.

Android build fails

If Android build fails, check:

  • Expo SDK version;
  • expo-reteno-sdk version;
  • Android Gradle plugin version;
  • Gradle files;
  • Kotlin / Java version;
  • minSdkVersion;
  • google-services.json;
  • Firebase dependencies;
  • Expo config plugin output;
  • generated native Android project;
  • Kotlin DSL compatibility;
  • Android package name / applicationId;
  • duplicate Firebase Messaging service definitions.

Do not repeat the same failed action without a new hypothesis.

If the issue requires local environment access or cannot be resolved safely by the agent, ask the developer to run the app through Expo CLI, Android Studio, or Gradle and provide:

  • exact error message;
  • Gradle logs;
  • Metro logs;
  • emulator or device details;
  • reproduction steps.

Android API level is below 26

Reteno Expo Android setup requires Android API level 26 or higher.

If the selected Android emulator/device is below API 26:

  • do not validate Reteno SDK on that environment;
  • report test environment as unavailable for Reteno validation;
  • ask the developer to provide API 26+ emulator or device.

Do not treat missing contact/token/statuses on Android API <26 as an SDK integration bug.

Android mobilePushToken is missing

If the Android contact exists but mobilePushToken is missing, check:

  • app is not running in Expo Go;
  • Android API level is 26 or higher;
  • google-services.json exists;
  • google-services.json matches the effective Android application ID;
  • Reteno app is configured for Android / FCM;
  • Firebase server-side configuration is completed in Reteno;
  • notification permission is granted on Android 13+;
  • SDK initialization path is executed;
  • app was launched after integration changes;
  • network is available;
  • token assignment has had time to sync.

Push-handler conflict detection

Before configuring Reteno push handling, inspect existing push handlers.

Check for:

  • expo-notifications;
  • @react-native-firebase/messaging;
  • custom native Android push handlers;
  • custom native iOS notification delegates;
  • existing notification response listeners;
  • existing foreground push listeners;
  • existing background push listeners;
  • existing deeplink / navigation handlers;
  • existing AppDelegate notification methods;
  • existing Android Firebase Messaging services;
  • existing notification service extensions.

Do not replace existing push handling.

Compose with existing handlers and document what was preserved.

If multiple handlers own the same token, click, or payload flow, report the conflict before modifying behavior.

If the conflict cannot be resolved safely by the agent, mark the step as:

blocked / push-handler conflict requires developer review

Expected result:

  • existing push behavior is preserved;
  • Reteno registration and listeners are added without duplicate processing;
  • token ownership is clear;
  • click handling is not swallowed by existing navigation logic;
  • custom native handlers are not overwritten.

iOS build fails

If iOS build fails, check:

  • Expo SDK version;
  • expo-reteno-sdk version;
  • Expo config plugin output;
  • mode;
  • notificationService;
  • devTeam;
  • appGroups;
  • ios/Podfile;
  • CocoaPods installation;
  • npx pod-install;
  • .xcworkspace;
  • generated native iOS project;
  • Push Notifications capability;
  • App Groups capability;
  • Notification Service Extension;
  • Notification Content Extension;
  • EAS app extension config;
  • signing and provisioning;
  • GoogleService-Info.plist, if Firebase / FCM is used.

Do not repeat the same failed action without a new hypothesis.

If the issue requires local environment access or cannot be resolved safely by the agent, ask the developer to run the app through Expo CLI, Xcode, EAS Build, or CocoaPods and provide:

  • exact error message;
  • Xcode logs;
  • EAS build link or logs, if applicable;
  • simulator or device details;
  • reproduction steps.

EAS Build fails because of app extensions

If EAS Build fails after adding iOS Notification Service Extension or Notification Content Extension, check:

  • app extension configuration in Expo config;
  • extension bundle identifiers;
  • App Group identifier;
  • Apple Developer Team ID;
  • signing / provisioning;
  • managed credentials;
  • EAS build profile.

Do not change EAS credentials or Apple Developer account settings automatically.

Ask the developer or admin to update credentials or approve the required app extension configuration.

iOS mobilePushToken is missing

If the iOS contact exists but mobilePushToken is missing, check:

  • app is not running in Expo Go;
  • development/native build is used;
  • mode matches the build:
    • development for debug/simulator/development builds;
    • production for TestFlight/App Store builds;
  • notificationService is correct:
    • apns;
    • firebase;
  • App Group is configured;
  • Notification Service Extension is configured;
  • push permission is granted;
  • Firebase / FCM setup is correct, if notificationService is firebase;
  • Reteno.setDeviceToken(...) is called when iOS Firebase / FCM token handling requires it;
  • APNs sandbox / production route is correct;
  • selected Reteno app token type matches the selected route.

DELIVERED is missing

If test push is sent but DELIVERED is missing, check:

  • contact exists;
  • mobilePushToken is present;
  • token type matches selected Reteno app;
  • test push was sent to the correct contact;
  • Firebase project is correct;
  • APNs sandbox / production route is correct;
  • iOS mode is correct;
  • iOS notificationService is correct;
  • Android API level is 26+;
  • app is not Expo Go;
  • notification permission is granted;
  • Notification Service Extension and App Groups are configured for iOS;
  • Reteno MCP status lookup is available;
  • Reteno UI or backend data has delivery evidence;
  • device logs show receipt or errors.

If ERROR is available, use mobile push error code documentation.

Do not assume SDK integration is wrong until token routing, push provider configuration, and environment compatibility are confirmed.

CLICKED is missing

If DELIVERED is available but CLICKED is missing, check:

  • delivered notification was actually opened;
  • agent or developer opened the correct notification;
  • app opened after notification interaction;
  • push handler / link handler is preserved;
  • custom navigation did not swallow the interaction;
  • terminated-state handling is tested separately from background state;
  • auto-open links behavior is configured as expected;
  • Reteno MCP can retrieve click status;
  • fallback evidence exists through SDK logs, device logs, Reteno UI, or backend data.

If you cannot open the notification automatically, request manual opening and continue validation after confirmation.

Deeplink or custom push data does not work

If deeplink or custom push data validation is part of the current integration task, test separately:

  • foreground state;
  • background state;
  • terminated state;
  • custom URL scheme;
  • universal link or Android App Link;
  • normal HTTPS URL;
  • actual destination screen.

Check:

  • Expo Router configuration, if used;
  • React Navigation configuration, if used;
  • linking configuration;
  • Reteno push payload;
  • auto-open links setting;
  • custom push data callback;
  • duplicate navigation handling.

Do not treat successful warm-start behavior as proof that cold-start handling works.

iOS terminated-state payload handling limitation

Treat iOS terminated-state payload handling as a known SDK limitation until getInitialNotification() is confirmed fixed for the selected expo-reteno-sdk version.

Do not mark terminated-state iOS payload handling as failed SDK integration if:

  • push delivery works;
  • the app opens after push interaction;
  • foreground push handling works;
  • background push handling works;
  • DELIVERED and CLICKED evidence is available;
  • the only missing part is initial payload retrieval after terminated-state open.

Report it as:

known SDK limitation / pending SDK fix

Document:

  • selected expo-reteno-sdk version;
  • iOS version;
  • test environment;
  • whether app opened;
  • whether payload was available;
  • whether getInitialNotification() returned data or null;
  • whether foreground/background behavior passed.

Do not use terminated-state iOS payload failure as the only reason to mark core mobile push integration as failed when delivery and click tracking passed.

In-App message does not appear

If In-App message validation is part of the current integration task and the message does not appear, check:

  • app is not Expo Go;
  • SDK is initialized;
  • contact exists;
  • target contact is eligible;
  • segment rules match;
  • frequency limits;
  • trigger event or app-open trigger;
  • In-App message is active and published in the selected non-production environment;
  • selected Reteno app/environment is correct;
  • MCP capability exists or Reteno UI fallback is available;
  • app session is active;
  • logs show In-App fetch/display evidence.

Do not publish or modify In-App messages without explicit developer or admin approval.

Reteno MCP status lookup is unavailable or returns an error

If MCP status lookup capabilities are unavailable, return empty results, or return an error, do not assume validation failed immediately.

Use approved fallback evidence when available:

  • Reteno UI;
  • backend data;
  • SDK logs;
  • device logs;
  • on-device API responses;
  • redacted network logs;
  • developer / QA confirmation.

When fallback evidence is used:

  • document which MCP capability was unavailable or failed;
  • document which fallback evidence was used;
  • correlate evidence only through available and verified identifiers;
  • do not include full push tokens or secrets;
  • report the result as Passed with warnings if required validation passed through fallback evidence;
  • report the result as Blocked or Partial if no reliable fallback evidence is available.

Final Report Format

You must return a concise structured final report after every Expo integration run.

Do not use a long field-by-field report by default.

Use:

  • Core report for every run;
  • Android section only if Android validation was part of the current integration task;
  • iOS section only if iOS validation was part of the current integration task;
  • Feature-specific section only for features requested in the current integration task.

Validation result is mandatory.

Reteno MCP is the preferred Reteno-side validation path, but MCP itself is not the validation result.

If MCP is unavailable or a required MCP capability is missing, use reliable fallback evidence when available:

  • Reteno UI;
  • backend data;
  • SDK logs;
  • device logs;
  • redacted network logs;
  • on-device API response;
  • developer / QA confirmation.

Do not mark validation as passed unless the result is verified through at least one reliable evidence source.

Core report

FieldResult
Report typeComplete / Partial
Overall statusPassed / Passed with warnings / Blocked / Failed
Repository / project
Branch / commit
Initial worktree stateClean / Dirty / Unknown
Application root
Expo workflowManaged / Prebuild-CNG / Bare RN with Expo modules / Unknown
Target validation platformAndroid / iOS / Both
expo-reteno-sdk version
Initialization pathPlugin auto-init / JavaScript-controlled / Mixed / Unknown
Initialization path changedYes / No
Prebuild runNo / Android / iOS / Both / Clean prebuild
Native folders committed or generatedCommitted / Generated / Mixed / Not applicable
Manual generated-native changes usedYes / No
Generated-native changes survive prebuildYes / No / Not verified / Not applicable
Durable fix mechanismExpo config / config plugin / package patch / generation script / manual reapply / other / not applicable
Temporary package patches usedYes / No
Package patch removal follow-up requiredYes / No / Not applicable
Development build usedYes / No
Build/run resultPassed / Failed / Not run
Core push validationPassed / Failed / Blocked / Not run
Evidence sourcesMCP / Reteno UI / backend / SDK logs / device logs / manual confirmation
SDK access keys redacted from app logsYes / No / Not applicable
SDK access key exposed in logs/tool outputYes / No
SDK access key rotation recommendedYes / No / Not applicable
Shared Reteno test app data detectedYes / No
Contact correlation methodtoken / deviceId / local SDK storage / messageId / messageTag / Reteno UI / backend / other
Reteno app label mismatch warningYes / No / Not applicable
Blockers
Warnings
Changed files
Recommended next steps

Android section, if applicable

Include this section only when Android validation was part of the current integration task.

Report:

  • Android package name / application ID;
  • Firebase config source:
    • expo.android.googleServicesFile;
    • generated native file;
    • other;
  • whether google-services.json exists and matches the effective Android application ID;
  • whether expo-build-properties is used for minSdkVersion;
  • resolved Android minSdkVersion;
  • whether resolved minSdkVersion is 26 or higher;
  • whether Gradle Kotlin DSL was detected;
  • Kotlin DSL result:
    • not detected;
    • detected and verified;
    • detected and blocked pending SME review;
  • Android notification permission APIs used:
    • Reteno.requestNotificationPermission();
    • Reteno.getNotificationPermissionStatus();
    • not available in selected SDK version;
  • permission result;
  • token type expected: FCM;
  • token type verified: FCM / other / not verified;
  • mobilePushToken result: present / missing / not verified;
  • test push result;
  • DELIVERED result;
  • CLICKED result;
  • Android-specific fallback evidence, if used.
  • whether @react-native-firebase/messaging was installed:
    • Yes;
    • No;
    • Not applicable;
  • whether RNFirebase Android manifest components were detected;
  • whether Android push-handler conflict was detected;
  • whether Android conflict fix was applied through an Expo config plugin;
  • whether the Android merged manifest was verified after rebuild;
  • whether more than one FirebaseMessagingService handled com.google.firebase.MESSAGING_EVENT;
  • whether Reteno Android Messaging service remained active;
  • whether real Android notification posting was verified on-device;
  • Android notification posting evidence:
    • adb shell dumpsys notification;
    • adb logcat;
    • screenshot;
    • manual confirmation;
    • other.

iOS section, if applicable

Include this section only when iOS validation was part of the current integration task.

Report:

  • iOS bundle ID;
  • iOS deployment target;
  • whether iOS deployment target is 15.1 or higher;
  • Firebase config source, if Firebase / FCM is used:
    • expo.ios.googleServicesFile;
    • generated native file;
    • other;
  • whether GoogleService-Info.plist exists and matches the effective iOS bundle ID;
  • mode: development / production;
  • notificationService: apns / firebase;
  • iosDeviceTokenHandlingMode: automatic / manual / not applicable;
  • whether devTeam was required by effective Xcode / EAS signing;
  • App Group name;
  • whether App Group uses the required format;
  • Notification Service Extension status;
  • Notification Service Extension deployment target;
  • Notification Content Extension status, if applicable;
  • EAS app extension config status, if applicable;
  • token type expected: APNs / FCM;
  • token type verified: APNs / FCM / other / not verified;
  • whether @react-native-firebase/messaging was installed, if Firebase / FCM is used;
  • installed @react-native-firebase/messaging version, if applicable;
  • Firebase Messaging API style:
    • modular;
    • namespaced;
    • not applicable;
  • whether registerDeviceForRemoteMessages(...) was required before getToken(...);
  • whether registerDeviceForRemoteMessages(...) was called before getToken(...);
  • whether iOS FCM token was obtained on a physical device;
  • whether iOS physical-device manual tap was required;
  • who performed the iOS manual tap, if applicable;
  • whether the iOS notification was visible on the physical device;
  • whether the app opened after iOS notification tap;
  • whether iOS Firebase / FCM flow uses Reteno.setDeviceToken(...);
  • whether Reteno received FCM, not APNs, for iOS Firebase / FCM flow;
  • whether final Reteno token remained FCM after APNs registration callbacks;
  • whether raw APNs token overwrite was detected;
  • whether generated iOS Reteno token-forwarding code was inspected;
  • whether an expo-reteno-sdk package patch was required;
  • package patch mechanism, if used;
  • patched package version;
  • whether an upstream fix exists;
  • whether the local patch should be removed after SDK upgrade;
    • whether iOS registerDeviceForRemoteMessages(...) was required before getToken(...);
  • whether iOS registerDeviceForRemoteMessages(...) was called before getToken(...);
  • whether iOS FCM token was obtained on a physical device;
  • whether iOS physical-device manual tap was required;
  • who performed the iOS manual tap, if applicable;
  • whether the iOS notification was visible on the physical device;
  • whether the app opened after iOS notification tap.
  • mobilePushToken result: present / missing / not verified;
  • test push result;
  • DELIVERED result;
  • CLICKED result;
  • terminated-state payload result:
    • passed;
    • failed;
    • known SDK limitation;
    • not requested for this integration;
  • iOS-specific fallback evidence, if used.

Feature-specific section, if applicable

Include this section only for features requested in the current integration task.

For each requested feature, report:

#### <Feature name>

- Requested: Yes
- Implemented: Yes / No / Not applicable
- Validated: Passed / Failed / Blocked / Not run
- Evidence source:
- Blocker or warning:

Feature-specific checks may include:

  • custom event;
  • user identification / externalCustomerId;
  • deeplink;
  • custom push data;
  • foreground push handling;
  • background push handling;
  • terminated-state push handling;
  • action buttons;
  • In-App message;
  • mobile-push + In-App flow.

If a feature was not part of the current integration task, report it as:

not requested for this integration

Do not use:

MVP
extended validation
optional
out of scope
out of MVP scope
included in scope
selected validation scope

Report rules

Use Complete only when all required validation checks for the current integration task pass.

Use Partial when any required build, run, runtime interaction, Reteno-side validation, contact correlation, token verification, test push, DELIVERED, CLICKED, or requested feature-specific validation step was skipped, blocked, or not verified.

Use Passed only when all required validation checks pass.

Use Passed with warnings only when required validation passes but non-blocking warnings remain, such as:

  • manual fallback was used;
  • MCP was unavailable but reliable fallback evidence verified the result;
  • non-required feature validation was not requested;
  • iOS terminated-state payload handling hit a known SDK limitation, while core delivery and click validation passed.

Use Blocked when the integration cannot continue because of missing credentials, missing Reteno access, unavailable test environment, Expo Go-only environment, unavailable Firebase/APNs setup, Kotlin DSL blocker, EAS signing issue, app extension signing issue, or required manual action that has not been completed.

Use Failed when the integration was attempted and a required validation check failed with evidence.

Do not require requestId or any other implementation-specific MCP response field in the final report.

Do not include secrets, SDK access keys, Firebase server credentials, APNs private keys, certificates, service account data, or full push token values in the final report.

If Android DELIVERED is confirmed but no system notification is posted, do not mark Android push validation as complete.

Investigate Android push-handler conflict before validating CLICKED.

If @react-native-firebase/messaging is used for iOS FCM token handling and causes Android manifest conflicts, document the Android-only config plugin fix and verify the merged Android manifest after rebuild.

If SDK access keys were exposed in app logs, Metro logs, terminal output, tool output, device logs, or screenshots, report rotation as required even when no secret was committed to git.

If Reteno test app records contain unrelated third-party data, report the exact correlation method used and do not rely on app labels alone.

Use:

Reteno app label was not used as the only correlation source. Validation was correlated through exact token/device/message evidence.

when shared test app data required exact correlation.

Use redacted values for sensitive identifiers when needed.

If a secret was exposed in tool output, do not repeat the secret value in the final report.

Report only:

  • what type of secret was exposed;
  • where it was exposed: logs, terminal output, tool output, screenshot, or prompt;
  • whether rotation is recommended;
  • which provider/dashboard should be used for rotation.

Use:

Secret exposure detected: Yes
Secret rotation recommended: Yes

Do not include the exposed value itself.

Developer Preparation Checklist

Before asking an AI coding agent to run this Expo integration, confirm:

  • project can be opened by the agent;
  • branch/worktree state is safe;
  • package manager is known;
  • Expo workflow type is known or detectable;
  • prebuild permission is clear;
  • Reteno SDK access key handling method is approved;
  • Firebase/APNs setup is available;
  • Reteno MCP is connected;
  • supported development build environment is available;
  • Expo Go is not used for SDK validation;
  • developer/QA can perform manual runtime actions if automation is unavailable.