Webhooklar

API Reference

Webhooklar

Outbound event teslimatı, webhook kaydı ve operasyonel izleme yüzeyi.

Webhook yüzeyi mesaj, cihaz ve abonelik olaylarını dış sistemlere taşımak için kullanılır. Her webhook kaydı kullanıcı scope’unda tutulur ve yalnız owner tenant tarafından yönetilir.

Desteklenen event’ler

Webhook oluştururken yalnızca aşağıdaki event adları kabul edilir. Listede olmayan bir değer gönderirseniz istek invalid event hatasıyla reddedilir.

EventNe zaman tetiklenir
message.sentMesaj WhatsApp’a iletildi
message.deliveredAlıcının cihazına teslim edildi
message.readAlıcı mesajı okudu
message.failedGönderim başarısız oldu
message.receivedCihaza gelen (inbound) mesaj alındı
device.connectedCihaz bağlandı
device.disconnectedCihaz bağlantısı koptu
device.statusCihaz durumu değişti
subscription.renewedAbonelik yenilendi veya dönem güncellendi
subscription.cancelledAbonelik iptal edildi
Kampanya bitişi için ayrı event yoktur

campaign.completed / campaign.failed gibi kampanya event’leri yoktur. Bir kampanyanın ilerleyişini takip etmek için kampanyaya ait message.sent, message.delivered, message.read ve message.failed event’lerini payload’daki batch_id alanına göre gruplayın; tüm mesajların terminal (delivered/read/failed) duruma ulaşması kampanyanın tamamlandığı anlamına gelir.

POST/v1/webhooks
X-API-Key200 OK

Webhook oluştur

Belirli event türleri için hedef URL kaydı oluşturur ve teslimat denemelerini takip eder.

secret alanı yalnızca oluşturma yanıtında bir kez döner. Bu değeri güvenli bir yerde saklayın; gelen webhook isteklerinin X-WA2Notify-Signature başlığını doğrulamak için gereklidir ve daha sonra tekrar gösterilmez (kaybederseniz webhook’u silip yeniden oluşturun).

Webhook URL alanı `required,url` doğrulamasından geçer. En az bir event zorunludur; yalnızca desteklenen event adları kabul edilir.

İstek

curl --request POST \
--url {{BASE_URL}}/v1/webhooks \
--header "X-API-Key: {{API_KEY}}" \
--header "Content-Type: application/json" \
--data '{
  "url": "https://erp.ornek.com/hooks/wa2notify",
  "events": ["message.delivered", "message.read", "message.failed"]
}'

Yanıt

JSON
{
"data": {
  "id": "8ebbc8af-0ef1-4ea7-bb5b-c4aa8fa3f946",
  "url": "https://erp.ornek.com/hooks/wa2notify",
  "events": ["message.delivered", "message.read", "message.failed"],
  "secret": "8f2c1e6a-4b7d-4b1a-9c2e-3f5a7d9b0c11",
  "is_active": true,
  "failure_count": 0,
  "last_success": null,
  "last_failure": null,
  "created_at": "2026-04-01T10:12:00Z",
  "updated_at": "2026-04-01T10:12:00Z"
}
}
GET/v1/webhooks
X-API-Key200 OK

Webhookları listele

Kayıtlı webhook hedeflerini, aktiflik durumunu ve hata sayısını listeler.

Aktif event hedeflerinizi düzenli olarak bu uçtan çekip kendi operasyon panelinizde de gösterebilirsiniz.

`failure_count`, `last_success` ve `last_failure` alanları teslimat sağlığını izlemek için tutulur.

İstek

curl --request GET \
--url {{BASE_URL}}/v1/webhooks \
--header "X-API-Key: {{API_KEY}}"

Yanıt

JSON
{
"data": {
  "webhooks": [
    {
      "id": "8ebbc8af-0ef1-4ea7-bb5b-c4aa8fa3f946",
      "url": "https://erp.ornek.com/hooks/wa2notify",
      "events": ["message.delivered", "message.read"],
      "is_active": true,
      "failure_count": 0,
      "last_success": "2026-04-01T10:18:40Z",
      "last_failure": null,
      "created_at": "2026-04-01T10:12:00Z",
      "updated_at": "2026-04-01T10:18:40Z"
    }
  ]
}
}
DELETE/v1/webhooks/:webhook_id
X-API-Key200 OK

Webhook sil

Webhook kaydını kalıcı olarak kaldırır.

Başka müşteriye ait webhook_id değerleri owner kontrolünden geçemediği için silinemez.

Silinen kayıt için yeni event teslimatı yapılmaz.

İstek

curl --request DELETE \
--url {{BASE_URL}}/v1/webhooks/{{WEBHOOK_ID}} \
--header "X-API-Key: {{API_KEY}}"

Yanıt

JSON
{
"data": {
  "message": "Webhook deleted successfully"
}
}

Payload örneği

Webhook body’si event, timestamp ve data alanlarından oluşur. message.* event’lerinde data, mesajın tam gösterimidir:

