JavaScript packages
Updated
Three small packages on npm, typed, with no dependencies. @bot403/node verifies tokens on your server, @bot403/browser gets tokens in the browser, and @bot403/react wraps that in a hook. Each one is a thin layer over the same two requests, so the curl call below is exactly what the server package sends:
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>"}'
npm install @bot403/node # your server
npm install @bot403/browser # fetch() submissions, forms you build in JavaScript
npm install @bot403/react # the useBot403 hook
On the server: @bot403/node
It runs wherever fetch exists: Node.js 18 and later, Bun, Deno, Cloudflare Workers, Vercel and Next.js route handlers. Keep the secret key in an environment variable.
// npm install @bot403/node
import { Bot403 } from "@bot403/node";
// Next.js App Router: app/api/contact/route.js
export async function POST(request) {
const bot403 = new Bot403({ secretKey: process.env.BOT403_SECRET_KEY });
const result = await bot403.verifyForm(await request.formData());
if (!result.ok && result.reason !== "quota") {
// result.reason: expired, replay, invalid, wrong_site, honeypot or automation
return Response.json({ error: "Spam check failed" }, { status: 400 });
}
// ...handle the form
return Response.json({ ok: true });
}
verifyForm sends every field of the form, so a honeypot field set on the site is checked too. verify(token) checks one token when you already have it. Both resolve with ok: true, or with ok: false and a reason. The verify page explains each reason.
They reject with a Bot403Error only when the request itself fails: a wrong secret key (status 401), too many verify calls (429), or no network (status 0). Decide what your form does in that case; letting it through is the usual answer, because Bot403 being unreachable is not a reason to lose a message.
quota comes with HTTP 402 and only in a capped month (after two calendar months over the plan). The package returns it as a normal result. The examples in these docs let the form through on quota; block it instead if spam costs you more than a lost message.
import { Bot403 } from "@bot403/node";
// Options: a self-hosted instance and a shorter timeout
const bot403 = new Bot403({
secretKey: process.env.BOT403_SECRET_KEY,
baseUrl: "http://localhost:8087",
timeoutMs: 5000,
});
// Totals for the plan window and the quota: used, limit, cap
const stats = await bot403.stats();
In the browser: @bot403/browser
A plain HTML form needs only the script tag. Use this package when you submit with fetch() or build forms in JavaScript. It adds the same 6 KB script to the page once and gives it types.
// npm install @bot403/browser
import { solve, attach, FIELD } from "@bot403/browser";
// A fresh single-use token for one request
const token = await solve("pk_live_YOUR_PUBLIC_KEY");
await fetch("/contact", { method: "POST", body: new URLSearchParams({ email: "[email protected]", [FIELD]: token }) });
// Or protect a form element you created
const form = document.createElement("form");
await attach(form, { publicKey: "pk_live_YOUR_PUBLIC_KEY" });
load({ publicKey }) adds the script with the key, so it protects every form on the page, as the script tag does. Pass nonce if your Content Security Policy uses nonces; the script options page lists the CSP lines.
React: @bot403/react
// npm install @bot403/react
import { useBot403, FIELD } from "@bot403/react";
export function ContactForm() {
const getToken = useBot403("pk_live_YOUR_PUBLIC_KEY");
async function onSubmit(event) {
event.preventDefault();
const body = new FormData(event.currentTarget);
body.set(FIELD, await getToken());
await fetch("/api/contact", { method: "POST", body });
}
return (
<form onSubmit={onSubmit}>
<input name="email" type="email" required />
<button>Send</button>
</form>
);
}
The hook starts loading the script when the component mounts, so the first token is quick. Call getToken() once per submission: tokens are single-use.
MIT license. Packages: @bot403/node, @bot403/browser, @bot403/react.