DocumentationAPI v1 · 1.3.0

Packages

SDK PHP

Installer et utiliser labyrinthe/connect-php : SMS, OTP, webhooks et erreurs en PHP 8.1+.

Client PHP officiel : PHP 8.1+, ext-json, ext-mbstring. Utilise Guzzle par défaut, ou tout client PSR-18.

Version 1.2.0. Code source, exemples et journal des modifications : dossier php sur GitHub.

Installation#

Shell
composer require labyrinthe/connect-php

Configuration#

PHP
<?php
use Labyrinthe\Connect\Client;

// From the environment: LABYRINTHE_API_KEY (required), LABYRINTHE_BASE_URL (optional)
$client = Client::fromEnv();

// Or explicitly (named arguments)
$client = new Client(
    apiKey: 'lsk_test_xxxxxxxxxxxxxxxx',
    timeout: 30.0,          // seconds, per attempt
    maxRetries: 2,          // 0 to 10, on network errors, 429 and 5xx
    defaultSender: 'MABOUTIQUE',
);

$client->isTestMode(); // true with a lsk_test_ key
OptionDéfautRôle
apiKey—Clé lsk_live_… ou lsk_test_….
baseUrlhttps://api.labyrinthe-rdc.com/api/public/v1Un hôte seul reçoit /api/public/v1.
timeout / connectTimeout30.0 / 10.0Secondes.
maxRetries / maxRetryWait2 / 10.0Relances et attente Retry-After maximale acceptée.
defaultCountryCode243Indicatif des numéros locaux.
defaultSendernullSender ID quand sender est omis.
httpClient, logger—Client PSR-18 et journal PSR-3.

Les services sont des propriétés : $client->sms, $client->balance, $client->senderIds, $client->otp. Chaque méthode accepte aussi requestId: (en-tête X-Request-Id) ; $client->lastResponse() donne le statut, l'identifiant de requête et les limites de la dernière réponse.

Envoyer un SMS#

PHP
<?php
use Labyrinthe\Connect\Client;

$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)

$message = $client->sms->send(
    to: '243812345678',
    body: 'Votre commande 1042 est prête.',
    reference: 'CMD-1042',
    idempotencyKey: 'order-1042-ready', // replaying within 24 h sends nothing again
);

echo $message->id, ' ', $message->status->value; // queued

Plusieurs destinataires (jusqu'à 1000, tout ou rien) :

PHP
<?php
use Labyrinthe\Connect\Client;

$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)

$batch = $client->sms->sendMany(
    recipients: ['243812345678', '0899999999'],
    body: 'Rappel : reunion demain 9h.',
    sender: 'MABOUTIQUE', // an approved Sender ID of the account
    reference: 'RAPPEL-2026-11-06',
    idempotencyKey: 'reminder-2026-11-06',
);

foreach ($batch as $message) {
    echo $message->recipient, ' ', $message->status->value, PHP_EOL;
}

Estimer le coût, consulter le solde, lister les messages :

PHP
<?php
use Labyrinthe\Connect\Client;

$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)

// Body first, then the recipients (duplicates are merged as for a real send).
$estimate = $client->sms->estimate('Votre code est 482913', ['243812345678', '0812345678', '0899999999']);

echo $estimate->encoding?->value, ' ', $estimate->totalCredits; // gsm7 2
if (! $estimate->isAffordable()) {
    // top up before sending
}
PHP
<?php
use Labyrinthe\Connect\Client;

$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)

$balance = $client->balance->get();

echo $balance->balance, ' credits available, ', $balance->held, ' held';
PHP
<?php
use Labyrinthe\Connect\Client;

$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)

$page = $client->sms->list(status: 'delivered', from: '2026-11-01', perPage: 50);
echo $page->total, ' messages';

// Walk every page lazily
foreach ($page->autoPaging() as $message) {
    echo $message->id, ' ', $message->recipient, PHP_EOL;
}

Vérification OTP#

1. Envoyer le code
<?php
use Labyrinthe\Connect\Client;
use Labyrinthe\Connect\Exception\OtpResendTooSoonException;

$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)

