Flutter AI-Assisted Integration

Operating instructions for an AI coding agent integrating the Flutter SDK

Purpose

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

The agent 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 Flutter SDK into an existing Flutter application.

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

Do not treat this as a native iOS or native Android integration by default.

Start with the Flutter project structure and apply native iOS or Android changes only when the Flutter SDK setup requires them.

Quick Start Prompt

Use this Flutter runbook as your operating instruction.

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

First inspect the project and detect the Flutter project structure, package manager, app entry point, existing Reteno usage, Firebase setup, FCM/APNs setup, native iOS/Android folders, and existing push notification logic.
Use the official Reteno Flutter SDK Guide as the primary source of truth for Flutter-specific setup.

Use the official Flutter Migration Guide when the project already has Reteno Flutter SDK installed, uses an older setup, or may require migration to the Flutter-first initialization flow.

Use the official Flutter Android SDK Setup when the Flutter integration requires Android-specific changes in the generated android/ folder.

Use the official Flutter iOS SDK Setup when the Flutter integration requires iOS-specific changes in the generated ios/ folder.

Use the official Flutter Push Handling Guide for push notification handling, token handling, push actions, custom push data, and deeplinks.

Use the official Flutter SDK Release Notes to check SDK versions, migration notes, compatibility issues, and known fixes.

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

  • selected reteno_plugin version; verify the current version through Flutter SDK release notes, package metadata, pubspec.yaml / pubspec.lock, or SME confirmation before using it;
  • whether the selected reteno_plugin version is compatible with the current Flutter, Dart, Android, iOS, Firebase, and native Reteno SDK setup;
  • iOS deviceTokenHandlingMode: automatic, manual, or external;
  • whether setPushToken(...) is required;
  • whether user identification requires setUserAttributes(userExternalId: ...);
  • whether push permission should be requested through requestPushPermission();
  • Android API level of the selected test environment;
  • iOS App Group exact name;
  • Notification Service Extension dependency setup;
  • APNs sandbox routing for iOS development builds;
  • Firebase initialization order before Reteno for manual or external token modes.
  • whether iOS validation uses sandbox Firebase / Reteno configuration;
  • whether the current run must be reported as sandbox-only and not merge-ready;
  • whether production validation must be repeated with the real bundle ID, real Firebase project, real Reteno mobile app, real access key, and real GoogleService-Info.plist;
  • whether GoogleService-Info.plist exists and is added to the Runner target resources;
  • whether the selected iOS push route is Firebase / FCM or direct APNs;
  • whether deviceTokenHandlingMode is correct for the selected route;
  • whether Firebase is initialized before Reteno for manual or external token modes;
  • whether firebase_core is required for Firebase initialization;
  • whether adding Flutter firebase_messaging would create a competing FCM delegate and should be avoided;
  • how notification-center delegate handling works for the selected reteno_plugin version;
  • whether the application already assigns UNUserNotificationCenter.current().delegate;
  • whether the selected plugin version wraps an existing delegate during plugin registration;
  • whether introducing or changing an application notification-center delegate is actually required;
  • whether iOS CLICKED validation must be performed from background or lock-screen state;
  • whether DELIVERED and CLICKED must be validated with separate polling steps or separate test pushes;
  • whether Xcode Embed Foundation Extensions build phase is placed immediately after Resources;
  • whether pre-existing build blockers or validation-only dependency overrides were used;
  • whether SDK access key was shared through chat, logs, prompts, or tool output and must be rotated.

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

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 Flutter app startup, Firebase configuration, push notification handlers, deeplink handling, authentication flow, analytics logic, or existing native platform configuration.

Use the existing Flutter project setup. Do not switch package managers, app architecture, 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 keys, 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, 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, or manual device actions.

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

  • the exact build or runtime error;
  • crash reason, if available;
  • relevant logs;
  • device, simulator, or emulator details;
  • 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;
  • 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.

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 and the current customer application codebase for Flutter SDK integration.

SourceHow to access itUse it forIf unavailable
Customer application codebaseInspect the current repository or workspace opened in the AI coding agentDetect Flutter project structure, pubspec.yaml, Flutter entry point, native ios/ and android/ folders, Firebase setup, push setup, existing Reteno usage, and files to modifyAsk the user to open the project in the AI coding agent or provide the required project files
Reteno Flutter SDK GuideOpen Reteno Flutter SDKUse it as the primary source of truth for Flutter SDK overview and supported Flutter setupAsk the user to provide the Flutter SDK documentation page
Reteno Flutter Migration GuideOpen Flutter Migration GuideUse it for Flutter 1.9.0+ setup, Flutter-first initialization, migration from older native wiring, diagnostics, and plugin conflictsAsk the user or developer to confirm the Flutter SDK version and migration requirements
Reteno Flutter Android SDK SetupOpen Flutter Android SDK SetupApply Android-specific setup required by the Flutter plugin: AndroidX, Gradle, Firebase/FCM, google-services.json, AndroidManifest, and native Android configurationAsk the developer to confirm Android setup requirements
Reteno Flutter iOS SDK SetupOpen Flutter iOS SDK SetupApply iOS-specific setup required by the Flutter plugin: Notification Service Extension, App Groups, Podfile, AppDelegate, APNs/FCM token handling, and push permissionAsk the developer to confirm iOS setup requirements
Reteno Flutter Push HandlingOpen Flutter Push HandlingConfigure or verify Flutter push handling, push actions, custom push data, deeplinks, and push event handlingAsk the developer to confirm Flutter push handling requirements
Flutter SDK Release NotesOpen Flutter SDK Release NotesCheck Flutter SDK versions, release history, migration-related changes, known fixes, and compatibility notesAsk the user or developer to confirm the SDK version and required setup
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.txtUse it as the AI-readable documentation index for finding the latest Reteno docs, SDK guides, and API referencesUse direct documentation links or ask the user to provide the relevant Reteno documentation page
SDK Release NotesOpen SDK Release NotesCheck platform SDK versions, release history, compatibility notes, migration-related changes, and known fixesAsk the user or developer to confirm the SDK version and required 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
Known SDK issues / troubleshooting referenceUse the provided recurring SDK issues summary, if available in the current task or workspaceTroubleshoot common Flutter SDK integration issues, missing push statuses, token configuration issues, delayed setup, In-App issues, SDK version issues, and platform-specific gapsIf unavailable, use official Reteno documentation, SDK Release Notes, mobile push error codes, MCP results, and project logs

Note — Reteno MCP: Reteno MCP must be connected in the same environment where the AI coding agent runs. Before using MCP for validation, 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.

Note — Reteno llms.txt: Reteno llms.txt is a documentation index for AI agents. Use it to find the latest Reteno documentation pages, SDK guides, and API references. It does not replace Reteno MCP validation.

Agent Behaviour Rules

Follow these rules throughout the Flutter SDK integration:

  • Inspect the project before making any code changes.
  • Detect the Flutter project structure, package manager, Flutter entry point, native iOS/Android folders, current SDK version, Firebase setup, push notification setup, 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 documentation.
  • Use the existing project setup. Do not switch package managers, app architecture, Firebase setup, push provider, or SDK versions unless the developer explicitly confirms this.
  • Treat Flutter as the highest-level framework. Do not start with native iOS or Android setup unless the Flutter runbook or official Flutter SDK docs require native changes.
  • Preserve existing application logic.
  • Do not overwrite Flutter app startup, 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 keys, 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 Dart code, native project configuration, dependency setup, or available and authorized MCP capabilities.
  • Do not change external app configuration, credentials, APNs/FCM setup, sandbox/production settings, Firebase project settings, or Reteno dashboard settings without explicit developer or admin confirmation.

Chat-provided SDK access key rotation

If a Reteno SDK access key was shared through chat, prompt, terminal output, tool output, logs, screenshots, or any transcript, treat it as exposed.

Even if the key is later moved into a gitignored file or injected through build-time configuration, the original exposure still requires follow-up.

If exposure happened:

  • do not repeat or copy the exposed value in the final report;
  • recommend rotating the exposed key in Reteno;
  • document that rotation is required;
  • move the key to the approved project configuration method before real builds;
  • use CI/CD secret storage for production or release builds when available.

For real builds, do not rely on chat-provided values.

Expected final report wording:

SDK access key was provided through chat; treat it as exposed and rotate it before production use.

Flutter SDK access key handoff for local validation

Prefer a developer-approved local configuration source over pasting the Reteno SDK access key directly into chat.

For local Flutter dry-run validation, an approved gitignored Dart-define file may be used when this fits the project.

Example local configuration:

{
  "RETENO_ACCESS_KEY": "<local-value>"
}

Example filename:

reteno.env.json

The filename is a local runbook convention and is not a required Reteno SDK filename.

Add the local file to .gitignore.

Read the value in Flutter code through build-time configuration, for example:

const retenoAccessKey = String.fromEnvironment('RETENO_ACCESS_KEY');

Run the application with:

flutter run --dart-define-from-file=reteno.env.json

Use the project's existing configuration mechanism instead when one already exists.

Do not introduce a new configuration package only to pass the Reteno SDK access key when Dart defines or the existing project configuration are sufficient.

Do not print or echo the complete SDK access key during validation.

Do not include the complete key in:

  • final reports;
  • screenshots;
  • prompts;
  • tool output;
  • terminal transcripts;
  • committed files.

For production or release builds, use the project-approved CI/CD or build configuration mechanism.

Report:

  • SDK access key handoff method;
  • local configuration source;
  • whether the local source is gitignored;
  • whether the key was exposed through chat, logs, prompts, screenshots, terminal output, or tool output;
  • whether rotation is required;
  • intended production handoff method.

SDK access key exposure through debug logs

When Reteno SDK debug logging is enabled, inspect whether device or network logs expose authentication or SDK access-key values.

Possible sensitive log evidence may include:

X-Reteno-Access-Key
Authorization

Do not limit access-key redaction checks to the literal SDK access-key value.

Authentication headers or other log fields may contain an encoded, transformed, or otherwise reversible representation of the same credential.

For example:

Authorization: Basic <encoded-value>

must be treated as sensitive even when the literal Reteno SDK access key is not visible in the log line.

Do not copy, decode, or expose the complete encoded authentication value in:

  • prompts;
  • chat;
  • screenshots;
  • terminal transcripts;
  • tool output;
  • issue descriptions;
  • final reports.

If credential exposure must be verified, perform the verification locally without printing the decoded credential.

If the encoded or transformed authentication value can reconstruct or expose the Reteno SDK access key:

  • treat the SDK access key as exposed;
  • redact the encoded value from retained evidence;
  • recommend rotating the SDK access key before production use;
  • document that encoded credential exposure occurred.

Redacting only the literal SDK access-key string is not sufficient when another logged value can reconstruct the same credential.

Apply redaction to both:

  • direct SDK access-key values;
  • encoded or reversible authentication representations derived from the key.

Do not copy full header values into the final report.

If the complete Reteno SDK access key appears in device logs, network logs, terminal output, or captured tool output:

  • treat the key as exposed;
  • redact the value from evidence;
  • document the exposure;
  • recommend rotation before production use.

Do not assume that moving an already exposed key into a gitignored file removes the need for rotation.

For production and release builds, disable unnecessary SDK debug logging.

For development builds, consider controlling Reteno debug logging through a dedicated project-approved flag instead of enabling it automatically for every debug build.

The final report should state:

  • whether Reteno debug mode was enabled;
  • whether the SDK access key appeared in logs;
  • whether log evidence was redacted;
  • whether key rotation is required.

Change-safety boundaries

Follow these change-safety rules throughout the Flutter integration.

Sandbox Firebase / app identity validation path

The agent may use a sandbox Firebase / Reteno app configuration only when the developer explicitly approves this as a dry-run validation path.

This may require changing the Android applicationId or iOS bundle ID to match a preconfigured Firebase / Reteno mobile app.

If the agent changes Android applicationId or iOS bundle ID for sandbox validation:

  • document that this is an approved sandbox-only validation path;
  • do not report the branch as merge-ready or production-ready;
  • document the original Android applicationId or iOS bundle ID;
  • document the temporary sandbox Android applicationId or iOS bundle ID;
  • document which Firebase project was used;
  • document which Reteno mobile app was used;
  • document whether Firebase client config matches the temporary app identity;
  • document any app behavior that may break because of the app identity change;
  • do not treat unrelated product behavior broken by the app identity change as a Reteno SDK defect;
  • require repeated validation before production use with the real app identity, real Firebase project, real Reteno mobile app, real SDK access key, and real platform config.

A successful sandbox validation proves that the SDK flow can work in the selected environment.

It does not prove that the production app configuration is complete.

If sandbox Firebase / Reteno configuration is used, the final report must clearly state:

Overall status: Passed with warnings
Merge readiness: Not ready / sandbox-only
Production follow-up required: Yes

A successful sandbox validation proves that the SDK flow can work in the selected environment.

It does not prove that the production app configuration is complete.

Before production use, repeat validation with:

  • real bundle ID / application ID;
  • real Firebase project;
  • real Reteno mobile app;
  • real SDK access key;
  • real GoogleService-Info.plist or google-services.json;
  • real signing / provisioning setup.

Pre-existing build fixes bundled with Reteno integration

If the app cannot build before Reteno validation can start, identify whether the failure is pre-existing or introduced by Reteno changes.

A pre-existing build fix may be used only when it is the smallest safe change required to reach build/run validation.

Examples:

  • dependency locked to a version incompatible with the current Flutter / Dart version;
  • iOS deployment target below dependency minimums;
  • Firebase config file present in the repository but not added to the Runner target resources;
  • Xcode build phase order issue after adding or embedding an extension;
  • validation-only dependency override or build-system workaround.

Document each pre-existing build fix separately from Reteno SDK integration changes.

For each pre-existing build fix, report:

  • failed command;
  • exact error;
  • why the issue is unrelated to Reteno;
  • files changed;
  • whether the fix is validation-only;
  • whether the fix is merge-ready;
  • whether project-owner review is required.

If pre-existing build fixes are bundled into the same commit as the Reteno integration, report:

Pre-existing build fixes are bundled with the Reteno integration commit and require project-owner review before merge.

