Braze Developer Guide: Integrating Remote Commands with Tealium

This guide provides a comprehensive walkthrough for developers looking to integrate Braze, a leading customer engagement platform, with Tealium’s powerful remote command capabilities on Android and Swift/iOS mobile applications. By leveraging this integration, you can orchestrate and manage Braze functionalities remotely, directly from your Tealium Customer Data Hub.

Prerequisites

Before you begin, ensure you have the following in place:

  • Braze API Key: You will need a valid Braze API key to authenticate your integration.
  • Mobile Libraries: One of the Tealium mobile libraries must be implemented in your project:
  • Remote Command Integration: Choose one of the following remote command configurations:
    • JSON Configuration File: Braze Remote Command JSON File (Requires Android-Kotlin 1.0.0+ or iOS-Swift 2.1.0+) – Ideal for streamlined setup and version control.
    • Tealium iQ Tag Management Remote Command Tag: Configure mappings directly within Tealium iQ Tag Management for a no-code approach.

Understanding the Integration

The Braze and Tealium remote command integration operates through these core components:

  1. Braze Native SDK: The foundation of the integration, providing the full spectrum of Braze’s mobile marketing and engagement features.
  2. Remote Commands Module: This Tealium module acts as a bridge, encapsulating Braze methods and enabling remote execution.
  3. Configuration (JSON or Tag): This layer translates incoming Tealium events into native Braze SDK calls. You can choose between a static JSON file hosted locally or remotely, or dynamic configuration via Tealium iQ Tag Management.

A significant advantage of this integration is the simplified SDK management. Adding the Braze remote command module to your application automatically handles the installation and building of necessary Braze libraries. This eliminates the need for manual vendor-specific code additions, streamlining your development process. For projects utilizing dependency managers, there’s no need to install the Braze SDK separately.

When choosing your remote command approach, consider these recommendations:

  • JSON Configuration File: Generally recommended for vendor integrations. It offers a structured, version-controlled configuration that can be hosted either within your app bundle or remotely for dynamic updates.
  • Tealium iQ Tag Management: Leverage Tealium iQ Tag Management for a user-friendly, no-code configuration. This method is particularly beneficial for marketing teams to manage and adjust Braze mappings without requiring code deployments.

For a deeper understanding of vendor integrations within Tealium Remote Commands, refer to the vendor integrations documentation.

Installation Guide

Choose the installation method that aligns with your project setup: Dependency Manager (Swift Package Manager, CocoaPods, Carthage, Gradle, Yarn/NPM) or Manual Installation.

Dependency Manager Installation

Swift Package Manager (iOS – Swift)

  1. In Xcode, navigate to File > Add Packages… > Add Package Dependency.

  2. Enter the repository URL: https://github.com/tealium/tealium-ios-braze-remote-command.

  3. Specify your version rules. Up to next major is generally recommended for stability and compatibility. If the TealiumBraze package doesn’t appear, refresh your Swift package cache.

  4. Select the TealiumBraze module and choose the desired app target for installation.

    For multi-target projects, manually link the module to additional targets:

    1. Select your project in the Project Navigator.
    2. Choose your app target under TARGETS.
    3. Go to General > Frameworks, Libraries & Embedded Content and add the TealiumBraze module.

    For integrating other Tealium Swift modules, consult the Swift Package Manager instructions.

CocoaPods (iOS – Swift)

  1. Remove any existing tealium-swift and pod "BrazeKit" entries from your Podfile to avoid conflicts. The TealiumBraze framework inherently includes tealium-swift.

  2. Add the TealiumBraze pod dependency to your Podfile:

    pod "TealiumBraze"

    The TealiumBraze pod automatically includes these TealiumSwift dependencies:

    'tealium-swift/Core'
    'tealium-swift/RemoteCommands'
  3. Import both TealiumSwift and TealiumBraze modules in your TealiumHelper file and any other files accessing Tealium or Braze remote commands.

Carthage (iOS – Swift)

  1. Remove tealium-swift from your Cartfile as it is already included within TealiumBraze.

  2. Add the TealiumBraze dependency to your Cartfile:

    github "tealium/tealium-ios-braze-remote-command"

