BotBye! integration for Node.js HTTP applications.
Install
1
npm i @botbye/node-http
1
yarn add @botbye/node-http
No peer dependencies — uses the built-in Node.js http module.
Configuration
Call init once at application startup, before handling any requests:
1
2
3
4
5
6
import { init } from "@botbye/node-http";
init({
// Use your project server-key
serverKey: "00000000-0000-0000-0000-000000000000",
});
init options
| Option | Type | Required | Description |
|---|---|---|---|
| serverKey | string | Yes | Server key from your BotBye project |
| url | string | No | Override BotBye API endpoint (default: https://verify.botbye.com) |
| logger.level | "error" "warn" "info" "debug" "log" | No | Log level (default: "info") |
| logger.logger | TLogger | No | Custom logger instance implementing { error, warn, info, debug, log } |
| timeouts.evaluate | number | No | Timeout in milliseconds for each evaluate call |
Usage
Call evaluate in request handlers where bot protection is needed.
It accepts an event object describing what you know about the request and the context around it, and returns a promise that resolves to a decision.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { evaluate } from "@botbye/node-http";
const server = http.createServer(async (req, res) => {
const result = await evaluate({
type: "validate",
request: {
request: req,
// "x-botbye-token" is an example — pass the token from wherever you store it
token: req.headers["x-botbye-token"],
},
});
if (result.decision === "BLOCK") {
res.writeHead(403, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Forbidden" }));
return;
}
// proceed normally
});
There are three event types — validate, risk, and full — each suited for a different layer of your application.
validate — edge-level bot check
Use at the edge — server request handler, router middleware — when you just want to know: was this request made by a bot? No user or domain context needed.
Event fields:
1
2
3
4
5
6
7
8
9
10
11
12
{
type: "validate";
request:
// Option A: pass the http.IncomingMessage object directly — SDK extracts everything automatically
| { request: http.IncomingMessage; token?: string | null }
// Option B: construct request info manually
| { ip: string; headers: Record<string, string>; requestMethod?: string | null; requestUri?: string | null; token?: string | null };
customFields?: Record<string, string>;
}
The SDK extracts IP, headers, method, and URI from the http.IncomingMessage object automatically. You can also pass request info manually — see Option B above. The token is a one-time token generated by the BotBye client-side SDK that contains information about the user's device. Pass whatever the client sent; if no token is received, the decision will be "BLOCK".
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { evaluate } from "@botbye/node-http";
const server = http.createServer(async (req, res) => {
const result = await evaluate({
type: "validate",
request: {
request: req,
// "x-botbye-token" is an example — pass the token from wherever you store it
token: req.headers["x-botbye-token"],
},
});
if (result.decision === "BLOCK") {
res.writeHead(403, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Forbidden" }));
return;
}
// proceed normally
});
risk — domain-level risk scoring
Use inside services that already know the user: auth, payments, account management, etc. The purpose shifts from "is this a bot?" to "is something suspicious happening for this user?" — credential stuffing, account takeover, account sharing, logins from a new geo.
Event fields:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
type: "risk";
request:
// Only ip is needed at this level
| { ip: string; headers?: Record<string, string>; requestMethod?: string | null; requestUri?: string | null; token?: string | null }
// http.IncomingMessage also accepted if convenient
| { request: http.IncomingMessage };
event: {
type: string; // e.g. "login", "password_change", "checkout"
status: "ATTEMPTED" | "SUCCESSFUL" | "FAILED" | "UNKNOWN";
};
user: {
accountId: string;
username?: string | null;
email?: string | null;
phone?: string | null;
};
customFields?: Record<string, string>;
botbyeResult?: string; // if a validate call was made earlier, pass its result.botbyeResult here to link the requests; omit if there was no prior validate
}
event and user are the key fields here — they define what action is being performed and who is performing it, which is what drives the risk score. ip is equally important: BotBye tracks which IPs access the account to detect patterns like account sharing, credential stuffing, and suspicious geo logins. Pass it directly as { ip }, or pass the Node.js request object if that's more convenient.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { evaluate } from "@botbye/node-http";
// Inside an auth service, after a login attempt
async function onLoginAttempt({ ip, userId, email, loginSucceeded }) {
const result = await evaluate({
type: "risk",
request: {
ip,
},
event: {
type: "login",
status: loginSucceeded ? "SUCCESSFUL" : "FAILED",
},
user: {
accountId: userId,
email,
},
});
if (result.decision === "BLOCK") {
// Lock account, trigger MFA, send alert, etc.
}
}
Linking validate and risk events
When the same request is evaluated at two layers — for example, once at the edge (type: "validate") and then again inside a domain service (type: "risk") — BotBye can link both events and display them as a single event in the dashboard.
Step 1 — edge layer (HTTP server, gateway, or router): run validate and capture botbye_result:
1
2
3
4
5
6
7
8
9
10
const edgeResult = await evaluate({
type: "validate",
request: {
request: req,
// "x-botbye-token" is an example — pass the token from wherever you store it
token: req.headers["x-botbye-token"],
},
});
const edgeBotbyeResult = edgeResult.botbye_result;
// Pass edgeBotbyeResult downstream — a request header, function argument, shared context, etc.
Step 2 — domain service (auth, payment, account management): pass it as botbyeResult in the risk call:
1
2
3
4
5
6
7
8
9
10
11
12
13
const riskResult = await evaluate({
type: "risk",
request: { ip },
event: {
type: "login",
status: loginSucceeded ? "SUCCESSFUL" : "FAILED",
},
user: {
accountId: userId,
email,
},
botbyeResult: edgeBotbyeResult,
});
botbye_result is optional in the response — if it is absent, omit botbyeResult and the events will be recorded independently.
full — edge check and domain scoring in one call
Use when you have all context at once: raw request, token, user, and event. A login endpoint is a typical example — it receives the HTTP request and immediately knows the user and outcome.
Event fields:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
type: "full";
request:
| { request: http.IncomingMessage; token?: string | null }
| { ip: string; headers: Record<string, string>; requestMethod?: string | null; requestUri?: string | null; token?: string | null };
event: {
type: string;
status: "ATTEMPTED" | "SUCCESSFUL" | "FAILED" | "UNKNOWN";
};
user: {
accountId: string;
username?: string | null;
email?: string | null;
phone?: string | null;
};
customFields?: Record<string, string>;
}
Equivalent to running validate and risk in a single call.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { evaluate } from "@botbye/node-http";
const server = http.createServer(async (req, res) => {
// parse body, authenticate, etc.
const { email, password } = await parseBody(req);
const user = await findUser(email);
const loginSucceeded = user && (await checkPassword(user, password));
const result = await evaluate({
type: "full",
request: {
request: req,
token: req.headers["x-botbye-token"], // example header — pass the token from wherever you store it
},
event: {
type: "login",
status: loginSucceeded ? "SUCCESSFUL" : "FAILED",
},
user: {
accountId: user?.id ?? "unknown",
email,
},
});
if (result.decision === "BLOCK") {
res.writeHead(403, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Forbidden" }));
return;
}
// proceed normally
});
Response
evaluate always returns a Promise<TEvaluationResult>:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
type TEvaluationResult =
| {
decision: "ALLOW" | "BLOCK" | "CHALLENGE";
request_id: string;
risk_score: number;
scores: Record<string, number>;
signals: string[];
botbye_result?: string;
}
| {
decision: "ALLOW";
botbye_result?: string;
error: { message: string };
};
Check result.decision to decide how to handle the request:
- "ALLOW" — request appears legitimate, proceed normally
- "BLOCK" — bot or suspicious activity detected, block the request
- "CHALLENGE" — uncertain, consider issuing a CAPTCHA, MFA, or additional verification step
When the response contains an error field, BotBye could not evaluate the request (e.g. invalid server key). In that case decision defaults to "ALLOW" so that a misconfiguration does not block real users — but you should monitor and fix the underlying error.
Examples of BotBye API responses
Blocked (bot detected):
1
2
3
4
5
6
7
{
"request_id": "f77b2abd-c5d7-44f0-be4f-174b04876583",
"decision": "BLOCK",
"risk_score": 0.95,
"scores": { "bot": 0.95 },
"signals": ["AutomationTool"]
}
Allowed:
1
2
3
4
5
6
7
{
"request_id": "f77b2abd-c5d7-44f0-be4f-174b04876583",
"decision": "ALLOW",
"risk_score": 0.05,
"scores": { "bot": 0.05, "ato": 0.02 },
"signals": []
}
Challenge:
1
2
3
4
5
6
7
8
{
"request_id": "f77b2abd-c5d7-44f0-be4f-174b04876583",
"decision": "CHALLENGE",
"risk_score": 0.65,
"scores": { "bot": 0.65 },
"signals": ["SuspiciousFingerprint"],
"challenge": { "type": "captcha", "token": "..." }
}
Invalid server-key:
1
2
3
4
{
"decision": "ALLOW",
"error": { "message": "[BotBye] Bad Request: Invalid Server Key" }
}
Middleware usage
You can extract bot protection into a reusable helper and apply it before routing logic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { evaluate } from "@botbye/node-http";
async function botProtection(req, res) {
const result = await evaluate({
type: "validate",
request: {
request: req,
token: req.headers["x-botbye-token"], // example header — pass the token from wherever you store it
},
});
if (result.decision === "BLOCK") {
res.writeHead(403, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Forbidden" }));
return false;
}
return true;
}
const server = http.createServer(async (req, res) => {
if (!(await botProtection(req, res))) return;
// handle request
});
Advanced: multiple instances
Use factory to create independent SDK instances (useful when protecting multiple projects from one service):
1
2
3
4
5
6
7
8
9
10
11
12
13
import { factory } from "@botbye/node-http";
const sdk = factory();
sdk.init({
// Use your project server-key
serverKey: "00000000-0000-0000-0000-000000000000",
});
const result = await sdk.evaluate({
type: "validate",
request: { request: req, token },
});
Dev utilities
1
2
3
import { dev } from "@botbye/node-http";
dev.setLoggerLevel("debug"); // "error" | "warn" | "info" | "debug" | "log"