PHP SDK
PHP SDK

Introduction

The Botbye PHP SDK provides comprehensive bot protection for PHP applications. Built with modern PHP standards (PSR-18/PSR-17), it works with any HTTP client — Guzzle, Symfony HttpClient, Buzz, and others.

Requirements

  • PHP 8.1 or higher
  • Composer
  • Any PSR-18 compatible HTTP client

Installation

Install the SDK via Composer:

1
composer require botbye/botbye-php-sdk

You also need a PSR-18 HTTP client and PSR-17 factories. Install one of the ready-made implementations:

Guzzle (most common):

1
composer require guzzlehttp/guzzle

Symfony HttpClient:

1
composer require symfony/http-client nyholm/psr7

Or write a custom adapter for your HTTP transport (e.g. WordPress wp_remote_request) — in that case you only need a PSR-17 factory:

1
composer require nyholm/psr7

Configuration

Basic Configuration

Initialize the Botbye client with your server-key (available inside your Project) and a PSR-18 HTTP client:

With Guzzle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
use Botbye\Client\BotbyeClient;
use Botbye\Client\BotbyeConfig;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;

$config = new BotbyeConfig(
    serverKey: '00000000-0000-0000-0000-000000000000' // Use your project server-key
);

$httpClient = new Client(['timeout' => 2.0]);
$psr17Factory = new HttpFactory();

$client = new BotbyeClient(
    config: $config,
    httpClient: $httpClient,
    requestFactory: $psr17Factory,
    streamFactory: $psr17Factory,
);

With Symfony HttpClient:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
use Botbye\Client\BotbyeClient;
use Botbye\Client\BotbyeConfig;
use Symfony\Component\HttpClient\Psr18Client;
use Nyholm\Psr7\Factory\Psr17Factory;

$config = new BotbyeConfig(
    serverKey: '00000000-0000-0000-0000-000000000000' // Use your project server-key
);

$httpClient = new Psr18Client();
$psr17Factory = new Psr17Factory();

$client = new BotbyeClient(
    config: $config,
    httpClient: $httpClient,
    requestFactory: $psr17Factory,
    streamFactory: $psr17Factory,
);

Custom adapter (e.g. WordPress wp_remote_request):

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php
use Botbye\Client\BotbyeClient;
use Botbye\Client\BotbyeConfig;
use Nyholm\Psr7\Factory\Psr17Factory;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

// Wrap any HTTP transport as a PSR-18 client
class WpHttpClient implements ClientInterface
{
    public function sendRequest(RequestInterface $request): ResponseInterface
    {
        $response = wp_remote_request((string) $request->getUri(), [
            'method'  => $request->getMethod(),
            'headers' => array_map(
                fn(array $v) => implode(', ', $v),
                $request->getHeaders(),
            ),
            'body'    => (string) $request->getBody(),
            'timeout' => 2,
        ]);

        if (is_wp_error($response)) {
            throw new \RuntimeException($response->get_error_message());
        }

        $psr17 = new Psr17Factory();
        $psrResponse = $psr17->createResponse(
            wp_remote_retrieve_response_code($response),
        );

        return $psrResponse->withBody(
            $psr17->createStream(wp_remote_retrieve_body($response)),
        );
    }
}

$config = new BotbyeConfig(
    serverKey: '00000000-0000-0000-0000-000000000000' // Use your project server-key
);

$psr17Factory = new Psr17Factory();

$client = new BotbyeClient(
    config: $config,
    httpClient: new WpHttpClient(),
    requestFactory: $psr17Factory,
    streamFactory: $psr17Factory,
);

Timeouts

Timeouts are configured on the HTTP client you provide:

1
2
3
4
5
6
7
8
9
<?php
// Guzzle
$httpClient = new \GuzzleHttp\Client(['timeout' => 2.0, 'connect_timeout' => 1.0]);

// Symfony HttpClient
$httpClient = new Psr18Client(\Symfony\Component\HttpClient\HttpClient::create([
    'timeout' => 2.0,
    'max_duration' => 3.0,
]));

