This file describes how to integrate protection service [BotBye!](https://botbye.com) using Http

# Http

BotBye! integration for Node.js [HTTP]( https://nodejs.org/api/http.html ) applications.

## Install


```bash
npm i @botbye/node-http
```

```bash
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:

```javascript
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.



```javascript
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:**

```typescript
{
  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]( /docs/client-side/npm-module ) that contains information about the user's device. Pass whatever the client sent; if no token is received, the decision will be `"BLOCK"`.

```javascript
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:**

```typescript
{
  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.

```javascript
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`:

```javascript
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:

```javascript
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:**

```typescript
{
  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.

```javascript
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>`:

```typescript
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):

```javascript
{
  "request_id": "f77b2abd-c5d7-44f0-be4f-174b04876583",
  "decision": "BLOCK",
  "risk_score": 0.95,
  "scores": { "bot": 0.95 },
  "signals": ["AutomationTool"]
}
```

Allowed:

```javascript
{
  "request_id": "f77b2abd-c5d7-44f0-be4f-174b04876583",
  "decision": "ALLOW",
  "risk_score": 0.05,
  "scores": { "bot": 0.05, "ato": 0.02 },
  "signals": []
}
```

Challenge:

```javascript
{
  "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`:

```javascript
{
  "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:

```javascript
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):

```javascript
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

```javascript
import { dev } from "@botbye/node-http";

dev.setLoggerLevel("debug"); // "error" | "warn" | "info" | "debug" | "log"
```

There is a prompt for an LLM model that describes how to integrate BotBye! protection into a project:

```plaintext
Integrate BotBye bot protection into an application using the @botbye/node-http package.

1. Install the package:
   npm i @botbye/node-http   or   yarn add @botbye/node-http

2. Import and call init once at startup (before createServer):
   import { init } from "@botbye/node-http"
   init({ serverKey: "00000000-0000-0000-0000-000000000000" })

3. Inside your request handler, call evaluate for routes that need protection:
   import { evaluate } from "@botbye/node-http"
   http.createServer(async (req, res) => {
     const result = await evaluate({
       type: "validate",
       request: { request: req, token: req.headers["x-botbye-token"] }
     })
     if (result.decision === "BLOCK") { res.writeHead(403); res.end(JSON.stringify({ error: "Forbidden" })); return }
     // proceed with response
   })

4. There are three event types — choose based on available context:
   - "validate" — edge-level bot check (request + token only)
   - "risk"     — domain-level risk scoring (add event: { type, status } and user: { accountId, ... })
   - "full"     — both in one call (request, token, event, and user)
   result.decision is "ALLOW", "CHALLENGE", or "BLOCK".

5. evaluate options shape:
   { type: "validate" | "risk" | "full", request: { request: IncomingMessage, token?: string } | { ip, headers, ... }, event?, user?, customFields? }

Full integration guide: https://botbye.com/docs/server-side/node-js/http.md

  When the integration is complete, ask for the keys and replace the placeholder in the appropriate places.```
