Enable order signals in an Android app

The order signal captures when a user completes a transaction in your app — for example, a retail purchase, a flight booking, or an insurance application. It is essential for tracking conversions, calculating revenue, and triggering post-purchase campaigns.

Availability: Pro, Premium and Ultimate


Implementation considerations

When to fire the signal

Fire the order signal after the order is confirmed by your backend — not when the user taps the purchase button. At that point you have the order ID and all order data from the API response, so no intermediate storage is needed.

checkoutService.placeOrder(cart) { result ->
    when (result) {
        is Success -> {
            sendOrderSignal(result.order)
            navigateToConfirmation(result.order)
        }
        is Failure -> showError(result.error)
    }
}

Ordered items

Each item in the order is represented as a JSONObject inside a JSONArray. Pass the JSONArray as the value of orderedItems in the signal HashMap. If orderedItems fails validation — for example, due to a missing required item field — the entire signal is discarded.

Contact mapping

Use the audience field to map the signal to a contact in Connect. Commonly used identifiers are email address, phone number, and customer ID.

📘

Note

A customer ID mapped to the Contact key attribute in Connect is the most reliable identifier for known contacts. However, if no matching contact exists and the only attribute is a contact key, the signal is discarded — contact keys alone cannot create new contacts.

Sources of the identifier in an Android app:

  • In-memory session object (for example, UserSession.email)
  • SharedPreferences for persisted login state
  • Room database or other local store
  • Checkout form fields — the user's email or phone number entered during checkout, if collected

The value must be a JSONObject where each key is a contact attribute name as it appears in Connect (including capitalization and spacing) and each value is the attribute value. For phone number attributes, use the E.164 format: +[country code][area code][phone number] — no spaces, dashes, or special characters. If the + symbol is omitted, Connect adds it automatically.

The order signal is also a good opportunity to enrich the contact record with details collected during checkout — for example, a shipping postal code or city. Include any additional attributes as extra key-value pairs in the same audience object alongside the identifier.

📘

Note

We recommend attaching identifiers to as many signals as possible. If the user is not authenticated, omit audience — Connect will attempt to identify the visitor using other signals from the same session.


Configuration

Method

Connect.logSignal(data: HashMap<String?, Any?>?): Boolean

Sends the signal to the Acoustic Connect endpoint. The Connect SDK must be initialized via Connect.enable() before calling this method.

Pass order-level fields as a flat HashMap. Pass orderedItems as a JSONArray of JSONObjects, and audience as a JSONObject.

Signal fields

Order-level required

FieldSchema typeDescription
orderIdStringUnique order identifier returned by your backend
signalTypeStringSignal type. Value: "order".

Order-level optional

FieldSchema typeDescription
audienceJSONObjectKey-value pairs for contact mapping. Must be a flat JSONObject — not a HashMap. Keys must match contact attribute names exactly as they appear in Connect.
categoryStringSignal category. Value: "Behavior".
currencyStringISO 4217 currency code (for example, "USD", "EUR"). Defaults to the currency set for your Connect subscription
descriptionStringA description of the signal
nameStringA label to differentiate this signal from others (for example, "order from Android app"). Max 256 characters.
orderDiscountNumberTotal discount applied to the order Pass as a string
orderedItemsJSONArrayList of ordered items. Each item is a JSONObject — see item-level fields below
orderShippingHandlingNumberShipping and handling cost. Pass as a string
orderSubtotalNumberSubtotal amount before tax and shipping. Pass as a string
orderTaxNumberTax amount. Pass as a string
orderValueNumberTotal order value including tax and shipping. Pass as a string

Item-level required

FieldSchema typeDescription
itemQuantityNumberQuantity of this product purchased. Pass as a string
productIdStringUnique product identifier (may match SKU)
productNameStringName of the product
unitPriceNumberPrice the customer paid for one unit. Pass as a string

Item-level optional

