Webhook'larla olaylara tepki verin
Webhook URL'nizi ayarlayın, doğrulama testini geçin, her imzayı doğrulayın, 5 saniye içinde alındı yanıtı verin, kaçan olayları düzenli sorguyla kurtarın.
Webhook'lar, hesabınızda bir şey değiştiğinde sunucunuza bir HTTP POST gönderir: bir yorum gelir, bir işletme kaydı yayına girer, bir gönderi yayımlanır. Her hesap için tek bir URL vardır ve olay bazında abonelik yoktur; bu nedenle tek bir alıcı, belgelenmiş 18 olayın tamamını alır ve event alanına göre farklı işlem yolları izler.
1. Kontrol panelinde URL'yi ve sırrı ayarlayın
Önce 2. ve 3. adımlardaki alıcıyı oluşturun. Bir sır oluşturmak, imzalamayı zorunlu kılar ve uç noktanız doğrulamayı geçene kadar teslimatı duraklatır; bu nedenle hazır olmayan bir uç nokta tüm olayları engeller.
- HTTPS uç noktasını dağıtın. Özel adresler reddedilir ve yönlendirmeler izlenmez; bu nedenle öncesinde
wwwveya sondaki eğik çizgiden kaynaklanan bir yönlendirme bulunmayan nihai URL'yi kaydedin. 2. URL'yi kontrol panelinin Webhook'lar sayfasında kaydedin. 3. Bir imzalama sırrı oluşturun. Bu sır yalnızca bir kez gösterilir. SunucunuzdaLISTINGSAPI_WEBHOOK_SECRETolarak saklayın. 4. Uç noktayı doğrula'ya tıklayın. Platform, 2. adımdaki doğrulama sınamasını gönderir ve en fazla 10 saniye bekler.
URL'yi değiştirmek veya sırrı yeniden oluşturmak doğrulamayı sıfırlar; bu nedenle önce yeni sırrı dağıtın, ardından tekrar doğrulayın. Ayrıntılar Webhook'lara genel bakış sayfasındadır.
2. Doğrulama sınamasını yanıtlayın
Sınama, ayrılmış bir olay adı ve bir nonce içeren, imzalı bir POST isteğidir. Yalnızca nonce değerinin HMAC-SHA256 özetini küçük harfli onaltılık biçimde içeren bir 200 yanıtı verin.
{ "event": "endpoint.verification", "nonce": "9f2c1e642b1a4b0e9f1a2c7d5e8a1b3c", "timestamp": "2026-08-25T14:32:10Z"}3. Her teslimatta imzayı doğrulayın
Her imzalı istek X-ListingsAPI-Signature: sha256=<base64 HMAC-SHA256 of the raw body> taşır. Aldığınız baytların aynısı üzerinden aynı özeti hesaplayın, sabit sürede karşılaştırın ve eşleşme yoksa 401 döndürün. Önce JSON'u ayrıştırıp yeniden serileştirmeyin; bu işlem baytları değiştirir. Aşağıdaki iki alıcı da ham gövdeyi korur, imzayı doğrular, sınamayı yanıtlar ve yanıt vermeden önce olayı kalıcı olarak kaydeder.
import base64import hashlibimport hmacimport os from flask import Flask, abort, request app = Flask(__name__)SECRET = os.environ["LISTINGSAPI_WEBHOOK_SECRET"].encode() def signature_is_valid(raw_body: bytes, header: str | None) -> bool: digest = hmac.new(SECRET, raw_body, hashlib.sha256).digest() expected = "sha256=" + base64.b64encode(digest).decode() return hmac.compare_digest(header or "", expected) @app.post("/webhooks/listingsapi")def webhooks(): raw = request.get_data() # raw bytes, exactly as received if not signature_is_valid(raw, request.headers.get("X-ListingsAPI-Signature")): abort(401) body = request.get_json(silent=True) or {} # One-time handshake: hex digest of the nonce, not the body. if body.get("event") == "endpoint.verification": return hmac.new(SECRET, body["nonce"].encode(), hashlib.sha256).hexdigest(), 200 # Real event: durable write first, then acknowledge. Swap for your queue. inbox.put(raw) # must be committed before the 200 goes out return {"received": True}, 200import crypto from 'crypto';import express from 'express'; const app = express();const SECRET = process.env.LISTINGSAPI_WEBHOOK_SECRET as string; // express.raw keeps req.body as the untouched Buffer the HMAC covers.app.post('/webhooks/listingsapi', express.raw({ type: '*/*' }), async (req, res) => {const raw: Buffer = req.body;const digest = crypto.createHmac('sha256', SECRET).update(raw).digest('base64');const expected = Buffer.from('sha256=' + digest);const given = Buffer.from(req.get('X-ListingsAPI-Signature') ?? '');if (given.length !== expected.length || !crypto.timingSafeEqual(given, expected)) { return res.status(401).send('bad signature');} const body = JSON.parse(raw.toString('utf8')); // One-time handshake: hex digest of the nonce, not the body.if (body.event === 'endpoint.verification') { const answer = crypto.createHmac('sha256', SECRET).update(body.nonce).digest('hex'); return res.status(200).send(answer);} // Real event: durable write first, then acknowledge. Swap for your queue.await inbox.put(raw); // must be committed before the 200 goes outreturn res.status(200).json({ received: true });});Bir hesapta hiç sır oluşturulmamışsa teslimatlar imzasız gelir. Uygulamanızı bu moda dayandırmayın; yukarıdaki alıcı devreye girdikten sonra sırrı oluşturun.
4. Hızlıca alındı yanıtı verin ve asenkron işleyin
Bir teslimat, 5 saniyelik bağlantı ve 5 saniyelik okuma zaman aşımıyla tam olarak bir kez denenen tek bir POST isteğidir. Yeniden deneme, kademeli bekleme veya yeniden oynatma yoktur. İşleyiciniz 6 saniye sürerse veya sunucunuz 2 dakika boyunca çalışmazsa bu olaylar kaybolur. Yalnızca 2xx teslim edilmiş sayılır; 3xx, 4xx ve 5xx başarısızlık sayılır.
Bu nedenle istek işleme akışı yalnızca üç şey yapar: imzayı doğrular, ham gövdeyi bir kuyruğa veya tabloya yazar ve kaydın kesinleşmesini bekler, 200 döndürür. Veri zenginleştirme, REST API'ye yapılan geri çağrılar ve üçüncü taraflarla etkileşim kuran tüm işlemler kuyruktan yürütülür. listing.submission dışındaki tüm olaylar aynı yük yapısını paylaşır:
{ "event": "connection.location_connected", "timestamp": "2026-08-25T14:32:10Z", "account_id": 11073, "location_id": "279381", "data": { "platform": "google", "connected_account_id": "ba03bc4b-9f8c-4d3b-8e93-9628034c63cc" }}location_id, normalleştirilmeden aktarıldığı ve null olabildiği interaction.* olayları dışında, sayısal bir dizedir. listing.submission, timestamp veya account_id içermez ve status ile live_link alanlarını en üst düzeye yerleştirir. data içindeki isteğe bağlı alanlar, null olarak gönderilmek yerine atlanır. Tanımadığınız her event için 200 döndürün; belgelenmemiş olay aileleri de aynı URL'yi kullanır.
5. Yinelenen olayları ayıklayın ve sırasız teslimatı tolere edin
Aynı olayı iki kez alabilirsiniz ve aynı kaynak id değerini birçok kez alacaksınız: yorum her değiştiğinde interaction.review, aynı etkileşim id değeriyle yeniden tetiklenir ve hiçbir payload sürüm bilgisi taşımaz. Bu nedenle olayları asla id ve olay adı birleşimine göre yok saymayın; aksi takdirde gerçek düzenlemeleri kaçırırsınız. Ham gövdenin hash değerini hesaplayıp kısa bir zaman aralığında gelen tekrarları yok sayarak birebir kopyaları tespit edin, ardından tekrarlanan bir olayın aynı sonucu üretmesi için yazma işleminizde anahtar olarak kaynak id değerini kullanın.
import hashlibimport time SEEN: dict[str, float] = {} # use Redis with a TTL in productionWINDOW_SECONDS = 600 def is_duplicate(raw_body: bytes) -> bool: key = hashlib.sha256(raw_body).hexdigest() now = time.time() for k, seen_at in list(SEEN.items()): if now - seen_at > WINDOW_SECONDS: del SEEN[k] if key in SEEN: return True SEEN[key] = now return FalseSıralama da garanti edilmez. Olaylar bağımsız işlem hatlarından gelir, bu nedenle local_post.published, local_post.created olayından önce ulaşabilir ve timestamp zaman damgası değişiklik gerçekleştiğinde değil, teslimat oluşturulduğunda atanır. Sıralamanın önemli olduğu durumlarda kaynağı REST API üzerinden yeniden okuyun ve bu okumayı esas alın. Ayrıca profile.updated, konum başına 60 saniyede bir olayla sınırlanacak şekilde birleştirilir; bu nedenle olay ulaştığında yapılan yeniden okuma yalnızca ilk düzenlemeyi görür; zaman aralığı sona erdikten sonra tekrar okuyun.
6. Olayları diğer kılavuzlarla eşleştirin
event değerine göre dallanın ve her olayı ilgili kılavuz için yazdığınız koda aktarın. Webhook ne zaman olduğunu, o kılavuzdaki endpoint ise ne olduğunu bildirir.
Her olayın alan tabloları ve örnek payload içeriği olay referansında yer alır.
7. Kaçırılan bir olayı periyodik sorgulamayla telafi edin
Yeniden deneme olmadığı için her olay ailesinde, gerektiğinde başvurabileceğiniz ve kesin bilgiyi sağlayan bir okuma işlemi gerekir. Verileri yalnızca bir şeyler yanlış göründüğünde değil, düzenli aralıklarla uzlaştırın ve sırf bir olay gelmedi diye asla sıfır veya "değişiklik yok" yazmayın.
Kontrol paneli her denemeyi bir durumla kaydeder. success teslim edildiği anlamına gelir; bir HTTP kodu, timeout, network_error veya error, endpoint'inize ulaşıldığı ve işlemin başarısız olduğu anlamına gelir; blocked_url, endpoint_unverified ve rate_limited ise hiç istek yapılmadığı anlamına gelir, dolayısıyla çözüm kodda değil, yapılandırmada veya plandadır. Tam liste Teslimat davranışı bölümünde yer alır.
Sonraki adımlar
- Yorumları getirin: çoğu entegrasyonda ilk uygulanan ve yedek mekanizma olarak toplu taramayla desteklenen aile.
- İşletme kaydı durumunu ve canlı bağlantıları kontrol edin:
listing.submissiontarafından duyurulan bilgiler. - Başvuru kaynakları: Webhook'lara genel bakış, İmzalar ve doğrulama, Teslim davranışı, Olay başvuru kaynağı.