Verify on your server

Updated

Post the token to https://bot403.com/api/v1/verify with Authorization: Bearer sk_live_YOUR_SECRET_KEY. Send JSON {"solution": "…"}, or post the whole form as application/x-www-form-urlencoded. Bot403 reads the _bot403 field and the honeypot field from the form.

curl -X POST https://bot403.com/api/v1/verify \
  -H "Authorization: Bearer sk_live_YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"solution": "<the _bot403 field from the form>"}'

The same call in every language:

server.mjs
// npm install @bot403/node express
import express from "express";
import { Bot403 } from "@bot403/node";

// Keep the secret key on the server: BOT403_SECRET_KEY=sk_live_YOUR_SECRET_KEY
const bot403 = new Bot403({ secretKey: process.env.BOT403_SECRET_KEY });
const app = express();
app.use(express.urlencoded({ extended: false }));

app.post("/contact", async (req, res) => {
  // 1. Post the whole form. Bot403 reads the _bot403 field and the honeypot field
  const result = await bot403.verifyForm(req.body);

  // 2. Refuse when ok is false, except for reason "quota"
  if (!result.ok && result.reason !== "quota") return res.status(400).send("Try again");
  res.send("Thanks.");
});

app.listen(3000);

Never put the secret key in a page. The public key is safe to embed; the secret key is not.

Answers and failure reasons

AnswerMeaningWhat to do
{"ok": true, "site": "pk_…", "solved_at": "…"}A browser solved the puzzle for this site. The token was not used before.Accept the submission.
{"ok": false, "reason": "invalid"}The token is missing, damaged, or the puzzle answer is wrong.Reject. Log it.
{"ok": false, "reason": "expired"}The challenge is older than the site's expiry (default 10 minutes).Reject. Ask the visitor to send again; the script gets a new token.
{"ok": false, "reason": "replay"}The token was used before.Reject.
{"ok": false, "reason": "wrong_site"}The token belongs to a different site.Reject. Check that the page and the server use keys of the same site.
{"ok": false, "reason": "honeypot"}The honeypot field had a value.Reject, or answer with a fake success.
{"ok": false, "reason": "quota"}, HTTP 402Only in a capped month: after two calendar months over the plan in a row, the next month stops at the limit. A normal month never answers this, however far over you go.Your choice. The examples above let the submission through. Upgrade in Settings to lift the cap at once.

Only ok: true counts against the quota. HTTP 401 means the secret key is wrong or missing. HTTP 429 means too many verify calls for the site this minute (100 on Free, 1,000 on Starter, 3,000 on Pro). HTTP 402 (reason quota) only happens in a capped month, above the limit; the token is not burned, so it verifies after an upgrade. Never treat a 402 as a pass. Going over in a normal month changes nothing: verify answers as usual and Bot403 emails you at 80 %, 100 % and 150 %.

When Bot403 is down

The script never blocks a form. If the challenge request fails, the form submits without a token and your verify call answers with reason invalid. If the verify call itself fails with a network error, decide now what your server does: the examples above let the form through.