Webhook و Callback رویدادها
Webhook برای سامانههایی است که نمیخواهند وضعیت پیام یا پیامهای ورودی را دائماً Poll کنند. SMSDaily رویداد را با POST و بدنه JSON به آدرس HTTPS ثبتشده مشتری ارسال میکند.
در نسخه فعلی دو رویداد قابل عضویت هستند:
| رویداد | کاربرد |
|---|---|
message.status |
تغییر وضعیت یک پیام خروجی |
inbound.received |
در دسترس شدن یک پیام ورودی نرمالشده برای حساب |
رویداد webhook.test فقط توسط Endpoint تست تولید میشود و در فهرست Subscription ثبت نمیشود.
ایجاد Webhook
POST /v1/webhooks/
X-API-Key: YOUR_API_KEY
Content-Type: application/json
{
"url": "https://example.com/integrations/smsdaily/webhook",
"events": [
"message.status",
"inbound.received"
]
}
محدودیتهای امنیتی آدرس مقصد:
- فقط
HTTPSپذیرفته میشود. - در نسخه فعلی فقط پورت استاندارد
443مجاز است. - مقصد باید به IP عمومی اینترنتی resolve شود؛ localhost، شبکه خصوصی و آدرسهای رزروشده پذیرفته نمیشوند.
- Redirect سمت مقصد دنبال نمیشود.
- حداکثر ۵ Webhook برای هر حساب قابل تعریف است.
نمونه پاسخ ایجاد:
{
"id": "18a08676-47e2-4be0-b10b-2301135aec45",
"url": "https://example.com/integrations/smsdaily/webhook",
"events": ["inbound.received", "message.status"],
"isActive": true,
"signingSecret": "KEEP_THIS_SECRET_IN_YOUR_SECRET_STORE",
"createdAtUtc": "2026-08-12T07:10:00Z",
"updatedAtUtc": "2026-08-12T07:10:00Z"
}
signingSecret را هنگام ایجاد در Secret Store ذخیره کنید. پاسخهای فهرست و ویرایش، Secret را دوباره نمایش نمیدهند.
مدیریت Webhookها
GET /v1/webhooks/
PATCH /v1/webhooks/{id}
DELETE /v1/webhooks/{id}
POST /v1/webhooks/{id}/test
نمونه ویرایش:
{
"events": ["message.status"],
"isActive": true
}
Endpoint تست یک رویداد آزمایشی را در صف Callback قرار میدهد:
{
"eventId": "evt_test_73efed4184e947f695bfd313ddd11ab0",
"status": "queued"
}
ساختار Callback
نمونه رویداد تغییر وضعیت پیام:
{
"id": "evt_msg_10421",
"type": "message.status",
"createdAt": "2026-08-12T07:15:18Z",
"data": {
"trackingCode": "SMS-20260812-A1B2C3D4",
"clientReference": "order-781",
"sender": "300099000121",
"recipient": "09121234567",
"status": "delivered",
"changedAt": "2026-08-12T07:15:18Z"
}
}
مقادیر عمومی status همان وضعیتهای قرارداد Public API هستند: queued، processing، sent، delivered، failed و cancelled.
نمونه پیام ورودی:
{
"id": "evt_in_c3848e3d4ca74748a68979d634882ecf",
"type": "inbound.received",
"createdAt": "2026-08-12T07:18:41Z",
"data": {
"trackingCode": "IN-20260812-22AABBCC",
"sender": "09121234567",
"receiver": "300099000121",
"message": "تایید",
"receivedAt": "2026-08-12T07:18:41Z"
}
}
فقط پیام ورودیای Callback میشود که در هسته SMSDaily به حساب مشتری نگاشت و به وضعیت قابل استفاده (Available) رسیده باشد. وجود API یا Webhook ورودی به معنی پشتیبانی دریافت روی همه خطوط و Providerها نیست.
هدرهای امنیتی Callback
در هر درخواست SMSDaily این هدرها را ارسال میکند:
X-SMSDaily-Event-Id: evt_msg_10421
X-SMSDaily-Event: message.status
X-SMSDaily-Timestamp: 1786520118
X-SMSDaily-Signature: v1=9f...64-hex-characters...
امضا روی بدنه خام درخواست محاسبه میشود:
signedPayload = unixTimestamp + "." + rawRequestBody
signature = HMAC-SHA256(signingSecret, signedPayload)
برای بررسی امن:
- مقدار
X-SMSDaily-Timestampرا بخوانید و درخواستهای قدیمیتر از بازه قابل قبول خودتان، مثلاً ۵ دقیقه، رد کنید. - بدنه را قبل از Parse شدن و دقیقاً به همان شکل دریافتشده نگه دارید.
- HMAC را با
signingSecretمحاسبه کنید. - نتیجه را با مقدار پس از
v1=بهصورت ثابتزمان (constant-time) مقایسه کنید. X-SMSDaily-Event-Idرا برای Idempotency ذخیره کنید و یک رویداد تکراری را دوباره پردازش نکنید.
نمونه اعتبارسنجی با SDK رسمی .NET
using SmsDaily.Client;
var valid = WebhookSignature.Verify(
signingSecret,
Request.Headers["X-SMSDaily-Timestamp"].ToString(),
Request.Headers["X-SMSDaily-Signature"].ToString(),
rawBody,
tolerance: TimeSpan.FromMinutes(5));
if (!valid)
{
return Results.Unauthorized();
}
Retry و پاسخ Endpoint مشتری
Endpoint مشتری باید پس از اعتبارسنجی و ثبت رویداد، سریعاً پاسخ 2xx برگرداند. پردازش سنگین را بهتر است داخل Queue خودتان انجام دهید.
در صورت Timeout، خطای شبکه یا پاسخ غیر 2xx، SMSDaily Callback را با Backoff نمایی Retry میکند. تنظیم پیشفرض سرور حداکثر ۸ تلاش است و فاصله Retry تا حداکثر یک ساعت افزایش پیدا میکند. به همین دلیل Receiver باید حتماً Idempotent باشد.
Webhook جایگزین Endpointهای Polling نیست؛ میتوانید برای بازیابی یا تطبیق نهایی همچنان از GET /v1/messages/{trackingCode} و GET /v1/inbound-messages استفاده کنید.