Core

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",
});