Install and send your first SMS
composer require envoisms/envoisms-php, then $client = new EnvoiSMS\Client(getenv('ENVOISMS_API_KEY')). $client->send(['to' => '+212600000000', 'message' => '…', 'from' => 'MyBusiness']) returns the message id, which you find again with getMessage() and in your webhooks.
- 'from': your validated Sender ID, or omit it for your default
- sendBulk(['messages' => [...], 'from' => ...]) for a bulk send
- getMessage($id) and listMessages(50, 0) to follow your sends
Retry-safe sends and error handling
Every send() and sendBulk() carries an Idempotency-Key, generated or passed as the second argument: a retry after a timeout never bills the same message twice. The client itself retries 5xx responses and cURL errors up to maxRetries times (2 by default) with exponential backoff, then throws EnvoiSMSError with statusCode and errorCode.
- new Client(getenv('ENVOISMS_API_KEY'), maxRetries: 3, timeoutSeconds: 20)
- catch (EnvoiSMS\EnvoiSMSError $e): $e->statusCode, $e->errorCode
SMS or WhatsApp: choosing the channel
SMS is the default: it reaches every Moroccan mobile (IAM, Inwi, Orange) with no setup beyond the API key. 'channel' => 'whatsapp' sends from your own WhatsApp Business number: it must be connected (otherwise 403 WHATSAPP_NOT_CONNECTED), and free text is accepted only within 24 hours of the customer's last message (otherwise 400 OUT_OF_24H_WINDOW). Outside that window, send an approved template. Nothing is charged when the API refuses.
- 'template' => ['name' => 'order_shipped', 'language' => 'fr', 'variables' => ['89240']]
- 'cascade' => true: automatic fallback to SMS when the number has no WhatsApp
OTP codes and webhooks
$client->sendOtp(['to' => …, 'brand' => 'MyBusiness', 'code_length' => 6, 'expiry' => 600]) returns a session_id; $client->checkOtp($sessionId, $code) returns verified. getOtpSession() reads a session's status without consuming an attempt. For a delivery-status webhook, Client::verifyWebhookSignature($rawBody, $signature, $secret) checks the X-EnvoiSMS-Signature header against the raw request body.
- 'channel' => 'whatsapp' in sendOtp(): a WhatsApp code through the shared sender, no connection needed
- getBalance(), listPacks(), createTopup() for the account
- createOptout($phone) to record a STOP