Gradle (Android – Kotlin/Java)

  1. Ensure you have Tealium for Android (Kotlin) or Tealium for Android (Java) installed. Add the Tealium Maven URL to your project’s top-level build.gradle file if not already present:

    allprojects {
        repositories {
            mavenCentral()
            maven {
                url "https://maven.tealiumiq.com/android/releases/"
            }
        }
    }
  2. In your app project’s build.gradle file, add dependencies for both the Braze SDK and Tealium-Braze remote commands:

    dependencies {
        implementation 'com.tealium.remotecommands:braze:3.0.0'
        implementation 'com.braze:android-sdk-ui:29.0.1' // Ensure you use the latest Braze SDK version
    }

Yarn/NPM (React Native)

  1. Navigate to the root directory of your React Native project.

  2. Install the tealium-react-braze package using yarn or npm:

    yarn add tealium-react-braze

    or

    npm install tealium-react-braze
  3. React Native Autolinking (version 1.0.7+ of tealium-react-braze and React Native 0.60+) automates the linking process, eliminating the need to run react-native link.

Manual Installation

Manual installation is available for both iOS and Android platforms. Ensure you have the core Tealium library installed before proceeding.

Manual Installation (iOS – Swift)

Refer to the Tealium for Swift documentation for manual installation instructions for the core library. After installing Tealium for Swift, manually add the TealiumBraze framework to your project.

Manual Installation (Android – Kotlin/Java)

Refer to Tealium for Android (Kotlin) or Tealium for Android (Java) for core library manual installation. Then, follow these steps for Braze remote commands:

  1. Add flatDir to your project-level build.gradle file:

    allprojects {
        repositories {
            mavenCentral()
            flatDir {
                dirs 'libs'
            }
        }
    }
  2. Place the tealium-braze.aar file into the <project_root>/<module>/libs directory.

  3. Add the Tealium library dependency in your module’s build.gradle file:

    dependencies {
        implementation(name:'tealium-braze', ext:'aar')
    }

Initialization

The initialization process involves configuring Tealium Remote Commands and registering the Braze remote command module. You can choose to configure remote commands using either a JSON configuration file or the Tealium iQ Tag Management Remote Command tag.

Initialization for iOS (Swift)

var tealium : Tealium?
let config = TealiumConfig(account: "ACCOUNT",
                             profile: "PROFILE",
                             environment: "ENVIRONMENT",
                             dataSource: "DATASOURCE")
config.dispatchers = [Dispatchers.TagManagement, Dispatchers.RemoteCommands]
config.remoteAPIEnabled = true // Required for Remote Commands

tealium = Tealium(config: config) { _ in
    guard let remoteCommands = self.tealium?.remoteCommands else {
        return
    }

    // Webview Tag - Configure via Tealium iQ Tag Management
    let braze = BrazeRemoteCommand(brazeLocation: BrazeLocationProvider())

    // Local JSON - Configure using a local JSON file named "braze.json"
    // let braze = BrazeRemoteCommand(type: .local(file: "braze"), brazeLocation: BrazeLocationProvider())

    // Remote JSON - Configure using a remote JSON file hosted at the specified URL
    // let braze = BrazeRemoteCommand(type: .remote(url: "https://some.domain.com/braze.json"), brazeLocation: BrazeLocationProvider())

    remoteCommands.add(braze)
}

Initialization for Android (Kotlin)

val config = TealiumConfig(application,
                            "ACCOUNT",
                            "PROFILE",
                            Environment.DEV,
                            dispatchers = mutableSetOf(Dispatchers.RemoteCommands, Dispatchers.TagManagement));

// New code to add the Braze Remote Command
val braze = BrazeRemoteCommand(application,
                            true, // sessionHandlingEnabled
                            sessionHandlingBlacklist,
                            true, // registerInAppMessageManager
                            inAppMessageBlacklist)

var tealium = Tealium.create(TEALIUM_MAIN, config) {
    // Optional: Set config options not yet supported by the Tag in Tealium IQ
    // or to override settings locally.
    braze.registerConfigOverride { builder ->
        // builder.setIsLocationCollectionEnabled(true);
        // builder.setGeofencesEnabled(true);
        // builder.setPushDeepLinkBackStackActivityEnabled(true);
        // builder.setPushDeepLinkBackStackActivityClass(UserActivity.class);
    }

    // Register the Braze command

    // Webview Tag - Configure via Tealium iQ Tag Management
    remoteCommands?.add(braze);

    // Local JSON - Configure using a local JSON file named "braze.json"
    // remoteCommands?.add(braze, filename = "braze.json");

    // Remote JSON - Configure using a remote JSON file hosted at the specified URL
    // remoteCommands?.add(braze, remoteUrl = "https://some.domain.com/braze.json");
}

