Hosted iframe & WebView
Use the hosted SDK on any website or inside a native application.
On this page
Prerequisites
- Your platform’s Embed URL (https://sdk.adquest.batriders.ir/{publicId}).
- Your public API key.
- A backend route that creates participant sessions, and your origin on the platform’s Where AdQuest may appear list if the publisher keeps one.
Step 1: Load your platform’s embed URL
Ask the publisher for the Embed URL, https://sdk.adquest.batriders.ir/{publicId}. It appears in the dashboard once the platform is approved. Use the platform UUID from the URL, not its numeric ID. The hosted runtime loads your platform branding automatically. Mint participant tokens on your backend before initializing the embed.
Step 2: Connect the web message bridge
<iframe id="adquest" title="AdQuest quests and rewards"
style="width:100%;height:720px;border:0" allow="clipboard-write"></iframe>
<p id="adquest-status" role="status"></p>
<script type="module">
const frame = document.getElementById("adquest");
const status = document.getElementById("adquest-status");
const origin = "https://sdk.adquest.batriders.ir";
const publicId = "YOUR_PLATFORM_PUBLIC_ID";
const apiKey = "YOUR_PUBLIC_API_KEY";
Highlighted: const locale = "en-US"; // "en" or "fa": widget language (Persian is right-to-left)
const send = (type, payload) => frame.contentWindow?.postMessage({ version: 1, type, payload }, origin);
let refreshing = false;
async function receive(event) {
if (event.origin !== origin || event.source !== frame.contentWindow) return;
let message;
try { message = typeof event.data === "string" ? JSON.parse(event.data) : event.data; }
catch { return; }
if (message?.version !== 1) return;
if (["adquest:ready", "adquest:authRequired"].includes(message.type)) {
if (refreshing) return;
refreshing = true;
try {
const response = await fetch("/api/adquest/session", {
method: "POST",
cache: "no-store",
headers: { "Content-Type": "application/json" },
Highlighted: body: JSON.stringify({ locale }), // your backend creates the session with the same locale
});
if (!response.ok) throw new Error("Sign in and reopen rewards to continue.");
const session = await response.json();
if (message.type === "adquest:ready") {
Highlighted: send("adquest:init", { apiKey, ...session, hostContext: { locale } });
} else {
send("adquest:setParticipantToken", { participantToken: session.participantToken });
}
status.textContent = "";
} catch (error) { status.textContent = error.message; }
finally { refreshing = false; }
}
if (message.type === "adquest:error") status.textContent = message.payload?.message || "Could not load rewards.";
if (message.type === "adquest:close") frame.hidden = true;
}
window.addEventListener("message", receive);
frame.src = origin + "/" + encodeURIComponent(publicId);
// On screen teardown: window.removeEventListener("message", receive);
</script>Choose the language
Set hostContext.locale in adquest:init. It is part of hostContext, not a top-level init field. Use the same value when your backend creates the participant session.
// In adquest:init
Highlighted: send("adquest:init", { apiKey, ...session, hostContext: { locale: "en-US" } });
// Later, when the user changes language: new session first, then
send("adquest:setParticipantToken", { participantToken: newSession.participantToken });
Highlighted: send("adquest:setHostContext", { locale: "en-US" });Show the start splash
The start splash is a short animation that covers the embed while it loads, so a visitor never sees an empty frame or a spinner. It runs in your page, outside the iframe, which is why your page has to turn it on: with one script tag, or with one call if your app already bundles @adquest/sdk. Without either, nothing changes: the embed loads as before.
<script async src="https://sdk.adquest.batriders.ir/loader.js"
data-splash='{"preset":"black-friday","color":"#ff5a1f","title":"MEGA FRIDAY"}'></script>import { enableSplash } from "@adquest/sdk";
// once, when your app starts (it does nothing on the server)
enableSplash({ preset: "black-friday", color: "#ff5a1f", title: "MEGA FRIDAY" });| Setting | Value | Default |
|---|---|---|
| preset | "vault", "coins", "black-friday" or "none" | "vault" |
| color | Your brand colour as #rgb or #rrggbb | Gold |
| title | Headline of the black-friday splash, up to 24 characters | BLACK FRIDAY |
| tagline | Second line and moving tape of the black-friday splash, up to 60 characters | The title |
| dir | "rtl" or "ltr". Sets the text direction and the side the progress fills from. Nothing changes position | The wording’s own direction (black-friday), otherwise your page’s |
- Every setting is optional. A bare tag, or enableSplash() with no arguments, plays the default splash. preset "none" plays nothing.
- The settings live in your own code and nowhere else: nothing is stored on AdQuest’s side and nothing is requested for them, so even a first-time visitor sees exactly what you set. To change the splash, change the code.
- Add it once, in the head of the page or at app start, before the visitor reaches the AdQuest screen. Keep your iframe and bridge code as they are: the loader finds AdQuest iframes by their address.
- The loader is about 7 KB, has no dependencies and never calls the AdQuest API.
- A visitor sees the full animation once per browser session and a short fade after that. Visitors who ask their device for reduced motion get a still cover.
- The cover leaves when the embed sends adquest:contentReady, or after 10 seconds at the latest.
// e.g. when the visitor presses the button that opens AdQuest
openButton.addEventListener("pointerdown", () => window.AdQuestSplash?.warm());Step 3: Connect a native WebView
Load the same HTTPS embed URL and enable JavaScript for that trusted page.
Register the outgoing native bridge before the page loads. Decode version 1 envelopes and wait for adquest:ready.
Send adquest:init to window.AdQuestEmbed.receive(envelope) after you have that user’s participant session. Put the language in payload.hostContext.locale (en or fa). Build the envelope as data, then JSON-serialize it. Do not concatenate tokens into JavaScript.
Handle adquest:authRequired by refreshing through your backend. Send adquest:setParticipantToken with the new token.
Handle adquest:navigation, adquest:goBack and adquest:close so the native back control returns within the widget before dismissing it.
If your app provides the header, add navigation: "host" to the adquest:init payload (or send adquest:setNavigation). The widget hides its header and shows Back to platform on the home page. Dismiss the WebView when you receive adquest:exit.
Handle adquest:openUrl using your native URL policy. Allow intended HTTPS destinations and supported deep links; do not allow arbitrary schemes or untrusted navigations to access the bridge.
| Host | Receive messages from embed |
|---|---|
| Android / generic native bridge | AdQuestBridge.postMessage(JSON string) |
| iOS WKWebView | window.webkit.messageHandlers.adquest.postMessage(envelope) |
| React Native WebView | window.ReactNativeWebView.postMessage(JSON string) |
| Flutter InAppWebView | flutter_inappwebview.callHandler('adquest', envelope) |