Skip to content

PHP Server SDK

Official PHP server-side SDK — published as captchala/captchala-php on Packagist.

Three jobs the SDK handles for you:

  1. Validate — verify a pt_ pass token from the browser SDK.
  2. Issue — mint a one-time sct_ server token to bind the upcoming challenge to a specific action / IP / UID.
  3. Moderate — multi-modal (text + image) content moderation against the same OpenAI-compatible pipeline the dashboard uses.

Install

bash
composer require captchala/captchala-php

Requires PHP ≥ 8.0 with ext-json. Uses cURL when available, falls back to file_get_contents.

Validate (pt_ token)

php
use Captchala\Client;

$client = new Client('your_app_key', 'your_app_secret');
$result = $client->validate($_POST['captcha_token']);

if (!$result->isValid()) {
    http_response_code(400);
    exit($result->getError());   // e.g. "token_expired", "client_ip_mismatch"
}
// Verification passed; proceed with the request.

If you issued the original server_token with bind_uid, compare the uid returned with the user you actually expect:

php
if ($result->getUid() !== $expectedUserId) {
    http_response_code(400);
    exit('user mismatch');
}

If you bound the token to an IP, pass the user IP on the verify side too:

php
$client->validate($token, false, $request->ip());

Issue a server token

For high-value flows (login, register, payment) the recommended pattern is: backend mints a one-time sct_ token, hands it to the browser, browser passes it as the serverToken prop. Each token is single-use, action-scoped, and optionally bound to IP / UID at issuance time.

php
$issue = $client->issueServerToken(
    action:    'login',
    bindingIp: $request->ip(),
    ttl:       300,         // seconds; default 300
    maxUses:   5,           // SDK retry budget
    bindUid:   $user->id,   // pair with ValidateResult::getUid() on verify
);

if (!$issue->isOk()) {
    return ['error' => $issue->getError()];   // rate_limit_exceeded, invalid_action, ...
}

return ['server_token' => $issue->getToken()];  // hand to browser

Moderate content

Multi-modal — accepts a mix of text and image URLs in OpenAI-compatible format:

php
$result = $client->moderationCheck([
    ['type' => 'text', 'text' => $userComment],
    ['type' => 'image_url', 'image_url' => ['url' => $uploadedImageUrl]],
], userId: $user->id);

if (!$result->isOk()) {
    // request error: invalid_credentials, no_content, transport failure, ...
    return ['error' => $result->getError()];
}

if ($result->isFlagged()) {
    // upstream model flagged; inspect categories for the why
    if ($result->hasCategory('violence', 'csam')) {
        // hard block
    }
}

Plain-text shortcut:

php
$result = $client->moderationText('user comment here', userId: $user->id);

Categories are model-defined (e.g. violence, hate, sexual, self-harm); iterate getCategories() defensively rather than hard-coding a fixed set.

Result classes

ClassMethods
ValidateResultisValid(), getError(), getUid(), getChallengeId(), getAction(), isOffline(), isClientOnly(), getWarning(), toArray()
IssueResultisOk(), getToken(), getExpiresIn(), getIssuedAt(), getError(), getMessage(), toArray()
ModerationResultisOk(), isFlagged(), hasCategory(...$names), getCategories(), getContentType(), getRaw(), getError(), getMessage(), toArray()

MIT-licensed examples · CaptchaLa is operated independently