iOS Quickstart (WKWebView)
You will: pass your signed-in user's id to your server, host one small web page (the bootstrap), show it in a WKWebView, listen for three events, and credit the user from a webhook. No SDK to install — the intelligence is in the web page; the app is a careful container.
How it fits together
- Your appKnow your user
After sign-in, your app has a stable user id. That id — never an email — is who earns.
- Your serverMint a token
Your server signs a 10-minute launch token for that user with your secret key.
- Bootstrap pageStart the widget
A page on your server loads the widget with that token.
- WKWebViewShow and listen
The webview displays the page; your app hears earn, height, and token-expired events.
- Your serverCredit from the webhook
AIRewards POSTs
reward.earnedto your server. That — not the app event — is when you credit.
Step 1 · Pass the end-user id
Pick the id you already use for the signed-in user in your own system (for example user_88123). Rules: stable per user, at most 128 characters, never an email or a name. Caps, reporting, and the webhook all key on it, so changing a user's id later splits their history.
Step 2 · Mint the token on your server
Add one endpoint to your server, for example GET /airewards/token?user_id=…, that returns the signed launch token from the launch-token recipe (Node, Python, and PHP samples there). Authenticate the request the same way you authenticate the rest of your app — the token proves your server vouched for this user. Never put the secret key in the app.
Step 3 · Host the bootstrap page
Serve this page from your server (for example at /airewards/earn?user_id=…). It fetches the token and starts the widget. This exact page also opens in a desktop browser, which is your best debugging tool.
<!DOCTYPE html>
<html><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Earn</title></head>
<body>
<div id="airewards"></div>
<script src="https://airewards.b-cdn.net/loader@1.js" async></script>
<script>
// This page is served by YOUR server. The user id arrives in the page
// URL (put it there only after your user is signed in — never trust a
// user id typed into a client-side form).
var userId = new URLSearchParams(location.search).get('user_id');
function initWidget() {
fetch('/airewards/token?user_id=' + encodeURIComponent(userId), { credentials: 'same-origin' })
.then(function (r) { return r.json(); })
.then(function (data) {
window.AIRewards.init({
container: '#airewards',
launchToken: data.airewardsToken, // signed on your server, see /docs#token
heightMode: 'auto',
onEarn: function () { /* UI hint only — credit comes from the webhook */ },
onTokenExpired: function () { location.reload(); }
});
});
}
if (window.AIRewards) initWidget();
else window.addEventListener('airewards:ready', initWidget);
</script>
</body></html>
New chat and chat history from native code: evaluate AIRewards.newChat(), AIRewards.getHistory(), or AIRewards.openConversation(id) in this page. Per-platform snippets are in New chat & history.
Put the user id in the bootstrap URL only from a signed-in server session. Anyone who can change that id can earn as that user in test mode; live mode is protected by the token, but keep the habit clean.
Step 4 · Show it in a WKWebView
import WebKit
final class EarnViewController: UIViewController, WKScriptMessageHandler {
private var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let config = WKWebViewConfiguration()
// The widget posts its events to a handler named "airewards".
config.userContentController.add(self, name: "airewards")
webView = WKWebView(frame: view.bounds, configuration: config)
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(webView)
// Your hosted bootstrap page, with your signed-in user's id.
let url = URL(string: "https://your-server.example/airewards/earn?user_id=user_88123")!
webView.load(URLRequest(url: url))
}
// Events from the widget arrive here as dictionaries.
func userContentController(_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage) {
guard let body = message.body as? [String: Any],
body["source"] as? String == "airewards",
let type = body["type"] as? String else { return }
switch type {
case "reward.earned":
refreshBalanceFromYourBackend() // UI hint only — crediting is the webhook's job
case "airewards:height":
if let height = body["height"] as? Int { resizeWebView(toHeight: CGFloat(height)) }
case "airewards:token-expired":
webView.reload() // the bootstrap fetches a fresh token
default:
break
}
}
private func resizeWebView(toHeight height: CGFloat) { /* update your layout constraint */ }
private func refreshBalanceFromYourBackend() { /* re-read the balance your server credited */ }
}
Three platform facts that matter: JavaScript is on by default in WKWebView; the handler name must be exactly "airewards" because that is who the widget posts to; and if your app uses WKAppBoundDomains, list both your bootstrap host and the Jutera host or navigation will be blocked.
Step 5 · Embed elegantly: fullscreen or inline
Fullscreen (simplest, recommended first): give the webview the whole screen and let the widget scroll internally. Nothing else to manage.
Inline in a scrolling page: the bootstrap uses heightMode: 'auto', and the widget reports its content height with airewards:height events. Resize the webview to that height (disable the webview's own scrolling) so your page scrolls as one. Do not put an auto-height widget inside a fixed-height clipped container — growing content will be cut off.
Step 6 · Set the webhook and credit the user
In Dashboard → Webhook, save your HTTPS endpoint. When a user earns, AIRewards POSTs reward.earned there, signed with your webhook secret. Verify the signature (copy-paste verifiers in the main guide), credit that user_id in your own currency, and dedupe on event_id — retries reuse it. The app event in Step 4 only refreshes your UI; if your webhook is down, nobody is credited, and we email the account owners when that happens.
Before you ship
- Token endpoint requires your app's normal authentication; the secret key exists only on the server.
- Bootstrap page opens and completes a test duel in a desktop browser.
- Handler registered with the exact name
"airewards"; earn event refreshes the balance display. - Webhook verifies signatures, dedupes on
event_id, and credits only from the webhook. - Test event from the dashboard Webhook page lands and credits a test user.
WKAppBoundDomains(if used) lists your bootstrap host and the Jutera host.
Questions: hello@jutera.com · Android guide →