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.adsis rewarded video only — no interstitials (kumo.ads.interstitialresolves 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 need no code and no rewards. Turn on Banner, Social Bar or both in the dashboard (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
audienceiskidsormixed. - 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: fixedorsticky, 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 is0pxor 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#
{
"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 (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.jsonas 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.
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 (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:
// { currencies: { coins: 150 }, items: {} }
const inv = await kumo.inventory.get();
// e.g. buy a continue
await kumo.inventory.consume({ currencies: { coins: 50 } });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.
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.