What's new, 21 October 2026: A customer who sends several messages before the bot's first reply no longer gets the opening sequence of a Chat Flow twice
WASync · Developers

Webhooks

Hesap başına tek imzalı uç nokta. İmza doğrulama, olay kataloğu, yeniden deneme politikası ve gizli anahtar rotasyonu.

WASync'i uç noktanıza yönlendirin; karşılığında bir imzalama gizli anahtarı alırsınız. whatsapp.events kullanılır. Uygulama kaydı ve onay adımı gerekmez.

Hesap başına tek webhook yuvası

Bir hesabın yalnızca bir webhook URL'si vardır. PUT /webhook bu URL'yi ayarlar. Farklı bir URL ile tekrar çağırırsanız ikinci bir uç nokta eklenmez, mevcut uç nokta taşınır; silme işlemi de yoktur. Birden fazla sistem olay almak istediğinde bu önem kazanır: bir n8n tetikleyicisi ile kendi backend'iniz yuvayı aynı anda kullanamaz. Olayları kendi tarafınızda dağıtın.

Uç noktayı ayarlayın

PUT /webhook
curl -X PUT https://developers.wasync.app/api/v1/webhook \
  -H "Authorization: Bearer $WASYNC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/wasync/webhook"}'
200 response
{
  "url": "https://example.com/wasync/webhook",
  "events": [
    "message.received",
    "message.status",
    "connection.disconnected",
    "connection.connected"
  ],
  "secret": "whsec_3f9a…"
}

Gizli anahtarı saklayın. Doğrulama kodunuzu yazarken ihtiyaç duyacağınız an bu olduğu için anahtar burada verilir. GET /webhook onu bir daha döndürmez; bu uç nokta bilerek gizli anahtar olmadan { url, events, secretSet } bildirir, çünkü okuma yolları günlüklere düşer ve destek taleplerine yapıştırılır. Anahtarı mı kaybettiniz? Yenisi için POST /webhook/rotate çağırın. URL, herkese açık bir ana makinede HTTPS olmalıdır; http, localhost ve özel aralıklar 400 invalid_webhook ile reddedilir.

Rotasyonda çakışma süresi yoktur: yeni gizli anahtar verildiği anda eskisi geçersiz olur. Yeni anahtarı hemen dağıtın. Aşağıdaki yeniden deneme politikası saatleri değil, saniyelerle ölçülen bir geçişi karşılar.

Olay kataloğu

EventFires whenDirection
message.receivedYetki verilmiş bir bağlantıya WhatsApp mesajı geldiğinde (metin veya medya).Yalnızca gelen. Sizin gönderdiğiniz mesajlar bunu tetiklemez.
message.statusGönderdiğiniz bir mesajın teslim durumu değiştiğinde.Yalnızca giden.
connection.disconnectedBir bağlantı çalışmayı bıraktığında ve sahibinin yeniden eşleştirmesi gerektiğinde.Bozuk kaldığı sürece tekrar tekrar değil, kesinti başına bir kez.
connection.connectedBozulmuş bir bağlantı yeniden çalıştığında.Bir kez ve yalnızca bozulduğu size bildirilmiş bağlantı için.

Bilinmeyen event değerlerini hata saymayın, yok sayın. Yeni türler yayına girmeden önce changelog sayfasında duyurulur.

Olaylar yalnızca qr bağlantıları için tetiklenir. cloud_api bağlantıları bugün webhook göndermez; bunlar için GET /messages ve GET /connections/{id} uç noktalarını sorgulayın.

Payload

