Ktor

Ktor

Installation

Add the dependency:

1
2
3
4
5
<dependency>
    <groupId>com.botbye</groupId>
    <artifactId>kotlin-module</artifactId>
    <version>3.0.1</version>
</dependency>
1
implementation("com.botbye:kotlin-module:3.0.1")

Configuration

Phishing lives in its own dedicated BotbyePhishingClient, separate from the evaluate Botbye client. It is identified by a public, browser-safe clientKey, so it needs no server key — create it once at startup and reuse it across requests. On construction it makes a one-off, best-effort server-integration init handshake that reports this server-side integration to BotBye; it is non-blocking and never affects your request path.

BotbyePhishingConfig takes 2 values: endpoint (optional, defaults to https://verify.botbye.com) and clientKey.

Getting clientKey

clientKey is the public, browser-safe identifier of your phishing project. It travels in the asset URL path, so it is safe to expose — no secret token and no Base64 encoding are required.

Find it on the Get Started screen of your phishing project in the BotBye dashboard.

1
2
3
4
5
6
7
8
9
import com.botbye.phishing.BotbyePhishingClient
import com.botbye.phishing.BotbyePhishingConfig

val phishing = BotbyePhishingClient(
    BotbyePhishingConfig(
        endpoint = "https://verify.botbye.com",
        clientKey = "<public-client-key>",
    )
)

Usage

Expose a single Ktor route that proxies phishing pixel requests. fetchImage is a suspend function, so it can be called directly inside route handlers.

When you use an SVG image, your application must also expose an endpoint on the client origin that handles requests to /{imageId}.png, where imageId is the value that arrives in the forwarded pixel query as image_id.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
routing {
    get("/api/phishing/image") {
        val origin = call.request.header("Origin")
        val query = call.request.queryParameters.entries()
            .associate { (k, v) -> k to v.first() }

        val response = phishing.fetchImage(origin = origin, query = query)

        if (response.error != null) {
            call.respondText(response.error?.message ?: "upstream error", status = HttpStatusCode.BadGateway)
            return@get
        }

        val contentType = response.headers["Content-Type"] ?: "image/png"
        call.response.header(HttpHeaders.ContentType, contentType)
        call.respondBytes(bytes = response.body, status = HttpStatusCode.fromValue(response.status))
    }
}

Settings

Configuration parameters for phishing integration:

Setting Description Required Default Value
endpoint Host of the phishing API no https://verify.botbye.com
clientKey Public client-key of your phishing project yes -