Initialization for Android (Java)

import com.tealium.remotecommands.BrazeRemoteCommand;

Tealium.Config config = Tealium.Config.create(application, "ACCOUNT", "PROFILE", "ENVIRONMENT");
Tealium teal = Tealium.createInstance(TEALIUM_MAIN, config);

BrazeRemoteCommand braze;

// Initialize with default configuration options.
braze = new BrazeRemoteCommand(config);

// Or alternatively initialize with additional config options:
Set<Class> sessionHandlingBlacklist = new HashSet<>();
Set<Class> inAppMessageBlacklist = new HashSet<>();
// sessionHandlingBlacklist.add(MainActivity.class);
// inAppMessageBlacklist.add(UserActivity.class);
braze = new BrazeRemoteCommand(config,
                            true, // sessiongHandlingEnabled
                            sessionHandlingBlacklist, // sessionHandlingBlacklist
                            true, // registerInAppMessageManager
                            inAppMessageBlacklist); // inAppMessageBlackList

// Optional: Set config options not yet supported by the Tag in Tealium IQ
// or to override settings locally.
braze.registerConfigOverride(new BrazeRemoteCommand.ConfigOverrider() {
    @Override
    public void onOverride(AppboyConfig.Builder b) {
        b.setPushDeepLinkBackStackActivityEnabled(true);
        b.setPushDeepLinkBackStackActivityClass(UserActivity.class);
    }
});

// Register the Braze command
teal.addRemoteCommand(braze);

Initialization for React Native

import BrazeRemoteCommand from 'tealium-react-braze';

// Webview Tag - Configure via Tealium iQ Tag Management
let braze = { id: BrazeRemoteCommand.name }

// Local JSON - Configure using a local JSON file named "braze.json"
// let braze = { id: BrazeRemoteCommand.name, path: "braze.json" }

// Remote JSON - Configure using a remote JSON file hosted at the specified URL
// let braze = { id: BrazeRemoteCommand.name, url: "https://some.domain.com/braze.json" }

let config = TealiumConfig {
    // ...
    remoteCommands: [braze]
}

JSON Configuration Template

If you opted for JSON configuration, utilize this template as a starting point. It includes common mappings applicable to standard e-commerce implementations. Customize these mappings to align with your specific data layer and Braze attribute structure.

{
  "config": {
    "api_key": "YOUR_API_KEY",
    "custom_endpoint": "sdk.iad-01.braze.com",
    "session_timeout": 30,
    "trigger_interval_seconds": 100,
    "enable_geofences": true,
    "enable_automatic_geofences": true,
    "enable_automatic_location": true,
    "push_story_identifier": "YOUR_IDENTIFIER",
    "use_uuid_as_device_id": true,
    "forward_universal_links": true
  },
  "mappings": {
    "customer_id": "user_id",
    "customer_first_name": "first_name",
    "customer_last_name": "last_name",
    "customer_gender": "gender",
    "customer_language": "language",
    "customer_email": "email",
    "customer_home_city": "home_city",
    "customer_dob": "date_of_birth",
    "customer_alias": "user_alias",
    "customer_alias_label": "alias_label",
    "pet": "set_custom_attribute.pet",
    "pet_count": "set_custom_attribute.pet_count",
    "pet_count_unset": "unset_custom_attribute.pet_count_unset",
    "pet_count_increment": "increment_custom_attribute.pet_count_increment",
    "pet_names": "set_custom_array_attribute.pet_names",
    "pet_names_append": "append_custom_array_attribute.pet_names_append",
    "pet_names_remove": "remove_custom_array_attribute.pet_names_remove",
    "screen_name": "screen_name",
    "email_notification": "email_notification",
    "push_notification": "push_notification",
    "event_name": "event_name",
    "current_level": "event.current_level",
    "start_date": "event.start_date",
    "high_score": "event.high_score",
    "product_id": "product_id",
    "product_quantity": "product_qty",
    "product_unit_price": "product_unit_price",
    "currency_code": "product_currency",
    "rewards_member": "purchase.rewards_member",
    "rewards_points_earned": "purchase.rewards_points_earned",
    "date_joined_program": "purchase.date_joined_program",
    "latitude": "location_latitude",
    "longitude": "location_longitude",
    "horizontal_accuracy": "location_horizontal_accuracy",
    "vertical_accuracy": "location_vertical_accuracy"
  },
  "commands": {
    "launch": "initialize",
    "setengagement": "emailnotification,pushnotification",
    "log_custom_event": "logcustomevent",
    "set_location": "setlastknownlocation",
    "custom_attribute": "setcustomattribute",
    "unset_custom_attribute": "unsetcustomattribute",
    "custom_array_attribute": "setcustomarrayattribute",
    "append_custom_array_attribute": "appendcustomarrayattribute",
    "remove_custom_array_attribute": "removecustomarrayattribute",
    "increment_custom_attribute": "incrementcustomattribute",
    "log_purchase": "logpurchase",
    "user_attribute": "userattribute,useridentifier,useralias",
    "user_login": "useridentifier,useralias",
    "user_alias": "useralias"
  }
}