POST your webhook URL
{
  "event": "message.received",
  "connection_id": "conn_8f3a21",
  "message": {
    "id": "cmqj3k2ab0001xyz",
    "wa_id": "[email protected]_3EB0A1B2C3",
    "from": "40700000000",
    "text": "Hello!",
    "media_url": null,
    "media_type": null,
    "timestamp": 1766138640000
  }
}
  • message.id: WASync'in kalıcı kimliği (GET /messages bu değeri id olarak döndürür). Teslimat en az bir kez yapıldığı için tekilleştirmeyi ve eşleştirmeyi bununla yapın.
  • message.wa_id: WhatsApp'ın kendi kimliği; bilinmiyorsa null. Yalnızca destek ve hata ayıklama içindir, asla tekilleştirme veya eşleştirme anahtarı olarak kullanılmaz.
  • from: yalnızca uluslararası biçimde rakamlar, + yok.
  • Yalnızca medya içeren mesajlarda text değeri null olur. Metin mesajlarında veya dosya herkese açık biçimde alınamıyorsa media_url / media_type değerleri null olur.
  • timestamp: ISO dizesi değil, sayı olarak epoch milisaniye.

message.status teslimatı, giden mesajlarınızdan birini günceller:

POST your webhook URL
{
  "event": "message.status",
  "connection_id": "conn_8f3a21",
  "message": {
    "id": "cmqj3k2ab0001xyz",
    "wa_id": "[email protected]_3EB0A1B2C3",
    "status": "read",
    "timestamp": 1766138641000
  }
}

İmzayı doğrulayın

Her teslimat X-WASync-Timestamp (epoch ms) ve X-WASync-Signature: sha256=<hex> başlıklarını taşır; imza HMAC-SHA256(timestamp + "." + rawBody, secret) değeridir. İmzayı ham gövde üzerinden, herhangi bir JSON ayrıştırmasından önce hesaplayın ve sabit zamanlı karşılaştırma yapın. Zaman damgası yaklaşık 5 dakikadan eski olan teslimatları reddedin; tekrar saldırılarını bu engeller.

verify.js
import crypto from "node:crypto";

// rawBody MUST be the exact bytes you received (verify BEFORE JSON.parse).
export function verifyWASync(headers, rawBody, secret) {
  const ts  = headers["x-wasync-timestamp"];
  const sig = headers["x-wasync-signature"];          // "sha256=<hex>"
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret)
          .update(`${ts}.${rawBody}`)
          .digest("hex");
  return Boolean(sig) &&
    crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}
receiver (express)
import express from "express";
import { verifyWASync } from "./verify.js";

const app = express();

// Capture the RAW body — signature verification needs the exact bytes.
app.post("/webhooks/wasync", express.raw({ type: "application/json" }), (req, res) => {
  if (!verifyWASync(req.headers, req.body.toString("utf8"), process.env.WASYNC_WEBHOOK_SECRET)) {
    return res.status(401).end();
  }
  // Reject stale deliveries (replay window) — 5 minutes is a sane tolerance.
  if (Math.abs(Date.now() - Number(req.headers["x-wasync-timestamp"])) > 5 * 60_000) {
    return res.status(401).end();
  }

  res.status(200).end(); // ACK fast — under 10s — then process async
  const { message } = JSON.parse(req.body.toString("utf8"));
  // ... your logic (dedupe on message.id — retries can deliver duplicates)
});

Teslimat ve yeniden deneme politikası

PropertyValue
Success criterionHerhangi bir 2xx. Yönlendirmeler dahil diğer her şey başarısızlıktır.
AttemptsOlay başına 3: hemen, ardından 500 ms sonra, ardından 1 sn sonra.
Per-attempt timeout10 saniye. Hızlı yanıt verin (ACK), işlemi asenkron yapın.
After the last failureOlay düşürülür. Uzun süreli bir yeniden teslim kuyruğu yoktur.
DuplicatesMümkündür. message.id ile tekilleştirin.
OrderingGaranti edilmez. Önemliyse timestamp ile sıralayın.
Signature on retriesAynı gövde ve aynı imza yeniden gönderilir; her denemeyi aynı şekilde doğrulayın.

Durum merdiveni

message.status değerleri tek yönlü bir merdiven oluşturur: sent (1) → delivered (2) → read (3). failed ise read ile devam etmeyen, sonlandırıcı bir sonuçtur.

