Skip to content

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.

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.
}

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.

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.