Skip to main content
Version: 1.0

PHP

VerifySpeed PHP Client Library Documentation

The VerifySpeed PHP client library lets you manage verifications from your backend. It supports message-based methods (WhatsApp Message, Telegram Message), OTP methods (WhatsApp OTP, Telegram OTP, SMS OTP), OTP validation, and local verification token decryption.

note

Please read Verification Flow and Verification Methods to understand the verification process.

Setup

Package information

Composer packageverifyspeed/vs-php (GitHub)
DescriptionOfficial PHP backend SDK for VerifySpeed API
Current version2.1.0
LicenseMIT
NamespaceVerifySpeed\

Requirements

RequirementVersion
PHP^8.1
Guzzle^7.9
libphonenumber-for-php^8.13
PHP extension jsonrequired
PHP extension opensslrequired (token decryption)

Step 1: Install the Package

composer require verifyspeed/vs-php

To pin a specific release:

composer require verifyspeed/vs-php:^2.1

Composer installs Guzzle for HTTP requests, libphonenumber-for-php to validate and normalize phone numbers for OTP methods, and uses the openssl extension for verification token decryption.

Step 2: Configure the Server Key

Set your server key once before calling any API methods. You can obtain the key from the dashboard.

<?php

use VerifySpeed\VerifySpeed;

VerifySpeed::setServerKey('YOUR_SERVER_KEY');

Usage

Creating a Verification

Call VerifySpeed::createVerification() with the method name. For OTP methods, you must also pass a phone number; VerifySpeed sends a one-time password to that number.

createVerification parameters

ParameterRequiredDescription
$methodNameYesVerification method identifier (e.g., telegram-message, whatsapp-otp).
$languageNoLanguage for the verification flow (e.g., en, ar, ckb).
$phoneNumberFor OTP methodsUser's phone number. Required when $methodName is whatsapp-otp, telegram-otp, or sms-otp. The library validates and normalizes the number to E.164 format before sending the request. Omit for message-based methods.

If $methodName is an OTP type and $phoneNumber is omitted or empty, the client throws InvalidArgumentException with the message Phone number is required for OTP verification methods (whatsapp-otp, telegram-otp, sms-otp).

/**
* Creates a verification.
*
* @param string $methodName Verification method identifier (e.g., "whatsapp-message", "whatsapp-otp").
* @param string|null $language Optional language (e.g., "en", "ar", "ckb").
* @param string|null $phoneNumber User phone number. Required for OTP methods; normalized to E.164.
* VerifySpeed sends a one-time password to this number. Omit for message-based methods.
* @return VerificationResult
*
* @throws \InvalidArgumentException When required arguments are missing or invalid.
* @throws \RuntimeException When the server key is not set or the API request fails.
*/
VerifySpeed::createVerification(
string $methodName,
?string $language = null,
?string $phoneNumber = null
): VerificationResult;

Message-based method

For message-based methods (whatsapp-message, telegram-message), omit $phoneNumber. The response includes a deep link for the client app.

<?php

use VerifySpeed\VerifySpeed;

VerifySpeed::setServerKey('YOUR_SERVER_KEY');

try {
$result = VerifySpeed::createVerification(
methodName: 'telegram-message',
language: 'ar'
);

echo "Method: {$result->getMethodName()}\n";
echo "Verification Key: {$result->getVerificationKey()}\n";
echo "Deep Link: {$result->getDeepLink()}\n";

// Return verificationKey and deepLink to your mobile/web app.
} catch (\InvalidArgumentException $e) {
echo "Invalid input: {$e->getMessage()}\n";
} catch (\RuntimeException $e) {
echo "Request failed: {$e->getMessage()}\n";
}

OTP-based method

For OTP methods (whatsapp-otp, telegram-otp, sms-otp), pass $phoneNumber. VerifySpeed sends the OTP to that number. Your app collects the code from the user and sends it to your backend; your backend calls validateOtp() (see below).

<?php

use VerifySpeed\VerifySpeed;

VerifySpeed::setServerKey('YOUR_SERVER_KEY');

