In this article, we have tried to answer all the key questions. The first step in solving the task is to determine which protection system is being used. To do this, you can refer to the list of popular captchas and anti-bot protection systems, where you will find visual examples and key indicators that help you quickly understand what you are dealing with.
If you discover that your site uses ALTCHA, the next step is to study its properties and operation in more detail. In this article, you can also review the instructions on how to integrate ALTCHA so that you fully understand how it functions on your site. This will help you not only understand the current protection, but also properly plan its maintenance.
When testing forms that include ALTCHA, you often need to verify that the captcha works and is integrated correctly.
You can verify the captcha embedded on your site manually.
For automatic solving you can use tools like CapMonster Cloud, which accepts captcha parameters, processes them on its servers, and returns a ready-to-use token. Insert this token into the form to pass the check without user interaction.
Working with CapMonster Cloud via API typically involves the following steps:
Your ALTCHA solving request must include the following parameters:
type - CustomTask
class - altcha
websiteURL - the address of the page where the captcha is solved
websiteKey - for this task it is allowed to send an empty string.
challenge (inside metadata) - a unique challenge identifier obtained from the webpage.
iterations (inside metadata) - the number of iterations or the maximum value for calculations. Important: the iterations parameter corresponds to the maxnumber value!
salt (inside metadata) - the salt obtained from the site, used to generate hashes.
signature (inside metadata) - the digital signature of the request.
userAgent - Browser User-Agent. Provide only a current UA from Windows OS.
https://api.capmonster.cloud/createTask{
"clientKey": "API_KEY",
"task": {
"type": "CustomTask",
"class": "altcha",
"websiteURL": "https://example.com",
"websiteKey": "",
"userAgent": "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)",
"metadata": {
"challenge": "3dd28253be6cc0c54d95f7f98c517e68744597cc6e66109619d1ac975c39181c",
"iterations": "5000",
"salt":"46d5b1c8871e5152d902ee3f?edk=1493462145de1ce33a52fb569b27a364&codeChallenge=464Cjs7PbiJJhJZ_ReJ-y9UGGDndcpsnP6vS8x1nEJyTkhjQkJyL2jcnYEuMKcrG&expires=1761048664",
"signature": "4b1cf0e0be0f4e5247e50b0f9a449830f1fbca44c32ff94bc080146815f31a18"
}
}
}
{
"errorId":0,
"taskId":407533072
}https://api.capmonster.cloud/getTaskResult{
"clientKey":"API_KEY",
"taskId": 407533072
}
{
"errorId": 0,
"status": "ready",
"solution": {
"data": {
"token": "eyJhbGdvcml0aG0iOiJTSEEtMjU2IiwiY2hhbGxlbmdlIjoiNjMxOGUzYzc2NjhlOTZiMmFlYjg2NGU2NmRmMTliODRkYmYwZDBhMWRjNjYwMDdiMGM5ZGI4ODZiNTUxNjQxNiIsIm51bWJlciI6MTY0NDcsInNhbHQiOiJiMTQ1YWE5ZmU5MjA3NjBiMDgxYTAwNTMiLCJzaWduYXR1cmUiOiI5NzM4MmJlODE2NDUwNzk5MzlhYmU2M2ZkYzZjN2E3ZGYwOTM5MzNjODljZTk0ZjgzNTgxYmUyNDU3ZmI4MDM0IiwidG9vayI6MTczLjl9",
"number": "16447"
}
}
}
// npm install playwright
// npx playwright install chromium
const { chromium } = require("playwright");
const API_KEY = "YOUR_API_KEY";
const ALTCHA_PAGE = "https://example.com"; // Your website with ALTCHA
(async () => {
const browser = await chromium.launch({ headless: false, devtools: true });
const context = await browser.newContext();
const page = await context.newPage();
// Catching all responses from the ALTCHA endpoint
let challengeResp = null;
page.on("response", async (response) => {
try {
const url = response.url();
if (url.startsWith("https://captcha.example.com/altcha")) { // ALTCHA endpoint
challengeResp = await response.json();
console.log("Captured Altcha response:", challengeResp);
}
} catch (err) {
console.warn("Error parsing Altcha response:", err);
}
});
await page.goto(ALTCHA_PAGE, { waitUntil: "networkidle" });
// Click the widget if it exists
const widgetHandle = await page.$("altcha-widget");
if (widgetHandle) {
try {
await widgetHandle.click();
} catch {}
}
// Waiting for challenge to appear
const start = Date.now();
while (!challengeResp && Date.now() - start < 60000) { // Timeout increased
await new Promise((r) => setTimeout(r, 300));
}
if (!challengeResp) {
console.error("Failed to capture Altcha challenge.");
await browser.close();
return;
}
const { challenge, salt, signature, maxnumbers } = challengeResp;
// Creating a task on CapMonster Cloud
const createTaskBody = {
clientKey: API_KEY,
task: {
type: "CustomTask",
class: "altcha",
websiteURL: ALTCHA_PAGE,
websiteKey: "",
userAgent:"Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)",
metadata: {
challenge,
iterations: maxnumbers || 100000,
salt,
signature,
},
},
};
const taskResp = await fetch("https://api.capmonster.cloud/createTask", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(createTaskBody),
}).then((r) => r.json());
console.log("CreateTask response:", taskResp);
if (!taskResp?.taskId) {
console.error("CreateTask failed:", taskResp);
await browser.close();
return;
}
const taskId = taskResp.taskId;
// Getting the solution
let fullSolution = null;
const pollStart = Date.now();
while (Date.now() - pollStart < 120000) {
const res = await fetch("https://api.capmonster.cloud/getTaskResult", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ clientKey: API_KEY, taskId }),
}).then((r) => r.json());
if (res.status === "ready") {
fullSolution = res.solution;
console.log("Solution:", fullSolution);
break;
}
await new Promise((r) => setTimeout(r, 3000));
}
if (!fullSolution) {
console.error("No solution received in time.");
await browser.close();
return;
}
const token = fullSolution?.data?.token || fullSolution?.token || fullSolution?.data;
if (!token) {
console.error("Token not found in solution:", fullSolution);
await browser.close();
return;
}
//Inserting the token
await page.evaluate((t) => {
let input = document.querySelector("#captchaParaValidar");
if (!input) {
input = document.createElement("input");
input.type = "hidden";
input.id = "captchaParaValidar";
input.name = "captchaParaValidar";
(document.querySelector("form") || document.body).appendChild(input);
}
input.value = t;
let alt = document.querySelector('input[name="altcha"]');
if (!alt) {
alt = document.createElement("input");
alt.type = "hidden";
alt.name = "altcha";
(document.querySelector("form") || document.body).appendChild(alt);
}
alt.value = t;
const widget = document.querySelector("altcha-widget");
if (widget) {
widget.setAttribute("data-state", "verified");
const checkbox = widget.querySelector("input[type='checkbox']");
if (checkbox) {
checkbox.checked = true;
checkbox.dispatchEvent(new Event("change", { bubbles: true }));
}
const label = widget.querySelector(".altcha-label");
if (label) label.textContent = "Verified";
}
}, token);
console.log("Token injected:", token);
})();1. Installing the widget
Option 1 — via CDN (the simplest). Add this to your HTML <head>:
<script async defer src="https://cdn.jsdelivr.net/gh/altcha-org/altcha/dist/altcha.min.js" type="module"></script>Option 2 — via npm:
npm install altchaImport the widget into your JS file:
import "altcha";2. Adding the widget to the form.
Insert the <altcha-widget> component into the form where protection is required:
<form method="POST" action="/submit">
<altcha-widget challengeurl="/altcha/challenge"></altcha-widget>
<button type="submit">Send</button>
</form>challengeurl — your server endpoint for issuing the challenge.
If you use ALTCHA Sentinel (a ready server-side protection system against bots and spam with machine learning and traffic analysis), use its URL instead of your own server:
<altcha-widget
challengeurl="https://sentinel.example.com/v1/challenge?apiKey=YOUR_API_KEY">
</altcha-widget>3. Server-side validation.
How validation works:
Validation via ALTCHA Sentinel:
import { verifyServerSignature } from 'altcha-lib';
// The secret API key generated in Sentinel
const apiKeySecret = 'sec_...';
// Payload received from the widget
const payload = '...';
// Validation
const { verificationData, verified } = await verifyServerSignature(payload, apiKeySecret);
if (verified) {
// Validation successful — form data can be processed
}
const resp = await fetch('https://sentinel.example.com/v1/verify/signature', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ payload }),
});
const { verified } = await resp.json();
if (verified) {
// Validation successful — processing the form
}
The API automatically prevents reuse of the same payload.
4. Validation without Sentinel (your own server)
Generating the challenge:
import { createChallenge } from 'altcha-lib';
const hmacKey = '$ecret.key'; // Your HMAC secret key
const challenge = await createChallenge({ hmacKey });
// Return the challenge in JSON for the widgetPayload validation when the form is submitted:
import { verifySolution } from 'altcha-lib';
const hmacKey = '$ecret.key'; // Your HMAC secret key
const verified = await verifySolution(payload, hmacKey);
if (verified) {
// Validation successful — processing the form data
}
In this case, you create your own /altcha/challenge endpoint to issue tasks and implement validation on the server.
If you’ve taken over a website that already has a captcha or another protection system installed, but you don’t have access to the code, don’t worry! It’s quite easy to identify which technology is being used. To verify that everything works correctly, you can use the CapMonster Cloud recognition service in an isolated test environment to make sure that the token processing mechanism and the validation logic are functioning properly.
In the case of ALTCHA, it’s enough to detect the system, observe its behavior, and confirm that the protection is working correctly. In this article, we showed how to identify ALTCHA and where to find instructions on how to integrate or reconfigure it, so you can confidently maintain the protection and keep its operation under control.