# Veriyi Hazırlama

NDJSON formatı ve zorunlu alanlar
Kaynak: https://selwise.com/docs/data-import/preparing-data

Aktarım dosyaları **NDJSON** biçimindedir: her satır bağımsız bir JSON nesnesi.

> **Örneği panelden alın**
>
> Aktarım sayfasında, seçtiğiniz veri setine göre hazır bir örnek dosya gösterilir. Üç düğmesi var: **Bu örneği kullan** örneği yapıştırma kutusuna doldurur, **Kopyala** panoya alır, **Dosya olarak indir** `.ndjson` olarak kaydeder.
>
> En hızlı öğrenme yolu, örneği kullanıp doğrulamayı çalıştırmaktır: hiçbir şey yazılmadan raporun neye benzediğini, hangi sayıları verdiğini ve ürün kodu eşleşme oranının nasıl göründüğünü görürsünüz. Aynı sayfada her alanın ne işe yaradığını anlatan tablo da vardır.

## Neden NDJSON

Tek bir dev JSON dizisi, okunmak için tamamının belleğe alınmasını gerektirir. NDJSON satır satır işlenir; dosya boyutu ne olursa olsun bellek sabit kalır.

```
{"customerId":"C-1","email":"ali@ornek.com"}
{"customerId":"C-2","email":"ayse@ornek.com"}
```

Satırlar arasında virgül yok, dosyanın etrafında köşeli parantez yok.

## Kullanıcı kaydı

İki veri seti vardır: **kullanıcılar** ve **siparişler**. Kullanıcı dosyasında bir satır bir kişidir ve platformun o kişi hakkında okuduğu her şeyi taşır — kimliği, e-postası, telefonu, adı, özellikleri ve e-posta/SMS izni. Ayrı bir "iletişim listesi" dosyası yoktur: sitenizin kullanıcı dışa aktarımı zaten bunların hepsini tek kayıtta verir.

```json
{
  "customerId": "MUSTERI-1001",
  "email": "ali.yilmaz@ornek.com",
  "phone": "+905321112233",
  "firstName": "Ali",
  "lastName": "Yılmaz",
  "locale": "tr",
  "firstSeenAt": "2019-03-14T09:12:00+03:00",
  "lastSeenAt": "2026-08-30T18:44:00+03:00",
  "traits": {
    "loyalty_tier": "gold",
    "loyalty_points": 4820,
    "city": "İstanbul",
    "birthday": "1990-05-14"
  },
  "consent": {
    "email": {
      "optIn": true,
      "at": "2023-06-18T10:41:00+03:00",
      "source": "2023 bülten formu",
      "evidence": "https://ornek.com/bulten — CRM kaydı #4821"
    },
    "sms": {
      "optIn": true,
      "at": "2024-09-11T12:30:00+03:00",
      "evidence": "Sipariş onay kutusu, KVKK metni v3",
      "iys": "registered"
    }
  }
}
```

| Alan | Zorunlu | Açıklama |
|---|---|---|
| `customerId` | Evet* | Sizin sistemdeki müşteri kimliği; mağazada `Selwise.identify()` ile geçirdiğinizle **aynı** olmalı. `externalId`, `userId` ve `siteUserId` adları da kabul edilir. En fazla 64 karakter. |
| `email` | Evet* | Düz e-posta. Mesaj gönderilebilmesi için gereken tek biçim budur; şifreli saklanır, hiçbir ekranda tam hâliyle gösterilmez. |
| `emailHash` | — | E-postayı paylaşmak istemiyorsanız 64 karakterlik SHA-256 karması. Yalnızca kimlik eşleşmesi içindir — karmaya e-posta gönderilemez. |
| `phone` | — | `+905321112233` ya da Türkiye yazımıyla `05321112233`. SMS için gerekir. |
| `firstName`, `lastName`, `fullName` | — | Ad. E-postada kişiye adıyla hitap edilir; profil sayfasında özellik olarak görünür. `fullName` verilmezse ad ve soyaddan oluşur. |
| `locale` | — | Dil kodu (`tr`, `en`). Mesajdaki fiyatlar bu dile göre biçimlenir. |
| `firstSeenAt` | — | Kişinin ilk göründüğü an. Kayıtlı değerle harmanlanır; yalnızca geriye çekebilir. |
| `lastSeenAt` | — | Son hareket. Yalnızca ileriye taşıyabilir. |
| `traits` | — | Segmentlerde ve senaryolarda kullanacağınız serbest alanlar |
| `consent` | — | E-posta ve SMS izni; aşağıda |