try {
$result = VerifySpeed::createVerification(
methodName: 'whatsapp-otp',
language: 'en',
phoneNumber: '+14255552673'
);

echo "Verification Key: {$result->getVerificationKey()}\n";
// getDeepLink() is null for OTP methods — return verificationKey to your client app.
} catch (\InvalidArgumentException $e) {
echo "Invalid input: {$e->getMessage()}\n";
} catch (\RuntimeException $e) {
echo "Request failed: {$e->getMessage()}\n";
}

Validating OTP

After creating an OTP verification, call VerifySpeed::validateOtp() from your backend with the code the user entered and the verificationKey from createVerification(). See Validating OTP for the REST contract.

validateOtp parameters

ParameterRequiredDescription
$codeYesOTP code entered by the user (max 5 characters).
$verificationKeyYesverificationKey from createVerification() for the same OTP session.
/**
* Validates an OTP code for a verification session.
*
* @param string $code OTP code entered by the user (max 5 characters).
* @param string $verificationKey Verification key from createVerification().
* @return ValidateOtpResult
*
* @throws \InvalidArgumentException When code or verification key is missing or invalid.
* @throws \RuntimeException When the server key is not set or the API request fails.
*/
VerifySpeed::validateOtp(
string $code,
string $verificationKey
): ValidateOtpResult;

Example

<?php

use VerifySpeed\VerifySpeed;
use VerifySpeed\EncryptionTool;

VerifySpeed::setServerKey('YOUR_SERVER_KEY');

try {
$result = VerifySpeed::validateOtp(
code: '12345',
verificationKey: 'YOUR_VERIFICATION_KEY'
);

if ($result->getSucceed()) {
echo "Phone: {$result->getPhoneNumber()}\n";
echo "Token: {$result->getToken()}\n";

// Optional: decrypt token for methodName, dateOfVerification, verificationKey
$decrypted = EncryptionTool::decryptToken(
$result->getToken(),
VerifySpeed::getServerKey()
);
echo "Method: {$decrypted->getMethodName()}\n";
echo "Verified at: {$decrypted->getDateOfVerification()->format(\DateTimeInterface::ATOM)}\n";
} else {
echo "Error: {$result->getErrorMessage()} ({$result->getErrorCode()})\n";
}
} catch (\InvalidArgumentException $e) {
echo "Invalid input: {$e->getMessage()}\n";
} catch (\RuntimeException $e) {
echo "Request failed: {$e->getMessage()}\n";
}

When getSucceed() is false, check getErrorCode() and getErrorMessage(). Known API error codes:

errorCodeDescription
OTP_EXPIREDThe OTP session has expired.
OTP_INVALIDThe provided code does not match.
OTP_ALREADY_VERIFIEDThis verification was already completed.

Verifying Tokens

Use EncryptionTool to decrypt verification tokens (from validateOtp(), the mobile SDK, or other flows). Tokens expire after 5 minutes; expired tokens throw InvalidArgumentException.

/**
* Verifies and decrypts a verification token (alias of decryptToken).
*
* @throws \InvalidArgumentException When the token is invalid, corrupted, or expired
* @throws \RuntimeException When decryption fails
*/
EncryptionTool::verifyVerificationToken(string $token, string $serverKey): DecryptTokenResult;

/**
* Decrypts a verification token.
*
* @throws \InvalidArgumentException When the token is invalid, corrupted, or expired
* @throws \RuntimeException When decryption fails
*/
EncryptionTool::decryptToken(string $token, string $serverKey): DecryptTokenResult;
<?php

use VerifySpeed\EncryptionTool;

try {
$result = EncryptionTool::decryptToken('YOUR_VERIFICATION_TOKEN', 'YOUR_SERVER_KEY');

echo $result->getPhoneNumber() . "\n";
echo $result->getMethodName() . "\n";
echo $result->getVerificationKey() . "\n";
echo $result->getDateOfVerification()->format(\DateTimeInterface::ATOM) . "\n";
} catch (\InvalidArgumentException $e) {
echo "Token invalid or expired: {$e->getMessage()}\n";
} catch (\RuntimeException $e) {
echo "Decryption failed: {$e->getMessage()}\n";
}

On success, validateOtp() already returns phoneNumber. Use EncryptionTool when you also need methodName, dateOfVerification, or verificationKey from the token without calling the verification result API. For more examples, see Verification Result Documentation.

Models

VerificationResult

