Webhooks
Receive delivery events on your server in real time. HMAC signing, events, and retries.
Webhooks push message lifecycle events to your server in real time, removing the need for continuous status polling.
Configuration
The webhook URL and secret are configured per API key in your account under API Center → Security & IP → Webhook. A test-fire is available on the same page.
| Event | Meaning |
|---|---|
| sms.sent | Message handed to the operator. |
| sms.delivered | Handset delivery confirmed. |
| sms.failed | Message permanently failed. |
| otp.verified | OTP code was verified. |
| key.test | Manual test-fire. |
Signature verification
Every request is signed. The X-TurkeySMS-Signature header is in the form <algo>=<hmac> (default sha256) and is the HMAC of the body with your webhook secret. The X-TurkeySMS-Timestamp header carries the send time. Always verify the signature before processing.
<?php
$secret = "YOUR_WEBHOOK_SECRET";
$payload = file_get_contents("php://input");
$header = $_SERVER["HTTP_X_TURKEYSMS_SIGNATURE"] ?? "";
// Header format: "sha256=<hmac>"
[$algo, $sig] = array_pad(explode("=", $header, 2), 2, "");
$expected = hash_hmac($algo ?: "sha256", $payload, $secret);
if (!hash_equals($expected, (string) $sig)) {
http_response_code(403);
exit("Invalid signature");
}
$event = json_decode($payload, true);
// $event["event_type"], $event["data"]["sms_id"], $event["data"]["status"]
http_response_code(200);{
"event_type": "sms.delivered",
"timestamp": "2026-05-22T09:14:07Z",
"data": {
"sms_id": 1000007721,
"status": "delivered"
}
}Replay protection
In addition to verifying the signature, use the X-TurkeySMS-Timestamp header to reject replayed requests. Reject any event whose timestamp falls outside a tolerance window you consider acceptable (for example, a few minutes). This prevents a captured old request from being re-sent against your endpoint.
Retry and ordering
Failed deliveries are retried with a count and backoff configurable per key. Event ordering is not guaranteed — therefore process events idempotently (receiving the same event twice must not break state).
If your server responds with anything other than 2xx, the delivery is considered failed and retried. Return 200 on successful processing.