{
  "event": "message.delivered",
  "timestamp": "2026-04-01T10:18:40Z",
  "data": {
    "id": "7ac71306-3a37-4c45-a178-81dfbbca8728",
    "message_id": "MSG-20260401-0001",
    "whatsapp_id": "wamid.HBgL...",
    "device_id": "3baedacf-53bf-4061-a0f5-5235cab38875",
    "batch_id": null,
    "to": "+905551112233",
    "type": "text",
    "status": "delivered",
    "content": "Merhaba Ayşe",
    "sent_at": "2026-04-01T09:18:35Z",
    "delivered_at": "2026-04-01T10:18:40Z",
    "read_at": null,
    "error": "",
    "created_at": "2026-04-01T09:18:30Z",
    "updated_at": "2026-04-01T10:18:40Z"
  }
}
id ve message_id farklı alanlardır

data.id, POST /v1/messages/send yanıtındaki id ile aynı dahili mesaj UUID’sidir; olayları kendi kaydınızla eşlemek için bunu kullanın. data.message_id ise ayrı bir string kimliktir. Kampanya event’lerini data.batch_id alanına göre gruplayın.

İmza doğrulama

Her webhook isteği şu başlıklarla gönderilir:

Başlıkİçerik
X-WA2Notify-EventEvent adı (ör. message.delivered)
X-WA2Notify-SignatureHam istek gövdesinin HMAC-SHA256 imzası (hex)
X-WA2Notify-TimestampOlayın oluştuğu an (RFC3339, UTC)

İmza, webhook’un secret değeri anahtar olarak kullanılarak ham request body üzerinden hesaplanır: HMAC-SHA256(secret, rawBody) ve sonuç hex olarak kodlanır. İsteği işlemeden önce imzayı kendi tarafınızda yeniden hesaplayıp X-WA2Notify-Signature ile karşılaştırın:


// rawBody: gövdeyi JSON.parse etmeden ÖNCE, byte'ı byte'ına olduğu gibi alın.
function verifyWebhook(rawBody, headerSignature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  // Zamanlama saldırılarına karşı sabit süreli karşılaştırma.
  const a = Buffer.from(expected);
  const b = Buffer.from(headerSignature || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Örn. Express: express.raw({ type: "application/json" }) ile ham gövdeyi alın.
app.post("/hooks/wa2notify", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.header("X-WA2Notify-Signature");
  if (!verifyWebhook(req.body, signature, process.env.WA2NOTIFY_WEBHOOK_SECRET)) {
    return res.status(401).end();
  }
  const payload = JSON.parse(req.body.toString("utf8"));
  // ... payload.event / payload.data ...
  res.status(200).end();
});

İmza uyuşmuyorsa isteği reddedin. secret, yalnızca webhook oluşturma yanıtında bir kez döndürüldüğü için sunucu tarafında güvenli şekilde saklanmalıdır.

Teslimat kayıtları ve yeniden gönderim

Başarısız teslimatlar saatlere yayılan bir program (1dk / 5dk / 30dk / 2sa / 6sa / 12sa / 24sa) ile otomatik olarak yeniden denenir. Endpoint’iniz bir süre erişilemez olduysa, teslimat denemelerini listeleyip başarısız olanları elle yeniden kuyruğa alabilirsiniz.

GET/v1/webhooks/deliveries
X-API-Key200 OK

Teslimat denemelerini listele

Hesabınıza ait webhook teslimat denemelerini döndürür. `status`, `limit` ve `offset` sorgu parametreleriyle filtrelenebilir.

Teslimatlar kullanıcı scope’unda tutulur; yalnızca kendi hesabınıza ait denemeleri görürsünüz.

`status` değeri örneğin `failed` veya `delivered` olabilir. `limit` ve `offset` ile sayfalayın.

İstek

curl --request GET \
--url "{{BASE_URL}}/v1/webhooks/deliveries?status=failed&limit=20" \
--header "X-API-Key: {{API_KEY}}"

Yanıt

JSON
{
"data": {
  "deliveries": [
    {
      "id": "a1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "webhook_id": "8ebbc8af-0ef1-4ea7-bb5b-c4aa8fa3f946",
      "event": "message.delivered",
      "status": "failed",
      "attempt_count": 8,
      "max_attempts": 8,
      "occurred_at": "2026-04-01T10:18:40Z",
      "next_attempt_at": "2026-04-02T10:18:40Z",
      "last_attempt_at": "2026-04-02T10:18:40Z",
      "delivered_at": null,
      "last_error": "unexpected status: 502"
    }
  ]
}
}
POST/v1/webhooks/deliveries/:delivery_id/replay
X-API-Key200 OK

Teslimatı yeniden gönder

Başarısız (veya bekleyen olmayan) bir teslimat denemesini yeniden kuyruğa alır ve deneme bütçesini tazeler.

Yeniden gönderim, teslimatı hemen işlenecek şekilde bekleyen duruma alır ve en az bir deneme daha garantiler.

Zaten kuyrukta olan (`pending`/`processing`) bir teslimat yeniden gönderilemez ve `400` döner.

İstek

curl --request POST \
--url {{BASE_URL}}/v1/webhooks/deliveries/{{DELIVERY_ID}}/replay \
--header "X-API-Key: {{API_KEY}}"

Yanıt

JSON
{
"data": {
  "replayed": true
}
}

Operasyon önerileri

  • Webhook endpoint’inizde 2xx dışında dönen yanıtları loglayın.
  • failure_count artıyorsa hedef URL sağlığını kontrol edin.
  • Payload’ı işleyip kısa sürede 200 dönün; ağır işleri arka plan kuyruğuna alın.
  • Kalıcı başarısız teslimatları GET /v1/webhooks/deliveries?status=failed ile izleyin ve endpoint’iniz düzeldiğinde replay edin.