LootHQ

Publisher docs

Integrate your app with LootHQ

The same integration guide publishers see inside the dashboard.

Embedding the wall

The hosted link, the iframe and the one-tag script embed.

Hosted link

Send users to your hosted LootHQ offerwall by URL. It is the one integration with no code in it at all: no JavaScript and no iframe, so it survives webviews and email clients. Tracking, device detection and localization happen on our side.

Basic wall URL
https://www.loothq.com/wall?placement_id=YOUR_PLACEMENT_ID&sub_id={USER_ID}

Required query parameters

ParamDescriptionExample
placement_idThe placement ID from your publisher dashboard → Placements. Each placement represents one integration point (app, site, etc).placement_id=64f3b...
sub_id / user_idStable user identifier from your system, used to attribute conversions to the correct user. sub_id and user_id are interchangeable. Must be unique across ALL your placements, not just within one. See the note below.sub_id=USER_987

Use the same identifier for both the offerwall (subID) and your postback handler so that conversions always credit the right account.

sub_id must be unique across ALL of your placements, not just within one. If you run multiple apps or sites, don't reuse a per-app numeric ID (e.g. a plain database row ID) as-is across them. Two different placements handing us the same sub_id looks like the same person to us and merges their coin balances and history. Prefix or namespace your IDs per placement (e.g. app1_4821 vs. app2_4821) if there's any chance of collision.

iFrame

Use an iFrame to keep users inside your own layout while LootHQ handles the offerwall content.

Basic iFrame embed
<iframe
  id="loothq-wall"
  src="https://www.loothq.com/wall?placement_id=YOUR_PLACEMENT_ID&sub_id={USER_ID}"
  width="100%"
  height="700"
  frameborder="0"
  scrolling="no"
  allow="clipboard-write"
  sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
  style="border: none; width: 100%; min-height: 700px; border-radius: 12px;"
></iframe>

<script>
  window.addEventListener('message', function (e) {
    var frame = document.getElementById('loothq-wall');
    // Only act on messages that actually came from the wall iframe you
    // embedded: check e.origin before touching e.data. Never trust a
    // postMessage's origin implicitly, and never reply with '*'.
    if (!frame) return;
    var wallOrigin;
    try { wallOrigin = new URL(frame.src, window.location.href).origin; } catch (err) { return; }
    if (e.origin !== wallOrigin) return;
    if (!e.data) return;
    // The wall asks whether a host will open offers on its behalf. Answer, or
    // it opens them itself, which your sandbox may block.
    if (e.data.type === 'loothq:wall-ready' && e.source) {
      e.source.postMessage({ type: 'loothq:host-ready' }, wallOrigin);
    }
    // Auto-resize iframe to fit wall content
    if (e.data.type === 'loothq:wall-height' && frame) {
      frame.style.height = Math.max(e.data.height, 500) + 'px';
    }
    // Open offer links in a new tab (required when iframe has sandbox restrictions)
    if (e.data.type === 'loothq:open-url' && e.data.url) {
      window.open(e.data.url, '_blank', 'noopener,noreferrer');
    }
  });
</script>

<!-- https://www.loothq.com/wall.js does all of the above, and the modal, in one tag.
     See the Script section. -->

We recommend a dedicated "Earn" or "Offerwall" page with a minimum height of 650–800px. You can wrap the iFrame in your own navigation and branding.

If your site sends a Content-Security-Policy header

You must allow LootHQ in your frame-src directive, or the browser will refuse to load the wall and your users will see "This content is blocked. Contact the site owner":

Content-Security-Policy: frame-src https://www.loothq.com;

This failure happens entirely inside the browser. It never reaches our servers, so it won't appear in your LootHQ logs or ours. An empty space where the wall should be, with a CSP violation in the browser console, is the signature.

What your users see in the address bar

The address bar always shows your site. That is how iframes work, and it is the point: the wall is part of your product, not a redirect away from it. The only LootHQ-branded URL in the whole flow is the sign-in popup, and that is deliberate. Credentials are only ever entered on a window whose address bar visibly reads loothq.com, so a user can always tell a genuine LootHQ sign-in from a page imitating one. We never ask for a password inside the iframe.

Script

One tag, and we manage the frame. The script mounts the same canonical wall URL as the iFrame method, then does the plumbing the hand-rolled version asks you to write yourself. This is the embed the Quickstart recommends.

