DOCUMENTATION

Integration Guide

Everything you need to embed CaptchaKit in your signup forms and verify tokens on your server.

Integrate with Claude Code

The fastest way to add CaptchaKit to your app

Copy the prompt below, fill in your site key and API key from your dashboard, and paste it into Claude Code. Claude will find your form, add the widget, and wire up server-side verification — usually in under a minute.

I'm adding CaptchaKit to my app. CaptchaKit replaces the standard CAPTCHA checkbox with a short arcade game — users play for ~10 seconds to prove they're human. It's bot-proof and has zero friction for real users.

How it works:
- A <script> tag loads the widget from https://captchakit.com/widget.js
- A <div data-gamecaptcha data-site-key="..." data-game="connect4"> renders the game inside my form
- When the player passes, a signed token is stored on the div's data-token attribute and a gamecaptcha:pass event fires on document
- I must verify the token server-side before processing the form: POST https://captchakit.com/api/verify with header Authorization: Bearer <api_key> and body { token, siteKey }
- Tokens expire in 10 minutes and can only be used once

My credentials (from my CaptchaKit dashboard at captchakit.com/dashboard):
- Site key: [PASTE YOUR SITE KEY — starts with gc_]
- API key: [PASTE YOUR API KEY — starts with cgv_]
- Game I want to use: connect4

Please:
1. Add the CaptchaKit widget.js script to my page
2. Add the widget div inside my existing form where the CAPTCHA should go
3. Update my form submit handler to read the token and block submission if it's missing
4. Add server-side token verification in my form handler — reject the request if verification fails

Tips for best results

  • • Paste your actual site key and API key — Claude will put them in the right places
  • • Tell Claude which specific form or file to modify if your project has more than one
  • • If Claude asks to install a package, there is no npm package — the widget is a plain script tag
  • • After Claude makes changes, run your dev server and play a game to confirm the token is being sent

Quick Start

The simplest possible integration — two lines of HTML, one verification call.

index.htmlhtml
<!-- Step 1: Add CaptchaKit script to your page (before </body>) -->
<script src="https://captchakit.com/widget.js"></script>

<!-- Step 2: Add the widget inside your form -->
<form id="signup-form">
  <input type="email" name="email" placeholder="Email" />

  <div
    data-gamecaptcha
    data-site-key="gc_connect4_your_key_here"
    data-game="connect4"
    data-theme="dark"
  ></div>
  <!-- data-theme: "dark" (default) or "light" — match your site's background -->

  <button type="submit">Sign up</button>
</form>

<!-- Step 3: Read the token on submit -->
<script>
  document.getElementById('signup-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const token = e.target.querySelector('[data-gamecaptcha]').dataset.token;
    if (!token) return alert('Please complete the game first.');

    const res = await fetch('/api/signup', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email: e.target.email.value, gamecaptchaToken: token }),
    });
  });
</script>

IMPORTANT

Always verify the token on your server before processing form submissions. Client-side checks alone are insufficient — a bot could submit directly to your API.

embed.js Script Tag

The embed.js approach works in any HTML page — no bundler, no framework required. Drop a single <script> tag, add the widget div, and listen for the gc:pass event.

index.htmlhtml
<!-- 1. Load the script (once, anywhere on the page) -->
<script src="https://captchakit.com/embed.js" async></script>

<!-- 2. Drop the widget div wherever you want it in your form -->
<form id="my-form">
  <input type="email" name="email" placeholder="Email address" />

  <div data-site-key="gc_xxxxx" data-game="connect4"></div>

  <button type="submit">Sign up</button>
</form>

<!-- 3. Listen for the gc:pass event to read the token -->
<script>
  document.addEventListener('gc:pass', function (e) {
    var token = e.detail.token;

    // Store it so your submit handler can read it
    document.getElementById('my-form').dataset.gcToken = token;
  });

  document.getElementById('my-form').addEventListener('submit', async function (e) {
    e.preventDefault();
    var token = this.dataset.gcToken;

    if (!token) {
      alert('Please complete the game first.');
      return;
    }

    // Send the token to your server for verification
    await fetch('/api/signup', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        email: this.email.value,
        gcToken: token,
      }),
    });
  });
