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.
| Event | Ne zaman tetiklenir |
|---|---|
message.sent | Mesaj WhatsApp’a iletildi |
message.delivered | Alıcının cihazına teslim edildi |
message.read | Alıcı mesajı okudu |
message.failed | Gönderim başarısız oldu |
message.received | Cihaza gelen (inbound) mesaj alındı |
device.connected | Cihaz bağlandı |
device.disconnected | Cihaz bağlantısı koptu |
device.status | Cihaz durumu değişti |
subscription.renewed | Abonelik yenilendi veya dönem güncellendi |
subscription.cancelled | Abonelik iptal edildi |
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.
/v1/webhooksWebhook 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
{
"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"
}
}/v1/webhooksWebhookları 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
{
"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"
}
]
}
}/v1/webhooks/:webhook_idWebhook 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
{
"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ırdata.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-Event | Event adı (ör. message.delivered) |
X-WA2Notify-Signature | Ham istek gövdesinin HMAC-SHA256 imzası (hex) |
X-WA2Notify-Timestamp | Olayı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.
/v1/webhooks/deliveriesTeslimat 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
{
"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"
}
]
}
}/v1/webhooks/deliveries/:delivery_id/replayTeslimatı 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
{
"data": {
"replayed": true
}
}Operasyon önerileri
- Webhook endpoint’inizde
2xxdışında dönen yanıtları loglayın. failure_countartıyorsa hedef URL sağlığını kontrol edin.- Payload’ı işleyip kısa sürede
200dönün; ağır işleri arka plan kuyruğuna alın. - Kalıcı başarısız teslimatları
GET /v1/webhooks/deliveries?status=failedile izleyin ve endpoint’iniz düzeldiğinde replay edin.
