Integration Guide

Add CAPTCHA protection to your website in three steps.

1

Create an account & register your site

Register, create a workspace, add your domain, and verify ownership. After verification you receive a Client ID, API Key, and Client Secret.

⚠ The Client Secret is shown once at creation time. Store it securely — it is used only from your backend.
2

Add the widget to your page

The widget script requests a challenge from our API and renders it inside a container element. No external CDN — serve the snippet from your own domain or copy-paste it.

<!-- 1. Add a container where the CAPTCHA renders -->
<div id="captcha-widget"></div>

<!-- 2. Load the widget script (self-hosted on your site) -->
<script>
window.CAPTCHA_CONFIG = {
  apiBase:  "https://captcha.sabacore.ir",
  apiKey:   "YOUR_API_KEY",        // from dashboard
  container: "#captcha-widget",
  onSuccess: function(challengeId) {
    // store challengeId; send it with your form submission
    document.getElementById("captcha-id").value = challengeId;
  }
};
</script>
<script src="/js/captcha-widget.js" defer></script>

<!-- 3. Hidden field in your form -->
<input type="hidden" id="captcha-id" name="captcha_challenge_id">
<!-- Minimal HTML form example -->
<form method="POST" action="/your-form-handler">
  <div id="captcha-widget"></div>
  <input type="hidden" id="captcha-id" name="captcha_challenge_id">
  <button type="submit">Submit</button>
</form>
3

Verify the solution on your server

When the user submits your form, your backend calls the verify endpoint. Never call verify from the browser — it requires your Client Secret.

resp, err := http.Post(
    "https://captcha.sabacore.ir/v1/captcha/verify",
    "application/json",
    strings.NewReader(`{"challenge_id":"`+challengeID+`","solution":"`+solution+`"}`),
)
// add header: X-Client-Secret: YOUR_CLIENT_SECRET
$ch = curl_init("https://captcha.sabacore.ir/v1/captcha/verify");
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ["X-Client-Secret: YOUR_CLIENT_SECRET",
                               "Content-Type: application/json"],
    CURLOPT_POSTFIELDS     => json_encode([
        "challenge_id" => $challengeId,
        "solution"     => $solution,
    ]),
]);
$result = json_decode(curl_exec($ch), true);
if ($result["valid"]) { /* proceed */ }
import requests

r = requests.post(
    "https://captcha.sabacore.ir/v1/captcha/verify",
    headers={"X-Client-Secret": "YOUR_CLIENT_SECRET"},
    json={"challenge_id": challenge_id, "solution": solution},
)
if r.json()["valid"]:
    pass  # proceed
curl -X POST https://captcha.sabacore.ir/v1/captcha/verify \
  -H "X-Client-Secret: YOUR_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"challenge_id":"<id>","solution":"<answer>"}'

Verify response

// 200 OK
{ "valid": true }

// Error examples
{ "error": "challenge expired" }   // 410
{ "error": "challenge consumed" }  // 409

API reference

MethodPathAuthPurpose
GET /v1/health Liveness check
POST /v1/captcha/issue X-API-Key Issue a challenge (from browser)
POST /v1/captcha/verify X-Client-Secret Verify solution (backend only)

Rate limits

EndpointLimit
/v1/captcha/issue 60 req/min per IP · 6,000 req/hour per site
/v1/captcha/verify 120 req/min per site
Questions? Open an issue or contact us via your dashboard.