MojoWare
MarkdownOpen in ClaudeOpen in ChatGPT

Advertising cards

A WebApp can temporarily replace the playlist on an approved Advertising screen on the same kiosk. The operator chooses the media and allowed keys; the WebApp requests a key.

Start here · Bridge reference · Complete Markdown

Configure the receiving screen first

For example, a lower touchscreen runs your game while the upper display runs Advertising.

  1. Assign your external WebApp to its screen and an Advertising playlist to the receiving screen.
  2. In Monitors, select the Advertising screen, then its Advertising tab.
  3. Open Playback → Edit playback → WebApp cards.
  4. Under Allow a WebApp to show cards, select the assigned source WebApp. Default is Off.
  5. Select the Allowed cards keys and save.
  6. In the receiving screen's Cards view, configure the fleet default or screen override media for a custom key such as jackpot. Wait for the kiosk to download/apply that content before testing.

The source WebApp must run on another monitor. Its audience/origin approval and the target's card permission are separate requirements.

Standard keys are loading, webapp_error, offline, needs_service, and paused; they have built-in fallback visuals. Custom keys need configured media. Keys must match [a-z][a-z0-9_]{1,31}: 2–32 characters, starting with a lowercase letter, then lowercase letters, digits, or underscores.

Show and clear a card: showCard / clearCard

const bridge = await getKioskBridge(); // Helper from Start here.

const result = await bridge.showCard({
  key: "jackpot",
  seconds: 60
});
// result: { screens: ["<TARGET_SCREEN_ID>"], seconds: 60, key: "jackpot" }

// When this WebApp's card is no longer needed:
await bridge.clearCard();

seconds defaults to 300 and must be an integer from 1 to 300. Zero, fractions, larger durations, and extra option fields are rejected. key is required.

The page cannot pass a target monitor, URL, file, HTML, or image. Native code derives the source and targets from the live browser session and operator configuration. These are local bridge calls, not cloud requests.

clearCard() releases only this runtime's own card lease. It does not clear an operator's portal override or another runtime's lease. Success returns { screens: [...], seconds: 0, key: null }; an empty screen list can mean there was no owned lease left to clear.

Pause and resume the playlist: pauseAds / resumeAds

await bridge.pauseAds();                 // "paused" card for 300 seconds
await bridge.pauseAds({ seconds: 120 }); // Explicit bounded duration
await bridge.resumeAds();                // Clear this runtime's lease

pauseAds() is the paused card; the receiving screen must allow that key. resumeAds() and clearCard() clear the same owned lease. There is one active WebApp card lease per target, not a stack of independent pause and card requests.

Keep a card up during a longer activity

Renew before expiry while the activity is still running. This example uses one serialized renewal loop and a single cleanup path:

async function holdCardWhile(key, runActivity, onCardError) {
  const bridge = await getKioskBridge();
  let active = true;
  let timer;
  let pending = Promise.resolve();

  const renew = () => {
    pending = bridge.showCard({ key, seconds: 300 })
      .then(() => {
        if (active) timer = setTimeout(renew, 60_000);
      })
      .catch(error => {
        active = false; // Stop renewing; the bounded lease will expire.
        onCardError(error);
      });
  };

  // Fail before starting the activity if the initial request is denied.
  await bridge.showCard({ key, seconds: 300 });
  timer = setTimeout(renew, 60_000);
  try {
    return await runActivity();
  } finally {
    active = false;
    clearTimeout(timer);
    await pending; // Finish any renewal before clearing.
    try {
      await bridge.clearCard();
    } catch (error) {
      onCardError(error); // Do not silently claim that playback resumed.
    }
  }
}

Use one owner/controller in your app so two activities do not clear each other's lease. Handle cancellation by ending runActivity and running cleanup. Do not use a rapid retry loop when native access is denied.

Expiry, priority, and confirmation

General show/clear controls belong to Advertising. The system's own WebApp error/recovery pages are separate and do not grant arbitrary card control over external WebApp screens.