İçeriğe geç
Veri MerkeziVeri MerkeziAPI

Webhooklar

Gelen mesajları, teslim durumlarını ve hesap olaylarını sunucunuzda alın; aboneliği kurun, imzayı doğrulayın, yeniden denemeleri yönetin.

Webhooklar, WhatsApp tarafında olan olayları anında sunucunuza bildirir: müşteriden gelen mesaj, teslim ve okunma durumları, şablon onayı, numara ve kredi uyarıları. Bu sayfa aboneliği kurmayı, imzayı doğrulamayı ve teslimleri güvenilir biçimde işlemeyi anlatır.

#Nasıl çalışır

  1. Bir olay gerçekleşir (ör. müşteri mesaj gönderir).
  2. Veri Merkezi olayı abone olduğunuz adrese POST ile gönderir. Meta kaynaklı olaylarda (mesaj, durum, arama, form) gecikme genellikle 1 saniyenin altındadır; Meta dışından tetiklenen olaylar (credit.*, number.detached, number.purged, panelden contact.opt_out_changed, kampanya ya da otomasyon message.sent, privacy.erasure_completed) dakikalık kuyrukla en geç yaklaşık 1 dakikada gider.
  3. Sunucunuz 10 saniye içinde 2xx döndürür.
  4. 2xx dönmezse teslim artan aralıklarla yeniden denenir.

#1. Alıcı uç noktanızı hazırlayın

Uç noktanız:

  • Herkese açık bir https:// adres olmalıdır. İç ağ, özel, loopback ve link-local adresler reddedilir.
  • Yönlendirme (3xx) yapmamalıdır; yönlendirmeler izlenmez.
  • Ham istek gövdesini imza doğrulaması için ayrıştırmadan okuyabilmelidir.

#2. Abonelik oluşturun

curl -X POST https://api.verimerkezi.app/wa/webhooks \
  -H "Authorization: Bearer $VM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.example.com/wa-webhook",
    "events": ["message.received", "message.status.delivered", "message.status.read", "message.status.failed", "template.approved", "template.rejected"],
    "description": "Üretim botu"
  }'
Uyarı İmza anahtarı (secret, whsec_ ile başlar) yalnız oluşturma yanıtında döner. Hemen güvenli bir yere kaydedin. Kaybederseniz POST /webhooks/{id}/rotate-secret ile yeni anahtar alın.
  • Aynı adrese ikinci bir aktif abonelik oluşturulamaz (409 webhook_url_exists); mevcut aboneliği PATCH ile güncelleyin.
  • Bilinmeyen olay adı ya da boş liste 422 invalid_webhook döner.

#* (tüm olaylar) seçeneği

events alanını göndermezseniz ya da ["*"] gönderirseniz abonelik, o anki 12 klasik olaya genişletilerek kaydedilir. Sonradan eklenen olaylar bu aboneliğe kendiliğinden gelmez. Aşağıdaki tabloda "Hayır" ile işaretli olayları açıkça seçmelisiniz.

#3. İmzayı doğrulayın

Her istek şu başlıkları taşır:

METIN
Content-Type: application/json
X-VeriMerkezi-Signature-256: sha256=5d2a...e91f
X-VeriMerkezi-Timestamp: 1791619200
X-VeriMerkezi-Event-Id: 01a0c7ea-0d80-7d4d-aaf0-a2cc64a6bb00
X-VeriMerkezi-Event-Type: message.received
X-VeriMerkezi-Delivery-Attempt: 1
User-Agent: VeriMerkezi-Webhook/1.0

İmza şu formülle üretilir:

METIN
sha256= + HMAC_SHA256(anahtar, zaman_damgasi + "." + ham_govde)

Doğrulama adımları:

  1. Ham gövdeyi okuyun.
  2. X-VeriMerkezi-Timestamp 5 dakikadan eskiyse isteği reddedin.
  3. Beklenen imzayı hesaplayın ve başlıkla sabit zamanlı karşılaştırın.
const crypto = require('crypto');
const express = require('express');
const app = express();

app.post('/wa-webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const secret = process.env.VM_WEBHOOK_SECRET;
  const ts = req.header('X-VeriMerkezi-Timestamp') || '';
  const sig = req.header('X-VeriMerkezi-Signature-256') || '';
  const body = req.body.toString('utf8');

  if (Math.abs(Date.now() / 1000 - parseInt(ts, 10)) > 300) return res.status(401).end();

  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(ts + '.' + body).digest('hex');
  const a = Buffer.from(expected), b = Buffer.from(sig);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return res.status(401).end();

  const event = JSON.parse(body);
  kuyrugaEkle(event); // işi arka plana alın
  res.status(200).json({ ok: true });
});

