Getting Started

Server-Side Validation

Validate CAPTCHA tokens on your server

After a user completes the CAPTCHA challenge, you must validate the token on your server before processing the form submission.

Validation Endpoint#

Send a POST request to validate the token:

POST https://challenge.byebot.de/validate_token
Content-Type: application/json

Request Body

{
  "api_key": "YOUR_API_KEY",
  "token": "TOKEN_FROM_FORM"
}
FieldTypeDescription
api_keystringYour secret API key from the dashboard
tokenstringThe byebot-token value from the form submission

Response

Status CodeDescription
200 OKToken is valid — returns JSON body with challenge metadata
400 Bad RequestToken is invalid, expired, or already used — no body

Success Response Body

On 200 OK, the response includes a JSON body with metadata about the solved challenge:

{
  "success": true,
  "sitekey": "abc123def456ghij7890",
  "challenged_at": 1742140800,
  "solve_time_ms": 2340,
  "interactive_solved": false
}
FieldTypeDescription
successbooleanAlways true on 200
sitekeystringThe site key the challenge was solved for
challenged_atintegerUnix timestamp (seconds) when the challenge was created
solve_time_msintegerMilliseconds from challenge creation to solution
interactive_solvedbooleanWhether an interactive challenge was completed

Checking the HTTP status code is sufficient for basic validation. The response body is optional and provides additional metadata for logging or analytics.

Code Examples#

Node.js / Express

const express = require('express');
const app = express();
 
app.use(express.urlencoded({ extended: true }));
 
app.post('/submit', async (req, res) => {
  const token = req.body['byebot-token'];
 
  if (!token) {
    return res.status(400).json({ error: 'CAPTCHA token missing' });
  }
 
  const response = await fetch('https://challenge.byebot.de/validate_token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      api_key: process.env.BYEBOT_API_KEY,
      token: token
    })
  });
 
  if (!response.ok) {
    return res.status(400).json({ error: 'CAPTCHA validation failed' });
  }
 
  // Token is valid - process the form
  // Optionally parse response for challenge metadata:
  // const { sitekey, challenged_at, solve_time_ms, interactive_solved } = await response.json();
 
  res.json({ success: true });
});

Python / Flask

import requests
from flask import Flask, request, jsonify
 
app = Flask(__name__)
 
@app.route('/submit', methods=['POST'])
def submit():
    token = request.form.get('byebot-token')
 
    if not token:
        return jsonify({'error': 'CAPTCHA token missing'}), 400
 
    response = requests.post(
        'https://challenge.byebot.de/validate_token',
        json={
            'api_key': BYEBOT_API_KEY,
            'token': token
        }
    )
 
    if response.status_code != 200:
        return jsonify({'error': 'CAPTCHA validation failed'}), 400
 
    # Token is valid - process the form
    # Optionally parse response for challenge metadata:
    # metadata = response.json()  # {'sitekey': ..., 'challenged_at': ..., 'solve_time_ms': ..., 'interactive_solved': ...}
 
    return jsonify({'success': True})

Python / Django

import requests
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.conf import settings
 
@require_POST
def submit(request):
    token = request.POST.get('byebot-token')
 
    if not token:
        return JsonResponse({'error': 'CAPTCHA token missing'}, status=400)
 
    response = requests.post(
        'https://challenge.byebot.de/validate_token',
        json={
            'api_key': settings.BYEBOT_API_KEY,
            'token': token
        }
    )
 
    if response.status_code != 200:
        return JsonResponse({'error': 'CAPTCHA validation failed'}, status=400)
 
    # Token is valid - process the form
    # Optionally parse response for challenge metadata:
    # metadata = response.json()
 
    return JsonResponse({'success': True})

PHP

<?php
$token = $_POST['byebot-token'] ?? null;
 
if (!$token) {
    http_response_code(400);
    echo json_encode(['error' => 'CAPTCHA token missing']);
    exit;
}
 
