product
What a Cart Abandonment Model Actually Sees
A storefront emits a huge volume of behavioral data. Here's why pattern recognition beats a fixed rule — and what we deliberately don't collect.
We don't publish the exact signals or thresholds our model uses to decide when to intervene — that's on purpose, for the same reason a spam filter doesn't publish its exact rules. But we can talk honestly about the shape of the problem, and about what the model is built on.
A storefront produces more data than a single rule can use
Every session on a Shopify storefront running Navona generates a stream of client-side and web-pixel events — page views, product interactions, cart changes, navigation patterns, and more — each carrying a number of fields describing the moment it happened. Multiply that across a session, and a single shopper's visit isn't one data point. It's a sequence of many, each with its own context.
A fixed-rule script can only look at a handful of those signals at a time, combined with hand-picked thresholds: if this happens, and this hasn't happened, and it's been this long, trigger. That's a reasonable way to build a first version, and it works. But it has a ceiling. The rule doesn't get better with more traffic — it does the same thing on day one and on day one thousand, and every edge case it misses has to be found by a human and hand-coded in as a new branch.
Pattern recognition over the whole surface is a different tool
A model trained across that same event surface isn't limited to a handful of hand-picked signals evaluated one at a time. It can weigh many fields against each other simultaneously, and its read on what a given pattern means improves as more sessions accumulate. Where a fixed rule plateaus the day it ships, a model has a mechanism to keep improving — that's the actual argument for building one, independent of any specific signal it happens to weigh today.
We're deliberately not going to describe which signals matter most or how they're combined. That's true of the fixed-rule approach too, for the same reason: publishing the exact conditions that trigger an intervention would let anyone reverse-engineer around them, which defeats the point of having them at all.
The same behavior means different things on different stores
A pause in the middle of a session is not the same signal on a €30 impulse-purchase store as it is on a €300 considered-purchase store. On the cheaper store it might already indicate real hesitation; on the pricier one, the same pause might be completely normal deliberation while a shopper compares options. A single fixed threshold applied uniformly across stores gets this wrong in one direction or the other on any store that isn't close to average. A model that learns store-specific patterns, rather than applying one global rule everywhere, is built to account for exactly this kind of variance — store to store, and category to category within a store.
This is also why a signal ranking wouldn't be an honest thing to publish even if we wanted to: "signal X matters most" is only ever true in aggregate, and the entire point of the model is that it doesn't apply the same weighting everywhere.
What the model doesn't need
The strongest trust signal here isn't about what we collect — it's about what we don't. The intervention doesn't require an email address or a phone number to work. It reads and reacts to on-page shopping behavior in the moment, which means we're not asking a shopper to hand over contact information before we can do the job. If a tool you're comparing wants an email address to power its logic, that's a design choice, not a technical necessity — behavioral intervention doesn't need it.
What we'll say, and what we won't
We'll talk about the shape of the data, the reasoning for using a model instead of a fixed rule, and why one signal can't be ranked as universally more important than another. We won't publish the specific signals, the thresholds, or the timing our model uses to decide when to act. That's the same instinct behind being explicit that our own attribution numbers are attribution, not lift — see Attribution Is Not the Same as Lift.
Appendix: the event catalog, in full
"A large surface" is a claim that should be checkable. Here is every event type NavonaAI's storefront integration produces, current as of 26 Aug 2026. One field is deliberately incomplete, and we say exactly which one and why, rather than pretend the appendix is unabridged.
Storefront events (Shopify's own web-pixel taxonomy)
These are Shopify's standard pixel event types — not proprietary to
Navona, and the same events any pixel-based analytics tool on the
platform receives. Every event carries a common envelope
(id, name, timestamp, clientId) plus an event-specific payload.
| Event | Payload |
|---|---|
product_added_to_cart | Shopify's standard cart-line payload, forwarded unmodified |
product_removed_from_cart | Shopify's standard cart-line payload, forwarded unmodified |
product_viewed | Shopify's standard product payload, forwarded unmodified |
collection_viewed | collection.id, collection.title |
search_submitted | searchResult.query, matched product/variant IDs |
checkout_started | buyerAcceptsEmailMarketing, buyerAcceptsSmsMarketing, attributes, token, currencyCode, discountApplications, discountsAmount, email, phone, lineItems (variant ID + discount allocations), order, delivery |
checkout_completed | Same shape as checkout_started |
checkout_contact_info_submitted | none — fires with no payload |
checkout_address_info_submitted | none — fires with no payload |
checkout_shipping_info_submitted | none — fires with no payload |
payment_info_submitted | none — fires with no payload |
cart_viewed | none — fires with an empty payload |
page_viewed | none — fires with no payload |
alert_displayed | Shopify's standard alert payload, forwarded unmodified |
ui_extension_errored | the error object |
email and phone above are Shopify's own standard checkout fields,
present because a checkout inherently has them once a shopper reaches
that step — not something we ask for. The intervention itself, covered
above, never requires either.
Navona's own prevention events
Session- and outcome-level events our own script emits, independent
of the pixel above. Every one carries storeId, sessionId,
clientId, cartId, and an optional occurredAt.
| Event | What it marks |
|---|---|
session_started, cart_created | session and cart lifecycle |
popup_shown, popup_reshown | the offer rendered |
popup_accepted, popup_rejected | the shopper's response |
popup_copy_failed, popup_delivery_failed | reliability telemetry — the offer didn't reach the shopper cleanly |
chatbot_accepted, chatbot_rejected, objection_accepted, objection_rejected, user_engaged, user_messaged | the objection-handling conversation, where offered |
new_abandoning, repeat_abandoning, abandonment_detected | abandonment classification, first-time vs. repeat |
popup_shown and popup_accepted also carry a discountCode and a
trigger field. We're not publishing the trigger field's values
here — they're the specific behavioral classification a signal-level
ranking post would have exposed, which is the same thing this post
already said we wouldn't do. A small number of additional internal
telemetry events exist and aren't individually listed, for the same
reason.