Resmî SDK'larda hazır doğrulama yardımcısı vardır: Node.js VeriMerkeziClient.verifyWebhookSignature(secret, body, signature, timestamp), Python VeriMerkeziClient.verify_webhook_signature(...).

#Olay yapısı

Her olay aynı zarfı kullanır:

JSON
{
  "event": "message.received",
  "event_id": "01a0c7ea-0d80-7d4d-aaf0-a2cc64a6bb00",
  "event_type": "message.received",
  "occurred_at": "2026-10-10T10:00:00+03:00",
  "created_at": "2026-10-10 10:00:00",
  "data": {
    "wamid": "wamid.HBgMOTA1NTUxMjM0NTY3FQIAEhggQTNFQkQ2RjQ3MEIyQzk0RTE4AA==",
    "from": "905550000000",
    "user_id": null,
    "username": null,
    "name": "Ayşe Yılmaz",
    "type": "text",
    "text": "Merhaba, siparişim ne durumda?",
    "media": null,
    "timestamp": "1791619200",
    "phone_number_id": "1234567890",
    "contact": { "wa_id": "905550000000", "user_id": null, "username": null, "profile_name": "Ayşe Yılmaz" }
  }
}
AlanAçıklama
eventOlay adı.
event_idOlayın benzersiz kimliği (UUIDv7). Yeniden denemelerde aynı kalır.
event_type, created_atevent ve occurred_at alanlarının eş anlamlıları (geriye dönük uyumluluk).
occurred_atOlay zamanı, saat dilimi ofsetli ISO 8601.
dataOlaya özgü içerik.

data.text düz metindir; medyalı mesajda açıklamayı taşır. Dosya data.media.media_id ile indirilir. Buton yanıtlarında data.button alanı { payload, text } olarak dolar. Etkileşimli mesajlarda data.interactive alanı { type, name } taşır (ör. button_reply, list_reply; form yanıtında { "type": "nfm_reply", "name": "flow" }); düğme ve liste seçiminde ayrıca reply_id, reply_title ve (yalnız listede) reply_description gelir. Diğer mesajlarda nulldır.

#Olaylar

OlayNe zaman* kapsar
message.receivedMüşteri mesaj gönderdi.Evet
message.echoCoexistence: işletme telefondaki uygulamadan yazdı.Evet
message.status.sentMeta gönderimi kabul etti.Evet
message.status.deliveredMesaj teslim edildi.Evet
message.status.readMesaj okundu.Evet
message.status.failedGönderim başarısız; errors alanına bakın.Evet
template.approvedŞablon onaylandı.Evet
template.rejectedŞablon reddedildi; reason / rejected_reason ve rejected_reason_text alanlarına bakın.Evet
template.flaggedŞablon işaretlendi.Evet
template.pausedŞablon durduruldu.Evet
quality.changedNumara kalite puanı değişti.Evet
account.alertMeta hesap uyarısı.Evet
message.revokedBir mesaj silindi.Hayır
message.editedBir mesaj düzenlendi.Hayır
message.sentAPI dışından mesaj gönderildi (panel, otomasyon, kampanya).Hayır
message.historyCoexistence geçmiş aktarımından eski mesaj.Hayır
number.status_changedNumara bağlandı, koptu, işaretlendi ya da kısıtlandı.Hayır
number.name_changedGörünen ad için Meta kararı geldi.Hayır
number.detachedNumara hesabınızdan ayrıldı.Hayır
number.purgedAyrılan numaranın kayıt silmesi tamamlandı.Hayır
credit.lowKredi eşiğin altına düştü.Hayır
credit.exhaustedKredi tükendi.Hayır
privacy.erasure_completedKVKK silme talebi tamamlandı.Hayır
contact.opt_out_changedKişinin abonelik durumu değişti.Hayır
template.category_changedMeta şablon kategorisini değiştirdi.Hayır
template.quality_changedŞablon kalite puanı değişti.Hayır
template.disabledŞablon Meta tarafından devre dışı bırakıldı.Hayır
template.reinstatedDuraklatma ya da işaret kalktı; şablon yeniden kullanılabilir.Hayır
call.connectGelen arama çalıyor ya da başlattığınız arama yanıtlandı.Hayır
call.statusArama durumu değişti.Hayır
call.terminateArama bitti.Hayır
call.permission_replyMüşteri arama izni isteğini yanıtladı.Hayır
flow.completedMüşteri formu gönderdi.Hayır
flow.status_changedForm durumu değişti.Hayır
contact.syncedCoexistence: telefondaki uygulama rehberinde kişi eklendi, düzenlendi ya da silindi (toplu).Hayır
message.status.deletedMeta mesajın silindiğini bildirdi.Hayır
template.pending, template.in_appeal, template.lockedŞablon incelemede, itirazda ya da kilitli.Hayır
template.archived, template.unarchivedŞablon arşivlendi ya da arşivden çıktı.Hayır
template.deleted, template.pending_deletionŞablon silindi ya da silinmek üzere.Hayır
template.limit_exceededHesap şablon sınırına ulaştı.Hayır
number.limit_changedNumaranın Meta mesaj limiti değişti.Hayır
account.review_changedWhatsApp işletme hesabı incelemesi sonuçlandı.Hayır

