Offerwall embed
The hosted wall is one iframe. We render the offers, apply the targeting, handle the click and the redirect, and style it however you configured it.
The embed
<iframe
src="https://trovewall.com/wall/pk_live_a1b2c3?user_id=USER_ID"
width="100%"
height="800"
frameborder="0"
allow="clipboard-write"
referrerpolicy="origin">
</iframe>
Parameters
| Parameter | Required | Meaning |
|---|---|---|
user_id |
Yes | Your identifier for the end user. Opaque to us — we never learn who they are. It comes straight back in the postback. |
country |
No | ISO 3166-1 alpha-2. Overrides our own geo detection, which is what you want if you resolve it more accurately than a request IP can. |
device |
No | desktop, android, ios, tablet. Otherwise taken from the user agent. |
category |
No | Open the wall filtered to one category slug. |
sub1 … sub5 |
No | Your own tracking values. Stored on the click, returned on the postback, reportable. |
user_id. Per-user caps, duplicate
detection and the reward history all key off it. If it changes between sessions
the same person can complete the same offer repeatedly — and the advertiser will
reverse those conversions, out of your balance.
Auto-sizing the iframe
The wall posts its height to the parent window whenever it changes, so you can grow the iframe instead of scrolling inside it.
window.addEventListener('message', (event) => {
// Only trust messages from us.
if (event.origin !== 'https://trovewall.com') return;
if (event.data?.source !== 'trovewall-wall') return;
if (event.data.type === 'height') {
document.querySelector('#tw-wall').style.height = event.data.height + 'px';
}
});
Clicks open in a new tab
When an offer is tapped the wall opens the tracking link in the top window rather
than inside the iframe. This is deliberate: many advertisers set
X-Frame-Options and would simply fail to load, and app-store links
do not work framed at all. Nothing is required from you — but do not set
sandbox on the iframe without allow-popups and
allow-top-navigation-by-user-activation, or the click will be
swallowed.
Customisation
Everything visual is configured per app in the dashboard under Apps → Customise, and previewed live as you change it. The settings are stored as JSON and compiled into CSS custom properties at render time.
| Group | Settings |
|---|---|
| Identity | title, logo_url, currency_name, currency_icon_url, exchange_rate, support_url |
| Palette | color_bg, color_surface, color_surface_alt, color_text, color_muted, color_border, color_accent, color_accent_text, color_reward |
| Shape | radius, icon_size, icon_shape, card_min, gap |
| Layout | layout (grid · list · compact), show_tabs, default_tab, show_search, show_history, show_balance, sort |
| Advanced | custom_css, custom_header_html (sanitised) |
Reward history
With show_history enabled the wall gains a tab listing that user's
recent attempts — every offer they started, whether or not it
paid — and what became of each: started, clearing, credited, not eligible, or
never completed. It answers "where are my coins?" without a support ticket,
which is the single most common message any rewards product receives.
Listing attempts rather than conversions is deliberate. A user who did an offer and was not credited has no conversion, so a list of conversions shows them an empty page — which reads as "we have no record of you", and is the one answer guaranteed to produce a complaint.
Support
Every wall carries a Contact support link, and any attempt that did not
pay carries a Missing reward? link beside it. Both open a ticket against
that user, tied to the click they are asking about. They can return to it from
the same wall — it is keyed to their user_id, exactly as their
reward history is.
We answer inside the wall. If we uphold a claim, the conversion is recorded and your postback fires like any other — so the user is paid by your own system, on your own rules, and you never have to reconcile a credit you did not issue. A declined claim closes with a reason the user can read.
Content security
The wall sends Content-Security-Policy: frame-ancestors listing your
app's configured domain, so only you can embed it. Add every host that will frame
it — including staging — under Apps → Settings, or the browser will
refuse to render it.