Getting started

Integration guide

Platform-by-platform setup instructions for Next.js, Express, Laravel, and more.


This guide walks through verification for the most common server environments. All examples assume you have already added the client snippet and that the request carries a token, either from botardo.token() or from form protection. The server side is the same either way.

Next.js (App Router)

ts
// app/api/login/route.ts
import { sentinel } from "@botardo/server";
import { NextResponse } from "next/server";

export async function POST(req: Request) {
  const token = req.headers.get("x-bot-token") ?? "";
  const { decision } = await sentinel.verify(token);

  if (decision === "block") {
    return NextResponse.json({ error: "blocked" }, { status: 403 });
  }

  // handle the login...
  return NextResponse.json({ ok: true });
}

There's no challenge branch on the server. When we can't tell whether a session is a person, we challenge the visitor in the browser before the token exists. See when we can't tell yet.

Express / Node.js

ts
import express from "express";
import { sentinel } from "@botardo/server";

const app = express();
app.use(express.json());

app.post("/api/signup", async (req, res) => {
  const token = req.headers["x-bot-token"] as string ?? "";
  const { decision } = await sentinel.verify(token);

  if (decision === "block") return res.status(403).json({ error: "blocked" });
  // process...
  res.json({ ok: true });
});

PHP / Laravel

php
use Botardo\Sentinel;

Route::post('/checkout', function (Request $request) {
    $result = Sentinel::verify($request->header('x-bot-token'));

    if ($result->decision === 'block') {
        abort(403);
    }

    // process...
});
**Note:** The PHP SDK is available via Composer: `composer require botardo/server`.

Middleware for many routes

If most of your API should be scored, verify once in middleware and attach the result to the request:

ts
app.use("/api", async (req, res, next) => {
  const token = req.headers["x-bot-token"] as string ?? "";
  req.bot = await sentinel.verify(token);
  next();
});

Routes then reject when req.bot.decision is "block". A sensitive route, a payout rather than a search endpoint, can also read req.bot.risk to add extra care, such as an email check. See using risk.

Token location

Requests you make yourself carry the token wherever you put it; the header x-bot-token is the convention the SDKs read by default.

Form protection sends the token as the x-bot-token header for fetch and XHR submits. For a plain HTML <form method="post"> there is no header to set, so it injects a hidden field named _bt instead. Read whichever is present:

ts
const token = req.body["_bt"] ?? req.headers["x-bot-token"] ?? "";