Instalación y primer SMS
composer require envoisms/envoisms-php, y luego $client = new EnvoiSMS\Client(getenv('ENVOISMS_API_KEY')). $client->send(['to' => '+212600000000', 'message' => '…', 'from' => 'MiNegocio']) devuelve el id del mensaje, que encontrará con getMessage() y en sus webhooks.
- 'from': su Sender ID validado, u omítalo para el remitente por defecto
- sendBulk(['messages' => [...], 'from' => ...]) para un envío masivo
- getMessage($id) y listMessages(50, 0) para seguir sus envíos
Envíos reintentables y gestión de errores
Cada send() y sendBulk() lleva un Idempotency-Key, generado o pasado como segundo argumento: un reintento tras un timeout nunca factura dos veces el mismo mensaje. El propio cliente reintenta las respuestas 5xx y los errores de cURL hasta maxRetries veces (2 por defecto) con espera exponencial, y luego lanza EnvoiSMSError con statusCode y errorCode.
- new Client(getenv('ENVOISMS_API_KEY'), maxRetries: 3, timeoutSeconds: 20)
- catch (EnvoiSMS\EnvoiSMSError $e): $e->statusCode, $e->errorCode
SMS o WhatsApp: elegir el canal
El SMS es el canal por defecto: llega a todos los móviles marroquíes (IAM, Inwi, Orange) sin más configuración que la clave API. 'channel' => 'whatsapp' envía desde su propio número de WhatsApp Business: debe estar conectado (si no, 403 WHATSAPP_NOT_CONNECTED), y un texto libre solo se acepta dentro de las 24 horas siguientes al último mensaje del cliente (si no, 400 OUT_OF_24H_WINDOW). Fuera de esa ventana, envíe una plantilla aprobada. No se cobra nada cuando la API rechaza.
- 'template' => ['name' => 'order_shipped', 'language' => 'fr', 'variables' => ['89240']]
- 'cascade' => true: paso automático a SMS si el número no tiene WhatsApp
Códigos OTP y webhooks
$client->sendOtp(['to' => …, 'brand' => 'MiNegocio', 'code_length' => 6, 'expiry' => 600]) devuelve un session_id; $client->checkOtp($sessionId, $code) devuelve verified. getOtpSession() lee el estado de una sesión sin consumir un intento. Para un webhook de estado, Client::verifyWebhookSignature($rawBody, $signature, $secret) comprueba la cabecera X-EnvoiSMS-Signature sobre el cuerpo bruto de la petición.
- 'channel' => 'whatsapp' en sendOtp(): código por WhatsApp a través del remitente compartido, sin conexión
- getBalance(), listPacks(), createTopup() para la cuenta
- createOptout($phone) para registrar un STOP