Developer Help

Embed one script, then verify the response token on your backend with an origin or secret.

AstraCaph supports an open mode without mandatory frontend keys. You can embed the widget immediately, then verify tokens by your site origin or by a secret key in stricter deployments.

0. Pick your site origin

Example: https://example.com

Your backend will send this origin to AstraCaph
as the expected binding for the token.

1. Embed the widget

<script src="https://caph.astracat.ru/api/v1/widget.js" async defer></script>
<div id="astracaph-container"></div>

2. Read the hidden response token

<input type="hidden" name="astracaph-response" value="eyJ..." />

3. Verify on your backend

POST https://caph.astracat.ru/api/v1/verify
Content-Type: application/json

{
  "token": "user_generated_token",
  "origin": "https://example.com"
}

4. Optionally create a secret-bound profile

POST https://caph.astracat.ru/api/v1/sites
Content-Type: application/json

{
  "domain": "example.com",
  "name": "Example store"
}

5. Delete a bound domain

DELETE https://caph.astracat.ru/api/v1/sites
Content-Type: application/json

{
  "domain": "example.com",
  "secret": "sk_live_..."
}

Flow notes

AstraCaph's default mode does not require a frontend site key: embedding `/api/v1/widget.js` with `#astracaph-container` is enough.

POST /api/v1/verify can validate a token against `origin` or `domain`, which keeps Vercel deployments working even without a persistent site database.

POST /api/v1/sites creates a free site registration and returns a fresh siteKey plus secret when you need a dedicated isolated profile.

DELETE /api/v1/sites removes a registered domain only when the matching sk_live server secret is provided.

Challenge logic also queries multiple free IP databases to estimate whether the IP looks like hosting, proxy, VPN, Tor, mobile carrier, or residential ISP.

POST /api/v1/challenge accepts telemetry, computes a risk score and either returns a token or asks for a visual follow-up step.

POST /api/v1/verify validates the signed token, checks origin binding or a secret key, and consumes the token once.

POST /public/api/v1/verify supports the same verification flow and also accepts `origin`, `domain`, and `secretKey` for server-to-server integrations.

GET /api/v1/widget.js serves a framework-free loader designed to initialize inside any page with a matching container.

GET /captcha/widget serves an iframe-ready widget page with `theme=dark|light`, while `siteKey` remains optional for compatibility.

The `/sandbox` page lets you test custom HTML, widget behavior, and challenge / verify logs in one live debugging surface.

Recommended environment

ASTRACAPH_TOKEN_SECRET=replace_with_long_random_secret
# Optional if you want strict secret-based verification for a custom site:
ASTRACAPH_OPEN_SECRET=replace_with_private_open_verify_secret
# Optional seeded sites for internal or enterprise bindings:
ASTRACAPH_SITE_CONFIG=[{"name":"Seed Site","siteKey":"pk_live_example","secret":"sk_live_example","origins":["https://example.com"],"createdAt":0}]
UPSTASH_REDIS_REST_URL=...
UPSTASH_REDIS_REST_TOKEN=...
VERIFY_TRUSTED_IPS=203.0.113.10,203.0.113.11

Iframe Integration API

Compatible iframe docs for websites and backends

Below is a compatible integration flow using the iframe widget, postMessage, and server-to-server verification without mandatory frontend keys.

1. Iframe widget

<iframe
  src={\`https://caph.astracat.ru/captcha/widget?theme=dark\`}
  width="350"
  height="500"
  frameBorder="0"
  scrolling="no"
></iframe>

2. postMessage listener

window.addEventListener("message", (event) => {
  if (event.origin !== (process.env.NEXT_PUBLIC_APP_URL || "https://caph.astracat.ru")) {
    return;
  }

  if (event.data.type === "astracaph-verified") {
    const { token, success } = event.data.detail;

    if (success) {
      fetch("/your-backend-endpoint", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ token }),
      });
    }
  }
});

3. Backend verify: Node.js

app.post("/your-backend-endpoint", async (req, res) => {
  const { token } = req.body;
  const expectedOrigin = process.env.CAPTCHA_EXPECTED_ORIGIN || "https://example.com";

  try {
    const response = await fetch(
      \`https://caph.astracat.ru/public/api/v1/verify\`,
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ token, origin: expectedOrigin }),
      }
    );

    const data = await response.json();

    if (data.success) {
      res.status(200).json({ message: "CAPTCHA verified successfully." });
    } else {
      res.status(400).json({ message: "CAPTCHA verification failed." });
    }
  } catch (error) {
    res.status(500).json({ message: "Internal server error." });
  }
});

4. Backend verify: Python

import os
import requests
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/your-backend-endpoint", methods=["POST"])
def verify_captcha():
    token = request.json.get("token")
    expected_origin = os.environ.get("CAPTCHA_EXPECTED_ORIGIN", "https://example.com")
    captcha_verify_url = f"{os.environ.get('NEXT_PUBLIC_APP_URL', 'https://caph.astracat.ru')}/public/api/v1/verify"

    try:
        response = requests.post(
            captcha_verify_url,
            json={"token": token, "origin": expected_origin}
        )
        response.raise_for_status()
        data = response.json()

        if data.get("success"):
            return jsonify({"message": "CAPTCHA verified successfully."}), 200

        return jsonify({"message": "CAPTCHA verification failed."}), 400
    except requests.exceptions.RequestException:
        return jsonify({"message": "Internal server error."}), 500

Example successful response

{
  "success": true,
  "message": "Verification successful"
}