SDK reference

Mobile (iOS & Android)

The JS SDK targets browser iframes. On native apps you load the play shell directly into a WebView and receive lifecycle events through a JavaScript bridge.

The web SDK (Playgent.init) is iframe-based and not used on native apps. Instead, you load the play shell URL into a WKWebView (iOS) or WebView (Android) and register a JavaScript bridge. The shell detects the bridge and routes every lifecycle event to native code.

Enable WebView on the player

Native WebView embedding requires allow_webview: true on the player. Toggle it in Hub → Configuration → Players → (your player)Allow embedding in iOS / Android WebViews. Without it the shell refuses to load (origin allowlists don't apply to WebView traffic — allow_webview is the gate).

Shell URL

text
https://static.playgent.com/play/v2/?player=plyr_acme&game=sudoku&mode=daily&bridge=ios

Everything is configured through query parameters — there is no init() call.

Required

playerrequired
string

Player ID (plyr_…). Bundles theme, features, CTA, and analytics stream.

gamerequired
string

Game ID, e.g. sudoku, trivia, wordsearch.

bridgerequired
"ios" | "android"

Tells the shell to deliver lifecycle events through the matching JS bridge instead of window.parent.postMessage. Omit on web embeds.

Content & mode

mode
"pinned" | "daily" | "random"

How content is fed. See Content modes.

content
string

Publication ID (pub_…) for pinned, or channel ID (cha_…) for daily / random. Required for content-driven games; omitted for algorithmic ones.

lang
string
Language code (e.g. en, he, es).
difficulty
string
Game-specific difficulty.
date
string
YYYYMMDD. Pins daily mode to a specific date.

Theme & identity

theme
string
Built-in theme slug or custom theme ID (thm_…). Overrides the player's theme.
themeId
string
Custom theme ID — equivalent to theme for custom themes.
nav
"light" | "dark"
Navbar contrast override.
externalUserId
string
Max 128 chars. Flows into every event payload.
externalUsername
string
Max 64 chars. Flows into every event payload.

Every option from the JS SDK init reference has a query-parameter equivalent — disableHintshints=false, disableShareshare=false, muted=true, hintCount=3, ctaText, ctaButtonText, ctaUrl, playButtonText, and so on. The player's Hub configuration fills in any that you don't pass.

Bridge protocol

The shell emits the same seven lifecycle events as the web SDK (reference). On native they are delivered through a platform-specific JS interface instead of postMessage:

PlatformJS callPayload form
iOSwindow.webkit.messageHandlers.playgent.postMessage(payload)Native dictionary ([String: Any])
Androidwindow.PlaygentAndroidBridge.postMessage(json)JSON string

Every payload includes a type field — pg_ready, pg_start, pg_hint, pg_complete, pg_end, pg_error, plus pg_view when the shell determines visibility. Field names match the SDK event payloads.

Register the bridge before loading the URL

If the shell finishes loading before the iOS message handler (or Android JS interface) is attached, the first events — notably pg_ready — won't reach native code.

iOS (WKWebView)

Register a script message handler named playgent on the WKWebViewConfiguration, then load the shell URL with bridge=ios.

swift
import WebKit

final class PlaygentViewController: UIViewController, WKScriptMessageHandler {
  private var webView: WKWebView!

  override func viewDidLoad() {
    super.viewDidLoad()

    let config = WKWebViewConfiguration()
    config.userContentController.add(self, name: "playgent")

    webView = WKWebView(frame: view.bounds, configuration: config)
    view.addSubview(webView)

    let url = URL(string:
      "https://static.playgent.com/play/v2/?player=plyr_acme&game=sudoku&mode=daily&bridge=ios"
    )!
    webView.load(URLRequest(url: url))
  }

  func userContentController(
    _ userContentController: WKUserContentController,
    didReceive message: WKScriptMessage
  ) {
    guard message.name == "playgent",
          let payload = message.body as? [String: Any],
          let type = payload["type"] as? String else { return }

    switch type {
    case "pg_ready":    print("ready")
    case "pg_start":    print("started")
    case "pg_hint":     print("hint used", payload["hintsUsed"] ?? "")
    case "pg_complete": print("done", payload["score"] ?? "")
    case "pg_end":      print("CTA pressed")
    case "pg_error":    print("error", payload["code"] ?? "")
    default: break
    }
  }
}

Android (WebView)

Add a JavaScript interface named PlaygentAndroidBridge before loading the URL with bridge=android.

kotlin
import android.webkit.JavascriptInterface
import android.webkit.WebView
import org.json.JSONObject

class PlaygentBridge {
  @JavascriptInterface
  fun postMessage(json: String) {
    val payload = JSONObject(json)
    when (payload.optString("type")) {
      "pg_ready"    -> println("ready")
      "pg_start"    -> println("started")
      "pg_hint"     -> println("hint used: ${payload.optInt("hintsUsed")}")
      "pg_complete" -> println("done: ${payload.optInt("score")}")
      "pg_end"      -> println("CTA pressed")
      "pg_error"    -> println("error: ${payload.optString("code")}")
    }
  }
}

class PlaygentActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val webView = WebView(this)
    webView.settings.javaScriptEnabled = true
    webView.addJavascriptInterface(PlaygentBridge(), "PlaygentAndroidBridge")
    setContentView(webView)

    webView.loadUrl(
      "https://static.playgent.com/play/v2/?player=plyr_acme&game=sudoku&mode=daily&bridge=android"
    )
  }
}
Only expose postMessage

addJavascriptInterface injects whatever you give it into the JS context. Don't expose database handles, file I/O, or anything that grants the WebView access to your app's internals — keep the bridge surface to the single postMessage method shown above.

Driving the player

The bridge is one-way (shell → native). Methods from the JS SDK — player.play(), player.restart(), player.hint(), player.load(opts), player.setIdentity(...), player.share(opts) — are not exposed over the bridge. To change content, swap difficulty, or jump dates from native code, reload the WebView with updated query parameters:

swift
let url = URL(string:
  "https://static.playgent.com/play/v2/?player=plyr_acme&game=sudoku&mode=daily&date=20260601&bridge=ios"
)!
webView.load(URLRequest(url: url))

Going further

The Native WebView guide walks through the full integration — process pools, network-cache gotchas, and how allow_webview interacts with the origin allowlist when the same player is also embedded on web.