\* `customerId` ya da `email` (veya `emailHash`) — en az biri. `customerId` yoksa kişi e-postasıyla tanınır; aynı adresle sonradan gelen gerçek kimlik bu kaydı kendine katar. Yalnızca telefonu olan satır kimlik sayılmaz ve reddedilir.

### Her alan nereye yazılır

Tek kayıt, platformun kişiyi okuduğu her yere dağıtılır:

| Nereye | Ne | Kim okur |
|---|---|---|
| Kullanıcı profili | Kimlik, e-posta ve telefon karması, ilk/son görülme | Siparişler, segmentler, senaryolar, profil sayfası |
| Özellikler | `traits`, ayrıca `firstName`, `lastName`, `fullName` | Segment ve senaryo koşulları, profil sayfası |
| İletişim kaydı | Şifreli adres ve numara, ad, dil, izin ve kanıtı, İYS durumu | E-posta ve SMS gönderimi, kişiler ekranı, erişilebilirlik dalı |

Adı iki yere birden yazılmasının sebebi budur: e-postadaki ad hitabı iletişim kaydından, profil sayfası ve segmentler özelliklerden okunur.

### İzin: consent

```json
"consent": {
  "email": { "optIn": false, "at": "2025-01-20T08:00:00+03:00" },
  "sms":   { "optIn": true, "at": "2024-02-03T16:05:00+03:00", "evidence": "Islak imzalı form", "iys": "registered" }
}
```

| Alan | Zorunlu | Açıklama |
|---|---|---|
| `optIn` | Evet | `true` izin verildi, `false` geri çekildi ya da reddedildi |
| `at` | Onayda evet | İznin verildiği (geri çekildiği) an |
| `evidence` | Onayda evet | İzni neyin kanıtladığı: form bağlantısı, izin metni, imzalı kaydın yeri |
| `source` | — | Kendi kelimelerinizle nereden geldiği, örn. "2023 bülten formu". Kanalda [İYS entegrasyonu](/docs/customers/iys) açıksa ve burada geçerli bir İYS kaynağı (`HS_WEB`, `HS_FIZIKSEL_ORTAM` gibi) yazıyorsa, `at` tarihiyle birlikte İYS'ye bildirilir; İYS üç iş gününden eski onayı kabul etmez. |
| `ip` | — | Kaydınızda varsa iznin verildiği IP |
| `iys` | — | Yalnızca `sms`: `registered`, `rejected` ya da `unknown`. Türkiye numaralarına SMS yalnızca `registered` ise gider. |

Kurallar:

- **Tarihi ya da kanıtı olmayan onay kaydedilmez.** Kişi ve adresi yine aktarılır; o kanalda izinli sayılmaz ve rapor bunu sayar. Gönderen sizsiniz ve kanıt talebi size gelir — tarihsiz bir izni izinli saymak, önceden işaretlenmiş kutunun aynısıdır.
- **Ret (`false`) hiçbir şey gerektirmez** ve kalıcıdır: sonradan gelen bir toplu aktarım o adresi abone yapamaz.
- **Dosyadaki onay, daha önce kaydedilmiş bir iptali geri almaz.** Aboneliğini iptal etmiş biri ancak kendisi yeniden onay verirse (bülten formu, hesap ayarları, e-postadaki bağlantı) abone olur. Rapor bu adresleri ayrıca sayar.
- **İzin adresi olan kanal içindir.** E-posta izni düz `email`, SMS izni `phone` ister; `emailHash` ile e-posta izni verilemez.
- `consent` hiç yoksa adres saklanır ama izinsiz kalır — mesaj gönderilmez.

### traits neyi kabul eder