Supported Remote Commands and Braze Methods

The Tealium Remote Commands integration maps commands to corresponding Braze SDK methods. To invoke a Braze method, trigger the associated remote command in the specified format through Tealium.

Remote Command Braze Method
addtosubscriptiongroup user.addToSubscriptionGroup(id:)
appendcustomarrayattribute user.addToCustomAttributeArray()
emailnotification user.set(emailSubscriptionState:)
enablesdk enabled = true/false
flush requestImmediateDataFlush()
incrementcustomattribute user.incrementCustomUserAttribute()
initialize Braze(configuration:)
logcustomevent logCustomEvent()
logpurchase logPurchase()
pushnotification user.set(pushNotificationSubscriptionState:)
removefromsubscriptiongroup user.removeFromSubscriptionGroup(id:)
removecustomarrayattribute user.removeFromCustomAttributeArray()
setadtrackingenabled set(adTrackingEnabled:)
setcustomattribute user.setCustomAttribute())
setcustomarrayattribute user.setCustomAttributeArray()
setlastknownlocation user.setLastKnownLocation()
setsdkauthsignature set(sdkAuthenticationSignature:)
useralias user.add(alias:)
unsetcustomattribute user.unsetCustomAttribute()
userattribute user.set()
useridentifier changeUser()
wipedata wipeData()

With the Braze SDK bundled with the Tealium SDK, you can trigger any native Braze functionality via Tealium’s tag management system based on your configuration.

Detailed SDK Setup and Remote Command Parameters

SDK Setup Commands

Initialize (initialize)

The initialize command is crucial for setting up the Braze SDK. The Braze API key and custom endpoint are mandatory parameters that must be configured either in your JSON configuration file or within the Tealium iQ Tag Management tag settings.

Remote Command Braze Method (iOS) Braze Method (Android)
initialize Braze(configuration:) configure()

The following parameters are used to customize the SDK initialization:

Parameter Type Description
api_key (required) String Your Braze API Key.
custom_endpoint (required) String Sets a custom Braze API endpoint. Format: sdk.api.braze.eu (replace .eu with your region).
device_options [String] Whitelist of device fields to be collected by the Braze SDK. By default, all fields are collected.
enable_automatic_geofences Bool Enables automatic geofence collection (true or false).
enable_automatic_location Bool Enables location collection (true or false).
enable_geofences Bool Enables geofence collection (true or false).
flush_interval Double Sets the data flush interval in seconds. Values must be greater than 1.0.
is_sdk_authentication_enabled Bool Enables SDK authentication. Default is false.
push_story_identifier String Sets the app group name for the Push Story Notification Content extension.
request_processing_policy String Defines the SDK’s request processing policy (manual or automatic). Default is automatic.
session_timeout Int Session timeout in seconds (default: 30 seconds).
trigger_interval_seconds Int Overrides the minimum interval between in-app message triggers (default: 30 seconds).
use_uuid_as_device_id (iOS) Bool Determines if a randomly generated UUID should be used as the device ID. Default is true.
forward_universal_links Bool Specifies if the SDK should automatically recognize and forward universal links to system methods. Default is false.

