Webhooks
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.
Verify a callback
Section titled “Verify a callback”use Rayo\SmsSdk\Models\CallbackPayload;use Rayo\SmsSdk\Webhooks\WebhookSignatureVerifier;$signingSecret = getenv('SMS_CALLBACK_SIGNING_SECRET');
$incomingSignature = $_SERVER['HTTP_RAYO_SMS_SIGNATURE'] ?? '';
$method = $_SERVER['REQUEST_METHOD'] ?? 'POST';
$requestPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
$dateHeader = $_SERVER['HTTP_DATE'] ?? '';
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
$rawPayload = strtoupper($method) === 'POST'
? (string) file_get_contents('php://input')
: ($_SERVER['QUERY_STRING'] ?? '');
if (!WebhookSignatureVerifier::verify(
$incomingSignature,
$signingSecret,
$method,
$requestPath,
$rawPayload,
$dateHeader,
$contentType
)) {
http_response_code(401);
exit('Invalid signature');
}
Then parse the verified payload:
$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
Section titled “PSR-7 requests”Frameworks exposing a PSR-7 ServerRequestInterface can be verified directly:
$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
Section titled “Signed message format”The verifier joins these values with commas and calculates a hexadecimal HMAC-SHA256 digest:
- Lowercase request method.
- Content type for POST, otherwise an empty string.
- Request path.
- Raw body for POST or query string for GET.
Dateheader.
Signature comparison uses hash_equals() to avoid timing-sensitive string comparison.
