---
type: how-to
title: Integração com apps móveis
summary: Embuta o widget em um WebView de iOS, Android, React Native ou iframe de navegador, e leve o token verificado de volta ao seu código.
---

O Caputchin roda dentro de um app nativo por meio de uma página de embed hospedada. Você aponta um WebView para essa página, o visitante resolve o desafio dentro dela, e a página leva o token resultante (e qualquer erro) de volta ao seu código nativo. A verificação em si é idêntica à da web; só o shell anfitrião difere.

## 1. Abra a página de embed

Aponte um WebView para `https://caputchin.com/embed` com ao menos uma chave de site:

```
https://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory
```

Omita os parâmetros de jogo para a checkbox simples. Uma lista `games=a,b` escolhe um ao acaso por sessão.

<Callout type="warn">
Se a chave de site tem o portão de jogo ligado (sua configuração **Exigir um jogo para verificar**), a checkbox simples não passa: o embed precisa rodar um jogo em uma chave com portão. Passe um parâmetro `game` (ou `games`); o embed de checkbox pura, sem parâmetros de jogo, lança um erro `gate-unavailable` ali. Isso é proposital, porque o portão é imposto do lado do Caputchin. Veja [o portão de jogo](/docs/site-keys/games).
</Callout>

### Parâmetros de query

| Parâmetro | Obrigatório | Efeito |
|---|---|---|
| `sitekey` | sim | Sua chave pública (`cpt_pub_...`). Ausente, renderiza um shell de erro sem widget. |
| `game` | não | Um único id de jogo do marketplace (`owner/repo`, ou `owner/repo/leaf`). |
| `games` | não | Uma lista separada por vírgulas; um é escolhido ao acaso por sessão. |
| `game-src` | não | URL do seu próprio bundle de jogo hospedado, para um jogo auto-hospedado. |
| `layout` | não | `inline`, `modal`, `fullscreen` ou `auto`. O padrão é `inline` no embed. |
| `overlay-width` / `overlay-height` | não | Em `modal` / `fullscreen`, dimensiona o diálogo do jogo: `full`, um número de pixels, ou `auto` (o tamanho preferido do próprio jogo). Veja [Tamanho e disposição](/docs/references/widget-sizing). |
| `locale` / `skin` / `config` | não | Encaminhados ao widget para escolher um idioma, skin ou configuração. |
| `no-verify` | não | Roda o jogo sem portão de verificação (só o jogo, sem token). |
| `postMessageTarget` | não | Só para quem embuta em iframe de navegador (veja abaixo). Ignorado em um WebView nativo. |

Omita os três `game` / `games` / `game-src` para a checkbox simples. Não há parâmetro `mode`; o widget conduz o gatilho sozinho.

## 2. Leve o token até seu código

Quando o visitante passa, a página de embed empurra o token pelo canal que combina com seu anfitrião. Registre o handler correspondente.

### iOS (WKWebView, Swift)

O token chega como uma string crua em um message handler chamado `caputchin`:

```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://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory")!))
```

### Android (WebView, Kotlin)

O token chega como uma string crua em um `@JavascriptInterface` chamado `caputchin`:

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

webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(TokenBridge(), "caputchin")
webView.loadUrl("https://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory")
```

### React Native (react-native-webview)

O embed posta uma string JSON no canal único do React Native. Faça o parse e ramifique por `type`:

```jsx
<WebView
  source={{ uri: "https://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory" }}
  onMessage={(e) => {
    const msg = JSON.parse(e.nativeEvent.data);
    if (msg.type === "caputchin-token") {
      // send msg.token to your backend
    } else if (msg.type === "caputchin-error") {
      // msg.code, msg.message, msg.originalCode (optional)
    }
  }}
/>
```

### Iframe de navegador (postMessage)

Se você embuta a página em um `<iframe>` de navegador em vez de um WebView nativo, o repasse ao pai vem **desligado por padrão** e dispara só quando você opta por ele com uma origem-alvo exata via `?postMessageTarget=<origin>` (por exemplo `postMessageTarget=https://app.example.com`). Um curinga (`*`), um caminho, ou qualquer coisa que não se reduza a um simples esquema, host e porta é rejeitado, de modo que o token de uso único, vinculado ao site, nunca é difundido para um pai não verificado. Escute no pai e cheque `event.origin` antes de confiar na mensagem:

```js
window.addEventListener("message", (event) => {
  if (event.origin !== "https://embed-host.example.com") return;
  const msg = event.data;
  if (msg.type === "caputchin-token") {
    // msg.token
  } else if (msg.type === "caputchin-error") {
    // msg.code, msg.message
  }
});
```

## 3. Verifique no seu backend

Isto é o mesmo que na web: faça POST do token em `/siteverify` com seu segredo. Veja [Integração no lado do servidor](/docs/integration-guides/verify-on-your-backend).

## Receber erros (opcional)

Sempre que o widget bate em um erro, a página de embed o espelha por um conjunto paralelo de canais: o message handler `caputchinError` do iOS, a interface `caputchinError` do Android (`onError(json)`), o canal do React Native como `{ type: "caputchin-error", ... }` e o mesmo repasse de iframe protegido. O payload carrega `{ code, message, originalCode? }`. iOS e Android o recebem como string JSON; React Native e o repasse de iframe recebem o envelope tipado.

Isto é totalmente opcional. Um app que não registra nenhum handler de erro simplesmente não recebe erros, e a página de embed já mostra uma UI de retry dentro do WebView de qualquer forma.

## Veja também

- [Integração no lado do servidor](/docs/integration-guides/verify-on-your-backend) para a conferência do token.
- [Integração no lado do cliente](/docs/integration-guides/cdn) para o equivalente web.
