# Rayo SMS SDKs — Complete Documentation Canonical site: https://sms-sdks-docs.rayosms.com API base URL: https://api.rayosms.com/v1 Webhook signature header: RAYO-SMS-Signature # SDK overview Official SDKs for integrating web, backend, and native applications with the Rayo SMS Gateway API. Backend SDKs Server-side libraries for secure messaging workflows and webhook processing. PHP SDK Send and schedule SMS messages, inspect delivery status, check balances, and verify signed callbacks. Available View docs → Frontend SDKs Browser and frontend framework integrations. Frontend SDKs Official frontend SDK documentation will appear here when available. Soon to be provided Native SDKs Libraries designed for native mobile and desktop applications. Native SDKs Official native SDK documentation will appear here when available. Soon to be provided --- # PHP SDK overview Use the official Rayo SMS PHP SDK to send and schedule messages, query delivery status, and verify callbacks. The official PHP SDK provides a typed, object-oriented interface to the Rayo SMS Gateway API. It supports PHP 7.4 through current PHP releases and uses Guzzle 7 for HTTP requests. Send immediately or schedule delivery using an associative array or the `SmsRequest` value object. Check account credit balance, query message delivery status, and cancel schedules. Verify HMAC-SHA256 webhook signatures and parse GET or POST callback payloads. ## Requirements - PHP 7.4 or later - The PHP JSON extension - [Composer](https://getcomposer.org/) ## Package Install `rayo-sms/rayo-sms-php-sdk` from Packagist: ```bash composer require rayo-sms/rayo-sms-php-sdk ``` The project is open source under the MIT License. [Browse the source and report issues on GitHub](https://github.com/Rayo-SMS/rayo-sms-php-sdk). ## Where to begin - [Installation and configuration](https://sms-sdks-docs.rayosms.com/php/installation/): Install the package, create a client, and configure transport options. - [Five-minute quickstart](https://sms-sdks-docs.rayosms.com/php/quickstart/): Send a simulated SMS without consuming credits. - [API reference](https://sms-sdks-docs.rayosms.com/php/api-reference/): Review client methods, models, constants, and return values. --- # Installation & configuration Install and configure the Rayo SMS PHP SDK, API credentials, base URL, and HTTP options. ## Install with Composer 1. From your application directory, install the package: ```bash composer require rayo-sms/rayo-sms-php-sdk ``` 2. Load Composer's autoloader: ```php require_once 'vendor/autoload.php'; ``` 3. Create an API key in the web application under **Actions → Settings** and expose it to your process as `SMS_API_KEY`. Never place an API key or callback signing secret in browser code, a mobile application, source control, or documentation examples. ## Create a client The API base URL for this documentation build is `https://api.rayosms.com/v1`. ```php $client = new Client( getenv('SMS_API_KEY'), 'https://api.rayosms.com/v1' ); ``` The constructor accepts either a base URL string or an options array. A URL with or without the trailing `/v1/` is normalized to the same endpoint. ```php use Rayo\SmsSdk\Client; $client = new Client(getenv('SMS_API_KEY'), [ 'base_url' => 'https://your-api-host.example/v1', 'timeout' => 15.0, 'connect_timeout'=> 5.0, ]); ``` Supported transport options include: | Option | Purpose | | --- | --- | | `base_url` | Required API origin or versioned base URL | | `timeout` | Total Guzzle request timeout; defaults to `10.0` seconds | | `connect_timeout` | Connection timeout forwarded to Guzzle | | `headers` | Additional request headers | | `verify` | Guzzle TLS certificate verification setting | | `handler` | Custom Guzzle handler, useful in tests | | `http_client` | An existing PSR-18-compatible Guzzle `ClientInterface` implementation | The client automatically supplies `Authorization: Bearer …`, `Accept: application/json`, and the JSON content type when required. For the complete HTTP API, see the [API reference](https://api-docs.rayosms.com). --- # Quickstart Send a simulated SMS with the Rayo SMS PHP SDK without consuming credits. This example sends a **simulated** message. It exercises request validation and the API integration without consuming credits or delivering a message to the handset. 1. Install the SDK: ```bash composer require rayo-sms/rayo-sms-php-sdk ``` 2. Set your API key in the environment: ```bash export SMS_API_KEY='replace-with-your-private-api-key' ``` 3. Save and run the following script: ```php sendSms([ 'fromLabel' => 'MYBRAND', 'toMobileNumber' => '306912345678', 'smsBody' => 'Hello from the PHP SDK!', 'simulateSendFlag' => true, ]); ``` Set `simulateSendFlag` to `false`, or omit it, only when you are ready to send a real message. Real sends consume credits. On success, `sendSms()` returns an `SmsResponse`. Use `isSuccess()`, `getSmsServerRequestId()`, and `getClientRequestId()` to connect the response to your application records. Continue to [Sending SMS](https://sms-sdks-docs.rayosms.com/php/sending-sms/) for recipients, groups, callbacks, concatenation, and the `SmsRequest` value object. --- # Sending SMS Send messages to phone numbers and contact groups with arrays or SmsRequest objects. `Client::sendSms()` accepts either an associative array or an `SmsRequest` instance. At least one mobile number or contact group is required. ```php $response = $client->sendSms([ 'fromLabel' => 'MYBRAND', 'toMobileNumber' => ['306912345678', '306987654321'], 'toGroup' => ['Customers', 'Promotions'], 'smsBody' => 'Your order has been dispatched.', 'clientRequestId' => 'order-9543212', 'callbackWhenProcessedFlag' => true, 'simulateSendFlag' => false, 'concatFlag' => true, ]); ``` ```php use Rayo\SmsSdk\Models\SmsRequest; $request = new SmsRequest( 'MYBRAND', 'Your verification code is 123456.', ['306912345678', '306987654321'], null, 'verify-9543212', true, false, true ); $response = $client->sendSms($request); ``` Arrays of phone numbers or group names are normalized into semicolon-separated strings. String values may already use semicolons or commas as supported by the API. ## Request fields | Field | Type | Required | Description | | --- | --- | --- | --- | | `fromLabel` | `string` | Yes | Registered sender label | | `smsBody` | `string` | Yes | Message body, up to 1,000 characters | | `toMobileNumber` | `string\|string[]\|null` | One destination required | One or more international-format mobile numbers | | `toGroup` | `string\|string[]\|null` | One destination required | One or more account contact-group names | | `clientRequestId` | `string\|null` | No | Your own correlation identifier | | `callbackWhenProcessedFlag` | `bool\|null` | No | Request delivery callbacks | | `simulateSendFlag` | `bool\|null` | No | Validate and simulate without sending | | `concatFlag` | `bool\|null` | No | Allow concatenated SMS parts | | `scheduledAt` | `DateTimeInterface\|string\|null` | No | Future ISO-8601 delivery time | Use international-format numbers without a leading plus sign, for example `306912345678`. Sender labels and group names must already exist in your account. ## Inspect the response ```php if ($response->isSuccess()) { echo $response->getUsername(); echo $response->getSmsServerRequestId(); echo $response->getClientRequestId(); } ``` The response object is immutable and also implements `ArrayAccess` and `JsonSerializable` for read-only access to the raw API response. --- # Scheduling messages Schedule future SMS delivery and cancel a scheduled request with the PHP SDK. ## Schedule a message Pass `scheduledAt` as an ISO-8601 string with a timezone offset or as a `DateTimeInterface`. The scheduled time must be more than five minutes in the future. ```php use DateTimeImmutable; use Rayo\SmsSdk\Models\SmsRequest; $scheduledAt = new DateTimeImmutable('+2 hours'); $response = $client->sendSms(new SmsRequest( 'MYBRAND', 'Reminder: your appointment is tomorrow at 10:00.', '306912345678', null, 'appointment-1001', true, false, true, $scheduledAt )); if ($response->isScheduled()) { $scheduleId = $response->getSmsRequestScheduleId(); } ``` Persist `getSmsRequestScheduleId()` if your application may need to cancel the request later. ## Delete a schedule ```php $result = $client->deleteSchedule($scheduleId); if ($result->isSuccess()) { echo 'Deleted: ' . $result->getSmsRequestScheduleId(); } ``` `deleteSmsRequestSchedule()` is an alias matching the API operation name. A schedule can only be deleted before it is processed. Handle `RayoException` because an unknown, expired, or already processed schedule may be rejected. --- # Balance & delivery status Retrieve account credits and inspect SMS delivery status with the PHP SDK. ## Retrieve the credit balance ```php $balance = $client->getBalance(); echo 'Request ID: ' . $balance->getRequestId() . PHP_EOL; echo 'Credits: ' . $balance->getCredits() . PHP_EOL; ``` `getBalanceInCredits()` is an alias for `getBalance()`. ## Check delivery status Query by the server-generated request ID **or** your client request ID, never both: ```php $deliveries = $client->checkStatusByServerRequestId( 'cc479845-0f15-4ae7-b645-6ef6489868c1' ); // Alternatively: $deliveries = $client->checkStatusByClientRequestId('order-9543212'); foreach ($deliveries as $delivery) { printf( "%s: %s\n", $delivery->getMobileNumber(), $delivery->getStatus() ); } ``` The lower-level `checkStatus($smsServerRequestId, $clientRequestId)` method is also available and enforces the same either/or rule. ## Delivery status values | Constant | Value | Meaning | | --- | --- | --- | | `DeliveryStatus::NEW` | `new` | Initial state | | `DeliveryStatus::TEMP_LOCKED` | `temp_locked` | Internal processing state | | `DeliveryStatus::PROCESSING_ERROR` | `processing_error` | Processing failed and will not retry | | `DeliveryStatus::FAILURE` | `failure` | Repeated attempts failed | | `DeliveryStatus::SENT` | `sent` | Message left the network | | `DeliveryStatus::ERROR` | `error` | Temporary error; retry may occur | | `DeliveryStatus::DELIVERED` | `delivered` | Delivered to the handset | | `DeliveryStatus::NOT_DELIVERED` | `not_delivered` | Could not be delivered | | `DeliveryStatus::UNKNOWN_DELIVERY` | `unknown_delivery` | No handset confirmation within 24 hours | `SmsDeliveryStatusItem::isDelivered()` checks specifically for `delivered`. `isFinalStatus()` recognizes processing errors, failure, delivered, not delivered, and unknown delivery as terminal states. One SMS request can produce multiple delivery items when it targets several numbers or groups. --- # Webhooks Verify signed Rayo SMS delivery callbacks and parse GET or POST webhook payloads. When `callbackWhenProcessedFlag` is `true` and your account has a callback URL, the API sends signed delivery updates to your endpoint. The signature header for this documentation build is `RAYO-SMS-Signature`. Reject a callback unless its HMAC signature is valid. Read the raw POST body exactly once and do not re-encode it before verification. ## Verify a callback ```php use Rayo\SmsSdk\Models\CallbackPayload; use Rayo\SmsSdk\Webhooks\WebhookSignatureVerifier; ``` ```php $signatureHeader = 'RAYO-SMS-Signature'; $isValid = WebhookSignatureVerifier::verify( $incomingSignature, getenv('SMS_CALLBACK_SIGNING_SECRET'), $method, $requestPath, $rawPayload, $dateHeader, $contentType ); ``` Then parse the verified payload: ```php $callback = strtoupper($method) === 'POST' ? CallbackPayload::fromJson($rawPayload) : CallbackPayload::fromQueryString($rawPayload); echo $callback->getMobileNumber(); echo $callback->getStatus(); if ($callback->isFinalStatus()) { // Persist the terminal delivery result. } ``` ## PSR-7 requests Frameworks exposing a PSR-7 `ServerRequestInterface` can be verified directly: ```php $isValid = WebhookSignatureVerifier::verifyPsr7Request( $request, getenv('SMS_CALLBACK_SIGNING_SECRET'), 'your-signature-header-name' ); ``` `verifyOrThrow()` is available when exception-based control flow is preferred. `computeSignature()` can be used for deterministic testing. ## Signed message format The verifier joins these values with commas and calculates a hexadecimal HMAC-SHA256 digest: 1. Lowercase request method. 2. Content type for POST, otherwise an empty string. 3. Request path. 4. Raw body for POST or query string for GET. 5. `Date` header. Signature comparison uses `hash_equals()` to avoid timing-sensitive string comparison. --- # Error handling Handle SDK validation, HTTP, API, and response parsing errors with RayoException. The SDK throws `Rayo\SmsSdk\Exceptions\RayoException` for local validation failures, transport failures, non-successful API responses, and malformed JSON responses. ```php use Rayo\SmsSdk\Exceptions\RayoException; try { $response = $client->sendSms([ 'fromLabel' => 'MYBRAND', 'toMobileNumber' => '306912345678', 'smsBody' => 'Hello', ]); } catch (RayoException $exception) { $statusCode = $exception->getStatusCode(); $message = $exception->getMessage(); $apiError = $exception->getErrorMessage(); $resultCode = $exception->getResultCode(); $resultDescription = $exception->getResultDescription(); $payload = $exception->getErrorPayload(); $rawBody = $exception->getRawResponseBody(); } ``` ## Common local validation errors - Empty API key or base URL. - Unsupported base URL scheme. - Missing sender label or message body. - Message body longer than 1,000 characters. - No phone number or group destination. - Non-boolean request flags. - Missing or conflicting status identifiers. - Empty schedule ID or webhook signing secret. ## Operational guidance - Log correlation IDs and status codes, but never log API keys or signing secrets. - Treat `401` as an invalid or inactive API key. - Treat `404` as a missing resource and confirm the identifier. - Inspect `getResultDescription()` for API validation details. - Retry only failures that your application knows are transient; do not blindly retry local validation or authentication failures. Raw response bodies and error payloads may contain recipient or account data. Apply your normal privacy and retention rules before logging them. --- # API reference Reference for the PHP SDK client, request and response models, delivery constants, exceptions, and webhook verifier. This page summarizes the public API. For implementation details and the latest release history, [view the source repository](https://github.com/Rayo-SMS/rayo-sms-php-sdk). ## `Rayo\SmsSdk\Client` | Member | Returns | Purpose | | --- | --- | --- | | `__construct(string $apiKey, string\|array $baseUrlOrOptions, array $options = [])` | `Client` | Configure credentials, base URL, and Guzzle options | | `getBaseUrl()` | `string` | Return the normalized URL ending in `/v1/` | | `sendSms(SmsRequest\|array $payload)` | `SmsResponse` | Send immediately or schedule | | `getBalance()` | `BalanceResponse` | Retrieve credits | | `getBalanceInCredits()` | `BalanceResponse` | Alias for `getBalance()` | | `checkStatus(?string $serverId, ?string $clientId)` | `SmsDeliveryStatusItem[]` | Query using exactly one identifier | | `checkStatusByServerRequestId(string $id)` | `SmsDeliveryStatusItem[]` | Query by server ID | | `checkStatusByClientRequestId(string $id)` | `SmsDeliveryStatusItem[]` | Query by client ID | | `deleteSchedule(string $id)` | `DeleteScheduleResponse` | Cancel a future message | | `deleteSmsRequestSchedule(string $id)` | `DeleteScheduleResponse` | Alias for `deleteSchedule()` | Constants: `Client::DEFAULT_TIMEOUT` is `10.0`; `Client::API_VERSION_PREFIX` is `v1`. ## `SmsRequest` Constructor: ```php new SmsRequest( string $fromLabel, string $smsBody, string|array|null $toMobileNumber = null, string|array|null $toGroup = null, ?string $clientRequestId = null, ?bool $callbackWhenProcessedFlag = null, ?bool $simulateSendFlag = null, ?bool $concatFlag = null, DateTimeInterface|string|null $scheduledAt = null ); ``` Factory and serialization: `fromArray()`, `validate()`, `toArray()`, `jsonSerialize()`. Getters correspond to every constructor field. `SmsRequest::MAXIMUM_SMS_LENGTH` is `1000`. ## Response models All response models are immutable, JSON-serializable, and provide read-only `ArrayAccess` to their raw response. ### `SmsResponse` - State: `isSuccess()`, `isScheduled()`. - Getters: `getUsername()`, `getResultCode()`, `getResultDescription()`, `getClientRequestId()`, `getSmsServerRequestId()`, `getSmsRequestScheduleId()`, `getError()`. ### `DeleteScheduleResponse` - State: `isSuccess()`. - Getters: `getUsername()`, `getResultCode()`, `getResultDescription()`, `getSmsRequestScheduleId()`, `getError()`. ### `BalanceResponse` - Getters: `getRequestId()`, `getCredits()`. ### `SmsDeliveryStatusItem` - State: `isDelivered()`, `isFinalStatus()`. - Getters: `getSmsServerRequestId()`, `getClientRequestId()`, `getInternalSmsOutDeliveryId()`, `getMobileNumber()`, `getStatus()`. ### `CallbackPayload` - Factories: `fromArray()`, `fromJson()`, `fromQueryString()`. - State: `isDelivered()`, `isFinalStatus()`. - Getters: `getSmsServerRequestId()`, `getClientRequestId()`, `getInternalSmsOutDeliveryId()`, `getSenderId()`, `getMobileNumber()`, `getStatus()`, `getErrorDescription()`, `getRequestTimestamp()`, `getLastStatusUpdatedTimestamp()`. The timestamp parser accepts the documented `requestTimestamp` and the server variant `requestedTimestamp`. ## `DeliveryStatus` Constants: `NEW`, `TEMP_LOCKED`, `PROCESSING_ERROR`, `FAILURE`, `SENT`, `ERROR`, `DELIVERED`, `NOT_DELIVERED`, and `UNKNOWN_DELIVERY`. Use `DeliveryStatus::all()` to list every value and `DeliveryStatus::isFinal($status)` to test terminal status. ## `WebhookSignatureVerifier` | Method | Purpose | | --- | --- | | `computeSignature(...)` | Compute the expected HMAC-SHA256 digest | | `verify(...)` | Return whether a raw request signature is valid | | `verifyOrThrow(...)` | Throw `RayoException` for an invalid signature | | `verifyPsr7Request(...)` | Verify a PSR-7 server request | ## `RayoException` Extends `RuntimeException`. In addition to inherited exception methods, it exposes `getStatusCode()`, `getErrorPayload()`, `getRawResponseBody()`, `getErrorMessage()`, `getResultCode()`, and `getResultDescription()`. ---