Metin, sayı, boolean ve bunlardan oluşan düz listeler saklanır. İç içe nesneler ve 100 karakterden uzun anahtarlar **sessizce atılır** — satır reddedilmez, o alan yazılmaz. Tarihleri ISO biçiminde yazın (`"birthday": "1990-05-14"`); doğum günü gibi tarihe bağlı senaryo başlatıcıları bu biçimi okur.

Selwise'in siparişlerden ve ziyaretlerden kendisinin hesapladığı alanların adları — `totalOrders`, `totalRevenue`, `lastOrderDate`, `daysSinceLastOrder`, `firstSeenDate` gibi — özellik olarak **kullanılamaz**: segment koşulu siparişi olmayan kişide gerçek değer yerine sizin değerinizi okurdu. Bu anahtarlar atılır ve raporda listelenir; başka bir adla (`crm_totalOrders`) gönderin.

### Kimlik uzunluğu

`customerId` platform genelinde sınırlı uzunluktadır. Çok uzun bir kimlik doğrulama adımında reddedilir. Bu sınır bilinçlidir: daha önce uzun kimlikler kabul edilip ilk siparişte veritabanı hatası veriyordu.

## Sipariş kaydı

```json
{
  "orderId": "SIP-2024-118412",
  "customerId": "MUSTERI-1001",
  "placedAt": "2024-11-03T14:22:09+03:00",
  "currency": "TRY",
  "status": "delivered",
  "subtotal": 1249.00,
  "discountTotal": 125.00,
  "shippingTotal": 0,
  "taxTotal": 208.17,
  "total": 1124.00,
  "channel": "web",
  "items": [
    {
      "productItemCode": "KM0020801100044",
      "name": "Smart Colour Mascara",
      "quantity": 2,
      "unitPrice": 249.00,
      "totalPrice": 498.00
    },
    { "productItemCode": "KM0018200500011", "quantity": 1, "unitPrice": 626.00 }
  ],
  "metadata": { "storeCode": "TR-034", "couponCode": "WELCOME10" }
}
```

