---
type: tutorial
title: Adicione o widget ao seu site
summary: Adicione o widget ao seu formulário de contato em React, confirme uma verificação real no seu painel e depois imponha-a no seu backend Node.
---

Você tem a chave pública da BananaSeed da [lição anterior](/docs/getting-started/create-your-account). Agora colocamos o widget no formulário de contato em React, vemos o painel confirmar que um visitante real o resolveu e depois adicionamos a única chamada de servidor que transforma isso em proteção de verdade.

## O que você terá no final

- O widget no seu formulário de contato, verificando visitantes no navegador.
- Seu painel mostrando essas verificações.
- Seu backend Node descartando qualquer envio que não tenha um token válido.

## 1. Adicione o widget ao seu formulário React

Instale o pacote. Ele registra o elemento `<caputchin-widget>` (e o elemento `<caputchin-game>` para a próxima lição):

```bash
npm install @caputchin/widget
```

Importe-o uma vez na inicialização, como no ponto de entrada do seu app (`main.jsx`):

```js
import "@caputchin/widget";
```

Agora solte o widget no formulário de contato da BananaSeed. Quando a checkbox é resolvida, o widget adiciona um campo oculto `caputchin-token` ao formulário, então você o lê no envio exatamente como os seus próprios campos. Substitua `cpt_pub_...` pela sua chave pública:

```jsx
export function ContactForm() {
  async function handleSubmit(e) {
    e.preventDefault();
    const data = new FormData(e.currentTarget);
    await fetch("/api/contact", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({
        email: data.get("email"),
        message: data.get("message"),
        token: data.get("caputchin-token"),
      }),
    });
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" required />
      <textarea name="message" required />
      <caputchin-widget sitekey="cpt_pub_..." />
      <button type="submit">Send</button>
    </form>
  );
}
```

Numa página HTML pura sem etapa de build, ou dentro de um app móvel nativo? Veja [a integração via tag de script (CDN)](/docs/integration-guides/cdn) e [o guia de apps móveis](/docs/integration-guides/mobile-apps).

## 2. Veja-o verificar

Rode seu app e abra o formulário de contato. A checkbox do Caputchin aparece e se completa sozinha, sem clique nenhum. Você nem precisa enviar o formulário ainda: isso já é uma verificação real, e ela chegou ao Caputchin.

## 3. Confirme no seu painel

De volta ao painel, abra sua equipe e clique na sua chave de site. As estatísticas dela mostram um funil: **Sessões iniciadas**, **Concluídas no cliente**, **Verificadas no servidor** e **Ameaças bloqueadas**. Atualize a página; **Sessões iniciadas** e **Concluídas no cliente** agora marcam ambas pelo menos `1`. Essa é a sua visita: o widget carregou (iniciou) e o desafio foi resolvido no navegador (concluído no cliente), sem nenhum backend envolvido.

**Verificadas no servidor** ainda está em `0`, porque nada no seu servidor conferiu um token ainda. Essa lacuna é o próximo passo.

<Callout type="success">Seu widget está no ar, e o painel já está registrando verificações reais, antes de você ter escrito uma linha de código de backend. 🎉</Callout>

## 4. Verifique o token no seu backend

Concluído no cliente significa que o visitante resolveu o desafio, mas seu formulário não está protegido até que seu servidor confirme o token. Um bot pode ignorar o widget por completo e fazer POST direto no seu endpoint. Então seu backend confere cada token com o Caputchin antes de confiar na requisição. Seu segredo (`cpt_sec_...`) vive em uma variável de ambiente, nunca no navegador:

```js
import express from "express";

const app = express();
app.use(express.json());

app.post("/api/contact", async (req, res) => {
  const { email, message, token } = req.body;

  const verdict = await fetch("https://verify.caputchin.com/v1/siteverify", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      secret: process.env.CAPUTCHIN_SECRET,
      response: token,
    }),
  }).then((r) => r.json());

  if (!verdict.success) {
    return res.status(400).json({ error: "Could not verify you are human." });
  }

  // Verified. Handle the real message: store it, email it, whatever you do.
  res.json({ ok: true });
});

app.listen(3001);
```

Essa verificação de `verdict.success` é o ponto: um bot que posta direto em `/api/contact` não carrega token, `success` volta como `false`, e a mensagem é descartada antes de você sequer tocá-la.

Sem backend? Sem problema. Ligue a [verificação hospedada](/docs/hosted-verification/overview) e o Caputchin roda essa conferência por você, encaminhando só os envios verificados para um webhook ou sua caixa de entrada.

## 5. Confirme a conferência no servidor

Envie o formulário de contato mais uma vez, agora que seu endpoint está verificando. Volte às estatísticas da sua chave de site e atualize: **Verificadas no servidor** agora marca pelo menos `1` também, ao lado de Iniciadas e Concluídas no cliente. O funil inteiro está coberto, do navegador ao servidor.

Para ler esse funil ao contrário (o que as lacunas entre os contadores dizem sobre desistência de visitantes e erros de integração), veja [estatísticas e sessões](/docs/site-keys/statistics).

<Callout type="success">Seu formulário de contato está genuinamente protegido agora: cada envio é conferido no seu servidor, e bots sem token são recusados. 🎉</Callout>

## O que acabou de acontecer

| Passo | Onde | Resultado |
|---|---|---|
| `<caputchin-widget>` no seu formulário | React | A checkbox verifica ao montar e adiciona um campo de uso único `caputchin-token`. |
| Abrir a página | Navegador | Iniciadas e Concluídas no cliente sobem, sem precisar de backend. |
| `/siteverify` com seu segredo | Node ao Caputchin | `success: true` deixa a requisição passar; sem token ela é descartada, e Verificadas no servidor sobe. |

## Perguntas comuns

### Eu preciso de um backend para usar o widget?

Para a etapa no navegador, não. Para proteção de verdade, sim. O widget verifica no navegador por conta própria, mas um bot pode ignorá-lo e dar POST direto no seu endpoint, então seu servidor precisa confirmar o `caputchin-token` antes de confiar em um envio. Se você não roda um backend, ligue a [verificação hospedada](/docs/hosted-verification/overview) e o Caputchin faz essa conferência por você.

### Por que Verificadas no servidor continua zero depois que o widget carrega?

Porque nada no seu servidor ainda conferiu um token. Iniciadas e Concluídas no cliente sobem só pelo navegador; Verificadas no servidor só sobe quando seu backend chama `/siteverify`, que é o [passo 4](#4-verifique-o-token-no-seu-backend).

### O widget funciona fora do React?

Sim. É um elemento personalizado padrão, então ele entra em HTML puro, Vue, Svelte, Angular ou a WebView de um app nativo com a mesma única tag. Veja [a integração no cliente](/docs/integration-guides/cdn) e [o guia de apps móveis](/docs/integration-guides/mobile-apps).

### Para onde vai minha chave secreta?

Só no seu servidor, em uma variável de ambiente (`cpt_sec_...`). Ela autentica a chamada a `/siteverify` e nunca pode ser enviada ao navegador.

## A seguir

A checkbox funciona. Agora a parte divertida: troque-a por um jogo jogável de verdade, sem mudar nada no seu backend.

Continue para [Adicionar um jogo do marketplace](/docs/getting-started/add-a-game).