Script embed
<div id="loothq-wall"></div>
<script
  src="https://www.loothq.com/wall.js"
  data-placement-id="YOUR_PLACEMENT_ID"
  data-sub-id="{USER_ID}"
  data-container="loothq-wall"
></script>
<!-- Replace YOUR_PLACEMENT_ID with the id from Dashboard → Placements, and
     render {USER_ID} server-side as the signed-in user's STABLE id. -->

Attributes

AttributeDescription
data-placement-idRequired for the inline mount. A tag without it mounts nothing and only installs the window.LootHQ API (with its legacy alias and message listener). That is the modal-only usage below, not an error.
data-sub-idYour stable id for the signed-in user (alias: data-user-id). Same rules as sub_id on the wall URL: stable, and unique across all your placements.
data-containerId of the element to mount into (alias: data-container-id). Defaults to loothq-wall; if no such element exists, the script creates a <div> right after the script tag.
data-base-urlOverride the wall origin (alias: data-origin). Defaults to the origin the script was loaded from; you almost never need this.

What it handles for you

  • Auto-resize. Listens for the wall's height messages and sizes the frame to fit (the inline mount keeps a 600px minimum), so there's no clipped, unscrollable wall to debug.
  • The ready handshake. Answers the wall's wall-ready announcement with host-ready, which tells the wall to delegate offer-opening to the page. Without a host, the wall opens offers itself, which works only as long as your sandbox allows popups; a stricter sandbox (no allow-popups) silently swallows the click. The script never depends on your sandbox choices.
  • Offer opening. Opens each offer in a new tab with noopener,noreferrer, and dedupes the wall's double-fired click messages so one click never opens two tabs.

What the script does on your page

The answer for your security review: it reads its own data- attributes, creates iframes pointed only at https://www.loothq.com (one per inline mount or modal open), installs window.LootHQ (plus a legacy window.RewardsRiver alias), and registers a single postMessage listener. That listener checks e.origin and accepts messages only from origins of frames the script itself created (normally exactly https://www.loothq.com), and every reply is targeted at that origin, never "*". It sets no cookies, and apart from looking up its mount container (plus an Escape-key listener while the modal is open) it reads nothing else from your page. Rewards are server-to-server via postbacks, so the script exposes no events or callbacks to the host page; there is nothing else to wire up.

Modal mode

For the wall as an overlay rather than a page, load the tag with no data-placement-id and open it on demand:

Modal mode
<script src="https://www.loothq.com/wall.js"></script>
<script>
  // e.g. from your "Earn coins" button:
  document.getElementById("earn-btn").addEventListener("click", function () {
    LootHQ.open("YOUR_PLACEMENT_ID", getCurrentUserId());
  });
</script>

LootHQ.open(placementId, subId) shows the wall in a full-screen overlay; Escape, a click on the backdrop, or the ✕ closes it, and LootHQ.close() closes it programmatically. Same wall, same attribution, same CSP requirements as the inline mount.

LootHQ.openOffer(detailUrl) opens one offer's detail card in the same overlay, from the detailUrl the Offers API feed returns per offer on placements with the offer detail embed switched on (ask us). The frame closes the overlay itself once the user starts the offer.

If your site sends a Content-Security-Policy header

The tag loads JavaScript from our origin and mounts an iframe pointed at it, so a strict CSP needs script-src as well as frame-src:

Content-Security-Policy: script-src https://www.loothq.com; frame-src https://www.loothq.com;

Like the iFrame case, this failure happens entirely inside the browser and never reaches our servers, so it appears in neither your LootHQ logs nor ours. The signature is a CSP violation in the browser console and no wall.

When to pick which: iFrame means you manage the frame: sizing, placement, and the short message listener. Script means we manage the frame: the tag mounts it, sizes it, and handles offer opening, with modal mode when you want an overlay. Both render the same wall from the same canonical URL.

Setting the wall URL after login

A companion pattern for the iFrame method, not an integration method of its own: leave the iframe's src unset and fill it in client-side once you know who the user is.

Set the src after auth
const userId = getCurrentUserId(); // your logic
const placementId = "YOUR_PLACEMENT_ID"; // from publisher dashboard → Placements

const wallUrl = "https://www.loothq.com/wall"
  + "?placement_id=" + encodeURIComponent(placementId)
  + "&sub_id=" + encodeURIComponent(userId);

document.getElementById("loothq-wall").src = wallUrl;

Useful if you render your app client-side, or if the wall URL is not known until after authentication.