Şablon durum olaylarının (template.approved, rejected, paused, flagged, disabled, reinstated) yükünde previous_status, rejected_reason, rejected_reason_text, quality_score, occurred_at ve source (webhook ya da sync) bulunur; eski reason alanı korunur. Meta bildirimi gelmediyse geçiş şablon senkronunda yakalanır; aynı geçiş iki kez yayınlanmaz. Hata ve uyarı taşıyan olaylarda (message.status.failed, şablon olayları, account.alert, number.name_changed, account.review_changed) Türkçe message_tr / reason_tr ve action_tr alanları gelir; Meta'nın ham alanları aynen korunur. Sözlüğün tamamı: GET /meta/error-codes.

Her olayın örnek yükü: Webhook olayları.

#Teslim durumunu eşleştirin

message.status.* olayları wamid, recipient, timestamp, errors, phone_number_id, conversation, pricing, biz_opaque_callback_data, user_id ve direction alanlarını taşır. Gönderimde Idempotency-Key kullandıysanız biz_opaque_callback_data bu anahtarı içerir; olayı kendi kaydınızla ek alan göndermeden eşleştirebilirsiniz.

#Yeniden deneme

DenemeBekleme
1Olay anında
2+1 dakika
3+5 dakika
4+30 dakika
5+2 saat
6+12 saat
7+24 saat
  1. deneme de başarısız olursa teslim ölü (dead-letter) duruma geçer; toplam süre yaklaşık 38,6 saattir. Son 7 gündeki ölü teslimleri yeniden kuyruğa almak için:
BASH
curl -X POST https://api.verimerkezi.app/wa/webhooks/redeliver \
  -H "Authorization: Bearer $VM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "since": "2026-10-08T00:00:00Z" }'
İpucu Aynı olay birden fazla kez gelebilir. event_id değerini saklayın ve daha önce işlediğiniz olayları atlayın.

#Test edin

  • POST /webhooks/{id}/test aboneliğe anında imzalı bir test.ping olayı gönderir ve sonucu döndürür.
  • Test anahtarıyla POST /test/inbound sahte bir gelen mesaj üretir. Bkz. Test modu.

#İmza anahtarını döndürün

Aboneliği silmeden imza anahtarını yenileyin:

BASH
curl -X POST https://api.verimerkezi.app/wa/webhooks/17/rotate-secret \
  -H "Authorization: Bearer $VM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "grace_seconds": 86400 }'

Yeni anahtar yalnız bu yanıtta döner. grace_seconds süresince her teslimde birincil X-VeriMerkezi-Signature-256 başlığı yeni anahtarla, ek X-VeriMerkezi-Signature-256-Previous başlığı eski anahtarla imzalanır. Alıcınızı yeni anahtara geçirene kadar iki imzadan birini kabul edin; böylece hiçbir teslim düşmez.

#Aboneliği yönetin

İşlemUç
ListeleGET /webhooks
Güncelle (adres, olaylar, etkinlik, ad)PATCH /webhooks/{id}
PasifleştirPATCH /webhooks/{id} ile { "active": false }
SilDELETE /webhooks/{id}
Teslim sağlığıGET /health

Kaçan olayları mesaj kayıtlarından da geri alabilirsiniz: GET /messages. Bkz. Mesaj gönderme.

Bu sayfa işinize yaradı mı?

Sorunuz mu var? Destek talebi açın ya da [email protected] adresine yazın.