Overview
AirCaptcha combines lightweight interaction challenges with behavioral signals to distinguish humans from bots. It’s designed to be user-friendly and intentionally visible when needed.
Key Features:
- User-friendly challenges that are quick (and fun) to solve
- Adaptive difficulty based on risk signals
- Encrypted communication (AES-256-GCM)
- Behavioral signals (mouse movement, timing patterns)
- Optional proof-of-work for suspicious traffic
- Automation detection (Selenium, Puppeteer, etc.)
Installation
To use AirCaptcha on your website, you’ll need:
- Create an API key pair from your dashboard
- Include the client-side script with your Site Key (public)
- Verify tokens on your server using your Secret Key (private)
Key Types:
ac_site_...- Site Key (public) - Safe to include in frontend codeac_secret_...- Secret Key (private) - Keep secure, only use server-side
Quick start
Here’s the same integration pattern used by the working demo on the demo page (recommended):
<script src="/api/captcha/script.js"></script>
<form id="my-form">
<input type="email" name="email" required>
<button id="submit-btn" type="submit" disabled>Submit</button>
</form>
<script>
const submitBtn = document.getElementById('submit-btn');
const form = document.getElementById('my-form');
const captcha = new AirCaptcha({
apiEndpoint: '/api/captcha',
encryptionEnabled: true,
debug: false
});
captcha.on('ready', () => {
submitBtn.disabled = false;
});
captcha.on('error', () => {
submitBtn.disabled = true;
});
captcha.init();
form.addEventListener('submit', async (e) => {
e.preventDefault();
submitBtn.disabled = true;
const result = await captcha.execute();
if (!result.success) {
submitBtn.disabled = false;
return;
}
submitBtn.disabled = false;
});
</script>
Client-side setup
The client-side script provides the AirCaptcha class. It will present a user-friendly challenge when needed and return a verification token.
const captcha = new AirCaptcha({
apiEndpoint: '/api/captcha', // Required: Your captcha API endpoint
encryptionEnabled: true, // Recommended: required by this server build
debug: false // Enable console logging
});
Available Methods:
init()- Initialize the captcha (call once on page load)execute()- Run verification and get a tokenreset()- Reset the captcha stateon(event, callback)- Listen for events
Events:
ready- Captcha initialized and readysuccess- Verification successfulerror- An error occurred
Server-side verification
After getting a token from the client, verify it on your server by calling /api/captcha/validate-token.
ac_secret_...) in client-side code. Only use it on your server.
Server-to-server (recommended): send your secret key in the Authorization header.
# Python example (server-to-server)
import requests
def verify_captcha(token, secret_key, client_ip=None):
headers = {"Authorization": f"Bearer {secret_key}"}
payload = {"token": token}
if client_ip:
payload["client_ip"] = client_ip # optional
response = requests.post(
"https://your-domain.com/api/captcha/validate-token",
json=payload,
headers=headers,
timeout=5
)
data = response.json()
return data.get("valid", False)
// Node.js example (server-to-server)
const axios = require('axios');
async function verifyCaptcha(token, secretKey, clientIp) {
const res = await axios.post(
'https://your-domain.com/api/captcha/validate-token',
{ token, client_ip: clientIp }, // client_ip optional
{ headers: { Authorization: `Bearer ${secretKey}` }, timeout: 5000 }
);
return res.data.valid === true;
}
API reference
POST /api/captcha/init
Initialize a new captcha challenge.
Request:
{
"fingerprint": { ... } // Browser fingerprint data
}
Response:
{
"success": true,
"challenge": {
"challenge_id": "abc123",
"type": "interactive",
"expires_at": 1234567890000
}
}
POST /api/captcha/verify
Verify a challenge solution.
Request:
{
"challenge_id": "abc123",
"solution": { ... },
"fingerprint": { ... },
"behavior": { ... }
}
Response:
{
"success": true,
"token": "eyJ...",
"score": 0.15,
"risk_level": "low"
}
POST /api/captcha/validate-token
Validate a token on your server.
Request:
{
"token": "eyJ...",
"client_ip": "203.0.113.10" // optional (useful for server-to-server)
}
Headers (server-to-server):
Authorization: Bearer ac_secret_...
Response:
{
"valid": true,
"payload": {
"challenge_id": "abc123",
"score": 0.15,
"verified_at": 1234567890000
}
}
Configuration
Server-side configuration options (environment variables):
# Security
CAPTCHA_SECRET=your-secret-key-here
CORS_ORIGINS=https://your-domain.com
# Difficulty
POW_DIFFICULTY=4
POW_MAX_DIFFICULTY=6
# Timing
CHALLENGE_EXPIRY_SECONDS=120
TOKEN_EXPIRY_SECONDS=300
# Rate Limiting
RATE_LIMIT_REQUESTS=30
RATE_LIMIT_WINDOW=60
Security best practices
- Always verify tokens server-side - Never trust client-side verification alone
- Use HTTPS - All communication should be encrypted
- Keep your secret key safe - Never expose it in client-side code
- Monitor your traffic - Watch for unusual patterns
- Set appropriate rate limits - Prevent brute force attacks
- Use the risk score - Implement graduated responses based on score
Risk Score Guidelines:
0.0 - 0.2- Very likely human, allow immediately0.2 - 0.4- Probably human, allow with logging0.4 - 0.6- Suspicious, consider additional verification0.6 - 0.8- Likely bot, show fallback challenge or block0.8 - 1.0- Almost certainly bot, block request
Troubleshooting
Captcha not initializing
Check that:
- The script URL is correct and accessible
- Your site key is valid
- There are no JavaScript errors in the console
- CORS is properly configured on the server
High false positive rate
If legitimate users are being blocked:
- Lower your score threshold
- Check that behavioral data is being collected
- Ensure users have time to interact before verification
- Adjust your score threshold and consider a friendlier challenge for borderline cases
Token validation failing
Verify that:
- Tokens are being sent within the expiry window
- Your secret key matches on client and server
- The token hasn't been tampered with
- Client IP hasn't changed between challenge and verification