@botpoison/node
Read the Botpoison docs page "@botpoison/node": https://botpoison.com/documentation/libraries/botpoison-node.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
It needs Node.js 18 or later and has no dependencies. Load it with import or require: both return the class. classify() needs version 0.2.0 or later.
Creating an instance
Create an instance with your project's secret key, read from your server's environment. The same instance verifies solutions and classifies messages, because one key works with every product.
import Botpoison from "@botpoison/node";// or const Botpoison = require("@botpoison/node")// One instance for every product, with the project's secret key read from// your server's environment.const botpoison = new Botpoison({ secretKey: process.env.BOTPOISON_SECRET_KEY});verify(solution)
Verifies the _botpoison field a Challenge form sent. It resolves to { ok }: accept the form when ok is true, and reject it otherwise.
// 1. Import the libraryimport Botpoison from "@botpoison/node";// or const Botpoison = require("@botpoison/node")// 2. Create a new instance with your secret key, read from your server's// environment: BOTPOISON_SECRET_KEY=sk_xxxxxxxxconst botpoison = new Botpoison({ secretKey: process.env.BOTPOISON_SECRET_KEY});server.post("/message", async (req, res) => { const { message, _botpoison } = req.body; // 3. Verify the solution const { ok } = await botpoison.verify(_botpoison); if (!ok) { throw new Error("Invalid Botpoison solution"); } // ...});It sends the key as secretKey in the body. It sets no timeout of its own, so wrap the call in one. A fault on our side answers ok: false with the message Internal server error. See If Botpoison is unreachable for what to accept during an outage.
classify(content, options)
Sends a submission to Classify and resolves to a verdict: { label: "spam" | "clean", score, model }. It sends the key as an Authorization: Bearer header, and the body holds only the content and the model.
// 1. Import the libraryimport Botpoison from "@botpoison/node";// or const Botpoison = require("@botpoison/node")// 2. Create a new instance with your secret key, read from your server's// environment: BOTPOISON_SECRET_KEY=sk_xxxxxxxxconst botpoison = new Botpoison({ secretKey: process.env.BOTPOISON_SECRET_KEY});server.post("/contact", async (req, res) => { // 3. Classify the whole form and read the label const { label } = await botpoison.classify(req.body, { model: "spam-standard", timeout: 8000, }); if (label === "spam") { // Drop it, or file it away return res.status(202).end(); } // ...});Act on label, which is spam from a score of 0.8. model names the model that answered. See Labels and scores.
Content
The first argument is the content: the whole form as an object, such as req.body, or a string for a single message box. Send the whole form when you have it, since every field helps the verdict. A field's value may be a string, a number, a boolean, null, or a list of strings, numbers and booleans. A nested object is refused. Fields whose name starts with _ are ignored, so form controls such as _redirect can stay in. See the REST API for the limits.
Options
The second argument is required, since it names the model.
- modelstring, required
The model to classify with, by name: spam-standard is the only model today.
- timeoutnumber, optional
Milliseconds from the start of the call until it is abandoned, however the time is spent. Defaults to 8000.
| Parameter | Type | Description |
|---|---|---|
| modelRequired | string | The model to classify with, by name: spam-standard is the only model today. |
| timeoutOptional | number | Milliseconds from the start of the call until it is abandoned, however the time is spent. Defaults to 8000. |
An unknown model is rejected with a 400 that lists the valid models. See Models.
Without the library
Node.js 18 and later ship fetch, so you can call the REST API directly instead.
// Node.js 18 and later ship fetch, so there is nothing to install.// The secret key comes from your server's environment:// BOTPOISON_SECRET_KEY=sk_xxxxxxxxconst SECRET_KEY = process.env.BOTPOISON_SECRET_KEY;// 1. Send the submission to Classifyconst classify = async (content, model = "spam-standard") => { const response = await fetch("https://api.botpoison.com/classify", { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json", Authorization: `Bearer ${SECRET_KEY}`, }, body: JSON.stringify({ content, model }), }); // A refusal carries a JSON message, but a proxy in between may answer // with something else, so the status is checked before the body is parsed. if (!response.ok) { const { message } = await response.json().catch(() => ({})); throw new Error(message ?? `Classify answered ${response.status}`); } // { label: "spam" | "clean", score, model } return response.json();};server.post("/contact", async (req, res) => { // 2. Send the whole form, then read the label const { label } = await classify(req.body); if (label === "spam") { // Drop it, or file it away return res.status(202).end(); } // ...});Errors
From verify()
verify() answers a rejected solution with ok: false rather than an error. It rejects when the request fails before an answer arrives, with the parsed response body when the API answers a status other than 200, and with an Error when the response is not JSON or a 200 carries no ok.
From classify()
classify() rejects with a Botpoison.ClassifyError. Its code says what went wrong, its status carries the HTTP status when Classify answered, its body carries the parsed response body, and its message says why.
| Code | Meaning |
|---|---|
http | Classify answered with an error status. status is that status, and message is the reason it sent. |
timeout | No answer arrived within the timeout. |
network | The request failed before an answer arrived. |
invalid_response | Classify answered 200 with a body that is not a classification. |
try { const { label } = await botpoison.classify(req.body, { model: "spam-standard", }); // ...} catch (error) { if (error instanceof Botpoison.ClassifyError) { // error.code is "http", "timeout", "network" or "invalid_response". // error.status is the HTTP status when Classify answered. console.error(error.code, error.status, error.message); } // Deliver the message as clean, so an outage never loses a real one}Decide what a failure means for your form. If you deliver the submission as clean, an outage never loses a real one. Three statuses are worth handling apart.
| Status | Meaning |
|---|---|
| 401 | The key is missing, unknown or rerolled. Fix the key in your environment rather than retrying. |
| 429 | The key is past its rate limit, or a Free workspace has paused after its allowance. Wait as long as Retry-After says. |
| 503 | The model is unavailable and the call is not counted. Retry later with backoff. |
Every status code is listed in the REST API reference.
TypeScript
The package ships its own types. Beside the class, it exports these types for the Classify calls: ClassifyContent, ClassifyFields, ClassifyFieldValue, ClassifyOptions, ClassifyResult, ClassifyLabel, ClassifyError and ClassifyErrorCode. At runtime, the error class is Botpoison.ClassifyError.