Node.js Core
Install
1
npm i @botbye/node-core
1
yarn add @botbye/node-core
Configuration
Anti-phishing is identified by its own clientKey (available in your Phishing Project in the Dashboard), not the server key used by evaluate, so it is configured and wired up separately, via phishingModuleApiFactory.
1
2
3
4
5
6
7
8
9
10
11
import { phishingModuleApiFactory } from "@botbye/node-core";
import { nodePhishingHttpClient } from "@botbye/node-core/phishing-node-http-client";
const phishing = phishingModuleApiFactory({
httpClient: nodePhishingHttpClient,
});
phishing.init({
// clientKey from your Phishing Project on the Admin Dashboard
clientKey: "00000000-0000-0000-0000-000000000000",
});
Call phishing.init once at application startup, before serving any catcher requests.
phishingModuleApiFactory options
| Option | Type | Required | Description |
|---|---|---|---|
| httpClient | TPhishingHttpClient | Yes | HTTP client used for catcher calls. See [Phishing HTTP clients](#phishing-http-clients) below. |
| catcherRequestInfoExtractor | (request: R, global: TPhishingGlobalOptions) => TPhishingCatcherRequestInfo | No | Converts a custom request object into { headers }, enabling { request: R } in fetchCatcher. See [Building a custom integration](#building-a-custom-integration) below. |
| url | string | No | Override BotBye API endpoint. Can also be set via phishing.init. |
Phishing HTTP clients
Phishing calls use their own HTTP client interface (TPhishingHttpClient), separate from evaluate's THttpClient — the two are not interchangeable. Two built-in clients are available:
| Import path | When to use |
|---|---|
| @botbye/node-core/phishing-node-http-client | Standard Node.js environments (uses built-in http/https) |
| @botbye/node-core/phishing-fetch-http-client | Runtimes with the Fetch API (Deno, Bun, edge runtimes) |
phishing.init options
| Option | Type | Required | Description |
|---|---|---|---|
| clientKey | string | Yes | clientKey from your Phishing Project on the Admin Dashboard |
| url | string | No | Override BotBye API endpoint (default: https://verify.botbye.com) |
| logger.level | string | No | Log level: error, warn, info, debug, log (default: info) |
| logger.logger | TLogger | No | Custom logger implementing { error, warn, info, debug, log } |
| timeouts.fetchCatcher | number | No | Timeout in milliseconds for each fetchCatcher call |
Usage
Anti-phishing needs two routes on your own origin, each proxied through fetchCatcher:
SVG route — serves the SVG catcher. This is the URL your client code passes to getCatcher({ url }).
PNG route — serves the PNG that the SVG references (via innerPngUrl).
Without catcherRequestInfoExtractor, pass headers explicitly instead of a request object. For the SVG, innerPngUrl must be the absolute URL of your PNG route — the browser loads that PNG directly from your origin.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Absolute URL of your PNG endpoint — the SVG catcher references it through innerPngUrl.
const PNG_CATCHER_URL = "https://your-site.example/botbye-catcher.png";
async function serveSvgCatcher(headers) {
const catcher = await phishing.fetchCatcher({
headers,
format: "svg",
innerPngUrl: PNG_CATCHER_URL,
});
return { status: catcher.status, headers: catcher.headers, body: catcher.body };
}
async function servePngCatcher(headers) {
const catcher = await phishing.fetchCatcher({ headers, format: "png" });
return { status: catcher.status, headers: catcher.headers, body: catcher.body };
}
fetchCatcher always returns a Promise<TUpstreamFetchCatcherResult>:
1
2
3
4
5
6
type TUpstreamFetchCatcherResult = {
status: number;
headers: Record<string, string>;
body: Uint8Array;
error?: { message: string };
};
Relay status, headers, and body as-is in your response — the exact call depends on your runtime (res.writeHead/res.end, new Response(...), etc.).
We recommend embedding the SVG catcher: it is designed to keep tracking even when a phishing site copies all of your assets to its own infrastructure (the PNG route exists because the SVG catcher relies on it).
Building a custom integration
Just like requestInfoExtractor for evaluate, catcherRequestInfoExtractor lets fetchCatcher accept a framework's native request object directly — { request: R } — instead of { headers }.
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 { phishingModuleApiFactory } from "@botbye/node-core";
import { nodePhishingHttpClient } from "@botbye/node-core/phishing-node-http-client";
import type { Request } from "express";
const phishing = phishingModuleApiFactory<Request>({
httpClient: nodePhishingHttpClient,
catcherRequestInfoExtractor: (request) => ({
headers: request.headers as Record<string, string>,
}),
});
phishing.init({
clientKey: "00000000-0000-0000-0000-000000000000",
});
// Now fetchCatcher accepts an Express Request directly
app.get("/botbye-catcher.svg", async (req, res) => {
const catcher = await phishing.fetchCatcher({
request: req,
format: "svg",
innerPngUrl: PNG_CATCHER_URL,
});
res.status(catcher.status).set(catcher.headers).send(Buffer.from(catcher.body));
});
Multiple instances
Use phishingModuleApiFactory 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 { phishingModuleApiFactory } from "@botbye/node-core";
import { nodePhishingHttpClient } from "@botbye/node-core/phishing-node-http-client";
const phishingA = phishingModuleApiFactory({ httpClient: nodePhishingHttpClient });
const phishingB = phishingModuleApiFactory({ httpClient: nodePhishingHttpClient });
phishingA.init({
clientKey: "00000000-0000-0000-0000-000000000000",
});
phishingB.init({
clientKey: "11111111-1111-1111-1111-111111111111",
});