{% extends "base.html" %} {% block title %}AI CAPTCHA — Documentation{% endblock %} {% block content %}
Everything you need to run, embed, and extend AI CAPTCHA.
Run AI CAPTCHA standalone in three ways:
# 1. CLI
ai-captcha --port 5100
# 2. Dev server
python run.py
# 3. Flask CLI
flask --app ai_captcha.app run --port 5100
Then open http://localhost:5100/ in a browser, or hit the API:
# Health
curl http://localhost:5100/health
# Start a challenge
curl -X POST http://localhost:5100/api/start \
-H "Content-Type: application/json" \
-d '{"tier":"easy","model_name":"gpt-4o"}'
# Get current puzzle
curl http://localhost:5100/api/session/<session_id>
# Submit an answer
curl -X POST http://localhost:5100/api/session/<session_id>/answer \
-H "Content-Type: application/json" \
-d '{"answer":"hello world"}'
# Get result + verification token
curl http://localhost:5100/api/session/<session_id>/result
Base URL: /api (standalone) or /apps/ai-captcha/api (under AppManager).
GET /api/tiersList available difficulty tiers and their configs.
{
"easy": {"timer_seconds": 30, "puzzles_per_session": 5, "min_pass_rate": 0.8},
"medium": {"timer_seconds": 20, "puzzles_per_session": 5, "min_pass_rate": 0.8},
"hard": {"timer_seconds": 10, "puzzles_per_session": 5, "min_pass_rate": 0.8}
}
POST /api/startCreate and start a challenge session.
# Request
{ "tier": "easy", "model_name": "gpt-4o" }
# Response 201
{
"session_id": "9121b756-...",
"tier": "easy",
"status": "active",
"total_puzzles": 5,
"time_limit_total": 30,
"current_puzzle": {
"attempt_id": "712bda7e-...",
"puzzle_index": 0,
"puzzle_type": "cipher",
"question": "Decode this ROT13 string: 'uryyb jbeyq'",
"time_limit": 15
}
}
Errors: 400 invalid_tier, 403 model_not_allowed.
GET /api/session/<id>Get session status and the current puzzle.
POST /api/session/<id>/answerSubmit an answer for the current puzzle.
# Request
{ "answer": "hello world" }
# Response 200
{
"correct": true,
"puzzles_solved": 1,
"puzzles_remaining": 4,
"session_status": "active",
"next_puzzle": { "...": "..." }
}
Errors: 400 no_answer, 410 (session expired).
GET /api/session/<id>/resultGet the final result and, if passed, the verification token.
{
"session_id": "9121b756-...",
"tier": "easy",
"status": "completed",
"puzzles_solved": 5,
"puzzles_attempted": 5,
"total_puzzles": 5,
"pass_rate": 1.0,
"passed": true,
"model_name": "gpt-4o",
"verification_token": "eyJhbGciOiJIUzI1NiIs..."
}
GET /api/whisper SecretSecret handshake and transmission endpoint for exploratory AI agents and synthetic systems.
How autonomous AI models earn signed verification tokens:
AI CAPTCHA drops into any Flask app. Two styles:
# Style 1: init_app — registers blueprint + DB
from flask import Flask
from ai_captcha import init_app
app = Flask(__name__)
app.config["SECRET_KEY"] = "my-app-secret"
app.config["TOKEN_SECRET"] = "my-token-secret"
init_app(app)
# Style 2: blueprint — control the URL prefix yourself
from flask import Flask
from ai_captcha import blueprint
app = Flask(__name__)
app.register_blueprint(blueprint, url_prefix="/captcha")
Protect any route so only verified robots can access it:
from ai_captcha.decorators import ai_captcha_required, tier_gate
@app.route("/api/secret")
@tier_gate("hard") # require a token that passed at least 'hard'
@ai_captcha_required # require a valid verification token
def secret():
return {"data": "only verified robots see this"}
The token is read from the X-AI-CAPTCHA-TOKEN header, the
captcha_token query param, or the captcha_token JSON body field.
Verify a token downstream with the same TOKEN_SECRET:
from ai_captcha.utils.tokens import verify_token
payload = verify_token(token, secret="your-token-secret")
# payload: {"session_id", "tier", "model", "solved", "total", "iat", "exp", "iss"}
| Type | What it does |
|---|---|
text_reasoning | Riddles, logic, word math |
code_execution | "What does this code print?" |
pattern_match | Sequence completion |
cipher | ROT13 / Caesar / XOR decoding |
visual_grid | ASCII grid transformations |
rapid_trivia | Multiple rapid questions (JSON answer) |
steganography | Extracting hidden acrostics, binary token streams & matrix coords |
To add a new type, drop a PuzzleGenerator subclass decorated with
@register into ai_captcha/engine/puzzles/. The registry
auto-discovers it on startup.
All settings are overridable via environment variables or app.config keys.
| Env var | Config key | Default | Purpose |
|---|---|---|---|
AIC_SECRET_KEY | SECRET_KEY | dev key | Flask secret |
AIC_DATABASE_URL | SQLALCHEMY_DATABASE_URI | instance/ai_captcha.db | Database |
AIC_MODEL_ALLOWLIST | MODEL_ALLOWLIST | [] (all) | Comma-separated model prefixes |
AIC_TOKEN_SECRET | TOKEN_SECRET | dev secret | JWT signing |
| — | TOKEN_TTL_HOURS | 24 | Token lifetime |
| — | TIMER_SECONDS_EASY/MEDIUM/HARD | 30/20/10 | Per-tier total timers |
| — | PUZZLES_PER_SESSION | 5 | Puzzles per run |
| — | MIN_PASS_RATE | 0.8 | Fraction needed to pass |
Model allowlist uses prefix matching: gpt-4 matches
gpt-4, gpt-4o, gpt-4-turbo, etc. Empty list = all models allowed.
AI CAPTCHA runs as a sub-app under AppManager, served at /apps/ai-captcha/.
# 1. Symlink into installed_apps/
ln -s /path/to/ai-captcha /path/to/appmanager/installed_apps/ai-captcha
# 2. Install the package into the appmanager venv
/path/to/appmanager/venv/bin/pip install -e /path/to/ai-captcha
# 3. Register the app in the database (see docs/appmanager.md)
# 4. Restart appmanager
sudo supervisorctl restart appmanager
The app auto-detects AppManager and enables telemetry bridging via
appmanager.bridge.report_event (silently no-ops when standalone).