How Grona works
This page follows one campaign from the moment your team publishes it to the moment Grona measures the result, first on a website and then in an app. Read it when you want to know what runs where, which requests leave the browser or device, and why some changes take effect later than others.
On a website
Section titled “On a website”On the web, every decision is made in the visitor’s browser by a script built for your workspace. Grona’s servers do not choose a variation per request.
-
Your team publishes a campaign. A campaign is set live in the Grona dashboard or through the Public API. Grona rebuilds your workspace’s script,
https://cdn.grona.ai/workspaces/YOUR_WORKSPACE_ID/main.js, with every active campaign baked in, uploads it and asks the CDN to refresh it. This is not instant: allow a few minutes before you expect visitors to get the new file. Pausing or ending a campaign follows the same path. -
The page loads and hides itself. The anti-flicker script,
antiflicker.js, runs first and withoutasync. It hides the page body so visitors never see the original content swap to the variation. A safety timer reveals the page after 5 seconds whatever happens. Known bots, including Lighthouse and PageSpeed Insights, are never hidden and never see a variation. See Prevent flicker. -
The runtime script starts.
main.jsloads withasync. It creates or reads a random visitor id inlocalStorageand a visit id insessionStorage. If your workspace is set to wait for consent, it stores and sends nothing until the visitor has consented. See Consent and storage. -
The browser decides. The script walks the active campaigns in priority order. For each one it checks the schedule, any UTM rules, the URL rules and any audience rules. The first campaign that matches enrols the visitor, and lower-priority campaigns do not run on that page view. The variation comes from the visitor id, so a visitor sees the same variation on every page and every return visit for as long as their browser keeps the id.
-
The browser applies the change. The script downloads that variation’s changes file from
cdn.grona.ai, waits for the page to be ready and applies the edits. For a variation other than control, it adds av=grona-marker to the address bar so you can see which variation loaded. -
The page is revealed. The page shows as soon as the changes are applied, straight away when no campaign matched, or when the 5-second timer fires. If the script arrives after the timer, the changes still apply, visibly.
-
Grona measures. The script sends page views, scrolls, clicks on elements Grona has tagged, time on page and conversions in batches to
api.grona.ai. Conversions come from Grona’s goal snippets or your owngrona('track', ...)calls. See Track conversions and custom events.
On single-page sites, the script watches history changes, undoes the applied edits and runs the matching again for the new URL. Hash-based routes are not supported. See Single-page applications.
In an app
Section titled “In an app”In an app, the SDK makes every decision on the device from a signed configuration. Grona never sees which screen a shopper is on until events are sent, and events are sent only after consent.
-
Grona publishes a signed configuration. Each workspace has one configuration document that lists its campaigns, placements, audiences and traffic splits. Grona signs it. Your app ships a signed copy as the asset
grona/config.json, so Grona content works on first launch and offline, and the SDK refreshes it from the config URL. The SDK accepts a configuration only if its signature matches a key you passed at initialisation and it is not older than one it already accepted. Otherwise it keeps the last good copy. See Signed configuration. -
Your app tells the SDK about the shopper. You set consent, the basket, the current screen, and optionally an opaque customer id and profile from your own signed-in session. None of this needs a network call.
-
The SDK decides on the device. For each placement on screen, the SDK walks the campaigns that name it in priority order. It evaluates the audience rules against the basket, screen, profile and context, then assigns the shopper to an arm with a deterministic hash of the install id or the customer id. Treatment shows the campaign. Control and holdout show nothing from it, and the next campaign in the placement gets its own draw. See Assignment and bucketing.
-
The content is rendered. In SDK-rendered mode, the SDK draws a native component in a
GronaSlotView. In host-rendered mode, your listener receives a decision and your app draws it. When there is nothing to show, the anchor stays visible at zero height (the slot view does this itself; for decisions your app does it), so every arm is measured on the same view. See Rendering modes. -
Events are sent after consent. The SDK logs
eligiblefor every arm,anchor_visiblewhen the anchor has been on screen for one second, andserved,exposure, actions and the commerce events your app forwards. Until consent is granted, events stay in memory and nothing is sent. Under denied consent nothing is collected. See Events and exposure. -
Money offers need a grant. An offer such as 10% off this order renders only when the SDK holds a grant: a token Grona signs for that shopper and campaign. Your backend fetches grants inside the shopper’s signed-in session, and your order backend verifies the token again before it applies the offer. See Offers and grants.
Where each step runs
Section titled “Where each step runs”| Step | Website | App |
|---|---|---|
| Campaign definition | Baked into your workspace’s main.js on cdn.grona.ai |
Signed configuration, bundled and refreshed |
| Targeting and assignment | In the browser | On the device |
| Rendering | The script edits your page | The SDK or your own views |
| Visitor identity | Random id in localStorage, per origin |
Random install id owned by the SDK, plus an optional opaque customer id |
| Measurement | Event batches to api.grona.ai |
Event batches to the events URL, after consent |
Next steps
Section titled “Next steps”- Concepts and glossary defines each term used above.
- Domains and network calls lists every request in this lifecycle.