Skip to content
The mobile SDKs are available on request. Email support@grona.ai to get access.

Track events

This page sends commerce events to Grona. Grona logs its own campaign events, such as exposures and taps, but it needs your app to report what the shopper did next: products viewed, cart changes, checkout and purchase. Forward them from the same analytics layer that feeds your other tools.

Build each event with a GronaEvent factory and pass it to Grona.track. Money is in minor units, so 349 is 3.49.

Factory Send it when
GronaEvent.viewItem(productId, priceMinor) A product page opens.
GronaEvent.addToCart(productId, qty, priceMinor) A quantity of a product is added to the cart. priceMinor is the unit price.
GronaEvent.removeFromCart(productId, qty) A quantity of a product is removed from the cart.
GronaEvent.beginCheckout(totalMinor) The shopper starts checkout.
GronaEvent.purchase(orderId, totalMinor, productIds, treatments) Your order backend has confirmed an order.

Grona.track is safe from any thread.

  1. Send viewItem from your product screen:

    import ai.grona.sdk.Grona
    import ai.grona.sdk.GronaEvent
    Grona.track(GronaEvent.viewItem("sku_10432", 349))
  2. Send addToCart and removeFromCart from your cart store’s change listener, once per product whose quantity moved. That covers changes from Grona actions and from your own screens alike.

    fun onCartChanged(before: AcmeCart, after: AcmeCart) {
    val ids = (before.lines.map { it.productId } + after.lines.map { it.productId }).toSet()
    for (id in ids) {
    val delta = after.qty(id) - before.qty(id)
    when {
    delta > 0 -> Grona.track(GronaEvent.addToCart(id, delta, after.unitPriceMinor(id)))
    delta < 0 -> Grona.track(GronaEvent.removeFromCart(id, -delta))
    }
    }
    }
  3. Send beginCheckout when checkout opens:

    Grona.track(GronaEvent.beginCheckout(cart.totalMinor)) // 897 is 8.97
  4. Send purchase after your order backend answers. Pass every product id in the order, and only the treatments the backend applied:

    Grona.track(
    GronaEvent.purchase(
    orderId = order.id,
    totalMinor = order.totalMinor,
    productIds = order.lines.map { it.productId },
    treatments = appliedTreatments, // from Grona.activeTreatments(), filtered to what the backend applied
    ),
    )

For how to collect the treatments at checkout, see Offers and grants.

The SDK minimises what it sends. You pass full data and it strips what Grona does not need.

  • Session products. A session product is one that a Grona campaign showed or added in this session. viewItem, addToCart and removeFromCart are always counted, but they carry the product id and price only for session products. For any other product those fields are left out.
  • Purchases. purchase keeps the order id, the total, the currency of your latest basket snapshot, the treatments and only the session products in the order. The full basket never leaves the device.
  • Exception: keyed placements. On keyed placements such as cart_line, campaign events carry the line’s product id as their key, in every arm. So while a cart_line campaign is live, the product ids of the cart lines bound to it leave the device.
  • Never sent. Search text, names, phone numbers, emails and browsing history. Pass only an opaque customer id to identify.

Do not send these. The SDK logs them for every placement it serves.

Event When
eligible A decision is computed for a placement, once per visit, for every arm
anchor_visible The slot or anchor’s top edge has been on screen for one second, for every arm
served A treatment first reaches its slot or listener
exposure Half the content has been visible for one second (diagnostic)
action An add, add-all, replace or navigate from a tap or your call on a decision
dismiss A dismiss

For how these are analysed, see Events and exposure.

Events are held or sent according to the shopper’s consent. Once consent is granted:

  • The SDK sends events while the app is in the foreground every 30 seconds, once 20 are queued, and when the app goes to the background.
  • A batch holds at most 50 events and is gzipped and sent over https.
  • The queue on disk holds at most 1,000 events or 7 days of events. The oldest go first.
  • A failed batch is retried at most 3 times with backoff.

Without an eventsUrl in your options, the SDK sends no events and keeps no event queue. Debuggable builds keep the last 200 events in memory for the debug view.