PSR-3 Logger Integration

Integrate with any PSR-3 compatible logger (e.g., Monolog):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
use Botbye\Client\BotbyeClient;
use Botbye\Client\BotbyeConfig;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// Use your project server-key
$config = new BotbyeConfig(serverKey: '00000000-0000-0000-0000-000000000000');

$httpClient = new Client(['timeout' => 2.0]);
$factory = new HttpFactory();

$logger = new Logger('botbye');
$logger->pushHandler(new StreamHandler('/var/log/botbye.log', Logger::WARNING));

$client = new BotbyeClient(
    config: $config,
    httpClient: $httpClient,
    requestFactory: $factory,
    streamFactory: $factory,
    logger: $logger,
);

Usage

Evaluate incoming requests to detect bot activity:

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
<?php
use Botbye\Model\BotbyeValidationEvent;
use Botbye\Model\Headers;

$headers = Headers::fromArray(getallheaders());

// Extract the token from wherever you pass it: query param, header, body, etc.
$token = $_GET['botbye_token'] ?? '';

$response = $client->evaluate(new BotbyeValidationEvent(
    ip: $_SERVER['REMOTE_ADDR'],
    token: $token,
    headers: $headers->jsonSerialize(),
    requestMethod: $_SERVER['REQUEST_METHOD'],
    requestUri: $_SERVER['REQUEST_URI'],
));

if ($response->isBlocked()) {
    http_response_code(403);
    echo 'Access denied';
    exit;
}

// Request is allowed, continue processing
echo 'Welcome!';

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 — API gateway, route handler, middleware — when you just want to know: was this request made by a bot? No user or domain context needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
use Botbye\Model\BotbyeValidationEvent;
use Botbye\Model\Headers;

$headers = Headers::fromArray(getallheaders());

// Extract the token from wherever you pass it: query param, header, body, etc.
$token = $_GET['botbye_token'] ?? '';

$response = $client->evaluate(new BotbyeValidationEvent(
    ip: $_SERVER['REMOTE_ADDR'],
    token: $token,
    headers: $headers->jsonSerialize(),
    requestMethod: $_SERVER['REQUEST_METHOD'],
    requestUri: $_SERVER['REQUEST_URI'],
));

if ($response->isBlocked()) {
    http_response_code(403);
    echo 'Access denied';
    exit;
}

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

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
<?php
use Botbye\Model\BotbyeRiskScoringEvent;
use Botbye\Model\BotbyeUserInfo;
use Botbye\Model\EventStatus;
use Botbye\Model\Headers;

$headers = Headers::fromArray(getallheaders());

$response = $client->evaluate(new BotbyeRiskScoringEvent(
    ip: $_SERVER['REMOTE_ADDR'],
    headers: $headers->jsonSerialize(),
    user: new BotbyeUserInfo(
        accountId: $userId,
        username: $username,
        email: $email,
        phone: $phone,
    ),
    eventType: 'login',
    eventStatus: $loginSucceeded ? EventStatus::SUCCESSFUL : EventStatus::FAILED,
    botbyeResult: $previousBotbyeResult ?? null,
));