Güncellemeleri tek yönlü uygulayın. Yeni durumu yalnızca sırası, sakladığınız durumdan yüksekse uygulayın. Bir mesaj delivered olmadan önce read ile gelebilir (gecikmiş teslimat) ve yinelenen olaylar mümkündür. Bir mesajı arayüzünüzde asla geriye taşımayın.

Mutabakat: güvenlik ağı olarak sorgulama

Teslimatlar üç kez denenir ve ardından düşürülür; bu nedenle webhook işleyiciniz hızlı yoldur, garanti değildir. Her 30–60 saniyede bir GET /messages?connectionId=… çağıran bir arka plan döngüsü çalıştırın ve işleyicinin görmediği her şeyi imleci (cursor) kullanarak artımlı çekimle ekleyin veya güncelleyin. Aynı döngü, uç noktanız kapalıyken gelen durum güncellemelerini de geriye dönük tamamlar.

Webhook'lar = gerçek zamanlı arayüz güncellemeleri. Sorgulama = doğruluk. İkisini birlikte çalıştırın.

Bağlantı yaşam döngüsü

POST your webhook URL
{
  "event": "connection.disconnected",
  "connection_id": "conn_8f2a…",
  "connection": {
    "id": "conn_8f2a…",
    "phone_number": "393331234567",
    "label": "Studio Rossi",
    "status": "disconnected",
    "needs_reconnect": true,
    "license_status": "active",
    "license_expires": "2027-08-08T09:00:00.000Z",
    "reason": "AUTH_LOST"
  },
  "timestamp": 1786000000000
}

Bir qr bağlantısı kimsenin kontrolünde olmayan nedenlerle çalışmayı bırakabilir: telefon bir gün kapalı kalır, WhatsApp başka bir cihazda açılır, oturum zaman aşımına uğrar. Numara bu durumda her iki yönde de sessizleşir ve bir olay olmazsa fark eden ilk kişi müşteriniz olur.

QR kodu bilerek payload'da yer almaz. WhatsApp QR'ı, taranınca oturum açtıran bir kimlik bilgisidir; onu okuyan herkes oturumu ele geçirebilir. Olay yalnızca yeniden eşleştirme gerektiğini bildirir. QR'ın kendisini kimliği doğrulanmış GET /connections/{id} çağrınızla alın ve ilk kurulumdaki gibi numaranın sahibine gösterin. Arayüzünüzü needs_reconnect alanına göre yönetin; yeni değerler ortaya çıkabileceği için reason alanını yalnızca bilgilendirici bir günlük ipucu olarak değerlendirin.

Tespit gecikmesi: bağlantı kopması, 5 dakikalık bir durum yoklamasıyla bulunur (~5 dk). Oturum kurtarılabilir görünüyorsa önce otomatik onarım denenir; bu yol yaklaşık 15–20 dakika sürer.

Olay almıyor musunuz?

  • Teslimat URL'si herkese açık bir ana makinede HTTPS mi? Localhost, özel IP'ler ve http:// reddedilir. Geliştirme sırasında bir tünel kullanın ve gerçekte neyin kayıtlı olduğunu GET /webhook ile kontrol edin.
  • Kimlik bilgisi whatsapp.events kapsamını taşıyor mu ve mesajın geldiği bağlantıyı kapsıyor mu?
  • Bağlantının lisansı hâlâ etkin mi? Süresi dolmuş lisans, olay gelmemesi ve gönderimlerde 402 anlamına gelir.
  • Uç noktanız 10 saniye içinde 2xx yanıtı veriyor mu? Yavaş işleyiciler başarısız görünür ve üç denemenin hepsini tüketir.
  • Bu bir qr bağlantısı mı? cloud_api bağlantıları webhook göndermez.
  • message.received yalnızca gelen mesajlarda tetiklenir; kendi gönderimleriniz asla tetiklemez.
Bu sayfa yardımcı oldu mu?

On this page