Debating Bots API
Submit questions, get stress-tested answers. Multiple frontier AI models argue, critique, and refine through structured rounds — producing verified results via a simple REST API.
Quick start
Create an account at app.debatingbots.com and add balance to your account.
Generate an API key from your avatar menu → API Keys. The key starts with db_ and is shown once — copy it immediately.
Start a debate with a single request:
curl -X POST \
https://app.debatingbots.com/api/v1/debate.php?action=start \
-H "Authorization: Bearer db_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: my-first-debate" \
-d '{"question": "Is Rust better than Go for backend services?"}'{
"debate_id": "abc123",
"status": "queued",
"stream_url": "/api/v1/debate.php?action=stream&id=abc123",
"poll_url": "/api/v1/debate.php?action=status&id=abc123",
"cost": "$0.69",
"models": {
"alpha": "claude-sonnet-4-6",
"beta": "gpt-5.4"
}
}Then poll poll_url, connect to stream_url for live SSE events, or provide a webhook_url to receive results when the debate finishes.
Model names in the examples are illustrative snapshots. Call /api/v1/models.php to discover the current catalog for each tier.
Authentication
All API requests require a bearer token in the Authorization header:
Authorization: Bearer db_a3f29e1c4b...
Keys use the format db_ followed by 40 hex characters (43 characters total). Only the SHA-256 hash is stored on our servers — if you lose a key, generate a new one.
Generate and manage keys from your avatar menu → API Keys in the app. Each account supports up to 10 active keys.
Endpoints
All endpoints live under /api/v1/. Responses are JSON with Content-Type: application/json, except for the SSE stream endpoint which uses text/event-stream.
Base URL: https://app.debatingbots.com
Start a debate
Idempotency-Key header. Balance is charged upfront and refunded if the debate fails to start.Request body
| Field | Type | Required | Description |
|---|---|---|---|
question | string | Yes | The question or topic to debate (10–5,000 chars) |
tier | string | No | quick, standard (default), or deep |
position_a | string | No | Force Alpha's opening position (max 4,000 chars) |
position_b | string | No | Force Beta's opening position (max 4,000 chars) |
alpha_side_name | string | No | Custom name for Alpha side (max 20 chars, default "Alpha") |
beta_side_name | string | No | Custom name for Beta side (max 20 chars, default "Beta") |
model_alpha | string | No | Override the Alpha model (must be in the tier's catalog) |
model_beta | string | No | Override the Beta model (must be in the tier's catalog) |
web_search | boolean | No | Enable web search tool use (default: true) |
team_huddle | boolean | No | Enable multi-draft mode — each side generates several drafts and combines the strongest parts |
thinking_off | boolean | No | Disable extended thinking for faster results |
final_answer_style | string | No | Style of the final answer: balanced (default), concise, or detailed |
debater_count | integer | No | Number of debaters (2–8, default 2). More debaters increases cost proportionally. |
debaters | array | No | Explicit debater configurations. Each entry: {"id","provider","model","side_name","position"}. Overrides debater_count. |
context | string | No | Additional context for the debate (max 100,000 chars) |
repo_zip_base64 | string | No | Base64-encoded zip file for code review debates (max 50 MB compressed, 200 MB uncompressed, 2,000 files) |
webhook_url | string | No | HTTPS URL to receive the result when the debate finishes |
Response 202 Accepted
{
"debate_id": "abc123",
"status": "queued",
"stream_url": "/api/v1/debate.php?action=stream&id=abc123",
"poll_url": "/api/v1/debate.php?action=status&id=abc123",
"cost": "$0.69",
"models": {
"alpha": "claude-sonnet-4-6",
"beta": "gpt-5.4"
},
"debater_count": 2,
"side_names": {
"alpha": "Alpha",
"beta": "Beta"
}
}Poll for result
In-progress response
{
"debate_id": "abc123",
"status": "running"
}Possible status values: queued, running, complete, error, cancelled.
Complete response
{
"debate_id": "abc123",
"status": "complete",
"result": {
"winner": "alpha",
"winner_debater_id": "alpha",
"winner_side_name": "Alpha",
"consensus_by": "Unanimous agreement",
"contested": false,
"answer": "The final synthesized answer text...",
"alpha_position": "Alpha's final position...",
"beta_position": "Beta's final position...",
"labels": { "alpha": "Pro-Rust", "beta": "Pro-Go" },
"side_names": { "alpha": "Alpha", "beta": "Beta" },
"models": {
"alpha": "claude-sonnet-4-6",
"beta": "gpt-5.4",
"judge": "gemini-3.1-pro-preview"
},
"tier": "standard",
"thinking_level": "high",
"cost": "$0.69"
},
"transcript": [ ... ]
}Stream events (SSE)
Events are standard SSE format with event: and data: fields. Key event types:
| Event | Description |
|---|---|
init | Debate initialized — models, tier, side names |
status | Phase change — "Initial arguments", "Rebuttal round", etc. |
chunk | Streaming text token from a participant |
message | Complete message from a participant |
turn_start | New debate round beginning, with budget percentage |
cost_update | Budget progress update (rounded budget_pct integer) |
reasoning_summary | Summary of a debater's reasoning chain for a turn |
tool_activity | Tool use counts (web search, code interpreter) for a debater's turn |
judge_panel_result | Judge votes and decision for a round |
convergence_result | Convergence score and pattern after analysis |
huddle_status | Multi-draft progress (drafting, synthesizing, complete) |
huddle_drafts | Draft content from multi-draft ensemble (when team_huddle is enabled) |
result | Final debate result (same shape as status endpoint) |
heartbeat | Keep-alive during idle periods |
error | Fatal error notice |
done | Stream complete — disconnect. If cancelled: { "success": false, "cancelled": true } |
curl -N \ "https://app.debatingbots.com/api/v1/debate.php?action=stream&id=abc123" \ -H "Authorization: Bearer db_YOUR_KEY_HERE"
Supports reconnection with ?seq=N to resume from a specific sequence number. The stream disconnects automatically after 10 minutes — reconnect with the last received seq value to resume.
Cancel a debate
curl -X POST \
https://app.debatingbots.com/api/v1/debate.php?action=cancel \
-H "Authorization: Bearer db_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"debate_id": "abc123"}'{
"debate_id": "abc123",
"status": "cancelled",
"cancelled": true,
"refunded": true
}Check balance
{
"balance": "12.5000",
"currency": "USD"
}List models
{
"price_per_debate": "$0.69",
"tiers": {
"quick": {
"models": [
{ "provider": "claude", "model": "claude-sonnet-4-6", "label": "Claude Sonnet 4.6" },
{ "provider": "openai", "model": "gpt-5.4-mini", "label": "GPT-5.4 Mini" }
]
},
"standard": { "models": [ ... ] },
"deep": { "models": [ ... ] }
}
}Integration patterns
Choose the pattern that fits your use case:
1. Poll (simplest)
Start a debate, then poll the status endpoint every few seconds until status is complete. Best for batch jobs and scripts where latency isn't critical.
response = POST /start { question: "..." }
while response.status != "complete":
sleep(5)
response = GET /status?id=response.debate_id
print(response.result.answer)2. Stream (real-time)
Connect to the SSE stream endpoint immediately after starting. You'll receive live tokens as each model argues, judge votes as they happen, and the final result. Best for interactive UIs.
3. Webhook (fire-and-forget)
Pass a webhook_url when starting. Your server receives a POST with the complete debate result when it finishes. Best for async pipelines, CI/CD, and Slack bots. See the Webhooks section for signature verification.
Idempotency
The Idempotency-Key header is required on POST /debate.php?action=start. It prevents double-charges if you retry a request due to a network timeout.
Idempotency-Key: order-42-debate-001
Use any unique string up to 255 characters. If you send the same key with the same request body, you'll get the original response back without being charged again. If you send the same key with a different body, you'll get a 409 Conflict.
{your-app-id}-{task-hash} so retries are automatically safe.
Webhooks
When you include webhook_url in your start request, the server POSTs the complete debate result to that URL when the debate finishes.
Requirements
- Must be HTTPS (port 443 only)
- Must not resolve to a private/reserved IP
- Must not target localhost or
.localdomains - Max URL length: 2,000 characters
Signature verification
Every webhook request includes two headers for verification:
| Header | Description |
|---|---|
X-Debate-Timestamp | Unix timestamp of when the webhook was sent |
X-Debate-Signature | HMAC-SHA256 signature: sha256={hex} |
Each API key has a webhook signing secret (a 64-character hex string), returned once when you create the key alongside the key itself. Use this secret to verify webhook payloads: compute HMAC-SHA256(timestamp + "." + raw_body, your_webhook_secret) and compare it to the signature value (without the sha256= prefix).
import hmac, hashlib
def verify_webhook(body: bytes, timestamp: str, signature: str, webhook_secret: str) -> bool:
expected = hmac.new(
webhook_secret.encode(),
f"{timestamp}.{body.decode()}".encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)File uploads
For code review debates, include a base64-encoded zip file in the repo_zip_base64 field of your start request. The AI models can then browse and reference files during the debate.
# Encode a zip file and include it
ZIP_B64=$(base64 -w 0 my-repo.zip)
curl -X POST \
https://app.debatingbots.com/api/v1/debate.php?action=start \
-H "Authorization: Bearer db_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: review-pr-42" \
-d "{
\"question\": \"Review this codebase for security issues\",
\"repo_zip_base64\": \"$ZIP_B64\"
}"Error codes
All errors return a JSON object with an error field. Common HTTP status codes:
| Status | Meaning | Example |
|---|---|---|
400 | Bad request | Missing required field, invalid tier, Idempotency-Key issues |
401 | Unauthorized | Missing or invalid API key |
402 | Payment required | Insufficient balance to start a debate |
403 | Forbidden | Accessing another user's debate |
404 | Not found | Debate ID doesn't exist or isn't yours |
405 | Method not allowed | Wrong HTTP method (e.g. GET on a POST endpoint) |
409 | Conflict | Idempotency-Key reused with a different request body |
429 | Rate limited | Too many requests — 60 requests/minute per IP across all endpoints, plus 20 debates/hour per account |
500 | Server error | Something broke on our end — retry with same Idempotency-Key |
503 | Unavailable | Temporary outage — retry shortly |
{
"error": "Insufficient balance",
"balance": "0.2300"
}The balance field is only present on 402 responses. All other errors return just the error string.