Kacper Tomasiak 𝐇𝐔𝐊𝐍𝐀̨Ł w kwalifikacjach❗
ℹ️ Początek sobotniego konkursu indywidualnego w o 13:35. Transmisja w Eurosporcie 2 🔜
7 results for “HuK”
Kacper Tomasiak 𝐇𝐔𝐊𝐍𝐀̨Ł w kwalifikacjach❗
ℹ️ Początek sobotniego konkursu indywidualnego w o 13:35. Transmisja w Eurosporcie 2 🔜
https://www.europesays.com/fi/380552/ Espanjan itärannikko hukkui veteen – Taustalla pelätty ilmiö #24h #FI #Finland #Finnish #Lämpötila(Sää) #meret #Meteorologia #Sää #Science #Suomi #Tiede #Vesistöt
https://www.europesays.com/ee/304427/ VIDEO | Pariisi eeslinna ühe hukkunuga tulistamist uuritakse terrorismikuriteona #BreakingNews #BreakingNews #EE #Eesti #EestiKeel #Estonia #Estonian #FeaturedNews #FeaturedNews #Headlines #LatestNews #LatestNews #Maailm #News #Pariis #PopulaarseimadLood #Prantsusmaa #SebastienLecornu #TopStories #TopStories #ÜldisedUudised #Uudised #ViimasedUudised #World #WorldNews #WorldNews
https://www.europesays.com/ee/304777/ „Auto seisis keset teed!“ Viimsis süttinud Aston Martinis hukkus kogenud ärimees #BreakingNews #BreakingNews #DigilehtOhtulehtEe #EE #Eesti #EestiKeel #Estonia #Estonian #FeaturedNews #FeaturedNews #Headlines #LatestNews #LatestNews #News #PopulaarseimadLood #TopStories #TopStories #ÜldisedUudised #Uudised #ViimasedUudised
https://www.europesays.com/no/394999/ Farmen, TV2 | «Farmen»-Aurora fikk hukommelsestap: – Livredd #art #Entertainment #farmen #NO #Norge #Norway #RealityTV #story #tv2 #Underholdning
Reklam panelinde "lead" sayısı artarken satış ekibinin "bu lead'lerin hiçbiri alıcı değil" demesi, dijital pazarlamada en sık duyduğum cümlelerden biri. Ben Koray Yalçın; lead yönetimi, CRM mimarisi, dijital pazarlama ve yapay zekâ destekli otomasyonların kesiştiği projelerde çalışıyorum, özellikle gayrimenkul ve B2B tarafında. Bu cümlenin arkasında çoğu zaman teknik bir kopukluk yatar: Reklam platformu formun doldurulduğunu bilir, ama o kişinin üç hafta sonra showroom'a gelip sözleşme imzaladığını bilmez. Algoritma da doğal olarak form doldurmaya en yatkın kitleyi bulmaya devam eder, satın almaya en yatkın kitleyi değil.
Bu boşluğu kapatmanın yolu, CRM'de oluşan "nitelikli lead", "randevu gerçekleşti" ve "satış kapandı" gibi olayları sunucu tarafından Meta Conversions API'ye (CAPI) geri göndermek. Bu yazı bir pazarlama rehberi değil; bu köprüyü kodla kurarken karşılaştığım tasarım kararlarını anlatıyor: kullanıcı verisinin normalizasyonu ve hash'lenmesi, olay kimliği ile tekilleştirme, zaman penceresi, yeniden deneme stratejisi ve izin kontrolü. Örnekler Python ama mantık her dile taşınabilir.
Önce olay sözlüğü: Hangi CRM durumu hangi olay?
Kod yazmadan önce yapılması gereken en önemli iş, CRM'deki aşamaları sınırlı ve anlamlı bir olay listesine eşlemek. Her aşama değişikliğini Meta'ya göndermek gürültü üretir. Benim kullandığım tipik eşleme şöyle:
LeadGenellikle tarayıcı pikseli zaten gönderirTemsilci "nitelikli" işaretlediQualifiedLead (özel olay)Ölçüm kurgusuna göre seçilirRandevu gerçekleştiSchedule veya özel olay"Randevu alındı" değil, "gerçekleşti"Sözleşme imzalandıPurchasevalue ve currency zorunlu düşünülmeli
Burada kritik nokta şu: Olay, CRM'de geri alınabilir bir alan değişikliğinden değil, kalıcı bir olaydan tetiklenmeli. Temsilci bir fırsatı yanlışlıkla "kazanıldı" yapıp beş dakika sonra geri alırsa, gönderilmiş bir Purchase olayını geri çekmenin pratik bir yolu yoktur. Bu yüzden ben aşama değişikliklerini doğrudan API'ye bağlamak yerine bir conversion_events tablosuna yazıyorum ve gönderimi kısa bir bekleme süresinden sonra yapan ayrı bir işçiye bırakıyorum.
CREATE TABLE conversion_events (
id BIGSERIAL PRIMARY KEY,
event_id TEXT NOT NULL UNIQUE, -- tekilleştirme anahtarı
event_name TEXT NOT NULL,
lead_id BIGINT NOT NULL REFERENCES leads(id),
occurred_at TIMESTAMPTZ NOT NULL, -- olayın gerçek zamanı
value_minor BIGINT, -- kuruş/cent cinsinden
currency CHAR(3),
status TEXT NOT NULL DEFAULT 'pending', -- pending, sent, failed, skipped
attempts INT NOT NULL DEFAULT 0,
last_error TEXT,
send_after TIMESTAMPTZ NOT NULL DEFAULT now() + interval '15 minutes'
);
send_after alanı, "yanlışlıkla kazanıldı" senaryosuna karşı küçük bir tampon. İşçi göndermeden önce fırsatın hâlâ aynı aşamada olup olmadığını kontrol eder; değilse kaydı skipped yapar.
Kullanıcı verisini normalize etmek: Hash'ten önce gelen asıl iş
CAPI, e-posta ve telefon gibi kişisel verilerin SHA-256 ile hash'lenmiş olarak gönderilmesini bekler. Çoğu ekip hashlib.sha256(email) yazıp işi bitmiş sayar. Sorun şu ki hash, girdideki tek bir boşluğu ya da büyük harfi tamamen farklı bir değere dönüştürür. Meta tarafındaki eşleşme de birebir karşılaştırmayla çalıştığı için normalizasyon hatası, sessizce düşük eşleşme oranı olarak geri döner.
Benim sahada gördüğüm en yaygın hatalar:
0533 ..., +90 533 ..., 90533... gibi farklı biçimlerde saklanması.import hashlib
import re
import phonenumbers
def _sha256(value: str) -> str:
return hashlib.sha256(value.encode("utf-8")).hexdigest()
def norm_email(raw: str | None) -> str | None:
if not raw:
return None
v = raw.strip().lower()
return v if "@" in v else None
def norm_phone(raw: str | None, default_region: str = "TR") -> str | None:
if not raw:
return None
try:
num = phonenumbers.parse(raw, default_region)
except phonenumbers.NumberParseException:
return None
if not phonenumbers.is_valid_number(num):
return None
# Ülke kodu dahil, yalnızca rakamlar, başta + yok
return phonenumbers.format_number(num, phonenumbers.PhoneNumberFormat.E164).lstrip("+")
def build_user_data(lead) -> dict:
ud = {}
if (e := norm_email(lead.email)):
ud["em"] = [_sha256(e)]
if (p := norm_phone(lead.phone, lead.country_code or "TR")):
ud["ph"] = [_sha256(p)]
if lead.external_id:
ud["external_id"] = [_sha256(str(lead.external_id))]
# Tarayıcıdan yakalandıysa hash'lenmeden gönderilen alanlar
if lead.fbc:
ud["fbc"] = lead.fbc
if lead.fbp:
ud["fbp"] = lead.fbp
return ud
Bir detay daha: Kıbrıs gibi çok uluslu alıcı kitlesi olan pazarlarda default_region sabit "TR" olamaz. İngiltere'den gelen bir lead'in 07... ile başlayan numarası Türkiye varsayımıyla ayrıştırılırsa geçersiz sayılır ya da daha kötüsü yanlış bir numara üretir. Bu yüzden formda ülke seçimini ayrı bir alan olarak tutmak, normalizasyonun doğruluğunu doğrudan etkiliyor.
fbc, fbp ve external_id: Eşleşmeyi güçlendiren köprüler
E-posta ve telefon tek başına çoğu zaman yeterli olur, ama iki alan eşleşme kalitesini belirgin biçimde artırır:
fbc: Reklam tıklamasıyla gelen fbclid parametresinden türetilen tıklama kimliği. Kullanıcı sitenize reklamla geldiyse _fbc çerezinde bulunur.fbp: Pikselin tarayıcıya yazdığı _fbp çerezi.Bu değerler satış anında değil, lead oluştuğu anda yakalanmalı. Üç hafta sonra sözleşme imzalanırken kullanıcı tarayıcısında değil, satış ofisinde oturuyor. Bu nedenle form gönderiminde bu iki çerezi gizli alanlarla alıp lead kaydına yazmak, sonraki tüm offline olayların kalitesini belirliyor. Meta Lead Ads formlarından gelen lead'lerde ise platformun verdiği lead kimliğini saklamak aynı işi görür; CRM entegrasyonu senaryolarında bu kimliğin olayla birlikte gönderilmesi eşleşmeyi doğrudan kurar.
external_id ise sizin CRM'deki kişi kimliğiniz. Hem piksel hem sunucu tarafında aynı değer (hash'lenmiş olarak) gönderilirse, aynı kişinin farklı olaylarını birbirine bağlamak kolaylaşır.
event_id ile tekilleştirme: Aynı satışı iki kez saymamak
CAPI'de iki ayrı tekrar riski vardır:
Lead olayını hem tarayıcı pikseli hem sunucu gönderiyorsa, iki olay aynı event_name ve aynı event_id ile gelmelidir. Meta bu ikiliyi tek olay olarak sayar.event_id korunmalıdır.event_id rastgele üretilmemeli; olaydan deterministik olarak türetilmeli. Benim tercihim şu:
def make_event_id(event_name: str, lead_id: int, stage_version: int) -> str:
return f"{event_name.lower()}:{lead_id}:{stage_version}"
stage_version, fırsatın o aşamaya kaçıncı kez girdiğini tutan bir sayaç. Böylece bir müşteri gerçekten ikinci bir daire satın alırsa yeni bir Purchase üretilir, ama aynı satış için yapılan iki gönderim aynı kimliği taşır. Veritabanındaki UNIQUE kısıtı da aynı olayın kuyruğa iki kez girmesini engeller.
Zaman penceresi: event_time "şimdi" değil, olayın gerçek zamanıdır
Offline satış entegrasyonunda en sık yapılan hata, event_time alanına gönderim anını yazmak. Olayın değeri, ne zaman gerçekleştiğiyle anlam kazanır; sözleşme salı günü imzalandıysa ve işçi çarşamba gönderdiyse, event_time salı olmalıdır.
İkinci önemli nokta, Meta'nın kabul ettiği geriye dönük pencere. Bu yazıyı yazarken belgeler, olayların gerçekleştikten sonra sınırlı bir süre içinde gönderilmesini istiyor ve bu süre action_source değerine göre farklılaşabiliyor. Sınır zamanla değişebildiği için kodda sabit bir sayı gömmek yerine yapılandırmadan okuyorum ve pencereyi aşmış olayları göndermeyi denemek yerine skipped olarak işaretleyip raporluyorum:
from datetime import datetime, timezone, timedelta
MAX_AGE = { # yapılandırmadan gelir; güncel değeri resmi belgeden doğrulayın
"system_generated": timedelta(days=7),
"physical_store": timedelta(days=7),
}
def is_sendable(evt, action_source: str) -> bool:
age = datetime.now(timezone.utc) - evt.occurred_at
return age <= MAX_AGE.get(action_source, timedelta(days=7))
Bu kontrolün pratik sonucu, satış ekibinin CRM'i zamanında güncellemesinin bir veri kalitesi konusu haline gelmesi. Sözleşmeyi on gün sonra sisteme giren bir ekip, reklam optimizasyonuna o satışı hiç kazandıramaz. Bu yüzden ben bu entegrasyonu kurarken satış ekibi için "sözleşme imzalandığı gün CRM'de kapanır" kuralını da süreç tarafında yazılı hale getiriyorum.
action_source ve olay gövdesi
Sunucudan giden olay gövdesinin iskeleti şöyle:
def build_payload(evt, lead, action_source="system_generated") -> dict:
data = {
"event_name": evt.event_name,
"event_time": int(evt.occurred_at.timestamp()),
"event_id": evt.event_id,
"action_source": action_source,
"user_data": build_user_data(lead),
}
if evt.value_minor is not None:
data["custom_data"] = {
"value": evt.value_minor / 100,
"currency": evt.currency,
}
return {"data": [data]}
action_source alanı, olayın nerede gerçekleştiğini anlatır. Web formu için website, CRM'in kendi ürettiği aşama değişiklikleri için system_generated, satış ofisinde imzalanan sözleşme için physical_store gibi değerler kullanılabilir. Doğru değeri seçmek hem raporlamayı hem de uygulanan kuralları etkiler; bu yüzden olay sözlüğünde her olayın action_source değerini de sabitliyorum.
value tarafında bir uyarı: Konut gibi yüksek tutarlı satışlarda gerçek sözleşme bedelini göndermek bazı ekiplerin istemediği bir şey olabilir. Bu durumda tutarlı bir "değer skalası" (örneğin ürün tipine göre sabit bir puan) kullanmak, optimizasyon sinyalini korurken hassas ticari bilgiyi dışarı çıkarmamanın bir yolu. Hangisi seçilirse seçilsin, tutarlılık önemlidir; ay ortasında yöntemi değiştirmek raporları kırar.
Gönderim işçisi: Toplu istek, yeniden deneme ve hata sınıflandırma
İşçinin görevi basit görünür ama hata sınıflandırması önemli:
import requests, time
def send_batch(events, leads, pixel_id, token):
payload = {"data": []}
for evt in events:
payload["data"].extend(build_payload(evt, leads[evt.lead_id])["data"])
url = f"https://graph.facebook.com/{API_VERSION}/{pixel_id}/events"
resp = requests.post(url, params={"access_token": token}, json=payload, timeout=15)
if resp.status_code >= 500 or resp.status_code == 429:
raise RetryableError(resp.text) # üstel bekleme ile tekrar
if resp.status_code >= 400:
raise PermanentError(resp.text) # kaydı failed yap, alarm üret
return resp.json()
Kurallarım:
event_id aynı kalır.İzin ve veri minimizasyonu
Kişisel veriyi üçüncü bir platforma, hash'lenmiş olsa bile, göndermek bir hukuki dayanak gerektirir. Türkiye'de KVKK, Avrupa'dan gelen alıcılar için GDPR bu çerçeveyi çizer. Benim teknik tarafta uyguladığım kurallar:
Bu bir hukuki görüş değil; ama izin kontrolünü kodun içine bir if olarak gömmek, sonradan "kimin verisi nereye gitti" sorusuna cevap verebilmeyi sağlıyor.
Ölçüm: Entegrasyon gerçekten çalışıyor mu?
Kurulumdan sonra baktığım üç gösterge var:
occurred_at ile gönderim zamanı arasındaki fark. Satış ekibinin CRM'i geç güncellemesi burada hemen görünür.Purchase sayısı arasındaki fark. Birebir tutması beklenmez, ama fark aniden açılırsa token, izin veya şema tarafında bir şey bozulmuştur.Sıkça Sorulan Sorular
Piksel zaten kurulu, neden ayrıca CAPI gerekiyor?
Piksel yalnızca tarayıcıda olan olayları görür. Telefonla kapanan, satış ofisinde imzalanan ya da haftalar sonra gerçekleşen satışlar tarayıcıda olmaz. CAPI, bu olayları CRM'den doğrudan göndermeyi sağlar.
E-postayı hash'lemeden göndersem Meta kendisi hash'lemez mi?
Bazı resmi kütüphaneler hash işlemini kendisi yapar, ama kendi kodunuzla gönderiyorsanız normalizasyon ve hash sizin sorumluluğunuzdadır. Kontrolü elde tutmak, hatayı ayıklamayı da kolaylaştırır.
Aynı satışı yanlışlıkla iki kez gönderirsem ne olur?
Aynı event_name ve event_id ile gönderildiyse tekilleştirilir. Farklı kimlikle gönderildiyse iki satış sayılır; bu yüzden kimliği deterministik üretmek önemlidir.
Satış on gün sonra CRM'e girildi, yine de gönderebilir miyim?
Meta'nın kabul ettiği geriye dönük pencereyi aşmışsa olay reddedilir. Güncel sınırı resmi belgeden kontrol edin ve asıl çözümü süreçte arayın: satış, gerçekleştiği gün CRM'e girilmeli.
Gerçek sözleşme bedelini göndermek zorunda mıyım?
Hayır. Tutarlı bir değer skalası da optimizasyon için yeterli sinyal üretebilir. Önemli olan, seçilen yöntemin zaman içinde değişmemesi.
Bu entegrasyon küçük ekipler için fazla karmaşık değil mi?
Kuyruk tablosu, normalizasyon fonksiyonu ve bir gönderim işçisi birkaç yüz satır kod demek. Karşılığında reklam algoritması form dolduranları değil, satın alanları öğrenmeye başlar. Lead hacmi düşük ama satış değeri yüksek sektörlerde bu fark özellikle belirgin.
Koray Yalçın — lead yönetimi, CRM mimarisi, dijital pazarlama ve gayrimenkul teknolojileri (PropTech) üzerine çalışıyorum. CRM'den reklam platformlarına geri bildirim kurarken sizi en çok hangi adım zorladı? Yorumlarda konuşalım.#backend #crm #api #marketing #software #coding #development #engineering #inclusive #community
CRM'de kapanan satışı Meta Conversions API'ye geri bildirmek: Hash, event_id ve zaman penceresi üzerine geliştirici notları