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

Campaign activation and kill switch

A campaign on a device can stop in three ways: a new signed configuration switches it off, the configuration expires, or an activation source in your app reports that the campaign is no longer active. This page explains each one, how fast it reaches a device, and what the SDK does when it cannot tell. Read it when you connect an external campaign system or plan how you would stop a campaign in a hurry.

For every campaign, the SDK computes:

active = enabled AND now < expires_at AND (activation unknown OR any of the campaign's external_ids is active)
  • enabled: false in the signed configuration always wins.
  • Past the configuration’s expires_at, every campaign in it is inactive. No fetch is needed for that.
  • Campaigns without external_ids ignore activation.

An activation source is code you write and pass to the SDK at initialisation. It asks your own campaign or promotions system which campaigns are live, and returns the set of external ids that are active now, for example CMP-1024. When it does not know, it returns nothing (null).

  • A campaign with external ids stays active only while at least one of its ids is in the set. An answer can switch a campaign off.
  • An answer can never switch a campaign on. It cannot override enabled: false, revive an expired configuration, or add a campaign the configuration does not contain.
  • An unknown answer leaves the decision to the configuration: the campaign runs as the signed configuration says.

The answer is unknown when the source returns null, fails, takes longer than 10 seconds, or is still running an earlier call, and when its last answer is more than 10 minutes old.

For example, Acme Market’s promotions system ends CMP-1024, the external id of its weekend breakfast banner. At the next poll the source stops returning CMP-1024, and the banner disappears, including from screens already showing it.

  • It polls the source every 5 minutes while the app is in the foreground, and on demand when you call refresh.
  • It calls the source on a Grona background thread, sometimes at the same time as your own code. Make it thread-safe, and never touch views from it.
  • It waits at most 10 seconds for an answer. After that it treats activation as unknown and carries on with its other work. It does not call the source again until the earlier call has returned.

To pass a source to the SDK, see Initialise the SDK for Android or iOS.

Answers are trusted for at most 10 minutes

Section titled “Answers are trusted for at most 10 minutes”

An answer counts for at most 10 minutes after it arrives. A poll that fails, times out or finds the previous call still running makes activation unknown at once, and so does an answer older than 10 minutes. While activation is unknown, campaigns fall back to the configuration.

So a single failed poll, at most 5 minutes after the last good one, lets a campaign your source switched off become active again on the shopper’s next visit. For a stop that must hold even when your source is down, switch the campaign off in the signed configuration as well.

Once a campaign is switched off during a screen visit, it stays off for the rest of that visit. A later answer that switches it back on, or an answer that lapses to unknown, does not bring content back mid-visit. A campaign that is switched back on reappears on the shopper’s next visit to the screen.

A visit is keyed on placement, screen and key, not on the view. Binding the same placement, screen and key again continues the visit, so it does not bring the campaign back. Events and exposure explains visits in full.

Path Worst case for an app in the foreground
Activation source The next poll, at most 5 minutes later, plus up to 10 seconds for the source to answer
New configuration with the campaign switched off ttl_seconds after the last fetch. With the recommended 300 seconds, about 5 minutes while the config endpoint is healthy, and about 65 minutes while it is failing, because backoff can delay a refetch by up to 1 hour. Add up to 30 seconds for the fetch
expires_at At that moment, with no fetch
  • A stop applies at once to content already on screen, as soon as the answer or the configuration lands.
  • A backgrounded app fetches and polls nothing on its own. It applies a stop when it returns to the foreground and the refresh is due.
  • Grona sets ttl_seconds to 300 in configurations that carry money treatments, so a stop reaches those devices within the times above.

The SDK offers two refresh calls.

  • refresh without a listener polls the activation source and asks for a configuration fetch. It gives no signal when it is done.
  • refresh with a completion listener does the same work in a fixed order and tells you when it has finished.

The refresh with a listener runs these steps:

  1. It polls the activation source for an answer requested after your call. If a poll is already in flight, it waits for that poll and then polls again.

  2. It fetches the configuration, unless the last fetch was less than 300 seconds ago, the SDK is backing off after failed fetches (for up to 1 hour), or the app is in the background. If a fetch is already in flight, it waits for that fetch instead.

  3. It requests a re-evaluation of live placements, then calls your listener once, on the main thread.

The 10-second source timeout and the 30-second fetch timeout still apply, so the listener always fires: after failures, after timeouts, and even when the SDK has not been initialised.

Because stops stick for the visit, a campaign that the refresh switched back on does not reappear on the current screen. To show it at once, end the visit from the listener and then bind again: cancel the decision’s subscription for a host-rendered placement, or clear the slot’s screen for an SDK-rendered one. The platform guides cover both: host-rendered decisions for Android and iOS, and Grona components for Android and iOS.