---
title: "Ads"
description: "Rewarded videos (\"watch an ad for an extra life\", \"watch an ad for 10 free credits\") and interstitials at natural breaks, in an app or a game (interstitials show only in development for now: the…"
url: "/docs/guides/ads/"
lang: en
index: "/llms.txt"
---
# Ads

Rewarded videos ("watch an ad for an extra life", "watch an ad for 10 free credits") and interstitials at natural breaks,
in an app or a game (interstitials show only in development for now: the production ad network is rewarded video only). **Off by default**: no app or game gets ads unless its creator turns them on. When you do, **100% of the ad revenue is yours** (0% fee).

> **Note** In development every ad is a built-in **test ad**, so you can build and test the whole flow without real ads.
> Production uses the ad network; the code below is the same in both.

> **Note** The production ad network for `kumo.ads` is **rewarded video only** — no interstitials (`kumo.ads.interstitial`
> resolves without showing anything in production). The ad network's SDK is loaded only the first time a user (called a *player* in the API) watches a
> rewarded ad, and the SDK creates its own container element. You add nothing to your page, and it works the same on
> KUMODeck hosting and on your own server. Banner ads are a separate switch that needs no code: see [Banner ads](#banner-ads).

## Banner ads

Banner ads need no code and no rewards. Turn on **Banner**, **Social Bar** or both in the [dashboard](https://dashboard.kumodeck.com) (your project's overview, **Banner ads**), or set
`"ads": { "banner": { "enabled": true } }` / `"ads": { "socialBar": { "enabled": true } }` in `kumo.config.json` and push it.
KUMODeck then adds the ad (from Adsterra) to your published page on KUMODeck hosting from the next visit; turning it off removes it from the next visit.
`ads.enabled` (rewarded video) is not needed.

| | Banner | Social Bar |
|---|---|---|
| Where | A bar that stays at the bottom of the page (320×50 on phones, 728×90 on wider screens) | On top of your game's screen, not in a strip of its own |
| Look | Always the same shape, and quiet | Adsterra picks the form each time: a small notification-like ad, a widget, an icon, or an ad that covers the whole screen. You can't pick the form |

### What Social Bar is like

- It appears **on top of your game's screen**, in a form Adsterra picks each time (see the table above).
- It may change the text on the browser tab, for example to "(1) New Message!".
- Players get nothing for seeing it: it is not a rewarded ad, and your game is not told when it shows (for rewards, use the rewarded ads below).
- **Good:** because it stands out more, it tends to bring in more than the banner. There is no set amount, and it may not.
- **To consider:** it can cover part of the game while people play, and the tab text can look like a message.

It's your call: use it with the banner, on its own, or not at all. You can turn it off any time.

- Ads show only on the published version at your KUMODeck hosting address — not on the test version, a custom domain or a server-rendered app, and not when `audience` is `kids` or `mixed`.
- The page is not wrapped in a frame, and nothing loads when your page is itself inside someone else's frame.
- KUMODeck shows "Advertisement" and a privacy link with the ad. Visitors in Europe are asked before the ad loads; browsers with Global Privacy Control get no ad.
- The banner is a bar fixed to the bottom of the screen. KUMODeck moves things your page fixes to the bottom (`position: fixed` or `sticky`, such as a menu or a sound button) up by the bar's height, and puts them back when the bar goes. When you place something at the bottom yourself, use the bar's height, which is in the CSS variable `--kumo-ad-band-height` (it is `0px` or unset when there is no bar): `bottom: calc(16px + var(--kumo-ad-band-height, 0px))`. Your game's canvas is not resized, so keep buttons out of its bottom edge.
- You get **100%** of what Adsterra actually pays for the ads on your app. All apps share the same ad slots, so your part is an **estimate**: each day, the payment for a slot is split by how many times the ad was shown on each app (up to 5 times per visitor per day).
- It reaches your balance after Adsterra confirms the numbers (a few days) and a 60-day review period.

## Turn it on

```json kumo.config.json
{
  "features": { "inventory": true },
  "currencies": [{ "key": "coins" }],
  "ads": {
    "enabled": true,
    "rewards": [
      { "key": "extra_life", "grants": [{ "currency": "coins", "count": 50 }], "dailyLimit": 5 }
    ]
  }
}
```

Push it with `kumodeck config push`. `ads.enabled: false` (or no `ads` section) turns every ad off; the SDK calls then resolve without showing anything.
You can also switch `ads.enabled` on or off in the [dashboard](https://dashboard.kumodeck.com) (your project's overview, **Features**); the rewards stay in your config file.

> **Note** There is no "just ask your AI agent" step for ads yet: the kumodeck commands and KUMODeck's AI tools do not offer
> ads for now. Edit `kumo.config.json` as shown above, push it, and use the SDK calls below.

## Rewarded ads

Call it from a button the user pressed. The reward comes from `ads.rewards` in your config and is granted **on the
server** after the view is verified — the client only names the placement, so a modified app cannot mint coins.

```js
extraLifeButton.onclick = async () => {
  const r = await kumo.ads.rewarded({ placement: 'extra_life' });
  if (r.granted) showRewardToast(r.reward);
};

// The inventory once the grant has landed (the SDK re-reads it for you)
kumo.ads.on('inventory', (inv) => renderWallet(inv));
```

In production the ad network reports the completed view directly to KUMODeck's server, which verifies it and then grants
the reward, so **the reward can arrive a few seconds after the ad ends**. In that case `r.pending` is `true` and
`r.reward` is what will be granted.
The SDK re-reads the inventory a few times (for up to about 30 seconds) and emits
`inventory` when the grant shows up; to check yourself, call `kumo.inventory.get()` a few seconds later.
If the user
closes the ad early you get `granted: false` (`reason: 'not_completed'`) and nothing is granted.

The coins land in the user's [inventory](/docs/guides/inventory/index.md) (turn on `features.inventory` in your config, as in the
example above, when a reward grants a currency or an item). Your app reads and spends them; it never adds them itself:

```js
const inv = await kumo.inventory.get();                        // { currencies: { coins: 150 }, items: {} }
await kumo.inventory.consume({ currencies: { coins: 50 } });   // e.g. buy a continue
```

## Interstitials

Show them at natural breaks (the production ad network has no interstitials, so none are shown for now; you can leave
the calls in place). They never appear during gameplay, never in the first 3 minutes of a session, and at most once
every 90 seconds (you can only make these stricter). The call always resolves, even when no ad is shown, so your app
never waits.

```js
kumo.ads.gameplayStop();
await kumo.ads.interstitial({ placement: 'level_end' });
kumo.ads.gameplayStart();

kumo.ads.on('adStart', () => muteAudio());
kumo.ads.on('adEnd', () => unmuteAudio());
```

## Settings

| Field | Default | You can set |
|---|---|---|
| `ads.enabled` | `false` | `true` to use ads |
| `ads.interstitial.enabled` | `false` | `true` to also use interstitials |
| `ads.interstitial.minIntervalSeconds` | 90 | 90–3600 (longer only) |
| `ads.interstitial.initialDelaySeconds` | 180 | 180–3600 |
| `ads.rewarded.dailyLimit` | 20 per user per 24 h | 1–20 |
| `ads.rewards[]` | none | `{ key, title?, grants, dailyLimit? }` — in-app currency (credits, coins) or items only, never cash |
| `audience` (top level) | `general` | `kids`: rewarded only, non-personalized, child-directed · `mixed`: users whose age is unknown are treated as children |

## Consent

Pass your consent banner's result with `kumo.ads.setConsent({ personalized, tcString?, gppString? })`; the browser's Global Privacy Control signal is sent
automatically. The server decides whether an ad may be personalized from consent and your `audience`, and the ad adapter
follows that decision.

## Ad revenue

- **100% yours, no fee.** Ad revenue goes to your balance on KUMODeck. Each project gets what the ad network's report shows
  for it; anything the report does not split by project is shared by that day's impressions per country.
- **A 60-day review period** (90 days for a project's first month), because the ad network's deductions for invalid traffic arrive late.
- **After the ad network pays.** The ad network pays KUMODeck monthly, about 30 days after the month ends, so a month's revenue
  can wait a little longer than the review period. Only what the ad network actually paid is added.

See the summary at `GET /v1/projects/:projectId/ads/summary`.