</script>

DATA ATTRIBUTES

data-site-keyrequired

Your site key from the dashboard (e.g. gc_xxxxx).

data-game

Game ID to show. Defaults to connect4. Options: connect4, whackabot, colormatch, fruitsorter, mazerunner, pacmanlite, stackit.

gc:pass EVENT DETAIL

{ token: string, siteKey: string, game: string }

The event fires on documentand bubbles up from a sentinel element at the widget's original position. Send the token to your server and verify it with POST /api/verify.

How Verification Works

The token flow in 4 steps.

1

Widget loads

The script tag renders the game inside the div. The player's site key is read from the data-site-key attribute.

2

Player passes the game

When the human wins, the widget calls our server with the site key. The server confirms the key is valid and issues a signed JWT.

3

Token written to the element

The signed token is stored in data-token on the widget div. Your form submit handler reads it.

4

You verify server-side

Your server calls POST /api/verify with the token and site key. We return { success: true }. Only then do you process the form.

Token format (JWT, expires in 10 minutes)

{ siteKey: "gc_connect4_xxx", gameId: "connect4", iat: ..., exp: ... }

Server Verification

Call this endpoint from your server — never from the browser.

POST/api/verify

Request headers

Authorization: Bearer YOUR_API_KEYContent-Type: application/json

Request body

{ "token": "...", "siteKey": "gc_..." }

✅ Success response

{ "success": true, "gameId": "connect4" }

❌ Failure response

{ "success": false, "error": "Invalid or expired token" }
// Express example
app.post('/api/signup', async (req, res) => {
  const { email, gamecaptchaToken } = req.body;

  // Verify the token server-side
  const verifyRes = await fetch('https://captchakit.com/api/verify', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer YOUR_API_KEY',  // from your dashboard
    },
    body: JSON.stringify({
      token: gamecaptchaToken,
      siteKey: 'gc_connect4_your_key_here',
    }),
  });

  const { success, gameId } = await verifyRes.json();

  if (!success) {
    return res.status(400).json({ error: 'Bot detected. Please try again.' });
  }

  // ✅ Token is valid — proceed with signup
  await createUser(email);
  res.json({ ok: true });
});

Framework Guides

Complete examples for the most common stacks.

'use client'; // Next.js App Router

import { useRef, useState } from 'react';

export default function SignupForm() {
  const widgetRef = useRef<HTMLDivElement>(null);
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const token = widgetRef.current?.dataset.token;

    if (!token) {
      alert('Please complete the game first!');
      return;
    }

    setLoading(true);
    const res = await fetch('/api/signup', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        email: (e.target as HTMLFormElement).email.value,
        gamecaptchaToken: token,
      }),
    });

    setLoading(false);
    if (res.ok) window.location.href = '/welcome';
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" name="email" required />

      {/* The widget script must be loaded in a script tag,
          e.g. in <head> or via next/script with strategy="beforeInteractive" */}
      <div
        ref={widgetRef}
        data-gamecaptcha
        data-site-key="gc_connect4_your_key_here"
        data-game="connect4"
      />

      <button type="submit" disabled={loading}>
        {loading ? 'Signing up...' : 'Sign up'}
      </button>
    </form>
  );
}

Game Comparison

Choose the game that fits your form and audience.

GAMEAVG. TIMEDIFFICULTYBEST FORMOBILE
🔴Connect 415sEasyGeneral signups
🍒Pac-Man Lite8sEasyGaming sites
🔨Whack-a-Bot12sMediumLogin forms
🎨Color Match10sMediumSignup flows
🌀Maze Runner20sHardHigh-security forms
🍎Fruit Sorter15sMediumE-commerce checkouts
🧱Stack It10sEasyComment forms

API Reference

All endpoints at a glance.

POST/api/verify🔑 Auth required

Verify a game completion token. Requires Authorization: Bearer header.

POST/api/game-complete

Called by the embed widget internally. Returns a signed JWT on game pass.

GET/api/auth/me🔑 Auth required

Get the current authenticated user, their API key, and owned games.