if ($response->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 (validate) and then again inside a domain service (risk) — BotBye can link both events and display them as a single event in the dashboard.

Step 1 — edge layer (gateway, middleware, or entry point): run validate and capture the result:

1
2
3
4
5
6
7
8
9
10
11
<?php
// e.g. in a front controller or request handler
$edgeResponse = $client->evaluate(new BotbyeValidationEvent(
    ip: $_SERVER['REMOTE_ADDR'],
    token: $token,
    headers: $headers->jsonSerialize(),
    requestMethod: $_SERVER['REQUEST_METHOD'],
    requestUri: $_SERVER['REQUEST_URI'],
));
$edgeBotbyeResult = $edgeResponse->botbyeResult;
// Pass $edgeBotbyeResult downstream — a request variable, 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
<?php
// e.g. in an auth service after login attempt
$riskResponse = $client->evaluate(new BotbyeRiskScoringEvent(
    ip: $_SERVER['REMOTE_ADDR'],
    headers: $headers->jsonSerialize(),
    user: new BotbyeUserInfo(accountId: $userId, email: $email),
    eventType: 'login',
    eventStatus: $loginSucceeded ? EventStatus::SUCCESSFUL : EventStatus::FAILED,
    botbyeResult: $edgeBotbyeResult,
));

botbyeResult is null when absent — in that case, omit or pass null 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.

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
<?php
use Botbye\Model\BotbyeFullEvent;
use Botbye\Model\BotbyeUserInfo;
use Botbye\Model\EventStatus;
use Botbye\Model\Headers;

$headers = Headers::fromArray(getallheaders());

$token = $_GET['botbye_token'] ?? '';

$response = $client->evaluate(new BotbyeFullEvent(
    ip: $_SERVER['REMOTE_ADDR'],
    token: $token,
    headers: $headers->jsonSerialize(),
    requestMethod: $_SERVER['REQUEST_METHOD'],
    requestUri: $_SERVER['REQUEST_URI'],
    user: new BotbyeUserInfo(
        accountId: $userId,
        username: $username,
        email: $email,
        phone: $phone,
    ),
    eventType: 'login',
    eventStatus: $loginSucceeded ? EventStatus::SUCCESSFUL : EventStatus::FAILED,
));

if ($response->isBlocked()) {
    http_response_code(403);
    echo 'Access denied';
    exit;
}

Methods Reference

BotbyeClient

evaluate()

Evaluates an incoming request for bot activity.

Parameters:

  • event (BotbyeValidationEvent|BotbyeRiskScoringEvent|BotbyeFullEvent) - The event describing the request

BotbyeValidationEvent Properties:

  • ip (string) - Client IP address
  • token (string) - The Botbye token from the client
  • headers (array) - Request headers (use Headers::fromArray()->jsonSerialize())
  • requestMethod (string) - HTTP method (GET, POST, etc.)
  • requestUri (string) - Request URI

Returns: BotbyeEvaluateResponse

BotbyeEvaluateResponse

Represents the evaluation result.

Properties:

  • decision (Decision) - The decision: Decision::ALLOW, Decision::CHALLENGE, or Decision::BLOCK
  • riskScore (float) - Risk score from 0.0 to 1.0
  • signals (array) - List of detected signals
  • scores (array) - Detailed scores by category

Methods:

  • isBlocked() (bool) - Returns true if the decision is BLOCK

Headers

Represents HTTP headers.

Static Methods:

  • fromArray(array $headers) - Create Headers from an associative array

Error Handling

Error Handling Best Practices:

1. Fail Open - If Botbye service is unavailable, allow requests to proceed

2. Log Errors - Always log errors for monitoring and debugging

3. Graceful Degradation - Handle API errors without breaking user experience

4. Timeout Configuration - Set appropriate timeouts on your HTTP client to avoid blocking requests

Best Practices

Production Usage

1. Environment Variables - Store server keys in environment variables:

1
2
3
4
<?php
$config = new BotbyeConfig(
    serverKey: $_ENV['BOTBYE_SERVER_KEY']
);

Performance

1. Connection Pooling - Use default connection pool settings for optimal performance

2. Timeouts - Keep timeouts low (1-2 seconds) on your HTTP client to avoid blocking requests

3. Selective Protection - Only evaluate critical endpoints (login, checkout, etc.)

Security

1. Rate Limiting - Implement rate limiting alongside Botbye protection (can find more about it in Project Overview

2. Custom Fields - Use custom fields to link requests with user sessions for better analysis

Settings

Configuration parameters for BotbyeConfig:

Setting Description Required Default Value
serverKey Your BotBye server-key yes -
botbyeEndpoint Host of the API Server no https://verify.botbye.com
contentType Content type for requests no application/json

Demo

GitHub Repository - Full SDK source code with examples and tests.

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" }
}