FieldSchema typeDescription
discountNumberDiscount amount per unit. Pass as a string.
productCategoryStringProduct category from your catalog
productCategoryIdStringProduct category identifier
productDescriptionStringDescription of the product
promotionIdStringID from a marketing campaign that influenced the purchase
shoppingCartUrlStringURL or deep link to the shopping cart
virtualCategoryStringCategory based on navigation path (for example, "New arrivals", "Sale")

Things to know

  • Fields with schema type Number must be passed as strings. The SDK serializer silently drops numeric values.

  • Pass audience as a JSONObject, not a HashMap. A HashMap value is silently dropped by the SDK serializer and the contact will not be mapped.

  • Pass orderedItems as a JSONArray of JSONObjects. If orderedItems fails validation, the entire signal is discarded.

  • If a required field is missing or invalid, the entire signal is discarded. If a required field isn't applicable to your business, use a placeholder value.

Required imports

import org.json.JSONArray
import org.json.JSONObject

Basic example

val item = JSONObject().apply {
    put("productId", "SKU-12345")
    put("productName", "Wireless Headphones")
    put("itemQuantity", "1")
    put("unitPrice", "299.99")
}

val data = hashMapOf<String?, Any?>(
    "signalType" to "order",
    "orderId" to "ORD-98765",
    "orderValue" to "299.99",
    "currency" to "USD",
    "orderedItems" to JSONArray().put(item)
)
Connect.logSignal(data)

Complete example

This example fires an order signal from the confirmation screen after the backend confirms the order. It includes multiple ordered items, order totals, and optional contact mapping.

import org.json.JSONArray
import org.json.JSONObject
import com.acoustic.connect.android.connectmod.Connect

private fun sendOrderSignal(order: Order) {
    // Build ordered items — one JSONObject per line item
    val orderedItems = JSONArray()
    for (item in order.items) {
        val orderedItem = JSONObject().apply {
            // Fields with schema type Number must be passed as strings
            put("productId", item.id)
            put("productName", item.name)
            put("itemQuantity", item.quantity.toString())
            put("unitPrice", item.unitPrice.toPlainString())
            put("productCategory", item.category.displayName)
            put("currency", "USD")
        }
        orderedItems.put(orderedItem)
    }

    val data = hashMapOf<String?, Any?>(
        "signalType" to "order",
        "orderId" to order.id,
        "orderValue" to order.total.toPlainString(),
        "orderSubtotal" to order.subtotal.toPlainString(),
        "orderTax" to order.tax.toPlainString(),
        "orderShippingHandling" to order.shipping.toPlainString(),
        "currency" to "USD",
        "orderedItems" to orderedItems
    )

    // audience must be JSONObject — HashMap values are silently dropped
    val audience = JSONObject()
    UserSession.email?.let { email -> audience.put("Email Address", email) }
    order.shippingPostalCode?.let { postal -> audience.put("Postal Code", postal) }
    if (audience.length() > 0) {
        data["audience"] = audience
    }

    Connect.logSignal(data)
}

Troubleshooting

Signal not appearing in Connect?

  • Confirm Connect.enable() is called before logSignal.
  • Check that logSignal returns true. A false return indicates the SDK rejected the call.
  • Verify the appKey and postMessageUrl match the Connect org you're checking.

Signal marked as invalid in Connect?

  • Confirm all required fields are present: signalType and orderId.
  • Confirm each item in orderedItems has all required fields: productId, productName, itemQuantity, unitPrice.
  • Verify that Number fields are passed as strings — use quantity.toString() and price.toPlainString().

Contact not created or updated?

  • Confirm audience is a JSONObject, not a HashMap.
  • Verify attribute key names match exactly how they appear in Connect — including capitalization and spacing.

Order items missing from the signal?

  • Confirm orderedItems is a JSONArray of JSONObjects, not a List or nested HashMap.
  • Check for numeric values in item fields not passed as strings — they are silently dropped.

Related pages