caputchin
All docs
View raw .md

Embed Caputchin in a mobile app via WebView

Reference for the pattern is mobile. The widget is the same as the web version; only the host shell differs.

1. Open the embed page in a WebView

The platform hosts a mobile-optimized embed page that wraps the widget with mobile-friendly defaults (full viewport, touch-tuned).

URL shape:

https://embed.caputchin.com/?sitekey=cpt_pub_...&game=bubble-pop

Or with a pool:

https://embed.caputchin.com/?sitekey=cpt_pub_...&games=bubble-pop,memory-match

2. Bridge the wrapped token back to native code

The embed page emits the wrapped token via window.postMessage. Your WebView host listens for it.

iOS (WKWebView, Swift)

class TokenHandler: NSObject, WKScriptMessageHandler {
  func userContentController(_ controller: WKUserContentController,
                             didReceive message: WKScriptMessage) {
    guard let token = message.body as? String else { return }
    // send `token` to your backend
  }
}

let config = WKWebViewConfiguration()
config.userContentController.add(TokenHandler(), name: "caputchin")
let webView = WKWebView(frame: .zero, configuration: config)
webView.load(URLRequest(url: URL(string: "https://embed.caputchin.com/?sitekey=...&game=...")!))

Android (WebView, Kotlin)

class TokenBridge {
  @JavascriptInterface
  fun onToken(token: String) {
    // send `token` to your backend
  }
}

webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(TokenBridge(), "caputchin")
webView.loadUrl("https://embed.caputchin.com/?sitekey=...&game=...")

3. Verify on your backend

Same as web — POST the token to /siteverify with your secret. See the verify-server-side guide.

Receiving widget errors (optional)

The embed page also calls a second native handler — caputchinError — whenever the widget fires its error event. The handler receives a JSON-stringified payload with the same fields as the widget error event detail: { code, message, originalCode? }. The native side may parse it to log, retry, or branch.

Wiring is symmetric to the token handler and entirely optional — apps that don't register caputchinError simply don't receive errors; the embed page already shows a retry UI inside the WebView regardless.

iOS (WKWebView, Swift)

class ErrorHandler: NSObject, WKScriptMessageHandler {
  func userContentController(_ controller: WKUserContentController,
                             didReceive message: WKScriptMessage) {
    guard let json = message.body as? String,
          let data = json.data(using: .utf8),
          let payload = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return }
    // payload["code"], payload["message"], payload["originalCode"] (optional)
  }
}

config.userContentController.add(ErrorHandler(), name: "caputchinError")

Android (WebView, Kotlin)

class ErrorBridge {
  @JavascriptInterface
  fun onError(payloadJson: String) {
    val payload = JSONObject(payloadJson)
    // payload.getString("code"), payload.getString("message"),
    // payload.optString("originalCode", null)
  }
}

webView.addJavascriptInterface(ErrorBridge(), "caputchinError")

What you don't need to do

  • No native PoW. Cap stays in the WebView. See ADR-0005.
  • No mobile-specific game code. Game authors write one register() call that runs on both platforms.
  • No paid mobile tier. Mobile is free across all tiers — see mobile.

When native SDKs ship later

A future thin native wrapper (Swift / Kotlin) will encapsulate this WebView setup. It will be free, since it's a DX upgrade over the same WebView. Build slot earned when customer demand emerges — see roadmap.