Set SDK Auth Signature (setsdkauthsignature)

The setsdkauthsignature command is used for Braze SDK authentication.

Remote Command Braze Method (iOS) Braze Method (Android)
setsdkauthsignature set(sdkAuthenticationSignature:) setSdkAuthenticationSignature
Parameter Type
sdk_authentication_signature String

Flush (flush)

The flush command forces an immediate data flush to Braze servers.

Remote Command Braze Method
flush requestImmediateDataFlush

User Tracking Commands

Assigning a User ID (useridentifier)

The useridentifier command sets or changes the current user ID in Braze.

Remote Command Braze Method
useridentifier changeUser
Parameter Type
user_id (required) String
sdk_authentication_signature String

Aliasing Users (useralias)

The useralias command allows you to create an alias for a user, useful for scenarios like login/logout across different devices or platforms.

Remote Command Braze Method
useralias user.add(alias:)
Parameter Type
user_alias (required) String
alias_label (required) String

User Attributes Commands

Standard User Attributes (userattribute)

The userattribute command lets you set standard user attributes within Braze.

Event Braze Method
userattribute Set fields on the Braze.User object
Parameter Type
first_name String
last_name String
email String
date_of_birth String
country String
language String
home_city String
phone String
gender String

Set Custom Attribute (setcustomattribute)

The setcustomattribute command allows you to set custom user attributes in Braze.

Remote Command Braze Method
setcustomattribute user.setCustomAttribute
Parameter Type
set_custom_attribute.PROPERTY [String: Any] / JSONObject

Unset Custom Attribute (unsetcustomattribute)

The unsetcustomattribute command removes a custom user attribute from a user profile in Braze.

Remote Command Braze Method
unsetcustomattribute user.unsetCustomAttribute
Parameter Type
set_custom_attribute.PROPERTY String

Increment Custom Attribute (incrementcustomattribute)

The incrementcustomattribute command increments a numeric custom user attribute in Braze.

Remote Command Braze Method
incrementcustomattribute user.incrementCustomUserAttribute
Parameter Type
increment_custom_attribute.PROPERTY String

Set Custom Array Attribute (setcustomarrayattribute)

The setcustomarrayattribute command sets a custom array attribute for a user in Braze.

Remote Command Braze Method
setcustomarrayattribute user.setCustomAttributeArray
Parameter Type
set_custom_array_attribute.PROPERTY [String: [Any]] / JSONObject

Append Custom Array Attribute (appendcustomarrayattribute)

The appendcustomarrayattribute command adds a value to a custom array attribute in Braze.

Remote Command Braze Method
appendcustomarrayattribute user.addToCustomAttributeArray
Parameter Type
append_custom_array_attribute.PROPERTY [String: String] / JSONObject

Remove Custom Array Attribute (removecustomarrayattribute)

The removecustomarrayattribute command removes a specific value from a custom array attribute in Braze.

Remote Command Braze Method
removecustomarrayattribute user.removeFromCustomAttributeArray
Parameter Type
remove_custom_array_attribute.PROPERTY [String: String] / JSONObject

Custom Event Tracking Commands

Custom Event (logcustomevent)

The logcustomevent command logs a custom event to Braze.

Remote Command Braze Method
logcustomevent logCustomEvent
Parameter Type
event_name (required) String
event_properties [String: Any] / JSONObject

Notification Commands

Email Notifications (emailnotification)

The emailnotification command sets the email notification subscription status for a user in Braze.

Remote Command Braze Method
emailnotification setEmailNotificationSubscriptionType
Parameter Type Example
email_notification String ["optedin", "subscribed", "unsubscribed"]

Add to Subscription Group (addtosubscriptiongroup)

The addtosubscriptiongroup command adds a user to a specific Braze subscription group.

Remote Command Braze Method
addtosubscriptiongroup user.addToSubscriptionGroup(id:)
Parameter Type
subscription_group_id (required) String

Remove from Subscription Group (removefromsubscriptiongroup)

The removefromsubscriptiongroup command removes a user from a Braze subscription group.

Remote Command Braze Method
removefromsubscriptiongroup user.removeFromSubscriptionGroup(id:)
Parameter Type
subscription_group_id (required) String

Push Notifications (pushnotification)

