Guides
Send SMS
Single and bulk sending, cost estimate, segments and encoding, strip_accents, reference, tracking and message list.
Send an SMS#
POST /sms with recipient (one number) and body (1 to 1600 characters). Sending is asynchronous: 202 Accepted confirms queuing and credit debit, not delivery. Then track the status by webhook or GET /sms/{id}.
| Field | Description |
|---|---|
recipient | One recipient (alternative to recipients). |
recipients | 1 to 1000 recipients, not allowed with recipient. |
body | Text, 1 to 1600 characters. Required. |
sender | Approved Sender ID of the account (11 characters max). Defaults to the platform sender. |
reference | Free reference (64 characters max), returned in the message and the webhook, filterable. |
strip_accents | Replaces accents and typographic characters with ASCII (GSM-7, lower cost). |
Over HTTP#
Over HTTP
curl -X POST "https://api.labyrinthe-rdc.com/api/public/v1/sms" \
-H "Authorization: Bearer lsk_test_xxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: order-1042-ready" \
-H "Content-Type: application/json" \
-d '{"recipient":"243812345678","body":"Votre commande 1042 est prête.","reference":"CMD-1042"}'With the packages#
With the packages
<?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{
"data": [
{
"id": "9d5c0a3e-6f0b-4c2e-9a51-3b7f2d8e4c10",
"reference": "CMD-1042",
"recipient": "243812345678",
"sender": "LabConnect",
"encoding": "ucs2",
"status": "queued",
"segments": 1,
"credits_charged": 1,
"campaign_id": null,
"provider_message_id": null,
"error_code": null,
"error_message": null,
"created_at": "2026-11-06T09:39:11+00:00",
"submitted_at": null,
"delivered_at": null
}
]
}Send to many recipients#
Replace recipient with recipients (up to 1000). One message is created per recipient; duplicates are merged after normalization.
All recipients, the Sender ID and the balance are checked before anything is sent. If a single number is refused, nothing is sent and RECIPIENT_NOT_SENDABLE lists every refusal (details.rejected). If the balance does not cover the whole send, nothing is sent (INSUFFICIENT_CREDITS).
Over HTTP
curl -X POST "https://api.labyrinthe-rdc.com/api/public/v1/sms" \
-H "Authorization: Bearer lsk_test_xxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: reminder-2026-11-06" \
-H "Content-Type: application/json" \
-d '{"recipients":["243812345678","0899999999"],"body":"Rappel : reunion demain 9h.","sender":"MABOUTIQUE","reference":"RAPPEL-2026-11-06"}'With the packages
<?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;
}Credits, segments and encoding#
1 credit = 1 SMS segment. The cost depends on the text encoding:
| Encoding | One segment | Beyond, per segment | Triggered by |
|---|---|---|---|
gsm7 | 160 characters | 153 characters | ASCII text only |
ucs2 | 70 characters | 67 characters | Any non-ASCII character: é, à, ç, emoji, em dash, curly quotes… |
Lower the cost with strip_accents#
strip_accents: true (on POST /sms, /sms/estimate and /otp/send) replaces accents and typographic characters with their ASCII equivalent: the message switches to GSM-7, often half the price. Never applied without this parameter. Transliteration is server-defined (Café may become Cafe): do not rely on it for exact text.
Estimate the cost#
POST /sms/estimate computes encoding, segments and credits without sending anything. Recipients are counted as for a send (normalized, duplicates merged); invalid numbers are ignored. balance_after is negative when the balance is insufficient.
Over HTTP
curl -X POST "https://api.labyrinthe-rdc.com/api/public/v1/sms/estimate" \
-H "Authorization: Bearer lsk_test_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"body":"Votre code est 482913","recipients":["243812345678","0812345678","0899999999"]}'With the packages
<?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
}{
"data": {
"encoding": "gsm7",
"characters": 21,
"segments_per_message": 1,
"recipients_count": 2,
"total_credits": 2,
"balance_after": 98
}
}Reference#
reference (64 characters max) links a message to your business object (order number…). It is returned in Message.reference and in the message.status_changed webhook, and filterable with GET /sms?reference=. It is not unique: a bulk send shares it. To avoid duplicates, use idempotency instead.
Message statuses#
| Status | Meaning |
|---|---|
queued | Queued at Labyrinthe. |
submitted | Handed to the provider. |
sent | Accepted by the operator. |
delivered | Delivered to the phone (final). |
failed, rejected, expired, undeliverable | Failure (final). error_code and error_message give the reason. |
The message text is never returned by the API (it may contain an OTP code).
Get a message#
GET /sms/{id} searches by id across all the account's messages (whatever key or area sent them).
Over HTTP
curl "https://api.labyrinthe-rdc.com/api/public/v1/sms/9d5c0a3e-6f0b-4c2e-9a51-3b7f2d8e4c10" \
-H "Authorization: Bearer lsk_test_xxxxxxxxxxxxxxxx"With the packages
<?php
use Labyrinthe\Connect\Client;
$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)
$message = $client->sms->get('9d5c0a3e-6f0b-4c2e-9a51-3b7f2d8e4c10');
if ($message->status->isFinal()) {
echo $message->status->value, ' ', $message->errorCode ?? '';
}List messages#
GET /sms lists the messages created by the calling API key, newest first, with pagination (page, per_page up to 100).
status: one of the statuses above.recipient: number, any format (normalized before searching).reference: exact match.from,to: date (2026-11-01) or ISO 8601 date-time, inclusive.
Over HTTP
curl "https://api.labyrinthe-rdc.com/api/public/v1/sms?status=delivered&from=2026-11-01&per_page=50" \
-H "Authorization: Bearer lsk_test_xxxxxxxxxxxxxxxx"With the packages
<?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;
}