Do not report the branch as merge-ready until those fixes are separated or explicitly reviewed by the project owner.

Native integration reproducibility and Git tracking

Do not assume that a successful local Android or iOS build means the Flutter Reteno integration is reproducible from the repository.

Before reporting the integration as merge-ready, verify whether every required native integration file is:

  • tracked by Git;
  • intentionally generated from a tracked project configuration;
  • or reproducibly restored by an approved project-owned script or generation mechanism.

Inspect the repository ignore rules for required Reteno / Firebase native configuration.

Useful checks may include:

git status
git status --ignored
git check-ignore -v <path>

Check required files that are relevant to the current integration, including:

android/app/build.gradle
android/app/build.gradle.kts
android/settings.gradle
android/settings.gradle.kts
android/app/src/main/AndroidManifest.xml
android/app/google-services.json

ios/Podfile
ios/Podfile.lock
ios/Runner/AppDelegate.swift
ios/Runner/*.entitlements
ios/GoogleService-Info.plist
ios/NotificationServiceExtension/**
ios/Runner.xcodeproj/project.pbxproj

Do not require every example file above when it is not part of the current project.

If a required native file is ignored, untracked, or exists only in the current local working copy:

  • determine whether the project intentionally generates that file;
  • determine whether the Reteno-specific configuration can be reproduced from tracked sources;
  • do not modify repository ignore policy automatically;
  • ask the project owner before widening an existing .gitignore allowlist;
  • document the affected files;
  • document what would be missing in a clean checkout.

If required native Reteno configuration cannot be reproduced from the repository, report:

Merge readiness: Not ready

even when runtime validation passes.

A locally successful integration is not merge-ready if another developer or CI environment cannot reproduce:

  • Firebase configuration;
  • Android desugaring or Google Services setup;
  • iOS Pod configuration;
  • Notification Service Extension;
  • App Groups;
  • notification delegate configuration;
  • required Xcode project settings.

Prefer one of these project-approved outcomes:

  • track the required native files;
  • widen an existing narrow Git allowlist;
  • generate the required native setup through tracked configuration;
  • provide an approved reproducible project script.

Do not treat a pre-existing repository ignore policy as a Reteno SDK defect.

Document in the final report:

  • whether required Android native files are tracked or reproducible;
  • whether required iOS native files are tracked or reproducible;
  • which required native files are ignored;
  • whether a clean checkout can reproduce the integration;
  • whether project-owner action is required before merge.

Flutter Project Detection

Use this runbook for Flutter applications.

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

  • pubspec.yaml;
  • pubspec.lock;
  • lib/main.dart;
  • android/;
  • ios/;
  • android/app/build.gradle;
  • android/app/build.gradle.kts;
  • ios/Podfile;
  • ios/Runner.xcodeproj;
  • ios/Runner.xcworkspace;
  • Flutter plugin configuration;
  • Firebase configuration for Flutter;
  • google-services.json;
  • GoogleService-Info.plist.

Before making changes, detect:

  • Flutter project structure;
  • package manager: flutter pub;
  • Flutter SDK version, if available;
  • Dart entry point, usually lib/main.dart;
  • existing Reteno Flutter plugin usage, if any;
  • existing reteno_plugin dependency in pubspec.yaml;
  • current Reteno Flutter plugin version, if installed;
  • Firebase / FCM setup;
  • native Android setup under android/;
  • native iOS setup under ios/;
  • existing push notification setup;
  • existing deeplink handling;
  • existing custom event tracking;
  • existing authentication or user identification flow;
  • existing In-App message setup, if any.

Do not use the native Android or native iOS runbook directly for Flutter projects.

Apply native Android or iOS steps only when the Flutter SDK setup requires native platform changes.

If the project appears to be React Native, Expo, Cordova, Ionic, Unity, or native iOS/Android instead of Flutter, stop and ask the developer to confirm which platform runbook should be used.

Required Inputs

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

Before asking the developer, inspect the project and detect:

  • Flutter project structure;
  • pubspec.yaml;
  • current reteno_plugin version, if installed;
  • Flutter SDK version, if available;
  • Dart app entry point;
  • native android/ and ios/ folders;
  • Firebase / FCM configuration;
  • google-services.json;
  • GoogleService-Info.plist;
  • Android package name / applicationId;
  • iOS bundle ID;
  • 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 Flutter 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;
  • Android package name / applicationId, if Android validation is included and it cannot be detected;
  • iOS bundle ID, if iOS validation is included and it cannot be detected;
  • confirmation which 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 Flutter push setup and validation

  • 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 iOS push provider is configured as FCM or APNs, if iOS validation is included;
  • confirmation that GoogleService-Info.plist belongs to the correct Firebase project, if iOS uses FCM;
  • confirmation that Notification Service Extension and App Groups are configured for iOS, if iOS validation is included;
  • 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;
    • 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 can open the test push notification if the agent cannot interact with the environment directly.

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 Flutter app code;
  • confirmed trigger event name, if event-triggered In-App validation is part of the current integration task;
  • access to Reteno MCP, Reteno UI, backend data, event log, or logs to verify that the event was received and assigned to the correct contact.

Required for feature-specific In-App validation

Ask for these inputs 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;
  • confirmation that the event name eventTestInApp can be used for validation;
  • 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.

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:

  • login or registration flow for externalCustomerId validation;
  • test login or registration credentials, if required;
  • expected stable customer ID format for externalCustomerId;
  • confirmation that the developer, user, or QA engineer can complete the login or registration flow.

User identification with setUserAttributes(userExternalId: ...)

When validating user identification, do not only refer to externalCustomerId as a Reteno-side field.
In Flutter code, identify the user through:

Reteno().setUserAttributes(userExternalId: userExternalId);

Use a stable backend user ID as userExternalId whenever available.
Do not use unstable identifiers such as:

  • device ID;
  • mobile push token;
  • temporary session ID;
  • random UUID generated on the device.
    If the application exposes only email or another non-numeric identifier, document this as a product decision and warning in the final report.
    Expected result:
  • before login or registration, the contact is anonymous;
  • after login or registration, userExternalId is sent through setUserAttributes(...);
  • Reteno-side externalCustomerId is assigned to the same contact/device;
  • mobilePushToken remains assigned after identification.

Security rules

Do not include credentials, Firebase keys, APNs credentials, service account data, private keys, certificates, 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.

If a required input is missing, explain:

  • what information is missing;
  • why it is required;
  • whether the integration can continue without it;
  • which validation steps will be blocked until the input is provided.

Developer Preparation Checklist

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

The agent 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 Flutter project repository;
  • the correct branch for the integration run;
  • whether the run should be done on a temporary local branch;
  • whether there are local agent instructions, such as CLAUDE.md, Cursor rules, or similar files;
  • whether the project uses standard Flutter project structure;
  • whether the project has generated android/ and ios/ folders;
  • Flutter SDK version, if it cannot be detected automatically;
  • whether the app should be validated on Android, iOS, or both.

2. Reteno setup

The developer or admin should prepare or confirm:

  • selected Reteno organization / workspace;
  • selected Reteno mobile app for Android, if Android validation is included;
  • selected Reteno mobile app for iOS, if iOS validation is included;
  • Reteno SDK access key or approved configuration method for providing it;
  • whether the selected Android app is configured for FCM;
  • whether the selected iOS app is configured for FCM or APNs;
  • whether the selected Reteno app matches the Android package name / iOS bundle ID and environment;
  • whether the signed-in Reteno account has access to the selected app/environment.

Do not paste SDK access keys, Firebase keys, APNs credentials, private keys, certificates, or service account data into public chats, public files, or committed source files.

3. Firebase / APNs setup

For Android validation, the developer should confirm:

  • Firebase / FCM is configured for the Android app;
  • the correct google-services.json is available;
  • Android package name / applicationId matches Firebase and Reteno configuration;
  • existing Firebase Messaging service and notification handlers should be preserved.

For iOS validation, the developer should confirm:

  • whether iOS push uses FCM or APNs;
  • the correct GoogleService-Info.plist is available, if iOS uses FCM;
  • APNs sandbox / production setup is correct, if iOS uses APNs;
  • Apple signing, capabilities, App Groups, and Notification Service Extension setup can be modified or confirmed when required;
  • iOS bundle ID matches Firebase / APNs / Reteno configuration.

4. Flutter-specific setup

The developer should confirm:

  • whether reteno_plugin is already installed;
  • current reteno_plugin version, if installed;
  • whether the project uses older Reteno().initWith(...) initialization;
  • whether the project should migrate to Flutter-first initialization with Reteno().initialize(...);
  • whether existing native Reteno wiring exists in android/ or ios/;
  • whether existing native wiring is app-specific or Reteno-only legacy setup;
  • whether Reteno().diagnose() can be run after integration.

Do not remove native Firebase, push, deeplink, analytics, or authentication logic unless the developer confirms that it is Reteno-only legacy wiring and the Flutter Migration Guide supports removing it.

5. Test environment

The developer should prepare one or more supported test environments:

  • Android emulator, if Android validation is included;
  • physical Android device, if emulator validation is unavailable or unreliable;
  • iOS simulator, if supported and confirmed by the developer;
  • physical iOS device, if simulator validation is unavailable or production-like validation is required;
  • another supported environment.

The developer should also confirm:

  • the app can be built and run in the selected environment;
  • push notification permission can be granted;
  • the developer can manually open the test push notification if the agent cannot interact with the device, simulator, or emulator directly;
  • the developer can provide logs if build/run fails.

6. Reteno MCP and local automation tools

The developer should confirm whether Reteno MCP is connected in the same environment where the AI coding agent runs.

Reteno MCP is the preferred path for Reteno-side validation when the required capability is available:

  • contact lookup;
  • mobilePushToken verification;
  • test push creation and sending;
  • DELIVERED / CLICKED status checks.

Local device, simulator, or emulator automation tooling is separate from Reteno MCP.

Local automation tooling is used for runtime interaction:

  • running the app;
  • granting notification permission;
  • opening the test push notification.

If local automation tooling is unavailable, the developer should be ready to perform these actions manually and confirm completion.

If Reteno MCP is unavailable or a required capability cannot provide reliable evidence, use an approved Reteno UI, backend, SDK-log, device-log, redacted network-log, screenshot, or developer / QA fallback when available.

Do not report Reteno-side validation as blocked only because MCP itself is unavailable.

Report the result as Passed with warnings when the required Reteno-side result is independently verified through reliable fallback evidence.

Report the result as Blocked or Partial only when the required result cannot be verified through MCP or any approved fallback evidence.

If local device automation MCP is added during the current agent session, the developer may need to restart the agent environment before the tool becomes available.

7. Build and runtime logs

If the agent cannot build or run the app, the developer should provide:

  • exact build command used;
  • exact error message;
  • Flutter logs;
  • Android Gradle logs, if Android build fails;
  • Logcat output, if Android runtime fails;
  • Xcode logs, if iOS build or runtime fails;
  • CocoaPods logs, if iOS dependency setup fails;
  • simulator, emulator, or device details;
  • reproduction steps.

8. Feature-specific validation preparation

If the current integration task includes custom events, the developer should confirm:

  • a test custom event can be added to the Flutter app code;
  • the event name testAiEvent can be used;
  • event validation is possible through Reteno MCP, Reteno UI, backend logs, or event logs.

If the current integration task includes In-App messages, the developer or admin should confirm:

  • whether an In-App message already exists or should be created;
  • existing In-App message name or ID, if an existing In-App should be used;
  • whether default In-App message creation is available in the selected Reteno account;
  • whether the developer/admin can create, configure, publish, or confirm the In-App message manually in Reteno UI if required.

If the current integration task includes externalCustomerId, the developer should confirm:

  • login or registration flow can be tested;
  • test credentials are available, if required;
  • expected stable customer ID format is known.

If the current integration task includes deeplinks or custom push data, the developer should confirm:

  • expected deeplink format;
  • existing deeplink package or implementation;
  • expected behavior for cold start and resumed app state;
  • expected custom push payload fields.

9. Pre-flight result

Before code changes start, the agent should summarize the pre-flight status:

  • project access: ready / blocked;
  • SDK access key handling: ready / placeholder / missing;
  • Flutter SDK / reteno_plugin version: detected / needs confirmation;
  • Firebase / APNs setup: confirmed / needs confirmation;
  • Reteno MCP: connected / unavailable;
  • local device automation: available / unavailable;
  • test environment: ready / unavailable;
  • manual actions required from developer;
  • validation steps that may be blocked.

If a required item is missing, continue only with the steps that are not blocked.

Do not mark the integration as complete until build/run and required Reteno-side validation are completed through Reteno MCP or approved fallback evidence, or a blocker is clearly documented.

Flutter SDK Integration Workflow

Follow this workflow for Flutter SDK integration.

0. Prepare a safe working branch

Before making code changes, check the current Git branch and working tree state.

For evaluation, test, or temporary integration runs:

  • create and switch to a temporary local branch;
  • do not make temporary integration changes directly on main or master;
  • do not commit, push, or merge changes unless the developer explicitly asks for it.

If you cannot create or switch to a temporary branch, ask the developer to confirm how to proceed before modifying files.

Report the branch name and working tree state in the final report.

1. Inspect the project

Before making changes, inspect the Flutter project.

Identify:

  • Flutter project structure;
  • package manager: flutter pub;
  • Flutter SDK version, if available;
  • Dart entry point, usually lib/main.dart;
  • existing Reteno Flutter SDK usage, if any;
  • existing reteno_plugin dependency in pubspec.yaml;
  • current Reteno Flutter SDK version, if installed;
  • native Android setup under android/;
  • native iOS setup under ios/;
  • Firebase / FCM setup;
  • google-services.json;
  • GoogleService-Info.plist;
  • existing push notification setup;
  • existing notification handlers;
  • existing deeplink handling;
  • existing custom event tracking;
  • existing user identification or login flow;
  • existing In-App setup, if present;
  • files that may need to be updated.

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

2. Check official Flutter SDK documentation

Use the official Reteno Flutter documentation as the source of truth.

Use:

Do not start from native Android or native iOS setup unless the Flutter documentation or the project setup requires native platform changes.

3. Check whether Reteno Flutter SDK is already installed

If Reteno Flutter SDK is already installed, do not reinstall it from scratch.

Instead:

  • detect the current SDK version from pubspec.yaml, pubspec.lock, or installed package metadata;
  • check how the SDK is initialized;
  • check whether Android setup is complete;
  • check whether iOS setup is complete;
  • check whether push notification handling is configured;
  • check whether the current integration is incomplete, outdated, or misconfigured.

If Reteno Flutter SDK is not installed, continue with a new Flutter SDK integration flow.

4. Check SDK version, migration guide, and release notes

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

  • detect the current SDK version from project files, if installed;
  • check the official Flutter SDK Release Notes;
  • check the Flutter Migration Guide;
  • check whether the project uses an older initialization pattern;
  • check whether the issue may be related to an outdated SDK version, compatibility issue, migration requirement, or known fixed issue.

Before selecting the reteno_plugin version, verify the version from the current available sources.

Check:

  • Flutter SDK Release Notes;
  • package registry metadata;
  • pubspec.yaml;
  • pubspec.lock;
  • installed package metadata, when available;
  • SME or platform-owner confirmation, when available.

Do not hardcode a specific reteno_plugin version in this runbook as the permanently current version.

If release notes, package registry metadata, project files, and SME confirmation show different versions:

  • document the mismatch;
  • do not assume that an older SME-confirmed version is still the current version;
  • do not automatically upgrade the project;
  • ask the developer or platform owner which version should be used for the current integration run.

Use the selected version consistently throughout the integration.

Do not use pre-release, beta, alpha, or release-candidate versions unless the developer explicitly asks for them.

After selecting the Flutter plugin version, also determine the native Reteno SDK versions resolved by that plugin:

  • iOS Reteno pod version;
  • Android com.reteno:fcm version.

Use the selected plugin package metadata, plugin podspec, Gradle dependency resolution, Podfile.lock, package lock files, or other available dependency evidence.

Report both the selected reteno_plugin version and the resolved native Reteno SDK versions in the final report.

For Flutter SDK 1.9.0+, prefer the Flutter-first initialization flow with Reteno().initialize(...).

Reteno().initWith(...) may exist in older projects as a backward-compatible alias, but initialize(...) is the recommended setup for the current Flutter-first flow.

If the project already has Reteno-only native wiring from older versions, verify whether it should be removed according to the Flutter Migration Guide.

Do not remove native code that belongs to the application’s own Firebase, push, deeplink, analytics, or authentication logic.

Do not update the SDK automatically unless the developer explicitly asks for an update or the official documentation indicates that an update is required.

If documentation and package registry versions differ, ask the developer or platform owner which version to use.

5. Check prerequisites

Before applying SDK changes, confirm that the required prerequisites are available.

Check or ask for:

  • Reteno SDK access key or approved project configuration method;
  • selected Reteno app/environment;
  • target validation platform:
    • Android only;
    • iOS only;
    • both Android and iOS;
  • Android package name / applicationId, if Android validation is included;
  • iOS bundle ID, if iOS validation is included;
  • Firebase / FCM configuration;
  • google-services.json, if Android validation is included;
  • GoogleService-Info.plist, if iOS uses FCM;
  • APNs or FCM configuration for iOS, if iOS validation is included;
  • Reteno MCP access;
  • selected test environment for push validation.

If a prerequisite cannot be verified automatically, ask the developer for confirmation.

6. Apply Flutter SDK integration changes

Use the official Flutter SDK documentation to apply the integration.

Depending on the project, you may need to:

  • add reteno_plugin dependency;
  • update pubspec.yaml;
  • run flutter pub get;
  • initialize Reteno from the Flutter app startup path;
  • configure SDK access key through the approved project configuration method;
  • configure push notification permission request;
  • configure push handling according to the official Flutter Push Handling Guide;
  • configure custom event tracking, if required by the current integration task;
  • preserve existing Flutter startup logic;
  • preserve existing Firebase setup;
  • preserve existing push notification handling;
  • preserve existing deeplink handling;
  • preserve existing authentication and analytics logic.

Do not overwrite existing application logic.

6.0. Register required push handlers before awaited startup work

When the current integration uses Reteno push-received, notification-clicked, action-button, or related Flutter streams/listeners, register the required handlers early enough that a notification-triggered application start cannot lose the initial event.

Do not place required push/click listener registration only after a long asynchronous startup sequence.

When supported by the selected reteno_plugin version and compatible with the existing application architecture, prefer this ordering:

  1. register required Reteno push / click / action listeners synchronously;
  2. initialize Firebase when required by the selected token mode;
  3. initialize Reteno;
  4. continue the application's existing asynchronous startup work;
  5. request notification permission only when the application has reached the required UI / Activity state.

Do not introduce listeners for features that are not used by the application or current integration scope.

Do not move unrelated application initialization only to satisfy this ordering.

Preserve the existing architecture while ensuring that required Reteno notification callbacks are registered before they can be missed during cold-start processing.

Validate the relevant states when they are part of the current task:

  • foreground push;
  • background notification tap;
  • notification-triggered cold start;
  • action-button interaction.

Document in the final report whether required push/click/action handlers were registered before asynchronous startup work.

6.1 Push permission request

Use the Flutter SDK method:

Reteno().requestPushPermission();

Use this method when the current integration task includes mobile push validation.
Expected result:

  • the app requests push notification permission when required;
  • the permission result is reflected in SDK / Reteno-side state;
  • mobilePushToken can be assigned after permission and token setup are complete.
    Do not mark mobile push validation as complete until permission state and token assignment are verified.

7. Apply Android-specific setup, if Android validation is included

Apply Android native changes only when the Flutter setup requires them.

Check or update:

  • android/build.gradle;
  • android/app/build.gradle;
  • android/app/build.gradle.kts;
  • android/settings.gradle;
  • android/app/src/main/AndroidManifest.xml;
  • android/gradle.properties;
  • google-services.json;
  • Firebase / FCM configuration;
  • Android notification permission;
  • existing Firebase Messaging service;
  • existing notification handlers;
  • package name / applicationId;
  • minSdk;
  • Java compatibility settings;
  • AndroidX / Jetifier settings, if required;
  • notification icon / color metadata, if required.

For Android setup, verify:

  • Maven Central repository is available where required;
  • Firebase Messaging dependency is present when required;
  • AndroidX is enabled when required;
  • Java 1.8 compatibility is configured when required;
  • core library desugaring is configured. This is required for a clean Android build with the current Flutter Reteno SDK setup and must not be treated as optional.
    Do not skip desugaring as a project-specific optional step.

If Android build fails because desugaring is missing, add the required desugaring configuration according to the official Flutter Android SDK setup.

Document the change in the final report.

Android notification icon customization

Treat Android notification icon customization as feature-specific configuration.

Do not require a custom notification icon for core Reteno mobile push validation unless:

  • the current integration task explicitly requests custom notification appearance;
  • the existing application already defines a required notification icon;
  • official Reteno documentation for the selected SDK version requires additional icon metadata.

Do not assume that generic Firebase notification icon metadata is automatically used by Reteno.

When custom Reteno notification icon behavior is required:

  1. inspect the official Reteno Flutter / Android documentation for the selected SDK version;
  2. inspect the installed native Reteno Android SDK metadata and application manifest when necessary;
  3. determine the exact Reteno-specific metadata key expected by the selected version;
  4. verify that the referenced drawable resource exists;
  5. preserve the application's existing notification resources and Firebase configuration;
  6. build and run the application;
  7. send a test Reteno push;
  8. verify the actual notification icon on the selected Android device or emulator.

A selected SDK version may use Reteno-specific Android metadata for notification icon configuration.

Do not hardcode a metadata key from an older SDK version without verifying that it is still supported by the current selected version.

If current documentation or installed SDK evidence confirms a metadata key such as:

com.reteno.notification_icon

use it only for the selected version and project where it is verified.

Do not add or change notification icon metadata when custom icon behavior is not part of the current integration task.

A default notification icon that does not match a custom design requirement is not evidence that core Reteno push delivery failed.

Keep these validation results separate:

  • core push delivery;
  • click tracking;
  • notification appearance / custom icon configuration.

Document in the final report:

  • whether custom Android notification icon behavior was requested;
  • which metadata/configuration source was verified;
  • whether Reteno-specific icon configuration was added or already present;
  • whether the expected icon was visually validated.

7.1. Android runtime API level requirement

Reteno Flutter SDK requires Android 8.0+ runtime support.
Use an Android test environment with API level 26 or higher.
Do not validate Reteno Flutter SDK on Android emulators or devices below API 26.
Although a Flutter app may still install when the project has minSdkVersion 21, Reteno SDK will not work on Android below API 26.
Expected result on Android API < 26:

  • no contact may be created;
  • no mobilePushToken may be assigned;
  • Reteno MCP may not find the expected contact/token;
  • push validation cannot pass.
    If the selected emulator/device is below API 26, report it as an unavailable test environment, not as an SDK integration bug.
    Document Android API level in the final report.

Do not automatically raise the application's declared minSdk only to satisfy the Reteno runtime requirement.

The selected validation device or emulator must use Android API 26 or higher.

If the application declares minSdk below 26:

  • document the declared minSdk;
  • document that Reteno functionality is unavailable below Android API 26;
  • validate Reteno only on API 26 or higher;
  • do not change the supported-device range without developer or product-owner approval.

Treat raising minSdk as a product compatibility decision, not an automatic SDK integration fix.

Document both:

  • application minSdk;
  • validation runtime API level

in the final report.

If the app already uses a custom FirebaseMessagingService, preserve app-specific logic.

If the custom service was added only for legacy Reteno wiring, check the Flutter Migration Guide before removing it.

If the app must keep a custom Firebase Messaging service, keep a single app-level MESSAGING_EVENT service, make it compatible with Reteno handling according to the official docs, and avoid duplicate service conflicts.

7.2. Android 13+ permission timing

On Android 13+, call Reteno().requestPushPermission() only after an Android Activity is available.
Do not call requestPushPermission() too early during Flutter startup before the Activity exists.
Calling it before Activity availability may crash or fail.
Recommended approach:

  • initialize the SDK from the Flutter startup path;
  • wait until Flutter app startup / first screen / post-frame callback / app lifecycle point where Activity is available;
  • then call Reteno().requestPushPermission().
    If the app has an existing permission-prompt UX, preserve it and integrate Reteno permission update logic into the existing flow.
    Document where requestPushPermission() is called in the final report.

8. Apply iOS-specific setup, if iOS validation is included

Apply iOS native changes only when the Flutter setup requires them.
Check or update:

  • ios/Podfile;
  • ios/*.xcworkspace;
  • ios/*.xcodeproj;
  • AppDelegate;
  • Notification Service Extension;
  • Notification Content Extension, if carousel or GIF push UI is required;
  • App Groups;
  • APNs or FCM token handling;
  • GoogleService-Info.plist, if iOS uses FCM;
  • push notification permission flow;
  • existing UNUserNotificationCenterDelegate;
  • existing Firebase MessagingDelegate, if FCM is used;
  • existing deep link handling.

8.1. iOS notification delegate handling for the selected reteno_plugin version

Do not assign UNUserNotificationCenter.current().delegate only because this runbook contains an example.

First inspect:

  • the current AppDelegate;
  • the current Flutter iOS application lifecycle;
  • whether the application already assigns UNUserNotificationCenter.current().delegate;
  • whether another plugin assigns a notification-center delegate;
  • how the selected reteno_plugin version installs or wraps its notification-center coordinator;
  • the actual Flutter plugin registration point for the current Flutter version.

For plugin versions where Reteno installs its notification-center coordinator during plugin registration and wraps the delegate that already exists at that moment:

  • if the application already requires its own notification-center delegate, make sure that delegate is installed before Reteno plugin registration when required by the selected plugin version;
  • if the application does not already require its own notification-center delegate, do not introduce a new delegate assignment only for Reteno;
  • do not assign a new delegate after Reteno plugin registration if doing so would replace Reteno's coordinator.

Do not assume that every Flutter application contains the literal call:

GeneratedPluginRegistrant.register(with: self)

Newer Flutter lifecycle implementations may register plugins through another engine or scene lifecycle path.

Identify the actual plugin registration point used by the current application.

Preserve existing app-specific notification logic.

If the application already has notification delegate handling:

  • inspect the existing UNUserNotificationCenterDelegate;
  • inspect Firebase Messaging delegate setup, if FCM is used;
  • identify old Reteno forwarding calls;
  • verify whether the selected Flutter plugin version already handles notification forwarding;
  • remove legacy Reteno-only forwarding only when the current Flutter setup no longer requires it.

Expected result:

  • Reteno notification handling is installed correctly;
  • existing app-specific delegate behavior is preserved;
  • no later delegate assignment replaces Reteno notification handling;
  • the notification can be opened from background or lock-screen state;
  • CLICKED reaches Reteno and the Flutter application.

If DELIVERED works but CLICKED does not:

  • inspect notification-center delegate handling;
  • inspect plugin registration order;
  • inspect whether another library replaces the delegate after Reteno registration;
  • inspect duplicate legacy Reteno forwarding before changing unrelated push logic.

Treat delegate behavior as version-specific.

Do not apply a fixed delegate-order workaround unless it is supported by the selected reteno_plugin version, the current application lifecycle, official documentation, package implementation, or SME confirmation.

8.2. iOS device token handling mode

For Flutter iOS validation, explicitly choose and document deviceTokenHandlingMode during initialization.

The agent must not guess this mode.

Choose the mode according to:

  • selected iOS push route;
  • selected reteno_plugin version;
  • existing Firebase / APNs setup;
  • official Flutter iOS SDK documentation;
  • current application token ownership.

Available modes:

  • automatic;
  • manual;
  • external.

For an iOS Firebase / FCM route, manual may be used when the Flutter plugin owns or observes the Firebase token flow.

In this setup:

  • initialize Firebase before Reteno;
  • allow the selected Reteno Flutter plugin to observe the Firebase / FCM token through its supported integration path;
  • verify that Reteno receives an FCM token;
  • do not call setPushToken(...) only because the mode is named manual.

Use external when the application itself owns token retrieval and must explicitly pass the token to Reteno from Flutter code.

In external mode, use:

Reteno().setPushToken(token);

Use automatic only when supported by the selected push route and the selected plugin version.

Do not mix plugin-managed token handling with explicit setPushToken(...) submission unless the selected SDK version and official documentation require it.

Expected result:

  • the selected mode matches the actual token ownership model;
  • Firebase is initialized before Reteno when FCM is used;
  • Reteno receives the expected token type;
  • setPushToken(...) is called only when required by the selected mode.

Document in the final report:

  • selected deviceTokenHandlingMode;
  • selected iOS push route;
  • whether setPushToken(...) was required;
  • whether setPushToken(...) was implemented;
  • whether the final Reteno-side token type is FCM or APNs.

8.3. External token mode and setPushToken(...)

If deviceTokenHandlingMode is set to external, the agent must explicitly pass the push token to Reteno from Flutter code.
Use the Flutter SDK method:

Reteno().setPushToken(token);

Do not mark push setup as complete in external mode until the token is passed through setPushToken(...).
Expected result:

  • the app receives or obtains the push token;
  • the token is passed to Reteno through Reteno().setPushToken(token);
  • Reteno MCP, Reteno UI, backend data, or logs confirm that mobilePushToken is assigned to the contact.
    If setPushToken(...) cannot be called because the app does not expose the token to Flutter, document this as a blocker or request a native-to-Flutter token bridge from the developer.

Do not overwrite existing native iOS app lifecycle or notification handling.
If the project already has custom push notification handling, preserve it and add Reteno processing calls only where required.
For Flutter SDK 1.9.0+, if the project previously initialized Reteno from AppDelegate, check the Flutter Migration Guide and move initialization to Flutter startup when required.

Keep Notification Service Extension and App Groups when they are required for full iOS Reteno push behavior.

8.4. iOS App Group naming

When configuring App Groups for Flutter iOS, use the required Reteno shared-storage App Group format:

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

Use the main application 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;
  • the Notification Service Extension target.
    Expected result:
  • both targets have the same Reteno App Group;
  • the App Group uses the main app bundle ID;
  • the App Group does not use the extension bundle ID;
  • rich push / delivery status handling can access shared Reteno storage.

8.5. Notification Service Extension dependency

For Flutter iOS, the main Runner target receives the native Reteno iOS SDK through the selected reteno_plugin.

The Notification Service Extension target must explicitly include the native Reteno iOS SDK dependency.

Do not hardcode an older Reteno iOS pod version from this runbook.

Before editing the Notification Service Extension Podfile configuration:

  1. Verify the selected reteno_plugin version.
  2. Determine the native iOS Reteno pod version resolved by that plugin.
  3. Verify the resolved version through the selected plugin podspec, Podfile.lock, package metadata, or other dependency-resolution evidence.
  4. Use the same Reteno iOS SDK version in the Notification Service Extension target.

Example:

target 'NotificationServiceExtension' do
  inherit! :search_paths
  pod 'Reteno', '<resolved-Reteno-iOS-version>'
end

The Notification Service Extension Reteno pod version must match the native Reteno iOS SDK version resolved by the selected Flutter plugin.

Do not allow the main application target and Notification Service Extension target to resolve different Reteno iOS SDK versions.

Do not assume that the extension receives Reteno automatically from the Flutter plugin.

Expected result:

  • Runner builds with the selected Flutter plugin;
  • Notification Service Extension builds with the matching explicit Reteno dependency;
  • the main app and extension use the same Reteno iOS SDK version;
  • the extension can use Reteno notification service extension classes;
  • the application builds successfully after CocoaPods dependency resolution.

Document the resolved Reteno iOS SDK version in the final report.

9. Run diagnostics, if available

After initialization is configured, run Reteno diagnostics when the selected Flutter SDK version supports it.

Example:

final issues = await Reteno().diagnose();

Expected healthy local diagnostic result:

[]

A clean Reteno().diagnose() result is a diagnostic smoke check only.

Do not use:

[]

as proof that the full Reteno integration is complete.

A clean diagnostics result does not replace runtime and Reteno-side validation.

Even when Reteno().diagnose() returns no issues, continue with the required validation flow:

  • verify that the expected contact exists;
  • verify deviceId, when available;
  • verify mobilePushToken;
  • verify token type;
  • verify notification permission state;
  • send a test push;
  • verify DELIVERED;
  • open the notification;
  • verify CLICKED;
  • validate any additional Reteno feature included in the current integration task.

Report the diagnostics result separately from end-to-end validation results.

Do not mark the integration as complete based on diagnose() alone.

If diagnostics returns issue codes, inspect the returned issues before continuing.

Possible diagnostic categories may include:

  • missing FCM messaging service;
  • missing Reteno messaging service;
  • FCM messaging service conflict;
  • missing FCM token;
  • FCM token fetch failure;
  • legacy native wiring conflicts.

Report diagnostics only for the current validation run.

If diagnostics was not captured during the current run, do not report an earlier successful diagnostic result as current-run validation.

A result from an earlier run may be included only as historical context.

If diagnostics cannot be run, document why in the final report and continue with the remaining required Reteno-side validation where possible.

9.1. Static analysis and tooling-failure fallback

When static analysis is part of the current integration workflow, use the project's normal Flutter analysis command first.

Example:

flutter analyze

Distinguish between:

  • actual Dart / Flutter diagnostics reported for application code;
  • analyzer startup failure;
  • Flutter analysis-server crash;
  • local SDK or environment failure;
  • command execution failure unrelated to the changed code.

If flutter analyze reports actual code diagnostics:

  • inspect the diagnostics;
  • fix relevant issues when safe;
  • do not ignore errors introduced by the integration.

If flutter analyze itself crashes or cannot start correctly:

  • capture the exact tooling error;
  • do not automatically report the changed Reteno integration code as invalid;
  • do not repeatedly rerun the same failing command without a new hypothesis.

When available, use a narrower fallback analysis for the changed Dart files.

Example:

dart analyze <changed-paths>

Use only paths relevant to the current integration.

Do not represent targeted dart analyze as equivalent to a successful full-project flutter analyze.

A targeted fallback may show that the modified Dart code has no static-analysis errors while the full Flutter analyzer remains unavailable.

Document:

  • primary analysis command;
  • primary result;
  • whether the failure was a code diagnostic or tooling/environment failure;
  • fallback analysis command, if used;
  • fallback result;
  • files or paths covered by fallback analysis.

If the full-project analyzer remains unavailable but:

  • the relevant changed Dart code passes available targeted analysis;
  • the application builds;
  • the application runs;
  • required Reteno runtime validation passes;

report the analyzer limitation separately from the Reteno integration result.

10. Build and run the application

After applying code changes, build or run the application when possible.

Use the platform selected for validation:

  • Android emulator;
  • physical Android device;
  • iOS simulator, if supported and confirmed by the developer;
  • physical iOS device;
  • another supported test environment.

If build or run fails, try to fix the issue only when it can be resolved safely through Dart code, dependency setup, native project configuration, or supported tooling.

Do not retry indefinitely.

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, or manual device actions.

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

  • exact error message;
  • crash reason, if available;
  • relevant logs;
  • simulator, emulator, or device details;
  • reproduction steps.
    Continue only after the developer provides the missing information or confirms the blocker.

11. Validate the integration with Reteno MCP

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

Continue with the validation flow:

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

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

12. Troubleshoot and retry failed checks

If a build, integration, or validation check fails, do not stop immediately.

For each failed check:

  1. Identify the failed step.
  2. Capture the observed result, error, log, MCP response, or missing status.
  3. Check official Reteno Flutter documentation, native iOS/Android documentation when relevant, SDK Release Notes, mobile push error codes, MCP results, project logs, and known SDK issue references.
  4. Apply a safe fix if the issue can be resolved through Dart code, native project configuration, dependency setup, or available and authorized MCP capabilities.
  5. Build, run, or validate again after applying the fix.
  6. Continue the workflow if the issue is resolved.
  7. If the issue requires external access, unsupported MCP capability, Reteno dashboard changes, Firebase setup, APNs/FCM setup, credentials, or admin confirmation, ask the developer or admin to complete the required step.
  8. Document all attempted fixes and remaining blockers in the final report.

Do not repeat the same fix indefinitely.

If the issue cannot be resolved after a reasonable retry, document what was tried and escalate with evidence.

13. Return the final report

At the end of the integration, return a structured final report.

Include:

  • detected platform and framework;
  • evidence used for Flutter detection;
  • target validation platform: Android, iOS, or both;
  • Flutter SDK version;
  • Reteno Flutter SDK version;
  • files inspected;
  • files changed;
  • dependencies added or updated;
  • native Android changes, if any;
  • native iOS changes, if any;
  • manual steps requested;
  • manual steps completed;
  • build/run result;
  • Reteno MCP connection;
  • Reteno MCP validation results;
  • unresolved blockers;
  • recommended next steps.

Manual Setup Steps

Do not stop when manual interaction is required.

Before requesting manual action, check whether the step can be completed through Dart changes, native project configuration, supported tooling, or available and authorized MCP capabilities.

If you cannot complete a step programmatically, ask the developer to perform the required action, wait for confirmation, and then continue the integration or validation flow.

1. Complete project-level configuration when possible

If the project setup allows it, complete project-level configuration yourself.

For Flutter, this may include:

  • updating pubspec.yaml;

  • running flutter pub get;

  • updating Flutter initialization code;

  • updating native Android Gradle configuration when required;

  • updating native iOS Podfile or project configuration when required;

  • adding or preserving Firebase / FCM configuration;

  • updating push notification permission handling;

  • adding Reteno push processing calls where required;

  • updating deeplink handling only where required.

Ask the developer for manual help only if the step requires unavailable credentials, Firebase project access, Apple Developer account access, Reteno dashboard access, signing permissions, or environment access that you do not have.

2. Request external push provider setup

Ask the developer to complete external Firebase, APNs, or Reteno setup when it cannot be done from the codebase.

This may include:

  • creating or confirming the Firebase project;
  • providing the correct google-services.json;
  • providing the correct GoogleService-Info.plist;
  • configuring Firebase Cloud Messaging;
  • configuring APNs in Apple Developer account;
  • uploading Firebase service account, APNs keys, certificates, or configuration to Reteno;
  • confirming that the selected Reteno mobile app is configured with the correct token type: FCM or APNs;
  • confirming that the selected Reteno mobile app matches the selected test environment.

Continue only after the developer confirms that the external setup is complete.

3. Run the application

Ask the developer to run the application in the selected test environment.

The selected test environment can be:

  • Android emulator;
  • physical Android device;
  • iOS simulator, if supported and confirmed by the developer;
  • physical iOS device;
  • another supported environment.

Use the environment confirmed by the developer for the current integration or validation run.

If push validation fails in the selected environment, ask the developer to confirm whether the issue is related to:

  • Flutter runtime;
  • Android or iOS native build;
  • Firebase setup;
  • APNs setup;
  • Reteno configuration;
  • notification permissions;
  • another integration problem.

4. Grant push notification permission

When the push notification permission prompt is displayed, ask the developer or user to allow notifications.

Continue only after permission is granted or the permission state is confirmed.

5. Interact with the test push notification

After the test push is sent and DELIVERED status is available, ask the developer to open the test push notification.

After the developer confirms that the push notification was opened, continue validation through Reteno MCP and verify CLICKED status.

6. Complete authentication, if required

If externalCustomerId validation is part of the current integration task, ask the developer or user to complete login or registration.

Continue only after authentication is completed.

7. Complete In-App setup, if required

If In-App validation is part of the current integration task and the required MCP capability is not available, ask the developer or admin to complete the required In-App setup in Reteno UI.

This may include:

  • creating an In-App message;
  • confirming an existing In-App message name or ID;
  • configuring trigger rules;
  • attaching an In-App message to a mobile push;
  • publishing the In-App message;
  • confirming that the message is active.

Continue validation after the developer or admin confirms that the In-App setup is complete.

8. Continue validation

After each manual step is completed:

  • continue the SDK integration flow;
  • continue validation through Reteno MCP;
  • include all completed manual actions in the final report.

Validation Requirements

The agent 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:

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

Platform-specific token expectations

For Flutter Android validation:

  • expected token type is FCM.

For Flutter iOS validation:

  • expected token type is FCM if the selected Reteno iOS app is configured with FCM;
  • expected token type is APNs if the selected Reteno iOS app is configured with APNs.

The token assigned to the contact must match the selected Reteno mobile app configuration.

Feature-specific validation

Run feature-specific validation for every Reteno SDK feature included in the current integration task.

Feature-specific validation may include:

  • custom event validation;
  • In-App message validation;
  • mobile-push + In-App validation;
  • externalCustomerId validation;
  • deeplink validation;
  • custom push data validation;
  • action button validation.

If a required feature-specific check cannot be completed, document the reason in the final report as one of the following:

  • blocker;
  • manual step;
  • not available and authorized MCP capabilities;
  • missing MCP capability;
  • unavailable test environment;
  • unavailable Reteno UI / admin setup.

Validation With Reteno MCP

After applying Flutter 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 and validation

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 Flutter 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, 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.

[!NOTE]
Reteno MCP and local device automation tooling are separate.

Reteno MCP is used for Reteno-side validation:

  • contact lookup;
  • mobilePushToken verification;
  • test push creation and sending;
  • DELIVERED / CLICKED status checks.

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

  • running the app;
  • granting notification permission;
  • opening the test push notification.

If local device, simulator, or emulator automation tooling is unavailable, ask the developer to perform the device interaction manually and continue after confirmation.

If Reteno MCP is unavailable or a required MCP capability cannot provide reliable evidence, continue Reteno-side validation through another approved evidence source when available.

Approved fallback evidence may include:

  • Reteno UI;
  • backend data;
  • SDK logs;
  • device logs;
  • redacted network logs;
  • screenshots;
  • developer / QA confirmation.

Do not report validation as blocked only because MCP itself is unavailable.

Use Passed with warnings when the required result is independently verified through reliable fallback evidence.

Use Blocked or Partial when the required result cannot be verified through MCP or any approved fallback evidence.

MCP status lookup limitations

Reteno MCP is the preferred Reteno-side validation path, but individual capabilities may be unavailable, incomplete, delayed, or insufficient for every evidence need.

Do not assume validation failed immediately if one MCP lookup returns empty, incomplete, or latest-status-only data.

Known validation limitations to handle:

  • contact details may not expose mobilePushToken directly;
  • token lookup may require an app_uuid that is unavailable from contact listing;
  • message status lookup may return an empty object for some parameter shapes;
  • contact activity lookup may not reliably filter by contact;
  • contact message history may expose only the latest status per message instance.

When MCP evidence is incomplete:

  • use another available MCP capability;
  • use Reteno UI;
  • use backend data;
  • use SDK logs;
  • use device logs;
  • use redacted network logs;
  • adjust validation pattern to capture missing evidence, such as leaving one push untapped for DELIVERED;
  • document the evidence source in the final report.

If reliable fallback evidence confirms the required result, report the validation as:

Passed with warnings

If no reliable fallback evidence exists, report the step as:

Blocked

or:

Partial

MCP schema, runtime-contract, and query-shape mismatches

Treat the discovered MCP capability schema as the expected interface, but verify the actual runtime behavior.

An MCP capability may occasionally:

  • reject a request that appears valid according to the discovered schema;
  • require a different parameter placement;
  • accept only a narrower date representation;
  • return an empty object for a valid-looking lookup;
  • require an identifier that is not exposed directly by another MCP capability.

Do not treat an MCP schema/runtime mismatch as a Flutter SDK defect.

If the MCP server rejects a request that appears valid according to the discovered schema:

  1. capture the exact validation or server error;
  2. inspect the currently discovered capability signature;
  3. do not repeat the same rejected request indefinitely;
  4. derive an alternate request shape only from concrete runtime evidence;
  5. keep the action scoped to the same approved validation contact;
  6. document the mismatch.

Do not permanently hardcode an undocumented alternate request shape into the runbook as the universal MCP contract.

Test-push send request shape

If the current MCP runtime rejects a test-push request because a required recipient field is missing even though the discovered schema appears to place that field elsewhere:

  • inspect the runtime error;
  • verify the request shape accepted by the currently connected MCP environment;
  • use that shape only for the current runtime;
  • preserve single-contact scope.

Do not broaden the audience while troubleshooting request shape.

Contact message history date format

If contact message history unexpectedly returns an empty result for a narrow ISO-8601 timestamp range:

  • verify the date/time format accepted by the current MCP capability;
  • retry with another supported representation only when the runtime contract supports it.

When accepted by the current runtime, this may include date-only values such as:

YYYY-MM-DD

Do not conclude that message history does not exist based only on one ineffective date format.

Empty message-status lookup

If a message-status capability returns an empty object for the available parameter shapes:

  • do not fabricate a status;
  • do not repeatedly guess undocumented identifiers;
  • try another available MCP capability;
  • use contact message history;
  • use Reteno UI or backend evidence;
  • use SDK/device/network evidence when appropriate.

Contact lookup requiring an application UUID

If a token-based contact lookup requires an application UUID that is not available through a dedicated mobile-app listing:

  • inspect verified Reteno-side device data;
  • use an available device application identifier only when the returned MCP data clearly establishes the mapping;
  • do not guess the application UUID;
  • document the source of the identifier.

Broad contact listings

Do not rely on a large or weakly filtered contact listing when more precise current-run correlation is available.

Prefer:

  • current contactId;
  • current deviceId;
  • current application identity;
  • current push token;
  • current messageId;
  • current messageTag;
  • current validation timestamps.

If MCP runtime-contract limitations prevent clean evidence collection but reliable fallback evidence confirms the required result, report:

Passed with warnings

and document the MCP limitation separately.

If neither MCP nor approved fallback evidence can verify the required result, report the validation step as Blocked or Partial.

In-App capabilities to discover

When In-App validation is part of the current integration task, discover whether the current MCP environment supports any In-App capabilities.

Possible capabilities may include:

  • finding In-App messages by name or ID;
  • getting In-App message details;
  • attaching an existing In-App message to a mobile push;
  • verifying which In-App message is attached to a push;
  • checking In-App statuses.
    Do not assume these capabilities are available.
    Do not create, update, publish, attach, or send anything without explicit developer or admin approval.

If an In-App capability is unavailable, use Reteno UI, a preconfigured In-App message, developer/admin confirmation, backend data, SDK logs, device logs, or mark the step as blocked.

Document which evidence was used.

DELIVERED and CLICKED evidence separation

Do not assume that one immediately tapped push will always provide separately observable DELIVERED and CLICKED evidence.

Some Reteno MCP or backend history views may show only the latest status per message instance.

If the push is tapped before the first delivery-status poll, the message instance may appear directly as CLICKED, and the intermediate DELIVERED status may not be observable separately.

For clean core mobile push evidence, use one of these validation patterns:

Pattern A — Poll before tap

  1. Send one test push to the approved test contact.
  2. Keep the app backgrounded or the device locked.
  3. Do not open the notification immediately.
  4. Poll until DELIVERED is observed.
  5. Open the delivered notification.
  6. Poll until CLICKED is observed.
  7. Confirm both statuses belong to the same contact/message.

Pattern B — Separate delivery and click evidence

  1. Send one tagged test push and leave it untapped until DELIVERED is observed.
  2. Send another tagged test push for CLICKED validation.
  3. Open the second delivered notification.
  4. Confirm that both statuses belong to the same contact, device, token, and validation run.

If DELIVERED and CLICKED are confirmed on separate message instances, document this clearly in the final report.

Expected final report wording:

DELIVERED and CLICKED were confirmed for the same contact/device/token within the same validation run, but not on the same message instance.

Do not mark DELIVERED as missing only because the first visible status is already CLICKED.

Validation flow

1. Check selected Reteno mobile app configuration

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

Expected configuration:

  • Flutter Android: FCM;
  • Flutter iOS: FCM or APNs.

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 iOS uses APNs;

APNs sandbox routing for iOS development builds

For iOS development builds that use direct APNs, verify APNs sandbox routing before push validation.
Reteno sends APNs pushes to production by default.
For development builds and sandbox APNs tokens, ask Reteno Support to enable:

sandbox = true

for the selected Reteno mobile app.
Do not treat missing DELIVERED as an SDK integration issue until APNs sandbox routing is confirmed.
Expected result:

  • development build uses APNs sandbox token;
  • selected Reteno app is configured for sandbox APNs route;
  • Reteno Support has enabled sandbox mode when required;
  • test push can reach the development build.

If sandbox routing cannot be confirmed, report push delivery validation as blocked.

  • 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 configuration automatically unless this action is explicitly supported and confirmed by the developer or admin.

2. Create or identify the validation contact

Initialize the SDK and identify the contact associated with the current validation device.

For a clean first-run or fresh-install validation, the contact may initially be anonymous.

However, do not assume that every validation run starts with a new anonymous contact.

A reused emulator, simulator, physical device, application installation, or persistent Reteno device identity may resolve to an existing contact from an earlier test session.

The existing contact may already contain:

  • externalCustomerId;
  • previously saved user attributes;
  • existing channels;
  • earlier device or session data.

Do not treat pre-existing identity data as proof that the current integration called setUserAttributes(...).

Correlate the validation contact using available evidence.

Prefer:

  • contactId;
  • deviceId, when available;
  • current platform / OS information;
  • current application identity;
  • current app version;
  • current validation timestamps;
  • current SDK logs;
  • current Reteno-side device evidence.

Do not identify the validation contact by mobilePushToken alone.

A push token may be refreshed or associated with historical contact/device data.

Also verify that the selected contact belongs to the current device identity for the current validation run.

A physical device, simulator, emulator, or application installation may receive a new platform device identifier between runs.

On iOS, for example, the device identity used by Reteno may change when the effective IDFV changes.

If the current device identity differs from an earlier validation run:

  • do not reuse the previous contact only because it belongs to the same app or previously used push token;
  • do not reuse the previous push token as proof of current contact identity;
  • correlate the current contact with current-run SDK/device evidence;
  • compare the current deviceId with Reteno-side device data;
  • use current app identity, app version, OS information, and timestamps as additional evidence.

A stale contact may still contain a historical mobilePushToken.

A push send request to that stale contact may return successfully at the sending layer while producing:

  • no notification on the current device;
  • no current Notification Service Extension activity;
  • no DELIVERED;
  • no CLICKED.

Do not report this pattern as a Reteno SDK delivery defect until current device/contact correlation is verified.

If a stale contact is detected:

  1. capture the current runtime deviceId;
  2. identify the contact matching the current device;
  3. verify its current mobilePushToken;
  4. send a new test push to the correctly correlated contact;
  5. repeat DELIVERED / CLICKED validation.

Document whether the validation device identity changed since an earlier run and whether a stale contact was detected.

Expected result:

  • the correct contact for the current device is identified;
  • contactId is available;
  • deviceId is available when supported;
  • platform / OS information matches the current validation environment;
  • pre-existing identity data is distinguished from identity changes performed during the current integration run.

If user identification is not part of the current integration task, do not require the contact to be anonymous before continuing core mobile-push validation.

If the task explicitly validates anonymous-to-identified transition, use a clean test state or otherwise verify that the initial contact is actually anonymous before running that feature-specific flow.

2.1. Shared Reteno environment and concurrent validation correlation

A Reteno environment may be shared by multiple developers, automated agents, test applications, or validation runs.

Do not assume that the latest contact activity, latest mobile push message, or latest status belongs to the current integration run.

Concurrent validation activity may create:

  • additional mobile push messages;
  • additional DELIVERED or CLICKED statuses;
  • updates to the same contact;
  • activity from another device using the same Reteno environment;
  • unrelated messages created after the current test push.

For every current-run push validation, correlate the evidence using as many verified identifiers as are available.

Prefer:

  • contactId;
  • deviceId;
  • messageId;
  • message instance / interaction ID, when available;
  • messageTag;
  • current application identity;
  • platform;
  • current validation device;
  • send timestamp;
  • delivery timestamp;
  • click timestamp;
  • current-run SDK or device logs.

Do not use only:

  • the latest contact activity;
  • the latest message in history;
  • the latest DELIVERED status;
  • the latest CLICKED status;

as proof that the current test push passed.

If another validation run sends a push to the same contact while the current run is active:

  1. identify the message created by the current run;
  2. preserve its message identifier or tag when available;
  3. correlate its send time with current device/runtime evidence;
  4. verify DELIVERED for that message or clearly correlated message instance;
  5. verify CLICKED only after the current-run notification is opened;
  6. ignore unrelated concurrent messages when evaluating the current run.

If exact message-level correlation is unavailable, use multiple independent evidence sources and report the limitation.

Do not report a false failure only because unrelated newer activity appears after the current message.

Do not report a false pass by using DELIVERED or CLICKED evidence from another concurrent validation run.

Document in the final report:

  • whether the Reteno environment appeared to contain concurrent validation activity;
  • identifiers used to correlate the current contact and message;
  • whether the current-run push was uniquely identifiable;
  • whether unrelated concurrent activity affected evidence collection.

3. Validate contact attributes

Check whether the validation contact has valid timeZone and languageCode values.

  1. Inspect the implementation:

    • find where the Flutter app initializes or updates the anonymous contact;
    • check whether timeZone and languageCode are passed through SDK code;
    • if these values are generated automatically by the SDK, verify that the app does not override them with invalid custom values.
  2. Verify the saved contact data in Reteno:

    • use Reteno MCP, Reteno UI, backend data, or logs to open the created anonymous contact;
    • confirm that the saved contact profile contains valid timeZone and languageCode values.

Expected format:

  • timeZone must use TZ database format, for example Europe/Kyiv, Europe/Sofia, or America/New_York;
  • languageCode must use RFC 5646 format, for example en, en-US, uk, uk-UA, or de-AT.

Do not use invalid values such as GMT+2, UTC+3, Kyiv, +02:00, english, EN, ua, or uk_UA.

4. Request push notification permission

Trigger or verify the push notification permission flow.

Expected result:

  • the app requests notification permission when required;
  • notification permission is granted or the current permission state is confirmed;
  • the SDK receives or can access the mobile push token;
  • the token is sent to Reteno;
  • the token is assigned to the current validation contact.

If the AI agent cannot interact with the selected test environment directly, ask the developer to complete the permission step manually and continue after confirmation.

5. Verify mobile push token assignment

After push permission is granted and the SDK is initialized, use Reteno MCP to verify the contact state.

Expected result:

  • contactId is available;
  • deviceId is available, if this data can be retrieved;
  • mobilePushToken is available;
  • osName is available;
  • token type is available: FCM or APNs.

Expected token type:

  • Flutter Android contact must have an FCM token;
  • Flutter iOS contact must have an FCM token if the selected Reteno iOS app is configured with FCM;
  • Flutter iOS contact must have an APNs token if the selected Reteno iOS app is configured with APNs.

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

6. Send a test mobile push

After the validation contact is identified and mobilePushToken is assigned, use Reteno MCP to send a test mobile push to this contact.

Use a run-specific messageTag to make the current validation message distinguishable from pushes created by other users, agents, or concurrent test sessions.

Prefer a tag that is unique enough for the current run, for example:

flutter-validation-<date>-<short-run-id>

Do not include:

  • SDK access keys;
  • push tokens;
  • credentials;
  • personal data;
  • other sensitive values

inside messageTag.

Avoid relying only on generic tags such as:

test

when the Reteno environment is shared.

At send time, capture the available current-run identifiers:

  • messageId;
  • message instance / iid, when available;
  • interaction or request ID, when available;
  • messageTag;
  • contactId;
  • deviceId;
  • validation timestamp.

When Reteno contains activity from another concurrent session, validate only statuses correlated with the identifiers created by the current run.

Do not attribute another tester's DELIVERED, CLICKED, or other activity to the current integration.

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.

Do not create or send a broad campaign during validation.

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

If it is still blocked, document the MCP permission issue in the final report.

7. Verify push delivery status

After sending the test push, use Reteno MCP or backend data to check push statuses.

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;
  • incorrect Firebase project or Sender ID;
  • incorrect APNs configuration;
  • sandbox / production mismatch;
  • incorrect Reteno mobile app configuration;
  • token type mismatch;
  • invalid or outdated token;
  • notification permission disabled;
  • notification channel disabled on Android;
  • Notification Service Extension or App Groups issue on iOS;
  • SDK version or compatibility issue.

8. Verify push click tracking

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

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

The selected test environment can be:

  • Android emulator;
  • physical Android device;
  • iOS simulator, if supported and confirmed by the developer;
  • physical iOS device;
  • another supported environment confirmed by the developer.

If you can interact with the selected test environment directly and the required local automation permissions are granted, open the push notification yourself.

If you cannot interact with the selected test environment directly, ask the developer to open the push notification manually and continue after confirmation.

After the push notification is opened, use Reteno MCP to verify that the CLICKED status has been received.

Expected result:

  • CLICKED status is available for the same test push.

9. Verify that push statuses belong to the same contact and message

For correct validation, all push statuses must belong to the same test push and the same contact.

Verify the following identifiers:

  • contactId;
  • deviceId;
  • mobilePushToken;
  • messageId;
  • imid;
  • iid;
  • messageTag.

For core mobile push validation, verify:

  • DELIVERED;
  • CLICKED.

Do not use READ as a core mobile push validation status.

Keep mobile push status validation separate from feature-specific In-App or App Inbox status models.

If ERROR is present, treat it as a troubleshooting path.

SENT may be available on the backend, but it is not the main success status for delivery validation.

Same-message DELIVERED and CLICKED validation pattern

When possible, validate DELIVERED and CLICKED on the same message instance.

Recommended pattern:

  1. Send one test push to the approved test contact.
  2. Keep the app backgrounded or the device locked.
  3. Do not open the notification immediately.
  4. Poll Reteno-side evidence until DELIVERED is observed.
  5. Open the delivered notification.
  6. Poll Reteno-side evidence until the same message instance advances to CLICKED.
  7. Confirm that DELIVERED and CLICKED belong to the same contact, device, token, message, and message instance.

Expected result:

  • DELIVERED is observed while the notification is still untapped;
  • after the notification is opened, the same message instance advances to CLICKED;
  • the final report includes the message ID or message instance ID when available.

If Reteno MCP or backend history exposes only the latest status and the push was tapped too quickly, use the separate delivery/click evidence pattern instead.

Do not mark DELIVERED as missing only because the first visible status is already CLICKED.

10. Feature-specific validation: Verify custom event delivery

Use this check when the current integration task includes custom events or explicitly asks to validate custom events.

When custom event validation is included in the integration task, this check is required and must not be skipped.

Before running this check, verify that Reteno MCP can retrieve events for the contact or that another verification method is available, such as Reteno UI, event log, backend logs, or API response.

If MCP cannot retrieve event list / count for the contact or verify that the event belongs to the expected contactId / deviceId, document the missing MCP capability as a requested capability.

Create and send a test custom event through the SDK.

Use the following event name:

testAiEvent

Expected result:

  • testAiEvent is received in Reteno;
  • the event is assigned to the same contactId / deviceId used for mobile push validation;
  • the event payload is available;
  • the event can be found in backend logs, event log, or contact activity where applicable.

11. Feature-specific validation: Verify externalCustomerId after login or registration

Use this check when the current integration task includes user identification, login, registration, or explicitly asks to validate externalCustomerId.

Before login or registration, the contact must remain anonymous.

Expected result before login or registration:

  • contactId is available;
  • deviceId is available;
  • platform / OS information is available;
  • externalCustomerId is not assigned yet.

After login or registration, the app / SDK must send a stable customer user ID to Reteno as externalCustomerId.

Expected result after login or registration:

  • externalCustomerId is assigned;
  • it is assigned to the same contact / device that was created anonymously before login or registration;
  • mobilePushToken remains assigned to the correct contact;
  • subsequent SDK events are sent to the contact with externalCustomerId.

Do not use unstable values as externalCustomerId, such as:

  • deviceId;
  • mobile push token;
  • temporary session ID;
  • random UUID generated on the device;
  • email, if the product has a stable backend user ID.

12. Feature-specific validation: In-App message triggered by event

Use this check only when the current integration task includes In-App messages or explicitly asks to validate an In-App message triggered by an event.

Do not assume that Reteno MCP can create, publish, configure, inspect, trigger, or verify In-App messages.

Before running this validation, discover which MCP capabilities are available and authorized in the current agent environment.

Use only:

  • a non-production Reteno environment;
  • an approved test contact or test audience;
  • an approved existing or preconfigured In-App message;
  • a controlled app-open trigger or a uniquely named event trigger confirmed by the developer or admin.

Do not create, update, publish, configure, or trigger anything without explicit developer or admin approval.

Do not publish an integration-test In-App message to a production-wide audience.

To validate an In-App message triggered by an event:

  1. Confirm that In-App validation is part of the current integration task.
  2. Confirm the selected non-production Reteno environment.
  3. Confirm the approved test contact or test audience.
  4. Confirm whether an approved In-App message already exists.
  5. If an existing In-App message should be used, ask the developer or admin to provide the In-App message name or ID.
  6. If no approved In-App message exists, ask the developer or admin to create or confirm it in Reteno UI.
  7. Confirm the trigger type:
    • app open;
    • uniquely named event;
    • another approved trigger.
  8. If an event trigger is used, ask the developer or admin to confirm the event name.
  9. Do not hardcode eventTestInApp as a required event name. Use it only as an example if the developer confirms it.
  10. Discover whether MCP can find the In-App message by name or ID.
  11. If this capability is available, use MCP to find the In-App message.
  12. If this capability is unavailable, ask the developer or admin to confirm the In-App message in Reteno UI.
  13. Discover whether MCP can get In-App message details.
  14. If this capability is available, use MCP to inspect the In-App message details.
  15. If this capability is unavailable, ask the developer or admin to confirm the message details in Reteno UI.
  16. Confirm that the In-App message is active and eligible for the approved test contact or audience.
  17. Confirm frequency and targeting rules.
  18. Open the app in the selected test environment.
  19. Trigger the approved app-open condition or uniquely named event.
  20. If the agent cannot trigger the event automatically, ask the developer or QA engineer to trigger it manually and confirm completion.
  21. Verify that the In-App message appears in the app.
  22. Verify display, close, error, custom-data callbacks, and link/navigation behavior when available.
  23. Verify available In-App evidence using MCP, Reteno UI, backend data, event log, SDK logs, device logs, or manual confirmation.

Expected result:

  • the In-App message is approved for validation;
  • the In-App message exists in a non-production Reteno environment;
  • the In-App message is active and eligible for the approved test contact or audience;
  • the trigger is confirmed as app open, uniquely named event, or another approved trigger;
  • the In-App message appears in the app when the configured trigger and eligibility rules are satisfied;
  • display, close, error, custom-data callbacks, and link/navigation behavior are verified when available;
  • Read, Click, or Clicked In-App statuses are verified only if the required MCP, UI, backend, SDK log, device log, or manual evidence is available;
  • unavailable capabilities are documented as blocked, manual fallback required, or missing MCP capability;
  • the event, In-App message, and available statuses are correlated only through verified and available evidence.

13. Feature-specific validation: Mobile-push + In-App flow

Use this check only when the current integration task includes a mobile-push + In-App flow or explicitly asks to validate this flow.

Do not assume that Reteno MCP can create, publish, attach, inspect, send, or verify In-App messages or mobile push messages.

Before running this validation, discover which MCP capabilities are available and authorized in the current agent environment.

Use only:

  • a non-production Reteno environment;
  • an approved test contact or test audience;
  • an approved existing or preconfigured In-App message;
  • an approved mobile push message or campaign created specifically for validation.

Do not create, update, publish, attach, or send anything without explicit developer or admin approval.

To validate the mobile-push + In-App flow:

  1. Confirm that mobile-push + In-App validation is part of the current integration task.
  2. Confirm the selected non-production Reteno environment.
  3. Confirm the approved test contact or test audience.
  4. Confirm whether an approved In-App message already exists.
  5. If an existing In-App message should be used, ask the developer or admin to provide the In-App message name or ID.
  6. If no approved In-App message exists, ask the developer or admin to create or confirm it in Reteno UI.
  7. Discover whether MCP can find the In-App message by name or ID.
  8. If this capability is available, use MCP to find the In-App message.
  9. If this capability is unavailable, ask the developer or admin to confirm the In-App message in Reteno UI.
  10. Discover whether MCP can get In-App message details.
  11. If this capability is available, use MCP to inspect the In-App message details.
  12. If this capability is unavailable, ask the developer or admin to confirm the message details in Reteno UI.
  13. Discover whether MCP can attach an existing In-App message to a mobile push.
  14. If this capability is available and explicitly approved, attach the In-App message to the mobile push.
  15. If this capability is unavailable, ask the developer or admin to attach or confirm the attachment manually in Reteno UI.
  16. Discover whether MCP can verify which In-App message is attached to the mobile push.
  17. If this capability is available, verify the attachment through MCP.
  18. If this capability is unavailable, ask the developer or admin to confirm the attachment in Reteno UI.
  19. Discover whether MCP can send the mobile push to one approved test contact.
  20. If this capability is available and explicitly approved, send the mobile push only to the approved test contact.
  21. If this capability is unavailable, ask the developer or admin to send the test push manually through Reteno UI.
  22. Verify delivery evidence using available MCP capabilities, Reteno UI, backend data, SDK logs, device logs, or manual confirmation.
  23. Open the delivered push notification in the selected test environment.
  24. If the agent cannot open the push automatically, ask the developer or QA engineer to open it manually and confirm completion.
  25. Verify that the app opens after the push interaction.
  26. Verify that the In-App message appears, if this is expected for the configured flow.
  27. Verify available In-App evidence using MCP, Reteno UI, backend data, SDK logs, device logs, or manual confirmation.
  28. Document which validation actions were completed through MCP and which required manual or Reteno UI fallback.

Expected result:

  • the mobile push is sent only to the approved test contact or test audience;
  • delivery evidence is available through MCP, Reteno UI, backend data, SDK logs, device logs, or manual confirmation;
  • the In-App message is attached to the mobile push or the attachment is confirmed manually;
  • the app opens after the push notification is opened;
  • the In-App message appears when the configured trigger and eligibility rules are satisfied;
  • display, close, error, custom-data callbacks, and link/navigation behavior are verified when available;
  • Read, Click, or Clicked In-App statuses are verified only if the required MCP, UI, backend, SDK log, device log, or manual evidence is available;
  • unavailable capabilities are documented as blocked, manual fallback required, or missing MCP capability;
  • push, In-App message, and available statuses are correlated only through verified and available evidence.

Flutter 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.

Recurring Flutter SDK issue checks

When troubleshooting Flutter SDK integration, check recurring SDK issue patterns before escalating.

Common Flutter-related issues may include:

  • DELIVERED or CLICKED statuses are missing;
  • mobilePushToken is missing;
  • pushSubscribed=false;
  • Firebase / FCM token mismatch or missing token;
  • APNs / FCM mismatch on iOS;
  • incorrect Reteno mobile app configuration;
  • SDK version compatibility issues;
  • SDK initialization crashes or hangs;
  • delayed initialization issues;
  • Flutter plugin migration issues;
  • Android Gradle build issues;
  • iOS Pod install or Xcode build issues;
  • events are not tracked;
  • deeplink handling does not work;
  • In-App messages do not appear;
  • native iOS or Android configuration conflicts;
  • ANR or WorkManager-related issues on Android.

Use these patterns as troubleshooting hints. Always verify the issue against the current project, official Reteno Flutter documentation, native platform documentation when relevant, MCP results, logs, and build output before applying a fix.

Known non-blocking runtime log patterns

Do not classify an isolated warning, exception, or debug log as a Reteno integration failure without verifying the resulting runtime behavior.

A warning may be non-blocking when the expected Reteno functionality still works end-to-end.

Optional Flutter Firebase Messaging probe

When Flutter firebase_messaging is intentionally not installed because reteno_plugin owns the native Firebase Messaging path, Android logs may contain an optional integration probe failure similar to:

ClassNotFoundException
FlutterFirebaseTokenLiveData

Treat this as non-blocking only when all of the following are true:

  • Flutter firebase_messaging is intentionally absent;
  • Firebase initializes successfully;
  • the Reteno Flutter plugin owns the required native Firebase Messaging path;
  • an FCM token is obtained;
  • mobilePushToken is present in Reteno;
  • token type is FCM;
  • a test push is delivered;
  • DELIVERED is verified;
  • the notification can be opened;
  • CLICKED is verified.

Do not install Flutter firebase_messaging only to remove this log entry.

Adding it unnecessarily may introduce:

  • duplicate token ownership;
  • competing Firebase Messaging delegates;
  • duplicate background handlers;
  • additional native Firebase configuration;
  • changed application behavior.

If the app already uses Flutter firebase_messaging, preserve the existing integration and verify compatibility with the selected Reteno token-handling path.

If token registration or push processing actually fails, do not dismiss the class-loading error automatically.

Verify whether the selected reteno_plugin version requires the missing integration before treating the warning as benign.

iOS FCM token requested before APNs registration completes

During iOS Firebase startup, logs may temporarily indicate that Firebase cannot provide an FCM token because the APNs token is not available yet.

Do not fail the integration based on the initial warning alone.

Continue observing startup.

Treat the warning as transient when:

  • APNs registration completes afterward;
  • Firebase subsequently provides an FCM token;
  • Reteno receives an FCM-format mobilePushToken;
  • core push validation succeeds.

If no FCM token appears after APNs registration completes, continue the normal iOS Firebase token troubleshooting flow.

Empty Reteno link handler when the push contains no deeplink

A notification interaction may produce a link-handler warning when the test push does not contain a deeplink or URL.

Do not treat an empty link-handler parameter as a deeplink integration defect when:

  • the test push intentionally contains no deeplink;
  • deeplink validation is not part of the current integration task;
  • the notification opens correctly;
  • CLICKED is recorded correctly.

If deeplink handling is part of the current integration task, validate it separately with an approved push containing the expected deeplink.

For every non-blocking warning, document why it was considered benign and which end-to-end evidence proved that the required Reteno behavior still worked.

Flutter SDK dependency is missing or incorrect

If the Flutter SDK dependency is missing or incorrect, check:

  • pubspec.yaml;
  • pubspec.lock;
  • installed reteno_plugin version;
  • official Flutter SDK documentation;
  • Flutter SDK Release Notes.

Use the existing Flutter project setup.

Do not replace the package manager or delete lock files without developer confirmation.

SDK initialization is missing or incorrect

If the SDK does not initialize, check:

  • Flutter app entry point;
  • whether Reteno().initialize(...) is called once;
  • whether SDK access key is provided through the approved configuration method;
  • whether initialization happens before feature usage;
  • whether initialization is delayed by app startup, auth, navigation, or remote config logic;
  • whether legacy native wiring conflicts with Flutter-first initialization;
  • whether the project uses an older initWith(...) pattern and needs migration.

Expected result:

  • SDK initialization path is executed when the app starts;
  • anonymous contact can be created;
  • SDK key is not left as a placeholder;
  • initialization does not break existing app startup.

Firebase initialization order for manual and external token modes

For iOS manual and external token handling modes, initialize Firebase before Reteno when Firebase / FCM is used.
Required order:

  1. initialize Firebase;
  2. obtain or prepare FCM/APNs token handling;
  3. initialize Reteno with the selected deviceTokenHandlingMode;
  4. pass the token to Reteno when required by the selected mode.
    Do not initialize Reteno before Firebase when the selected token mode depends on Firebase Messaging.
    Expected result:
  • Firebase is configured before Reteno token handling starts;
  • FCM token can be received;
  • token is passed to Reteno through the correct path;
  • mobilePushToken appears in Reteno for the contact.

iOS Firebase / FCM token mode dependency rules

For iOS Firebase / FCM validation, explicitly choose the correct deviceTokenHandlingMode.

Use manual when:

  • the selected Reteno mobile app is configured for FCM;
  • the plugin should observe the Firebase token;
  • the app should not report a raw APNs token to Reteno;
  • the app does not need to call setPushToken(...) manually.

In manual mode:

  • initialize Firebase before Reteno;
  • add or verify firebase_core when the app does not already initialize Firebase;
  • do not add Flutter firebase_messaging only to obtain the token if reteno_plugin already owns the native Firebase Messaging token path;
  • avoid creating a competing FCM delegate unless the developer or Flutter SME confirms that the app needs it.

Use external only when the app owns token retrieval and must pass the token to Reteno through setPushToken(...).

Use automatic only when the selected route is APNs and Reteno should receive an APNs token.

Expected result for iOS FCM route:

  • Firebase initializes before Reteno;
  • Reteno receives an FCM token;
  • Reteno-side token type is FCM, not APNs;
  • setPushToken(...) is not required unless token mode is external.

Document in the final report:

  • selected deviceTokenHandlingMode;
  • whether firebase_core was added or already present;
  • whether firebase_messaging was intentionally omitted or already present;
  • whether Firebase initialized before Reteno;
  • whether Reteno received an FCM token.

GoogleService-Info.plist target membership check

Do not treat GoogleService-Info.plist as configured only because the file exists in the repository.

For iOS Firebase / FCM validation, verify that GoogleService-Info.plist:

  • exists in the expected iOS project location;
  • matches the effective iOS bundle ID;
  • belongs to the correct Firebase project;
  • is added to the Runner target resources;
  • is copied into the app bundle at build time.

If the file exists but is not a member of the Runner target, Firebase may have no configuration to load at runtime.

Expected result:

  • Firebase initializes successfully;
  • Firebase project and bundle ID match the selected validation app identity;
  • Reteno receives an FCM token when the iOS route is Firebase / FCM.

If Firebase initialization fails or no FCM token appears, check Runner target membership before assuming the SDK integration is broken.

Document in the final report:

  • whether GoogleService-Info.plist exists;
  • whether it matches the bundle ID;
  • whether it is included in Runner Resources;
  • whether Firebase initialized successfully.

iOS flutter run installs the app but cannot attach the debugger

On older physical iOS devices, flutter run may build, install, and launch the app successfully but fail to attach the debugger.

Possible error signatures:

Unable to locate DeviceSupport directory with suffix 'Symbols'

or:

Error launching application on <device-name>

Do not treat this as a Reteno SDK integration failure if the app installs and runs correctly.

Before diagnosing a DeviceSupport, Flutter, or Xcode compatibility issue, verify whether the physical iOS device is currently unlocked and available for application launch.

A physical device that was intentionally locked for push validation may prevent a later:

flutter run
flutter attach

or application relaunch from succeeding.

If debugger attach or application relaunch fails:

  1. confirm that the application built successfully;
  2. confirm that the application was installed successfully;
  3. check whether the physical iOS device is currently locked;
  4. ask the developer to unlock the device when required;
  5. retry the launch or debugger attach only after the device is available;
  6. inspect the exact native launch error before diagnosing a tooling compatibility problem.

Do not classify a locked-device launch failure as:

  • a Reteno SDK failure;
  • a Flutter integration failure;
  • a DeviceSupport mismatch;
  • an Xcode compatibility issue

without supporting evidence.

When available, approved device tooling may provide a native launch error that confirms the device is locked.

Also avoid validation sequencing conflicts.

If the tester must background or lock the device for push validation, complete required debugger attachment, current-run diagnostics capture, or other interactive development checks before intentionally locking the device when practical.

If diagnostics cannot be captured after the device state changes:

  • report diagnostics as not captured during the current run;
  • document the reason;
  • continue with other reliable current-run validation evidence when available;
  • do not reuse an earlier run's diagnostic result as current-run evidence.

This may happen when the installed Xcode version does not contain DeviceSupport files for the exact iOS version on the test device.

If debugger attach fails but the app launches:

  • confirm whether the app was installed successfully;
  • confirm whether the app opens on the device;
  • collect logs through another approved channel;
  • continue validation if reliable logs or Reteno-side evidence are available.

Possible fallback:

brew install libimobiledevice
idevicesyslog

Use idevicesyslog to stream device logs and look for Flutter lines, SDK initialization logs, permission logs, notification logs, and Reteno validation evidence.

Document in the final report:

  • iOS device model and iOS version;
  • Xcode version, if known;
  • whether app install succeeded;
  • whether debugger attach failed;
  • exact error text;
  • fallback log method used;
  • whether validation continued through syslog, MCP, Reteno UI, backend data, screenshots, or manual confirmation.

Do not add local machine workarounds, symlinks, or DeviceSupport shims to the repository.

If a local DeviceSupport workaround was used, report it as a local machine change only, not a project change.

iOS foreground push is not sufficient for CLICKED validation

Do not use a foreground-only push as proof that CLICKED can be validated on iOS.

A foreground push may be received by the SDK without showing a tappable banner, depending on the app's existing notification presentation delegate.

To validate CLICKED on iOS:

  1. Background the app or lock the device before sending the test push.
  2. Wait for the notification to appear.
  3. Open the delivered notification.
  4. Verify that the app opens.
  5. Verify that CLICKED is recorded for the same contact/message.

A foreground received event is not equivalent to a click.

If DELIVERED is present but CLICKED is missing, confirm that:

  • the notification was actually tappable;
  • the app was backgrounded or locked before the push was sent;
  • the delivered notification was opened by the tester or automation;
  • the notification delegate order preserves Reteno response handling.

Flutter migration or legacy native wiring issue

If the project already had Reteno Flutter SDK installed, check whether it uses an older integration pattern.

Check:

  • native Android Reteno-only Firebase Messaging service;
  • native Android Reteno-specific manifest service declarations;
  • legacy native Reteno bootstrap used only for Flutter plugin setup;
  • native iOS AppDelegate setup that duplicates Flutter plugin initialization;
  • Reteno().initWith(...) usage;
  • Flutter Migration Guide.

Do not remove native code that belongs to the app's own Firebase, push, deeplink, analytics, or authentication logic.

Remove legacy Reteno-only wiring only when the migration guide or developer confirms it is no longer needed.

Diagnostics return issues

If Reteno().diagnose() returns issues, inspect the returned issue codes before continuing validation.

Possible issue categories may include:

  • missing FCM messaging service;
  • missing Reteno messaging service;
  • FCM messaging service conflict;
  • missing FCM token;
  • FCM token fetch failure.

Resolve diagnostics issues before declaring integration complete.

If diagnostics cannot be run, document why in the final report.

Android build fails

If Android build fails, check:

  • android/build.gradle;
  • android/app/build.gradle;
  • Gradle plugin version;
  • Kotlin version;
  • Java compatibility settings;
  • minSdk;
  • AndroidX / Jetifier settings;
  • Firebase dependencies;
  • google-services.json;
  • package name / applicationId;
  • duplicate Firebase Messaging service definitions;
  • Flutter plugin registration.

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

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

iOS build fails

If iOS build fails, check:

  • ios/Podfile;
  • CocoaPods installation;
  • pod install;
  • .xcworkspace;
  • AppDelegate;
  • Notification Service Extension;
  • Notification Content Extension, if carousel or GIF push UI is required;
  • App Groups;
  • signing and capabilities;
  • GoogleService-Info.plist, if iOS uses FCM;
  • APNs / FCM setup;
  • Flutter plugin registration.

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

  • exact error message;
  • Xcode logs;
  • simulator or device details;
  • reproduction steps.

Xcode build phase order for Notification Service Extension

When adding a Notification Service Extension to a Flutter iOS app, verify the Xcode build phase order.

Embed Foundation Extensions must be placed immediately after Resources.

Do not append it at the very end of the phase list after Flutter's late build phases.

Recommended order:

Check Pods Manifest.lock
Run Script
Sources
Frameworks
Resources
Embed Foundation Extensions
Embed Frameworks
Thin Binary
Embed Pods Frameworks

If Embed Foundation Extensions is appended after Thin Binary, Xcode may copy the .appex into a bundle that has already been consumed and fail with a build phase dependency cycle.

Expected result:

  • Notification Service Extension is embedded into the app bundle;
  • Xcode build does not report a build phase dependency cycle;
  • Flutter Thin Binary remains after extension embedding.

If an iOS build phase cycle appears after adding the extension, inspect build phase order before changing SDK code.

Pre-existing build blockers required for validation

If the app cannot build or run before Reteno validation can start, identify whether the failure is pre-existing or introduced by Reteno changes.

Apply only the smallest safe fix required to reach build/run validation.

Document every pre-existing build fix separately from Reteno SDK integration changes.

Examples may include:

  • dependency version incompatible with the selected Flutter / Dart version;
  • iOS deployment target below dependency minimums;
  • Firebase config file present but not included in the app target;
  • validation-only dependency overrides;
  • build-system workaround required only for the dry run.

For each pre-existing build fix, report:

  • failed command;
  • exact error;
  • why the issue is unrelated to Reteno;
  • files changed;
  • whether the fix is validation-only;
  • whether the fix is merge-ready;
  • whether developer review is required.

Do not hide validation-only dependency overrides or build-system workarounds inside the Reteno integration summary.

If validation-only fixes remain in the working tree, report merge readiness as:

Not ready / needs project-owner review

Flutter 3.44+ iOS build requirement

If the project uses Flutter 3.44 or later, disable Swift Package Manager for the iOS build when required by the current Reteno Flutter SDK setup.
If Swift Package Manager remains enabled and the iOS build fails, treat this as a known Flutter iOS build compatibility issue.
Expected result:

  • iOS dependencies are resolved through CocoaPods as required by the Flutter Reteno setup;
  • pod install succeeds;
  • Xcode / Flutter iOS build succeeds.
    Document whether Swift Package Manager was enabled or disabled in the final report.

iOS DELIVERED status is missing

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

  • whether the selected route is FCM or direct APNs;
  • whether token type matches the selected Reteno app;
  • whether APNs sandbox routing is required;
  • whether Reteno Support enabled sandbox = true for development APNs builds;
  • whether Notification Service Extension builds and runs;
  • whether the App Group name is exactly group.<main-app-bundle-id>.reteno-local-storage;
  • whether the App Group is applied to both main app and Notification Service Extension;
  • whether Firebase is initialized before Reteno for FCM/manual/external modes;
  • whether the device token was passed to Reteno.
    Do not assume SDK integration is wrong until token routing and sandbox/production configuration are confirmed.

Contact is not found

If Reteno MCP cannot find the contact, check:

  • SDK initialization;
  • SDK access key;
  • app launch;
  • network connection;
  • whether the app was run after SDK integration changes;
  • whether Reteno MCP has access to the correct Reteno app or environment;
  • selected target platform: Android, iOS, or both.

Expected result:

  • the contact is found by contactId;
  • the contact has the expected Flutter platform / OS information.
  • Flutter Android contact has an FCM token;
  • Flutter iOS contact has an FCM token if the selected Reteno iOS app is configured with FCM;
  • Flutter iOS contact has an APNs token if the selected Reteno iOS app is configured with APNs.
  • Flutter deeplink setup;
  • app_links or another deeplink package, if used;
  • Android intent filters;
  • iOS URL schemes / associated domains;
  • initial link handling for cold start;
  • resumed app link handling;
  • whether initialization is delayed;
  • whether the app session is active;
  • whether MCP can find or verify the In-App message;
  • whether missing MCP capabilities prevent validation.

If MCP cannot create, find, configure, or attach the In-App message, ask the developer or admin to complete the step in Reteno UI and document the missing MCP capability as a requested capability.

ANR or WorkManager issues on Android

If the app hangs or ANR occurs on Android, check:

  • SDK version;
  • Android debug mode logs;
  • WorkManager initialization;
  • custom WorkerFactory setup;
  • whether WorkManager is initialized before Reteno needs it;
  • emulator or device API level;
  • whether the issue reproduces on the selected test environment.

Do not treat every PushDataWorker WorkManager FAILURE result as a Reteno integration defect.

If Logcat shows a sequence similar to:

PushDataWorker: doWork(): App is in foreground, nothing to do
WM-WorkerWrapper: Worker result FAILURE

the worker result may represent a no-op foreground path rather than an actual push or integration failure.

Before escalating:

  • inspect the log line immediately before the WorkManager result;
  • verify whether the application was in the foreground;
  • verify whether the same worker succeeds when queued Reteno data exists in the background;
  • verify whether contact, token, DELIVERED, or CLICKED validation is actually affected;
  • check for a real ANR, crash, or failed network interaction.

Do not report a Reteno defect based only on the word FAILURE in this WorkManager result.

If the worker failure occurs together with missing Reteno data, repeated retries, application hangs, ANR, or failed validation statuses, continue normal WorkManager troubleshooting.

If the issue cannot be resolved safely, ask the developer to provide Logcat output and WorkManager initialization details.

Reteno MCP cannot be connected in the current agent environment

If Reteno MCP cannot be connected in the current AI agent environment, do not treat this as a Flutter SDK integration failure.

Continue with all steps that can be completed without MCP:

  • inspect the Flutter project;
  • apply SDK integration changes;
  • configure Firebase / FCM setup in code, if required;
  • configure SDK initialization;
  • configure notification permission handling;
  • configure token handling;
  • build and run the application, if possible;
  • verify that the SDK initialization path is executed.

Do not mark the integration as complete until the required Reteno-side validation is completed through Reteno MCP or approved fallback evidence, or the remaining validation gap is clearly documented as Blocked or Partial.

In the final report, include:

  • MCP connection status;
  • reason MCP validation could not be completed;
  • code integration status;
  • build/run result;
  • validation steps blocked by MCP unavailability;
  • recommended next step.

mobilePushToken is missing

If the contact exists but mobilePushToken is missing, check:

  • selected iOS deviceTokenHandlingMode;
  • whether setPushToken(...) is required and called;
  • whether Firebase is initialized before Reteno for manual or external modes;
  • whether FCM/APNs token is actually received by the app;
  • whether requestPushPermission() was called at the correct time;
  • whether Android test device/emulator uses API 26 or higher;
  • whether APNs sandbox routing is enabled for iOS development builds;
  • whether Reteno app token type matches the selected platform route;
  • whether Notification Service Extension and App Group are configured correctly for iOS.
    Expected result:
  • token is assigned to the same contact that was created by the SDK;
  • token type matches selected Reteno mobile app configuration;
  • Reteno MCP, Reteno UI, backend data, logs, or final report confirms the token.

Build or run fails

If the app does not build or run, check:

  • dependency setup;
  • SDK version;
  • package manager configuration;
  • Flutter SDK version;
  • native Android project configuration;
  • native iOS project configuration;
  • Firebase plugin configuration;
  • package name / applicationId;
  • iOS bundle ID;
  • google-services.json;
  • GoogleService-Info.plist;
  • selected test environment configuration;
  • runtime crash logs, if available.

Try to resolve the issue only when it can be fixed safely through Dart code, dependency setup, native project configuration, or supported tooling.

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, or manual device actions.

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

  • exact error message;
  • crash reason, if available;
  • relevant logs;
  • simulator, emulator, or device details
  • reproduction steps

Do not continue MCP validation until the app builds and runs, or the build/run blocker is clearly documented.

Final Report Format

At the end of the integration, return a structured final report.

Do not report only that the integration is complete.

Include what you inspected, what you changed, what you validated, and what still requires attention.

Required report fields

Use the table below as the final report template.

Fill in the Result column after completing the integration and validation flow. If a field cannot be verified, explain why and mark it as a blocker or not available.

AreaResult
Report typeComplete / Partial
Platform detectedFlutter
Evidence used for platform detection
Run mode
Application
Git branch / working copy status
Flutter SDK version
Reteno Flutter SDK version
reteno_plugin versionselected/current version verified from release notes, package metadata, pubspec.yaml / pubspec.lock, or SME confirmation
Resolved native Reteno iOS SDK version
Resolved native Reteno Android SDK version
Native SDK version evidenceplugin podspec / Podfile.lock / Gradle dependency resolution / package metadata / other
iOS deviceTokenHandlingModeautomatic / manual / external / not applicable
iOS notification-center delegate handlingExisting app delegate before plugin registration / Reteno-only coordinator / Other / Not applicable
App-specific UNUserNotificationCenter delegate introduced by this integrationYes / No / Not applicable
Sandbox Firebase / Reteno app usedYes / No
Sandbox validation pathYes / No
Original iOS bundle ID
Temporary iOS bundle ID
GoogleService-Info.plist existsYes / No / Not applicable
GoogleService-Info.plist matches bundle IDYes / No / Not verified / Not applicable
GoogleService-Info.plist added to Runner ResourcesYes / No / Not verified / Not applicable
Xcode Embed Foundation Extensions phase order checkedYes / No / Not applicable
Embed Foundation Extensions positionAfter Resources / Other / Not applicable
iOS app state used for CLICKED validationBackground / Lock screen / Foreground / Not run
DELIVERED evidence methodSame push before tap / Separate untapped push / Reteno UI / Logs / Other
CLICKED evidence methodSame push after tap / Separate push / Reteno UI / Logs / Other
DELIVERED and CLICKED same message instanceYes / No / Not verified
Validation-only dependency overrides usedYes / No
Pre-existing build blockers fixedYes / No
SDK access key shared in chat/logs/tool outputYes / No
SDK access key rotation recommendedYes / No / Not applicable
SDK access key handoff methodapproved project config / Dart define file / environment / CI/CD / chat fallback / other
SDK access key local source
SDK access key source gitignoredYes / No / Not applicable
SDK access key exposed in device/network logsYes / No / Not verified
SDK access key exposed through encoded/reversible authentication dataYes / No / Not verified
Literal and encoded credential evidence redactedYes / No / Not applicable
Dedicated Reteno debug-logging flag usedYes / No / Not applicable
Reteno debug logging enabledYes / No / Not verified
Production SDK access key handoffCI/CD / approved build configuration / other / not confirmed
MCP limitations affected evidence collectionYes / No
MCP schema/runtime mismatch encounteredYes / No
MCP alternate request/query shape usedYes / No / Not applicable
MCP schema/runtime mismatch details
MCP fallback evidence used
Concurrent/shared Reteno validation activity detectedYes / No / Not verified
Current-run push uniquely correlatedYes / No / Not verified
Current-run correlation evidencecontactId / deviceId / messageId / messageTag / timestamps / runtime logs / other
Current-run message identifiers capturedYes / No
Run-specific messageTag usedYes / No / Not applicable
Concurrent activity affected validation evidenceYes / No
setPushToken(...) requiredYes / No / Not applicable
setPushToken(...) implementedYes / No / Not applicable
User identification methodsetUserAttributes(userExternalId: ...) / other / not requested
requestPushPermission() implementedYes / No / Not applicable
Android permission request timingAfter Activity available / Too early / Not applicable
Android runtime API level
Android application minSdk
Android minSdk below Reteno runtime requirementYes / No / Not applicable
Android API level valid for RetenoYes / No
Custom Android notification icon requestedYes / No
Reteno-specific notification icon configuration verifiedYes / No / Not applicable
Android notification icon visually validatedYes / No / Not applicable
Desugaring configuredYes / No / Not applicable
Firebase initialized before RetenoYes / No / Not applicable
Flutter firebase_messaging installedYes / No / Not applicable
Optional Firebase Messaging bridge probe observedYes / No / Not verified
Optional bridge warning affected core push validationYes / No / Not applicable
iOS App Group name
iOS App Group uses main app bundle IDYes / No / Not applicable
Notification Service Extension Reteno Podresolved version matching selected reteno_plugin / Missing / Not applicable
NSE Reteno pod matches resolved main-app Reteno iOS SDK versionYes / No / Not applicable
Flutter 3.44+ SPM disabledYes / No / Not applicable
APNs sandbox routing confirmedYes / No / Not applicable
Reteno Support sandbox request neededYes / No / Not applicable
Target validation platformAndroid / iOS / Both
Package managerflutter pub
Dart entry point
Android package name / applicationId
Original applicationId
Temporary applicationId
Merge readinessReady / Not ready / Not applicable
Production follow-up requiredYes / No
iOS bundle ID
Files inspected
Files changed
Files added
Dependencies added or updated
Android native changes
iOS native changes
Required Android native integration files tracked or reproducibleYes / No / Not applicable
Required iOS native integration files tracked or reproducibleYes / No / Not applicable
Required native integration files ignored by GitYes / No
Ignored required native files
Clean checkout can reproduce Reteno native integrationYes / No / Not verified
Native integration reproducibility follow-up requiredYes / No
Firebase / FCM configuration
Firebase / Reteno configuration typeProduction / Staging / Sandbox / Demo
APNs configuration, if applicable
SDK access key statusProvided / Placeholder / Missing
SDK access key handling
Android diagnostics result, if available
Android diagnostics captured during current runYes / No / Not applicable
iOS diagnostics result, if available
iOS diagnostics captured during current runYes / No / Not applicable
Diagnostics limitation / fallback reason
Manual steps requested
Manual steps completed
Build/run result
Primary static-analysis command
Primary static-analysis resultPassed / Code diagnostics found / Tooling failure / Not run
Static-analysis fallback usedYes / No
Static-analysis fallback command
Static-analysis fallback result
Test environment
Device automation / mobile-mcp status
Runtime validation result
Required Reteno push/click/action listeners registered before awaited startup workYes / No / Not applicable
Notification-triggered cold-start handling validatedYes / No / Not requested
Non-blocking runtime warnings observedYes / No
Non-blocking warning details
End-to-end evidence proving warning was benign
Reteno MCP connection
Reteno MCP validation result
Validation skipped by requestYes / No
Resume validation from
Contact found
contactId
deviceId
Contact lookup methodcontactId / deviceId
Contact correlation evidencecontactId / deviceId / token / app identity / runtime logs / timestamps / other
Contact identified by mobilePushToken aloneYes / No
Pre-existing contact identity detectedYes / No / Not verified
Device identity changed since previous validation runYes / No / Not known
Stale Reteno contact or push token detectedYes / No / Not verified
Current-run deviceId independently confirmedYes / No / Not available
mobilePushToken
Token typeFCM / APNs
Notification permission state
Notification channel state, Android only
pushSubscribed state, if available
Test push sent
Push message identifier, if available
messageTag, if used
DELIVERED status
CLICKED status
Status/contact consistency
Feature-specific validation checks
In-App message name / ID
In-App attachment result
In-App statuses
Missing MCP capabilities / requested capabilities
Blockers
Recommended next steps

If sandbox Firebase / Reteno configuration was used, report:

Overall status: Passed with warnings
Merge readiness: Not ready / sandbox-only
Production follow-up required: Yes

Do not report sandbox validation as production-ready.

A successful sandbox validation proves that the SDK flow can work in the selected environment.

It does not prove that the production app configuration is complete.

If DELIVERED and CLICKED are confirmed on separate message instances, report that clearly and explain why.

If validation-only dependency overrides or pre-existing build fixes were used, report them separately from Reteno SDK integration changes.

Flutter report

For the Flutter report, include:

  • whether the project was detected as a Flutter application;
  • evidence used for Flutter detection;
  • Flutter SDK version;
  • Reteno Flutter SDK version;
  • whether the integration run used a temporary local branch or copy;
  • which application was used;
  • target validation platform: Android, iOS, or both;
  • which Flutter files were updated;
  • which native Android files were updated, if Android validation is included;
  • which native iOS files were updated, if iOS validation is included;
  • whether Firebase / FCM setup was detected or updated;
  • whether APNs setup was detected or updated, if applicable;
  • whether SDK initialization was configured;
  • whether diagnostics were run, if available;
  • whether push notification permission handling was configured or confirmed;
  • whether token handling was configured or confirmed;
  • whether the app built and ran successfully;
  • which test environment was used;
  • whether Reteno MCP was connected;
  • whether Reteno MCP found the contact;
  • whether contactId was available;
  • whether deviceId was available;
  • whether mobilePushToken was assigned to the contact;
  • whether token type was FCM or APNs;
  • whether the token type matched the selected Reteno mobile app configuration;
  • whether the test push was sent through Reteno MCP;
  • whether DELIVERED status was received;
  • whether the developer opened the test push notification;
  • whether CLICKED status was received after the push notification was opened;
  • whether any blocker prevented validation.

Feature-specific validation report

Report each feature-specific validation check according to the current integration task.

Use the following statuses:

  • not requested for this integration;
  • completed;
  • blocked;
  • manual step required;
  • missing MCP capability.

If a feature was part of the integration task but could not be validated, do not mark it as not requested.
Mark it as blocked, manual step required, or missing MCP capability and explain why.
Report:

  • custom event validation: completed / blocked / not requested for this integration;

  • In-App validation: completed / blocked / manual step required / missing MCP capability / not requested for this integration;

  • mobile-push + In-App validation: completed / blocked / manual step required / missing MCP capability / not requested for this integration;

  • externalCustomerId: completed / blocked / not requested for this integration;

  • deeplink validation: completed / blocked / not requested for this integration;

  • custom push data validation: completed / blocked / not requested for this integration;

  • action button validation: completed / blocked / not requested for this integration.
    If In-App validation is part of the current integration task, report:

  • In-App message name and ID;

  • whether the In-App message already existed or was created/confirmed manually;

  • whether Reteno MCP found the In-App by name or ID;

  • whether Reteno MCP retrieved In-App details;

  • whether Reteno MCP attached the In-App to a mobile push, if applicable;

  • whether Reteno MCP verified which In-App was attached to the push;

  • whether In-App statuses were received: Read, Click, Clicked;

  • any missing MCP capabilities or manual/admin step.

Failed validation

If any validation step fails, include:

  • failed step;
  • observed result;
  • expected result;
  • likely cause, if known;
  • relevant contactId;
  • relevant deviceId, if available;
  • relevant mobilePushToken, if available;
  • relevant push message identifier, if available;
  • relevant messageTag, if used;
  • MCP response or error details, if available;
  • logs or screenshots requested from the developer, if applicable;
  • recommended next step.

Blocker reporting

If a blocker prevents the integration or validation from being completed, report:

  • what is blocked;
  • why it is blocked;
  • who needs to resolve it;
  • whether the remaining validation steps can continue;
  • what should be done next.

Do not mark the integration as complete if a required validation step failed, was skipped, or could not be verified.