Returned by createVerification(). Contains the verification key and, for message-based methods, a deep link.

MethodDescription
getMethodName()The verification method used (e.g., whatsapp-otp).
getVerificationKey()Key that identifies this verification; pass to your client app or validateOtp().
getDeepLink()URL for deep-link verification. null for OTP methods.
toArray()Associative array with methodName, verificationKey, and deepLink.
  • deepLink is set for message-based methods (whatsapp-message, telegram-message).
  • deepLink is null for OTP methods (whatsapp-otp, telegram-otp, sms-otp).

ValidateOtpResult

Returned by validateOtp().

MethodDescription
getSucceed()true when the OTP is valid for the given verificationKey.
getToken()Verification token when getSucceed() is true; otherwise null.
getPhoneNumber()Verified phone number in E.164 when getSucceed() is true; otherwise null.
getErrorMessage()Human-readable error when getSucceed() is false; otherwise null.
getErrorCode()Machine-readable error code when getSucceed() is false; otherwise null.
toArray()Associative array with succeed, token, phoneNumber, errorMessage, and errorCode.

DecryptTokenResult

Returned by EncryptionTool::decryptToken() and EncryptionTool::verifyVerificationToken().

MethodDescription
getPhoneNumber()Verified phone number in E.164 format.
getDateOfVerification()DateTimeImmutable when verification completed (UTC).
getMethodName()Verification method used (e.g., whatsapp-otp).
getVerificationKey()Key that identifies the verification session.
toArray()Associative array with phoneNumber, dateOfVerification, methodName, and verificationKey.

Exceptions

ExceptionWhen thrown
\InvalidArgumentExceptionEmpty or invalid arguments for createVerification() or validateOtp(); missing phone number for OTP methods; invalid phone number format; invalid, corrupted, or expired verification token in EncryptionTool.
\RuntimeExceptionServer key not set (setServerKey not called); non-success HTTP response; network or deserialization errors; decryption failure in EncryptionTool.

Phone number validation

For OTP methods, the library uses libphonenumber to parse and validate $phoneNumber and formats it as E.164 before calling the API. Invalid numbers throw InvalidArgumentException with a message such as Invalid phone number format. Please use E.164 format (e.g., +1234567890).

API Example (Laravel)

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use VerifySpeed\EncryptionTool;
use VerifySpeed\VerifySpeed;

class VerificationController extends Controller
{
public function __construct()
{
VerifySpeed::setServerKey(config('services.verifyspeed.server_key'));
}

public function create(Request $request)
{
$request->validate([
'methodName' => 'required|string',
'phoneNumber' => 'nullable|string',
'language' => 'nullable|string',
]);

try {
$verification = VerifySpeed::createVerification(
methodName: $request->input('methodName'),
language: $request->input('language'),
phoneNumber: $request->input('phoneNumber')
);

return response()->json($verification->toArray());
} catch (\InvalidArgumentException $e) {
return response()->json(['error' => $e->getMessage()], 400);
} catch (\RuntimeException $e) {
return response()->json(['error' => $e->getMessage()], 502);
}
}

public function validateOtp(Request $request)
{
$request->validate([
'code' => 'required|string|max:5',
'verificationKey' => 'required|string',
]);

try {
$result = VerifySpeed::validateOtp(
code: $request->input('code'),
verificationKey: $request->input('verificationKey')
);

if (!$result->getSucceed()) {
return response()->json($result->toArray(), 422);
}

return response()->json($result->toArray());
} catch (\InvalidArgumentException $e) {
return response()->json(['error' => $e->getMessage()], 400);
} catch (\RuntimeException $e) {
return response()->json(['error' => $e->getMessage()], 502);
}
}

public function decryptToken(Request $request)
{
$request->validate(['token' => 'required|string']);

try {
$result = EncryptionTool::decryptToken(
$request->input('token'),
config('services.verifyspeed.server_key')
);

return response()->json($result->toArray());
} catch (\InvalidArgumentException $e) {
return response()->json(['error' => $e->getMessage()], 400);
} catch (\RuntimeException $e) {
return response()->json(['error' => $e->getMessage()], 502);
}
}
}

License

This package is distributed under the MIT License. Copyright VerifySpeed ([email protected]).