Keys
Add your site key as NEXT_PUBLIC_FMK_SITE_KEY and your secret as FMK_SECRET in .env.local. Only the site key reaches the browser.
FitMyKB Verify
Load the widget with next/script, read the token in a server action, and check it with siteverify before you send anything.
Add your site key as NEXT_PUBLIC_FMK_SITE_KEY and your secret as FMK_SECRET in .env.local. Only the site key reaches the browser.
next/script loads api.js once. The widget finds the .fmk-captcha box, and when the visitor passes it adds fmk-captcha-response to the form, so the server action receives it with the other fields.
// app/contact/page.tsx
import Script from "next/script";
import { send } from "./actions";
export default function Contact() {
return (
<form action={send}>
<input name="email" type="email" required />
<textarea name="message" required />
<div className="fmk-captcha" data-sitekey={process.env.NEXT_PUBLIC_FMK_SITE_KEY} />
<button>Send</button>
<Script src="https://fitmykb.com/captcha/api.js" strategy="afterInteractive" />
</form>
);
}Check the token before doing anything else. The reply also carries a score from 0 to 1 and, with score-only mode, the action name, if you want to treat borderline visitors differently.
// app/contact/actions.ts
"use server";
export async function send(form: FormData) {
const reply = await fetch("https://fitmykb.com/captcha/siteverify", {
method: "POST",
body: new URLSearchParams({
secret: process.env.FMK_SECRET!,
response: String(form.get("fmk-captcha-response") ?? ""),
}),
cache: "no-store",
}).then((r) => r.json());
if (!reply.success) throw new Error("Please complete the check.");
// …send the message
}The same check works in pages/api: read req.body['fmk-captcha-response'] and post it to siteverify the same way.
Add both environment variables in the project settings. Preview deployments use *.vercel.app addresses, so add them to the website's domains or keep a separate test-mode website for previews.