| Alan | Zorunlu | Açıklama |
|---|---|---|
| `orderId` | Evet | Sizin sistemdeki sipariş kodu (en fazla 255 karakter). Tekrar eden aktarımlarda tekilleştirme bununla yapılır. |
| `placedAt` | **Evet** | ISO-8601 zaman damgası, tercihen UTC offset ile. Bir günden fazla **gelecekte** bir tarih reddedilir. |
| `customerId` | — | Alıcının kullanıcı dosyasındaki `customerId` değeri (`siteUserId`, `userId`, `externalId` de kabul edilir). Yoksa sipariş e-postası üzerinden o adresin sahibi olan kullanıcıya bağlanır; o da yoksa sipariş kullanıcısız kaydedilir. Bkz. [Kullanıcılar ve siparişler nasıl eşleşir](#kullanıcılar-ve-siparişler-nasıl-eşleşir). |
| `email` / `emailHash` | — | Misafir siparişini kullanıcıya bağlamanın yolu |
| `items` | — | Sipariş satırları. En fazla **100** satır; fazlası siparişi reddeder (ısıtma adımı da 100'den fazla ya da 2'den az ayrı ürünlü siparişleri çiftlerde saymaz). |
| `total` | Koşullu | Satır yoksa **zorunlu**. Negatif olamaz: iade bir negatif sipariş değil, `status` değeridir. Verilmezse satır toplamlarından hesaplanır. |
| `currency` | — | Verilmezse sitenin para birimi kullanılır |
| `status` | — | Sizin durum metniniz; küçük harfe çevrilir ve 20 karakterde kesilir. Şu değerler ciro dışı sayılır: `cancelled`, `canceled`, `refunded`, `partially_refunded`, `partial_refund`, `failed`, `failure`, `voided`, `void`, `declined`, `chargeback`, `returned`, `return`. Türkçe durumları ("iptal", "iade") bu İngilizce karşılıklara çevirin; yoksa ciroya girer. Durum yoksa sipariş ciroya girer. |
| `subtotal`, `discountTotal`, `shippingTotal`, `taxTotal` | — | Sipariş kırılımı |
| `channel` | — | `web`, `app`, `store`, `callcenter` |
| `metadata` | — | Mağaza kodu, kupon gibi kayıtla saklanacak diğer alanlar |

### Sipariş satırı alanları

| Alan | Zorunlu | Açıklama |
|---|---|---|
| `productItemCode` | Fiilen evet | Kodsuz satır reddedilmez ama hiçbir ürünle eşleşmez ve eşleşme oranını düşürür. Ürün kodu (en fazla 100 karakter). `productCode`, `sku`, `itemCode`, `productId` adları da kabul edilir. |
| `quantity` | — | Verilmezse 1 |
| `unitPrice` | — | `price` adı da kabul edilir |
| `totalPrice` | — | Verilmezse birim fiyat × adet |
| `name` | — | Sipariş anındaki ürün adı (`title` da kabul edilir; en fazla 500 karakter) |

### `placedAt` neden zorunlu

Siparişin gerçek tarihi budur ve tüm raporlar bu tarihe göre çalışır. Verilmezse aktarılan tüm geçmişiniz bugüne tarihlenir ve iki yıllık veri tek güne yığılır.

> **Saat dilimi belirtin**
>
> `2024-03-14T10:22:00` gibi offset'siz bir damga belirsizdir. `+03:00` ekleyin. Dosyadaki damgalarda offset yoksa panel bunları **UTC** sayar — yerel saat dilimi hiçbir zaman tahmin edilmez. Offset'siz tarihleri başka bir dilimde okumak için aşağıdaki **Time zone for dates without one** seçeneğini kullanın; rapor kaç satırın bundan etkilendiğini sayar (**Dates without a time zone**).

## Seçenekler

Doğrulama ve aktarım düğmelerinin üstündeki açılır **Options** (Seçenekler) bölümünde:

| Seçenek | Açıklama |
|---|---|
| **Time zone for dates without one** | Saat dilimi taşımayan damgalar için UTC offset'i (`Z` ya da `+03:00` gibi). Varsayılan `Z` (UTC). Geçersiz bir değer alanın yanında uyarı verir. Hem kullanıcı hem sipariş dosyalarında geçerlidir. |
| **Overwrite existing traits** | Yalnızca **Users** sekmesinde. Açıkken (varsayılan) dosyadaki özellik değerleri kayıtlıların üzerine yazar. Kapatın: canlı trafiğin yazdığı daha taze değerlere dokunmaması gereken bir ek (top-up) aktarımda. |

### Ürün kodu

`productItemCode` alanı, ürün beslemenizdeki `item_code` ya da `sku` ile **aynı** olmalıdır. Bu eşleşme, aktarımın en kritik noktasıdır — doğrulama adımı size oran verir. Bkz. [Doğrulama ve Uygulama](/docs/data-import/validate-and-apply).

Alan adı esnektir: `productItemCode` bulunamazsa sırasıyla `productCode`, `sku`, `itemCode` ve `productId` denenir. Hiçbiri yoksa satır **hata vermez** — ürün kodu boş kalır ve eşleşme oranı düşer. Bu yüzden doğrulama adımındaki oranı mutlaka okuyun.

## Kullanıcılar ve siparişler nasıl eşleşir

Aynı kişi aktarıma birden fazla yoldan gelir: kullanıcı dosyasında bir satır olarak, sonra verdiği her siparişte — bazılarında hesabıyla, bazılarında misafir olarak, yalnızca e-postasıyla. İki dosyayı hangi sırayla aktarırsanız aktarın hepsi **tek profilde** toplanır. Kurallar şunlar:

1. **Müşteri kimliği her zaman kazanır.** Kullanıcı satırındaki `customerId` ile siparişteki `customerId` aynıysa kayıtlar aynı profile yazılır.
2. **Kimliği olmayan satır, adresin sahibine bağlanır.** Yalnızca e-postası olan bir sipariş ya da kullanıcı satırı önce aynı dosyada o adresi kullanan bir müşteri kimliğine, sonra daha önce aktarılmış kullanıcılarınıza bakılarak bağlanır.
3. **Sahibi henüz yoksa misafir profil açılır.** Her adres için tek bir misafir profil oluşur; aynı adresin ikinci misafir siparişi de ona yazılır.
4. **Sonradan gelen gerçek kimlik misafir profili kendine katar.** Önce siparişleri, sonra kullanıcıları aktardıysanız — ya da bir müşterinin önce misafir, sonra hesabıyla siparişi varsa — misafir profilin siparişleri, özellikleri, segment üyelikleri ve iletişim kaydı gerçek kullanıcıya taşınır ve misafir profil silinir.
5. **Canlı iletişim kayıtları da bağlanır.** Sitenizin bülten formundan daha önce gelmiş, kimliği olmayan bir e-posta kaydı, aynı adresle aktarılan kullanıcıya bağlanır.
6. **Aynı adresi kullanan iki farklı müşteri kimliği birleştirilmez.** Tek posta kutusunu paylaşan iki hesap sizin kararınızdır. Rapor bu adresleri sayar; böyle bir adrese sahip misafir satırı en son etkin kullanıcıya bağlanır.
7. **Silinmiş kullanıcı geri gelmez.** Kanalda silinmiş ya da KVKK/GDPR kapsamında unutulmuş bir kullanıcının profili, adresi ve izni yeniden yazılmaz; ona ait sipariş ciro için yazılır ama kişiye bağlanmaz.

E-postalar karşılaştırılmadan önce boşluklardan arındırılır ve küçük harfe çevrilir: `  Ayse@Ornek.COM ` ile `ayse@ornek.com` aynı adrestir. `emailHash` gönderiyorsanız karmayı bu biçimde üretin (`sha256(küçük_harf(kırpılmış_adres))`) — başka türlü üretilmiş bir karma hiçbir kayıtla eşleşmez ve bunu fark ettirecek bir hata da oluşmaz.

Doğrulama raporundaki **eşleştirme** satırları, uygulama adımının bu kurallarla ne yapacağını önceden söyler: kaç satırın e-postayla bir kullanıcıya bağlanacağını, kaç misafir profilin birleştirileceğini ve kaç adresin birden fazla müşteri kimliğince kullanıldığını.

## Dosyayı gönderme

| Yöntem | Sınır | Ne zaman |
|---|---|---|
| **Upload a file** (Dosya yükle) | 50 MB (gzip'li olabilir; açılmış hâli en fazla 1 GB) | Normal kullanım |
| **Public URL** (URL) | 2 milyon satıra kadar; boyut sınırı yok | Dosya 50 MB'tan büyükse |
| **Paste rows** (Satır içi) | İstek başına 1000 kayıt | Küçük setler, deneme |

Dosya yükleme NDJSON'ı, JSON dizisini (`[{…},{…}]`) ve `{"results": […]}` biçimindeki sayfalı dışa aktarımı kabul eder; hepsi yüklenirken satır başına bir kayıt olarak saklanır, rapordaki satır numaraları bu sıraya göredir. Excel ya da CSV dosyası kabul edilmez, önce JSON'a çevrilmesi gerekir.

Yüklenen dosya müşteri verisidir ve yalnızca kullanıldığı süre boyunca saklanır: aktarım başarıyla tamamlandığı anda Selwise'tan silinir. Hiç aktarılmazsa 24 saat sonra kendiliğinden silinir; **Dosyayı sil** ile hemen de silebilirsiniz. Doğrulama ve aktarım raporları dosya silindikten sonra da kalır.

URL yöntemi için dosyayı **HTTPS** ile erişilebilir bir yere koyup adresini verirsiniz. Adres kullanıcı adı ya da şifre taşıyamaz (`https://kullanici:sifre@…` reddedilir): erişim için süreli, imzalı bir bağlantı kullanın. İmzalı bir bağlantının sorgu kısmı panelde gösterilmez ve aktarım bitince saklanmaz. Sunucu adresin yanıt vermeye başlaması için 60 saniye bekler.

Satır içi yöntemde her istek bir dilim gönderir. Tekrar gönderilen bir dilim güvenlidir: kayıtlar tekil kimliklerine göre yazılır, aynı satır iki kez yazılmaz.

## Satır sınırı

Tek bir satır en fazla 1 MB olabilir. Aşırı büyük `traits` nesneleri bu sınıra takılır.
