Discord is where many developer teams already communicate. Instead of building a separate alerting UI, push CAPTCHA pipeline status directly to a Discord channel — rich embeds for balance, errors, and daily summaries.
Setup
- Open your Discord server settings
- Go to Integrations → Webhooks
- Click New Webhook and name it "CaptchaAI Alerts"
- Copy the webhook URL
- Store it as
DISCORD_WEBHOOK_URLenvironment variable
Python — Discord Alert System
import os
import time
import requests
from datetime import datetime
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
DISCORD_WEBHOOK = os.environ["DISCORD_WEBHOOK_URL"]
session = requests.Session()
class DiscordCaptchaAlerts:
COLORS = {
"success": 0x2ECC71, # Green
"warning": 0xF39C12, # Orange
"error": 0xE74C3C, # Red
"info": 0x3498DB, # Blue
}
def __init__(self, webhook_url):
self.webhook_url = webhook_url
def send_embed(self, title, description, color_key="info", fields=None):
embed = {
"title": title,
"description": description,
"color": self.COLORS.get(color_key, self.COLORS["info"]),
"timestamp": datetime.utcnow().isoformat() + "Z",
"footer": {"text": "CaptchaAI Pipeline Monitor"}
}
if fields:
embed["fields"] = fields
payload = {"embeds": [embed]}
resp = requests.post(
self.webhook_url, json=payload, timeout=10
)
resp.raise_for_status()
def balance_alert(self, balance, threshold):
severity = "error" if balance < 2 else "warning"
self.send_embed(
title="💰 Balance Alert",
description=f"CaptchaAI balance is **${balance:.2f}**",
color_key=severity,
fields=[
{"name": "Threshold", "value": f"${threshold:.2f}", "inline": True},
{"name": "Severity", "value": severity.upper(), "inline": True},
{"name": "Action", "value": "Top up your balance at captchaai.com", "inline": False}
]
)
def error_spike(self, error_rate, error_count, total_count, top_errors):
error_list = "\n".join(
f"• `{code}`: {count}" for code, count in top_errors.items()
)
self.send_embed(
title="⚠️ Error Rate Spike",
description=f"Error rate: **{error_rate:.1%}** ({error_count}/{total_count})",
color_key="error",
fields=[
{"name": "Error Breakdown", "value": error_list or "No details", "inline": False},
{"name": "Window", "value": "Last 5 minutes", "inline": True}
]
)
def queue_alert(self, depth, workers_active):
self.send_embed(
title="📊 Queue Backup",
description=f"Queue depth: **{depth}** pending tasks",
color_key="warning",
fields=[
{"name": "Active Workers", "value": str(workers_active), "inline": True},
{"name": "Est. Drain Time", "value": f"{depth // max(workers_active, 1)} min", "inline": True}
]
)
def daily_summary(self, stats):
self.send_embed(
title="📈 Daily CAPTCHA Summary",
description=f"**{stats['total']}** tasks processed",
color_key="success" if stats["success_rate"] > 0.92 else "warning",
fields=[
{"name": "Success Rate", "value": f"{stats['success_rate']:.1%}", "inline": True},
{"name": "Avg Latency", "value": f"{stats['avg_latency']:.1f}s", "inline": True},
{"name": "Total Cost", "value": f"${stats['cost']:.2f}", "inline": True},
{"name": "Errors", "value": str(stats["errors"]), "inline": True},
{"name": "Balance", "value": f"${stats['balance']:.2f}", "inline": True},
{"name": "Peak Queue", "value": str(stats["peak_queue"]), "inline": True},
]
)
def solve_recovered(self, previous_rate, current_rate):
self.send_embed(
title="✅ Pipeline Recovered",
description=f"Solve rate recovered: {previous_rate:.1%} → {current_rate:.1%}",
color_key="success"
)
alerts = DiscordCaptchaAlerts(DISCORD_WEBHOOK)
class PipelineMonitor:
def __init__(self, check_interval=60):
self.check_interval = check_interval
self.results = [] # (timestamp, success, error_code)
self.last_balance_alert = 0
self.last_error_alert = 0
self.cooldown = 300 # 5 minutes between alerts
def record(self, success, error_code=None):
self.results.append((time.time(), success, error_code))
# Keep last 5 min
cutoff = time.time() - 300
self.results = [r for r in self.results if r[0] > cutoff]
def run_checks(self):
now = time.time()
# Balance check
if now - self.last_balance_alert > self.cooldown:
balance = self._check_balance()
if balance is not None and balance < 10:
alerts.balance_alert(balance, threshold=10)
self.last_balance_alert = now
# Error rate check
if now - self.last_error_alert > self.cooldown and len(self.results) > 10:
total = len(self.results)
errors = [r for r in self.results if not r[1]]
error_rate = len(errors) / total
if error_rate > 0.15:
error_breakdown = {}
for _, _, code in errors:
if code:
error_breakdown[code] = error_breakdown.get(code, 0) + 1
alerts.error_spike(error_rate, len(errors), total, error_breakdown)
self.last_error_alert = now
def _check_balance(self):
try:
resp = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "getbalance", "json": 1
})
data = resp.json()
if data.get("status") == 1:
return float(data["request"])
except Exception:
pass
return None
monitor = PipelineMonitor()
JavaScript — Discord Webhook Client
const axios = require("axios");
const API_KEY = process.env.CAPTCHAAI_API_KEY;
const DISCORD_WEBHOOK = process.env.DISCORD_WEBHOOK_URL;
const COLORS = {
success: 0x2ecc71,
warning: 0xf39c12,
error: 0xe74c3c,
info: 0x3498db,
};
async function sendDiscordEmbed(title, description, colorKey = "info", fields = []) {
await axios.post(DISCORD_WEBHOOK, {
embeds: [
{
title,
description,
color: COLORS[colorKey] || COLORS.info,
timestamp: new Date().toISOString(),
footer: { text: "CaptchaAI Pipeline Monitor" },
fields,
},
],
}, { timeout: 10000 });
}
async function alertBalance(balance, threshold = 10) {
const severity = balance < 2 ? "error" : "warning";
await sendDiscordEmbed(
"💰 Balance Alert",
`CaptchaAI balance is **$${balance.toFixed(2)}**`,
severity,
[
{ name: "Threshold", value: `$${threshold.toFixed(2)}`, inline: true },
{ name: "Severity", value: severity.toUpperCase(), inline: true },
]
);
}
async function alertErrorSpike(errorRate, details = {}) {
await sendDiscordEmbed(
"⚠️ Error Rate Spike",
`Error rate: **${(errorRate * 100).toFixed(1)}%**`,
"error",
[
{ name: "Total Tasks", value: String(details.total || 0), inline: true },
{ name: "Errors", value: String(details.errors || 0), inline: true },
]
);
}
async function sendDailySummary(stats) {
const color = stats.successRate > 0.92 ? "success" : "warning";
await sendDiscordEmbed(
"📈 Daily CAPTCHA Summary",
`**${stats.total}** tasks processed`,
color,
[
{ name: "Success Rate", value: `${(stats.successRate * 100).toFixed(1)}%`, inline: true },
{ name: "Avg Latency", value: `${stats.avgLatency.toFixed(1)}s`, inline: true },
{ name: "Balance", value: `$${stats.balance.toFixed(2)}`, inline: true },
]
);
}
// Balance monitoring loop
async function monitorBalance() {
try {
const resp = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "getbalance", json: 1 },
});
if (resp.data.status === 1) {
const balance = parseFloat(resp.data.request);
if (balance < 10) await alertBalance(balance);
}
} catch (err) {
console.error("Balance check failed:", err.message);
}
}
setInterval(monitorBalance, 300000); // Every 5 minutes
module.exports = { alertBalance, alertErrorSpike, sendDailySummary };
Discord Message Examples
Balance warning:
💰 Balance Alert CaptchaAI balance is $8.42 Threshold: $10.00 | Severity: WARNING
Error spike:
⚠️ Error Rate Spike Error rate: 22.5% (45/200) •
ERROR_CAPTCHA_UNSOLVABLE: 30 •TIMEOUT: 15
Daily summary:
📈 Daily CAPTCHA Summary 12,450 tasks processed Success Rate: 95.2% | Avg Latency: 22.4s | Balance: $142.30
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| 400 Bad Request | Invalid embed structure | Check fields array format; ensure all values are strings |
| Rate limited (429) | Too many messages per minute | Add cooldown between alerts (5 min minimum) |
| Webhook deleted | Someone removed it from server | Create a new webhook; update env var |
| Embeds not showing | embeds array missing |
Wrap embed object in {"embeds": [...]} |
FAQ
How many Discord webhooks can I have per channel?
15 per channel. Create one webhook for CaptchaAI and reuse it for all alert types.
Can I @mention users or roles in alerts?
Yes. Add "content": "<@USER_ID>" or "content": "<@&ROLE_ID>" to the webhook payload for critical alerts.
Should I use Discord for production alerting?
Discord works well as a secondary notification channel. For on-call paging, use PagerDuty or Opsgenie. For team visibility, Discord is excellent.
Next Steps
Get pipeline alerts where your team already lives — start with a CaptchaAI API key and connect to Discord.
Related guides:
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.