$response = file_get_contents('https://challenge.byebot.de/validate_token', false, stream_context_create([
    'http' => [
        'method' => 'POST',
        'header' => 'Content-Type: application/json',
        'content' => json_encode([
            'api_key' => getenv('BYEBOT_API_KEY'),
            'token' => $token
        ])
    ]
]));
 
$httpCode = $http_response_header[0];
if (strpos($httpCode, '200') === false) {
    http_response_code(400);
    echo json_encode(['error' => 'CAPTCHA validation failed']);
    exit;
}
 
// Token is valid - process the form
// Optionally parse response for challenge metadata:
// $metadata = json_decode($response, true);
 
echo json_encode(['success' => true]);

Go

package main
 
import (
    "bytes"
    "encoding/json"
    "net/http"
    "os"
)
 
func submitHandler(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    token := r.FormValue("byebot-token")
 
    if token == "" {
        http.Error(w, "CAPTCHA token missing", http.StatusBadRequest)
        return
    }
 
    payload, _ := json.Marshal(map[string]string{
        "api_key": os.Getenv("BYEBOT_API_KEY"),
        "token":   token,
    })
 
    resp, err := http.Post(
        "https://challenge.byebot.de/validate_token",
        "application/json",
        bytes.NewBuffer(payload),
    )
 
    if err != nil || resp.StatusCode != http.StatusOK {
        http.Error(w, "CAPTCHA validation failed", http.StatusBadRequest)
        return
    }
    defer resp.Body.Close()
 
    // Token is valid - process the form
    // Optionally decode response for challenge metadata:
    // var metadata map[string]interface{}
    // json.NewDecoder(resp.Body).Decode(&metadata)
 
    w.Header().Set("Content-Type", "application/json")
    w.Write([]byte(`{"success": true}`))
}

Ruby / Rails

class FormsController < ApplicationController
  def submit
    token = params['byebot-token']
 
    if token.blank?
      return render json: { error: 'CAPTCHA token missing' }, status: :bad_request
    end
 
    response = HTTParty.post(
      'https://challenge.byebot.de/validate_token',
      headers: { 'Content-Type' => 'application/json' },
      body: {
        api_key: ENV['BYEBOT_API_KEY'],
        token: token
      }.to_json
    )
 
    unless response.success?
      return render json: { error: 'CAPTCHA validation failed' }, status: :bad_request
    end
 
    # Token is valid - process the form
    # Optionally parse response for challenge metadata:
    # metadata = JSON.parse(response.body)
 
    render json: { success: true }
  end
end

Best Practices#

Always Validate Server-Side

Never trust client-side validation alone. Always validate the token on your server before processing any form submission.

Keep Your API Key Secret

  • Never expose your API key in client-side code
  • Use environment variables to store the key
  • Rotate keys if they are accidentally exposed

Handle Validation Failures Gracefully

  • Display a user-friendly error message
  • Allow users to retry the CAPTCHA
  • Log failures for monitoring

Validate Before Processing

Always validate the CAPTCHA token before performing any database operations or sending emails. This prevents bots from triggering expensive operations.

// Good: Validate first
const isValid = await validateCaptcha(token);
if (!isValid) {
  return res.status(400).json({ error: 'Invalid CAPTCHA' });
}
await saveToDatabase(formData);
 
// Bad: Don't do this
await saveToDatabase(formData);
const isValid = await validateCaptcha(token);

Token Properties

  • Tokens are single-use and expire after validation
  • Tokens have a limited lifetime (typically a few minutes)
  • Each token is bound to a specific site key

Troubleshooting#

Token Always Invalid

  • Verify your API key is correct
  • Check that you're using the production API key, not a test key
  • Ensure the token hasn't expired (validate promptly after form submission)

Missing Token

  • Verify the widget is properly initialized
  • Check that the form contains a byebot-token hidden field after verification
  • Ensure the widget's sitekey matches your site

Network Errors

  • Ensure your server can reach the ByeBot validation endpoint
  • Check for firewall rules blocking outbound requests
  • Implement retry logic for transient failures

Built with precision. Designed for developers.