Verifying on Your Backend
You have successfully integrated the Wicketkeeper widget into your frontend form. When a user solves the captcha and submits the form, the solution is sent to your application's backend.
This final, crucial step is to have your backend verify the solution with the Wicketkeeper server. This is a secure, server-to-server interaction that confirms the user is not a bot before you process their data.
The Verification Flow
The diagram below highlights the part of the process this page focuses on: the communication between Your App Backend and the Wicketkeeper Server.
The logic your backend needs to implement is straightforward:
- Receive the form submission from the user's browser.
- Extract the Wicketkeeper solution string from the request body (e.g., from the
wicketkeeper_response
field). - Parse this string from JSON into an object.
- Send this object in a
POST
request to your Wicketkeeper server's/v0/siteverify
endpoint. - Evaluate the JSON response from the Wicketkeeper server.
- Proceed with your application logic (like saving a user comment) only if the verification was successful.
Example: Node.js + Express
The example/
application provides a perfect, real-world example of this flow using Node.js, Express, and TypeScript. The core logic is shown below.
Any Language Will Do
While this example uses Node.js, the principle is universal. It is a standard HTTP POST request that can be implemented in any backend language, such as Python, PHP, Ruby, Java, or C#.
// example/src/server.ts
import express, { Request, Response } from "express";
import axios from "axios";
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
const VERIFY_URL =
process.env.VERIFY_URL || "http://localhost:8080/v0/siteverify";
app.post("/submit", async (req: Request, res: Response) => {
// 1. Extract the solution from the request body
const { name, email, wicketkeeper_response } = req.body;
if (!wicketkeeper_response) {
return res.status(400).send("⚠️ Missing Wicketkeeper solution");
}
// 2. Parse the JSON string into an object
let solution: { token: string; nonce: number; response: string };
try {
solution = JSON.parse(wicketkeeper_response);
} catch {
return res.status(400).send("⚠️ Invalid Wicketkeeper payload");
}
// 3. Send the solution to the Wicketkeeper server for verification
try {
const verifyRes = await axios.post(VERIFY_URL, solution, {
headers: { "Content-Type": "application/json" },
});
// 4. Check if verification was successful
if (!verifyRes.data || verifyRes.data.success !== true) {
console.warn("Wicketkeeper verify failed:", verifyRes.data);
return res.status(400).send("🚫 Wicketkeeper verification failed");
}
} catch (err: any) {
// 5. Handle errors if the Wicketkeeper server is down or returns an error
console.error("Verification error:", err.response?.data || err.message);
return res.status(500).send("❌ Verification service error");
}
// 6. If successful, proceed with your application logic
console.log("✅ Form received:", { name, email });
res.send(`Thanks, ${name}! We've received your email (${email}).`);
});
app.listen(8081, () => {
console.log(`🚀 Server listening on http://localhost:8081`);
});
Breakdown of the Code
Extract the Solution: The code first gets the
wicketkeeper_response
string from the POST body. If it's missing, it immediately returns a400 Bad Request
error.Parse the JSON: The
wicketkeeper_response
is a JSON string.JSON.parse()
converts it into the object that the Wicketkeeper server expects. This object contains thetoken
,nonce
, andresponse
.POST to
/v0/siteverify
: Usingaxios
(or any other HTTP client), the code sends the parsedsolution
object to theVERIFY_URL
. TheContent-Type
header must be set toapplication/json
.Check for Success: The most important check is
verifyRes.data.success === true
. The Wicketkeeper server will explicitly tell you if the verification passed. Ifsuccess
is nottrue
, you should treat the submission as invalid and return an error.Handle Service Errors: The
catch
block handles network errors or 5xx responses from the Wicketkeeper server (e.g., it can't connect to Redis). In this case, your application should return a500 Internal Server Error
to the user, as the problem is on the server side, not with the user's submission.Proceed with Logic: Only after all checks have passed does the code proceed to the core application logic (in this case, logging the data and sending a success message).