try {
    $otp = $client->otp->send('243812345678', locale: 'fr', idempotencyKey: 'login-'.session_id());
} catch (OtpResendTooSoonException $e) {
    exit('Wait '.$e->getRetryAfter().' s before asking for a new code.');
}

$_SESSION['otp_id'] = $otp->otpId;   // keep it server-side
$otp->codeLength;                     // 6
$otp->resendAfterSeconds;             // 60, for a "Resend" countdown
$otp->testCode();                     // '482913' with a lsk_test_ key, null in live
2. Vérifier le code
<?php
use Labyrinthe\Connect\Client;

$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)

$result = $client->otp->verify($_SESSION['otp_id'], $_POST['code']);

if ($result->verified) {
    unset($_SESSION['otp_id']); // phone verified: log the user in
} elseif ($result->reason?->value === 'invalid_code') {
    echo 'Wrong code, ', $result->attemptsRemaining, ' attempt(s) left.';
} else {
    echo 'Code expired or blocked: ask for a new one.'; // expired, max_attempts_reached, already_used
}

Recevoir les webhooks#

PHP
<?php
use Labyrinthe\Connect\Exception\InvalidWebhookPayloadException;
use Labyrinthe\Connect\Exception\SignatureVerificationException;
use Labyrinthe\Connect\Webhook\Event\BalanceLow;
use Labyrinthe\Connect\Webhook\Event\MessageStatusChanged;
use Labyrinthe\Connect\Webhook\Webhook;

try {
    $event = Webhook::constructEvent(
        file_get_contents('php://input'),
        $_SERVER['HTTP_X_SIGNATURE'] ?? '',
        getenv('LABYRINTHE_WEBHOOK_SECRET'),
    ); // checks the signature and the timestamp (5 min)
} catch (SignatureVerificationException|InvalidWebhookPayloadException) {
    http_response_code(400);
    exit;
}

// Deduplicate on $event->id()
match (true) {
    $event instanceof MessageStatusChanged => updateOrder($event->reference, $event->status->value),
    $event instanceof BalanceLow => alertTeam($event->balance, $event->threshold),
    default => null, // unknown events: answer 2xx
};

Erreurs et maintenance#

La maintenance (503 FEATURE_DISABLED et OTP_SERVICE_DISABLED) a sa propre exception : rien n'a été envoyé ni débité. Le package relance seul si Retry-After est court (10 s par défaut) ; sinon, mettez l'envoi en file d'attente. Voir Erreurs.

PHP
<?php
use Labyrinthe\Connect\Client;
use Labyrinthe\Connect\Exception\ApiException;
use Labyrinthe\Connect\Exception\FeatureDisabledException;
use Labyrinthe\Connect\Exception\InsufficientCreditsException;
use Labyrinthe\Connect\Exception\RateLimitException;
use Labyrinthe\Connect\Exception\RecipientNotSendableException;

$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)

try {
    $client->sms->send(to: '243812345678', body: 'Bonjour');
} catch (FeatureDisabledException $e) {
    // 503 maintenance: nothing was sent, no credit used
    $e->getFeature();      // 'api.sms_send'
    $e->getBlockedBy();    // 'api' when the whole API is off
    $e->getAdminMessage(); // platform message, or null
    $e->getUntil();        // ?DateTimeImmutable
    $e->getRetryAfter() !== null ? retryIn($e->getRetryAfter()) : queueForLater();
} catch (InsufficientCreditsException $e) {
    notifyTopUp($e->getAvailable(), $e->getRequired());
} catch (RecipientNotSendableException $e) {
    foreach ($e->getRejected() as $rejected) {
        echo $rejected->recipient, ' ', $rejected->reason->value, PHP_EOL;
    }
} catch (RateLimitException $e) {
    retryIn($e->getRetryAfter() ?? 60);
} catch (ApiException $e) {
    logger($e->getErrorCode(), $e->getStatusCode(), $e->getRequestId());
}

Mode test#

Avec une clé lsk_test_…, $client->isTestMode() vaut true et $otp->testCode() (une méthode, pas une propriété) renvoie le code. Webhook::sign($rawBody, $secret) fabrique une signature valide pour tester votre endpoint.