@botpoison/browser
Read the Botpoison docs page "@botpoison/browser": https://botpoison.com/documentation/libraries/botpoison-browser.md The Botpoison agent skill is at https://botpoison.com/skills/botpoison/SKILL.md. Use them to make this change in my project, on the stack it already uses. Ask me for keys or IDs rather than inventing them.
Install
Script tag
Without a bundler, load version 1.1.0 from a CDN. Load it async, so it never holds up the page.
<script src="https://unpkg.com/@botpoison/browser@1.1.0/dist/index.js" integrity="sha384-OV9jtDYCk1ORwcQiG2ooB9pcBIpHSRTng9CbA1vObEm7oaL+nRiFC45XXdQNg22i" crossorigin="anonymous" async></script>The tag names one version, and the integrity hash makes the browser refuse any other file. A new release, or a compromised CDN, cannot change the script on your page: it changes when you change the tag. To serve it from your own domain instead, download that file and point src at your copy, keeping the same hash.
npm
Install from npm when you use a bundler. Your lockfile pins the version, so it changes only when you upgrade.
Requirements
The library runs the check, a small proof of work, with BigInt and Web Crypto, so the page must be served over HTTPS, or from localhost while you develop. It has no dependencies, sets no cookies and stores nothing in the browser.
Automatic mode and events
Once the library is loaded, it protects every form with a data-botpoison-public-key attribute, including forms added to the page later. When the visitor presses Send, it holds the submission, runs the check, adds the solution in a hidden _botpoison field, and sends the form.
<!-- 1. Import the @botpoison/browser script, pinned to one version --><script src="https://unpkg.com/@botpoison/browser@1.1.0/dist/index.js" integrity="sha384-OV9jtDYCk1ORwcQiG2ooB9pcBIpHSRTng9CbA1vObEm7oaL+nRiFC45XXdQNg22i" crossorigin="anonymous" async></script><!-- 2. Add your public key to the data-botpoison-public-key attribute --><form action="https://example.demo" data-botpoison-public-key="pk_xxxxxxxx"> <input type="text" name="message" /> <button type="submit">Send</button></form>The form fires an event at each step. Listen for them to disable the submit button or show a loading state.
botpoison-challenge-startThe visitor pressed Send and solving began.botpoison-challenge-successThe check is done. The form is sent right after.botpoison-challenge-errorThe check could not be completed. The form is not sent.
<form id="form" action="https://example.demo" data-botpoison-public-key="pk_xxxxxxxx"> <input type="text" name="message" /> <button id="button" type="submit">Send</button></form><script> var formElement = document.getElementById("form"); var buttonElement = document.getElementById("button"); formElement.addEventListener("botpoison-challenge-start", function () { buttonElement.setAttribute("disabled", "disabled"); }); formElement.addEventListener("botpoison-challenge-success", function () { buttonElement.removeAttribute("disabled"); }); formElement.addEventListener("botpoison-challenge-error", function () { buttonElement.removeAttribute("disabled"); });</script>If the script cannot reach Botpoison, the form fires botpoison-challenge-error and does not send. Use it to tell the visitor what happened. See If Botpoison is unreachable for the server side.
The library sends the form itself, without a second submit event, so a script that sends the form some other way never sees it. For a form you send with JavaScript, leave the attribute off and call challenge() instead.
new Botpoison({ publicKey })
Creates an instance for a form you send with JavaScript, with the public key of your project.
import Botpoison from "@botpoison/browser";const botpoison = new Botpoison({ publicKey: "pk_xxxxxxxx"});challenge({ onProgress })
Runs the check and resolves to { solution }. Send the solution with the form as _botpoison. It rejects when the check cannot be completed, such as when the network is down or Botpoison is out of reach: catch it and tell the visitor, rather than sending the form without a solution.
import Botpoison from "@botpoison/browser";const botpoison = new Botpoison({ publicKey: "pk_xxxxxxxx"});const { solution } = await botpoison.challenge();Progress
onProgress is called with 0 when solving starts, with a growing fraction in steps while it runs, and with 1 when it is done. The values in between depend on how much work the check takes, so compare with a threshold or show a percentage rather than wait for an exact value such as 0.5.
import Botpoison from "@botpoison/browser";const botpoison = new Botpoison({ publicKey: "pk_xxxxxxxx"});const button = document.querySelector("button[type=submit]");const { solution } = await botpoison.challenge({ // From 0 to 1, in steps while the check runs onProgress: (progress) => { button.textContent = `Checking ${Math.round(progress * 100)}%`; },});button.textContent = "Send";Server rendering
Importing the library sets up automatic mode once per page. Where there is no document, such as the server render in Next.js or Nuxt, the import does nothing, so it needs no setup of its own. Botpoison.init() runs the same step for integrations that call it, and a second call does nothing.