DOCUMENTATION
Integration Guide
Everything you need to embed GameCaptcha in your signup forms and verify tokens on your server.
Quick Start
The simplest possible integration — two lines of HTML, one verification call.
<!-- Step 1: Add GameCaptcha script to your page (before </body>) -->
<script src="https://didyu.app/captchagames/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.
How Verification Works
The token flow in 4 steps.
Widget loads
The script tag renders the game inside the div. The player's site key is read from the data-site-key attribute.
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.
Token written to the element
The signed token is stored in data-token on the widget div. Your form submit handler reads it.
You verify server-side
Your server calls POST /captchagames/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.
/captchagames/api/verifyRequest headers
Authorization: Bearer YOUR_API_KEYContent-Type: application/jsonRequest 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://didyu.app/captchagames/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.
| GAME | AVG. TIME | DIFFICULTY | BEST FOR | MOBILE |
|---|---|---|---|---|
| 🔴Connect 4 | 15s | Easy | General signups | ✓ |
| 👾Pac-Man Lite | 8s | Easy | Gaming sites | ✓ |
| 🔨Whack-a-Bot | 12s | Medium | Login forms | ✓ |
| 🎨Color Match | 10s | Medium | Signup flows | ✓ |
| 🌀Maze Runner | 20s | Hard | High-security forms | ✓ |
| 🍎Fruit Sorter | 15s | Medium | E-commerce checkouts | ✓ |
| 🧱Stack It | 10s | Easy | Comment forms | ✓ |
API Reference
All endpoints at a glance.
/captchagames/api/verify🔑 Auth requiredVerify a game completion token. Requires Authorization: Bearer header.
/captchagames/api/game-completeCalled by the embed widget internally. Returns a signed JWT on game pass.
/captchagames/api/auth/me🔑 Auth requiredGet the current authenticated user, their API key, and owned games.