Classify
Getting started
Read the Botpoison Classify docs page "Getting started" at https://botpoison.com/documentation/classify/getting-started.md and use it to make this change in my project. Use the stack this project already runs on, and ask me for my keys rather than inventing them.
Classify a submission in two steps. Your server sends the form, and Classify returns a verdict: a label and a score. It reads the content, so it works on any text a person types: contact forms, comments, signups and support email.
01Send the submission from your server
After the form reaches your server, send it to /classify with your secret key. Send the whole form for the best verdict, or a string for a single message box. Keep the secret key on your server, never in the browser.
// 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_CLASSIFY_SECRET_KEY=sk_xxxxxxxxconst botpoison = new Botpoison({ secretKey: process.env.BOTPOISON_CLASSIFY_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: "standard", timeout: 8000, }); if (label === "spam") { // Drop it, or file it away return res.status(202).end(); } // ...});<?php// The secret key comes from your server's environment:// BOTPOISON_CLASSIFY_SECRET_KEY=sk_xxxxxxxx$secret_key = getenv("BOTPOISON_CLASSIFY_SECRET_KEY");// 1. Send the whole form to Classify$response = file_get_contents("https://api.botpoison.com/classify", false, stream_context_create(["http" => [ "method" => "POST", "header" => "Content-Type: application/json\r\nAuthorization: Bearer " . $secret_key, "content" => json_encode([ "content" => $_POST, "model" => "standard", ]), "timeout" => 8,]]));// 2. Read the labelif (json_decode($response)->label === "spam") { // Drop it, or file it away http_response_code(202); exit;}// ...import osimport requestsfrom flask import Flask, request# The secret key comes from your server's environment:# BOTPOISON_CLASSIFY_SECRET_KEY=sk_xxxxxxxxSECRET_KEY = os.environ["BOTPOISON_CLASSIFY_SECRET_KEY"]app = Flask(__name__)@app.post("/contact")def contact(): # 1. Send the whole form to Classify response = requests.post( "https://api.botpoison.com/classify", headers={"Authorization": f"Bearer {SECRET_KEY}"}, json={ "content": request.form.to_dict(), "model": "standard", }, timeout=8, ) response.raise_for_status() # 2. Read the label if response.json()["label"] == "spam": # Drop it, or file it away return "", 202 # ...package mainimport ( "bytes" "encoding/json" "net/http" "os" "time")// The secret key comes from your server's environment:// BOTPOISON_CLASSIFY_SECRET_KEY=sk_xxxxxxxxvar secretKey = os.Getenv("BOTPOISON_CLASSIFY_SECRET_KEY")var client = &http.Client{Timeout: 8 * time.Second}func contact(w http.ResponseWriter, r *http.Request) { // 1. Send the whole form to Classify r.ParseForm() content := map[string]string{} for key := range r.PostForm { content[key] = r.PostForm.Get(key) } body, _ := json.Marshal(map[string]any{ "content": content, "model": "standard", }) req, _ := http.NewRequest("POST", "https://api.botpoison.com/classify", bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+secretKey) res, err := client.Do(req) if err != nil || res.StatusCode != http.StatusOK { http.Error(w, "", http.StatusBadGateway) return } defer res.Body.Close() // 2. Read the label var result struct{ Label string `json:"label"` } json.NewDecoder(res.Body).Decode(&result) if result.Label == "spam" { // Drop it, or file it away w.WriteHeader(http.StatusAccepted) return } // ...}use axum::{extract::Form, http::StatusCode};use serde::Deserialize;use serde_json::json;use std::{collections::HashMap, time::Duration};#[derive(Deserialize)]struct Verdict { label: String,}// The secret key comes from your server's environment:// BOTPOISON_CLASSIFY_SECRET_KEY=sk_xxxxxxxxasync fn contact(Form(form): Form<HashMap<String, String>>) -> StatusCode { let secret_key = std::env::var("BOTPOISON_CLASSIFY_SECRET_KEY").unwrap_or_default(); // 1. Send the whole form to Classify let response = reqwest::Client::new() .post("https://api.botpoison.com/classify") .bearer_auth(secret_key) .timeout(Duration::from_secs(8)) .json(&json!({ "content": form, "model": "standard" })) .send() .await .and_then(|response| response.error_for_status()); let Ok(response) = response else { return StatusCode::BAD_GATEWAY; }; // 2. Read the label let Ok(verdict) = response.json::<Verdict>().await else { return StatusCode::BAD_GATEWAY; }; if verdict.label == "spam" { // Drop it, or file it away return StatusCode::ACCEPTED; } // ... StatusCode::OK}import com.fasterxml.jackson.databind.ObjectMapper;import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.time.Duration;import java.util.Map;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.server.ResponseStatusException;@RestControllerpublic class ContactController { // The secret key comes from your server's environment: // BOTPOISON_CLASSIFY_SECRET_KEY=sk_xxxxxxxx private static final String SECRET_KEY = System.getenv("BOTPOISON_CLASSIFY_SECRET_KEY"); private final HttpClient client = HttpClient.newHttpClient(); private final ObjectMapper mapper = new ObjectMapper(); @PostMapping("/contact") public ResponseEntity<Void> contact(@RequestParam Map<String, String> form) throws Exception { // 1. Send the whole form to Classify String body = mapper.writeValueAsString(Map.of("content", form, "model", "standard")); HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.botpoison.com/classify")) .header("Content-Type", "application/json") .header("Authorization", "Bearer " + SECRET_KEY) .timeout(Duration.ofSeconds(8)) .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() != 200) { throw new ResponseStatusException(HttpStatus.BAD_GATEWAY); } // 2. Read the label if ("spam".equals(mapper.readTree(response.body()).path("label").asText())) { // Drop it, or file it away return ResponseEntity.accepted().build(); } // ... return ResponseEntity.ok().build(); }}curl --request POST \ --url 'https://api.botpoison.com/classify' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --header 'authorization: Bearer sk_xxxxxxxx' \ --data '{ "content": { "name": "Jo", "email": "jo@example.com", "message": "Hello, could you send me a quote?" }, "model": "standard" }'In Node.js, classify() needs @botpoison/node 0.2.0 or later. Its options, its errors and a version without the library are in the @botpoison/node reference. Every other language posts to the REST API.
02Read the result
{ "label": "clean", "score": 0.04, "model": "standard-2026-10-10"}- SpamDrop it, or file it away.
- CleanDeliver it as usual.
Act on label. It is Botpoison's recommended cut on the score, spam from 0.8, so the two never disagree. To hold borderline messages for a person, or to draw your own line, use score. See Labels and scores.
Send the secret key test to try every path without real spam. Content containing botpoison-test-spam returns spam, botpoison-test-review returns clean with a score of 0.6, and botpoison-test-unavailable returns a 503. Test calls never count toward your plan. See Classify test mode.