The pushnotification command sets the push notification subscription status for a user in Braze.

Remote Command Braze Method
pushnotification setPushNotificationSubscriptionType
Parameter Type Example
push_notification String ["optedin", "subscribed", "unsubscribed"]

Purchase Tracking Commands

Log Purchase (logpurchase)

The logpurchase command logs a purchase event to Braze, including product details and revenue.

Remote Command Braze Method
logpurchase logPurchase
Parameter Type
product_id (required) [String] / String[]
product_currency (required) String / String
price (required) [Double] / Double[]
product_qty [Int] / Int[]
purchase_properties [AnyHashable: Any] / JSONObject

Location Tracking Commands

Set User’s Last Known Location (setlastknownlocation)

The setlastknownlocation command allows you to manually set the user’s last known location in Braze.

Remote Command Braze Method
setlastknownlocation user.setLastKnownLocation
Parameter Type
latitude (required) Double
longitude (required) Double
horizontalAccuracy (required) Double
altitude Double
verticalAccuracy Double

Data Privacy (Consent Options) Commands

Enable SDK (enablesdk)

The enablesdk command re-enables the Braze SDK if it was previously disabled.

Remote Command Braze Method (iOS) Braze Method (Android)
enablesdk enabled enableSdk

Disable SDK (disablesdk)

The disablesdk command disables the Braze SDK, preventing data collection and communication with Braze servers.

Remote Command Braze Method (iOS) Braze Method (Android)
disablesdk enabled disableSdk

Wipe Data (wipedata)

The wipedata command clears all locally stored data by the Braze SDK, respecting user privacy requests.

Remote Command Braze Method
wipedata wipeData

Set Ad Tracking Enabled (setadtrackingenabled)

The setadtrackingenabled command controls whether ad tracking is enabled for the Braze SDK, respecting user ad tracking preferences.

Remote Command Braze Method
setadtrackingenabled set(adTrackingEnabled:)
Parameter Type
ad_tracking_enabled (required) Bool

Accessing the Braze Instance Directly (iOS)

While remote commands provide a convenient way to manage core Braze functionalities, some advanced features like Content Cards, Feature Flags, and advanced Notification customizations may require direct interaction with the Braze instance.

To access the underlying Braze instance directly within your iOS application, use the onReady method provided by the BrazeRemoteCommand class. This method ensures the Braze instance is fully configured before providing access in the completion block.

brazeRemoteCommand.onReady { braze in
    // Use the Braze instance here to access advanced features
    // Example: braze?.contentCards.requestContentCards()
}

Once you obtain a reference to the Braze instance, you can leverage the full capabilities of the Braze SDK as documented in the official Braze documentation.

Automatic Push Integration Considerations

Braze’s latest SDK versions offer streamlined automatic push notification processing. Refer to the Braze Developer Guide: Automatic push integration for detailed information.

For automatic push integration, synchronous initialization of the Braze instance on the main thread within the didFinishLaunchingWithOptions method of your app delegate is mandatory. This necessitates direct Braze instance configuration, bypassing the remote command configuration for initialization.

Use the following code snippet for direct Braze instance configuration to enable automatic push integration:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let brazeConfig = Braze.Configuration(apiKey: "YOUR_API_KEY", endpoint: "YOUR_ENDPOINT")
    brazeConfig.push.automation = true
    // ...
    // Add other Braze SDK configurations here
    // ...
    let brazeInstanceWrapper = BrazeInstance()
    brazeInstanceWrapper.initializeBraze(brazeConfig: brazeConfig)
    let brazeCommand = BrazeRemoteCommand(brazeInstance: brazeInstanceWrapper)
    let tealiumConfig = TealiumConfig(...)
    tealiumConfig.remoteCommands = [brazeCommand]
    // ...
    // Complete Tealium initialization
    return true
}

Manual Push Integration Approach

For implementing manual push notification integration, retain the remote command configuration and enable push integration directly through the Braze instance.

Consult the Braze Developer Guide: Manual push integration for implementation specifics.

[ Contentsquare

“Contentsquare” ](/platforms/remote-commands/integrations/contentsquare/)

[ Branch

“Branch ” Branch
](/platforms/remote-commands/integrations/branch/)

Was this page helpful?

Thank you for your feedback!

This page was last updated: February 8, 2024

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *