# Solk Docs (Türkçe) — tüm içerik --- # Solk ile geliştirin > Solk CRM'i kendi yazılımlarınıza, otomasyon araçlarınıza ve yapay zekâ asistanlarınıza bağlamak için ihtiyacınız olan her şey. Solk, B2B satış ekipleri için kurulan bir CRM'dir: firmalar, kişiler, fırsatlar (satış projeleri), ziyaret ve telefon görüşmeleri, aksiyonlar, takvim, fuar leadleri ve e-posta yazışmaları tek yerde durur. Her firmanın kurulumu ayrıdır ve kendi adresinde çalışır (`https://.solk.app`); bu belgelerdeki örnekler `https://ornek.solk.app` adresini kullanır. Bu sitedeki kılavuzlar entegrasyon kurmayı, veri aktarmayı ve CRM'i başka araçlarla konuşturmayı anlatır. Başvuru bölümlerinde her uç noktanın parametreleri ve gerçek bir kurulumdan alınmış örnek yanıtları vardır. ## Geliştirici platformu :::cards - [REST API](/rest-api/overview) code | Kayıtları JSON ile okuyun ve yazın. Mobil uygulamanın kullandığı uçların aynısı; aynı yetki kuralları, aynı aşama kapıları. - [MCP bağlayıcısı](/mcp/overview) spark | Claude gibi yapay zekâ asistanlarına CRM'inizi bağlayın: soru sorun, görev ve görüşme kaydını sohbetten açın. - [Web kancaları](/guides/webhooks) bolt | Fırsat kazanıldığında, yeni firma ya da form başvurusu geldiğinde imzalı JSON alın. - [Gelen kancalar](/guides/inbound) inbox | Formlardan, telefon santralinden, veri ambarından ve randevu araçlarından CRM'e veri gönderin. ::: ## Nereden başlamalı? :::cards - [Hızlı başlangıç](/quickstart) rocket | Beş dakikada ilk API anahtarınızı açın ve ilk görevinizi oluşturun. - [Kimlik doğrulama](/rest-api/authentication) key | API anahtarı, OAuth ve mobil oturum belirteçlerinin farkı. - [Standart nesneler](/objects/companies) layers | Firma, kişi, fırsat ve aksiyonların alanları ve birbirine bağlanışı. - [Uygulamalar](/guides/apps) grid | Slack, Teams, Zapier, Google Drive ve 40'tan fazla hazır bağlantı. ::: ## Temel ilkeler **Kurulum başına adres.** Her müşterinin verisi kendi sunucu klasöründe ve kendi veritabanında durur. API isteklerini kurulumunuzun adresine gönderirsiniz; ortak bir `api.solk.app` yoktur. **Kullanıcı yetkisiyle çalışır.** Her belirteç bir kullanıcıya aittir. API'nin gördüğü kayıtlar ve yapabildiği değişiklikler o kullanıcının web arayüzünde görebildikleri ve yapabildikleriyle birebir aynıdır: modül izinleri, kayıt görünürlüğü ve düzenleme hakları API'de de geçerlidir. **İş kuralları atlanmaz.** Fırsat aşaması değiştirirken aşama kapıları, firma açarken mükerrer kayıt koruması, görüşme kaydederken aksiyon zinciri — web formunda ne oluyorsa API'de de o olur. Entegrasyonunuz CRM'in kurallarını bozamaz. **Türkçe ve İngilizce.** Bu belgeler iki dilde yayımlanır. API alan adları İngilizcedir; kullanıcıya gösterilen hata iletileri (`message`) ve bazı değer listeleri (görev öncelikleri, kayıp nedenleri) kurulumun dilinde, Türkçedir. :::note Bu belgeler Solk **v22** sürümünü anlatır. Kurulumunuzun sürümünü `GET /health` ya da [kimlik ucunun](/rest-api/auth/me) `app.version` alanı gösterir. Değişiklikler [sürüm notlarında](/changelog). ::: ## Yapay zekâ araçları için Bu sitenin tamamı makinelerin okuyabileceği biçimde de yayımlanır: - [`/llms.txt`](/llms.txt) — tüm sayfaların kısa dizini - [`/llms-full.txt`](/llms-full.txt) — tüm içerik tek dosyada - [`/openapi.json`](/openapi.json) — REST API'nin OpenAPI 3.1 tanımı - Her sayfanın sonuna `.md` eklerseniz Markdown sürümü gelir (ör. [`/quickstart.md`](/quickstart.md)). --- # Hızlı başlangıç > API anahtarı açın, kimliğinizi doğrulayın, ilk görevinizi oluşturun ve bir olaya abone olun. Bu kılavuz sonunda bir API anahtarınız olacak, CRM'den veri okumuş, bir görev açmış ve fırsat kazanıldığında haber alacak bir web kancası kurmuş olacaksınız. :::steps ### API anahtarı açın CRM'e **yönetici** hesabıyla girin ve **Ayarlar → Geliştiriciler** sayfasını açın (`https://ornek.solk.app/settings/developers`). Anahtara tanınır bir ad verin (ör. "Muhasebe eşitleme") ve **Anahtar oluştur**'a basın. Anahtar `sk_` ile başlar ve **yalnız bir kez** gösterilir; hemen bir parola kasasına kaydedin. Anahtar, onu açan yöneticinin yetkileriyle çalışır ve iptal edilene kadar geçerlidir. :::warning API anahtarı tam yetkili bir parola gibidir. Tarayıcıda çalışan koda, mobil uygulamaya ya da herkese açık bir depoya koymayın. Kullanıcı adına çalışan uygulamalar için [OAuth](/rest-api/oauth) kullanın. ::: ### Kimliğinizi doğrulayın Her isteğe `Authorization: Bearer ` başlığını ekleyin. İlk çağrı [`GET /api/v1/me`](/rest-api/auth/me) olsun: anahtarın kime ait olduğunu, açık modülleri ve kurulumun aşama anahtarlarını döndürür. :::code ```bash cURL curl https://ornek.solk.app/api/v1/me \ -H "Authorization: Bearer $SOLK_API_KEY" ``` ```javascript JavaScript const res = await fetch("https://ornek.solk.app/api/v1/me", { headers: { Authorization: `Bearer ${process.env.SOLK_API_KEY}` }, }); const me = await res.json(); console.log(me.user.full_name, me.constants.stages); ``` ```python Python import os, requests r = requests.get("https://ornek.solk.app/api/v1/me", headers={"Authorization": f"Bearer {os.environ['SOLK_API_KEY']}"}) me = r.json() print(me["user"]["full_name"], me["constants"]["stages"]) ``` ::: Yanıttaki `ok: true` isteğin başarılı olduğunu gösterir. Başarısız isteklerde `ok: false`, makinece okunur bir `error` kodu ve kullanıcıya gösterilebilecek bir `message` gelir — ayrıntı [Hatalar](/rest-api/errors) sayfasında. ### Kayıtları okuyun Açık fırsatları değerine göre sıralı alın: ```bash curl "https://ornek.solk.app/api/v1/opportunities?scope=all&sort=value&per=10" \ -H "Authorization: Bearer $SOLK_API_KEY" ``` Liste uçları `rows`, `total`, `page` ve `pages` döndürür. Tek bir kaydın tamamı için kimliğiyle isteyin: [`GET /api/v1/opportunities/{id}`](/rest-api/deals/get). ### Görev oluşturun ```bash curl https://ornek.solk.app/api/v1/tasks \ -H "Authorization: Bearer $SOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Fiyat listesini gönder", "customer": "Kuzey Plastik Sanayi", "due_date": "2026-10-05", "due_time": "10:30", "priority": "Yüksek" }' ``` Görev, anahtarın sahibine atanır (başka birine atamak için `assigned_to`). Firma adla bulunur; kendiliğinden proje açılmaz. Görev hemen CRM'de **Aksiyonlar** listesinde ve sorumlusunun telefon takviminde görünür. ### Olaylara abone olun **Ayarlar → Uygulamalar → Web kancası**'nı açın, alıcı adresinizi girin ve **Fırsat kazanıldı** olayını seçin. CRM, olay olduğunda adresinize imzalı bir JSON gönderir: ```json { "event": "opp_won", "title": "Fırsat kazanıldı", "text": "Kuzey Plastik Sanayi · Streç film tedariki · 338.400 € · Sorumlu: Deniz Aksoy", "url": "https://ornek.solk.app/opportunities/53", "data": { "id": 53, "rid": "006Xq3…", "name": "Streç film tedariki", "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "stage": "Win", "stage_label": "Win", "value": 338400.0, "currency": "EUR", "owner": "Deniz Aksoy" }, "workspace": "Örnek Kimya", "app": "Solk CRM", "sent_at": "2026-10-02T14:05:11" } ``` İmzanın nasıl doğrulanacağı [Web kancaları](/guides/webhooks) sayfasında. ::: ## Sonraki adımlar :::cards - [Kimlik doğrulama](/rest-api/authentication) key | Anahtar türleri, yetki ve iptal. - [Fırsatlar](/rest-api/deals/list) target | Aşama değiştirme, BANT, adımlar. - [Görüşme kaydet](/rest-api/activities/log) phone | Telefon ve ziyaret kaydını tek istekte açın. - [MCP](/mcp/overview) spark | Aynı işleri Claude'dan doğal dille yapın. ::: --- # Kurulumlar ve adresler > Her firmanın Solk kurulumu ayrı bir adreste çalışır. API, MCP, kanca ve keşif adresleri bu adresten türer. Solk'ta "çalışma alanı" yerine **kurulum** vardır: her müşteri firmanın kendi alt alan adı, kendi veritabanı ve kendi ayarları olur. Ortak bir API sunucusu yoktur; isteklerinizi doğrudan kurulumun adresine gönderirsiniz. | Ne | Adres | |---|---| | Web uygulaması | `https://.solk.app` | | REST API | `https://.solk.app/api/v1/…` | | MCP bağlayıcısı | `https://.solk.app/mcp` | | OAuth keşfi | `https://.solk.app/.well-known/oauth-authorization-server` | | SCIM 2.0 | `https://.solk.app/api/scim/v2` | | Gelen kancalar | `https://.solk.app/in//` | | Sürüm ve sağlık | `https://.solk.app/health` | | Takvim (CalDAV) | `https://.solk.app/radicale/` | Kurulum adresini bilmiyorsanız kullanıcılara sorun: tarayıcıda CRM'i açtıkları adres odur. [solk.app](https://solk.app) giriş sayfası firma adını yazınca doğru kuruluma yönlendirir. ## Sürüm Kurulumlar aynı kod tabanından güncellenir; ancak her kurulum kendi takviminde güncellendiği için bir an iki kurulum farklı sürümde olabilir. Çalışan sürümü öğrenmek için: ```bash curl https://ornek.solk.app/health ``` ```json { "ok": true, "version": "v22", "code": "bff893f1", "started": "2026-10-02T09:12:40", "restart_needed": false, "time": "2026-10-02T14:20:03", "schema_missing": {}, "last_backup": "2026-10-02", "last_sync": "2026-10-02T14:10" } ``` API'de yeni bir alan ya da uç kullanmadan önce sürümü kontrol etmek, eski sürümdeki bir kurulumda `404` almanızı önler. Yeni alanlar yanıtlara **eklenir**; mevcut alanların anlamı değişmez ve kaldırılmaz. ## Lisans ve modüller Her kurulumun lisansı hangi modüllerin açık olduğunu belirler: Müşteriler, Fırsatlar, Ziyaretler, Görevler, Takvim, Fuar leadleri, Raporlar, Satış öngörüsü, Destek talepleri, Sözleşmeler, E-posta otomasyonu, Yapay zekâ. Kapalı bir modülün uçları `403 module` döner. Açık modüller [`GET /api/v1/me`](/rest-api/auth/me) yanıtında `license.modules` ve kullanıcıya özel `user.modules` alanlarındadır. Lisans süresi dolduğunda kurulum yalnız yöneticiye açık kalır; diğer kullanıcıların API istekleri `403 license` alır. ## Saat dilimi ve tarih biçimi Kurulumlar Türkiye saatiyle (UTC+3) çalışır. API tarihleri `YYYY-MM-DD`, tarih-saatleri saat dilimi eki olmadan `YYYY-MM-DDTHH:MM:SS` (yerel saat) biçiminde verir ve bekler. Gelen kancalarda ISO 8601 (dilim ekli) ya da Unix zaman damgası gönderebilirsiniz; yerel saate çevrilir. --- # Kullanıcılar ve roller > Yönetici ve satış rolleri, modül izinleri, kayıt görünürlüğü ve API belirteçlerinin bunlarla ilişkisi. Her API isteği bir kullanıcının adına çalışır. Bu sayfa, o kullanıcının neyi görüp neyi değiştirebileceğini belirleyen üç katmanı anlatır. ## Roller {#roles} | Rol | Anahtar | Yapabildikleri | |---|---|---| | Yönetici | `admin` | Tüm kayıtlar, ayarlar, kullanıcılar, uygulamalar, Geliştiriciler sayfası, lisans ve faturalar. | | Satış | `sales` | Kendi kayıtları ve görünürlük kuralının izin verdikleri; ayar sayfaları kapalı. | API anahtarlarını (**Ayarlar → Geliştiriciler**) yalnız yöneticiler açar ve anahtar, açan yöneticinin kimliğiyle çalışır. Belirli bir satışçının yetkisiyle çalışan bir entegrasyon gerekiyorsa o kullanıcı [OAuth](/rest-api/oauth) ile izin vermelidir. ## Modül izinleri {#modules} Lisansta açık modüller kullanıcı bazında ayrıca kısıtlanabilir (**Ayarlar → Kullanıcılar → kullanıcı → Modüller**). Kullanıcının izni olmayan bir modülün uçları `403 module` döner: ```json { "ok": false, "error": "module", "message": "Bu modül lisansınızda / izinlerinizde yok.", "module": "fairs" } ``` Uç başvuru sayfalarında her ucun hangi modüle bağlı olduğu yazar. Kimlik, Bugün, arama, bildirim, e-posta ve Sor uçları modülden bağımsızdır. ## Kayıt görünürlüğü {#visibility} **Ayarlar → Güvenlik → Kayıt görünürlüğü** iki düzenden birini seçer: - **Herkes** — satışçılar tüm firmaları görür; yalnız kendi kayıtlarını (ya da sorumlusu boş olanları) düzenler. - **Takım** — satışçılar kendi departmanlarındaki kullanıcıların firmalarını ve sorumlusu boş firmaları görür; diğer departmanların firmaları ve bunlara bağlı fırsat, görev, e-posta gibi kayıtlar listelere gelmez. API bu kuralı aynen uygular: görünmeyen kayıtlar listelerde yer almaz, kimliğiyle istenirse `403 forbidden` ya da `404 not_found` döner. Liste uçlarındaki `scope` parametresi (`mine` · `all` · kullanıcı kimliği) görünürlüğü genişletmez, yalnız görünenler içinde süzer. ## Kullanıcı yaşam döngüsü - **Davet** — yönetici e-postayla davet eder; kullanıcı şifresini kendisi belirler. Kimlik sağlayıcınızla otomatik açmak için [SCIM](/rest-api/scim). - **Kapatma** — kapatılan kullanıcı giriş yapamaz; mobil oturumları ve API anahtarları anında düşer, OAuth izinleri geçersizleşir. Kayıtları ve geçmişi silinmez. - **Koltuk** — lisanstaki koltuk sayısı dolduysa yeni kullanıcı açılamaz ya da kapalı kullanıcı yeniden açılamaz. ## İki adımlı doğrulama ve IP kısıtı İki adımlı doğrulama açık kullanıcılar [mobil oturum açma](/rest-api/auth/login) ucunda `otp` alanını göndermelidir. API anahtarı ve OAuth belirteçleri ikinci adımı sormaz — açılırken zaten oturum açmış bir kullanıcı tarafından yetkilendirilmişlerdir. Yönetici bir kullanıcıya IP kısıtı koyduysa o kullanıcının belirteçleriyle gelen istekler de yalnız izinli ağlardan kabul edilir (`403 ip`). --- # Firmalar > Müşteri ve aday firmalar — CRM'deki her kaydın bağlandığı ana nesne. Firma (API'de `customer`) CRM'in merkezidir. Kişiler, fırsatlar, ziyaretler, denemeler, teklifler, siparişler, sözleşmeler, destek talepleri ve e-posta yazışmaları bir firmaya bağlanır. Firma kartı bunların hepsini tek sayfada gösterir; API'de [`GET /api/v1/customers/{id}`](/rest-api/companies/get) aynı içeriği döndürür. ## Durum | Değer | Anlamı | |---|---| | `Prospect` | Aday — henüz satış yapılmamış firma. Yeni firmaların varsayılanı. | | `AC` | Aktif müşteri. Bir fırsat **kazanıldığında** firma kendiliğinden `AC` olur. | Firmalar silinmez, **pasife alınır** (`active: false`): pasif firmalar listelerden ve hatırlatıcılardan çıkar, geçmişi korunur. API'de silme ucu yoktur; pasif firmaları `GET /api/v1/customers?active=0` listeler. Ayrıntı: [Pasifleştirme ve silme](/concepts/archiving). ## Alanlar Liste uçları her firma için **satır** biçimini, tekil uç **kart** biçimini döndürür. Kart, satırdaki her alanı da içerir. | Alan | Tür | Açıklama | |---|---|---| | `id` | integer | Kurulum içindeki kimlik. | | `rid` | string | 15 karakterlik kalıcı kayıt kimliği (`001…`). [Kayıt kimlikleri](/concepts/record-ids). | | `name` | string | Firma adı (en çok 200 karakter). | | `status`, `status_label` | string | `AC` / `Prospect` ve görünen adı. | | `owner` | user | Sorumlu satışçı (`id`, `full_name`, `role`). | | `city`, `sector`, `phone` | string | İletişim bilgileri. | | `main_contact` | object | Ana kişinin adı, telefonu, e-postası ve WhatsApp numarası (`wa`). | | `visit_state` | string | Ziyaret takvimi: `overdue` gecikti, `soon` yaklaşıyor, `ok`, `none` takvim yok. | | `last_visit`, `next_visit_due` | date | Son ziyaret ve sıradaki ziyaret günü. | | `open_opps` | integer | Açık fırsat sayısı. | | `at_risk` | boolean | Müşteri risk altında (uzun süredir ziyaret ya da sipariş yok). | | `potential_kg`, `unit` | number, string | Aylık potansiyel ve kurulumun ana birimi. [Birim ve döviz](/concepts/units-currency). | | `currency` | string | Firmanın işlem dövizi. | | `pending_state` | string | En acil açık aksiyonun durumu. | Kartta ayrıca: `address`, `website`, `note`, `supplier_note` (mevcut tedarikçi), `barrier` (engel), `mgmt_support`, `visit_period_days`, pay alanları (`share_start`, `share_current`, `share_target`), miktar hedefleri (`our_kg_current`, `our_kg_target`, `commit_kg`), `contacts`, `opps`, `visits`, `demos`, `offers`, `orders`, `open_actions`, `events` (zaman tüneli), `competitors`, `prices`, `threads` ve `can_edit` gelir. ## Mükerrer koruması Firma açarken ad, mevcut firmalarla karşılaştırılır: - Aynı ad (büyük-küçük harf, şirket türü ekleri ve noktalama yok sayılarak) varsa `409 exists` döner ve mevcut firma `customer` alanında gelir. - Benzer adlar varsa `409 similar` döner ve adaylar `similar` listesinde gelir. Doğru firmayı seçin ya da yine de açmak için `confirm_new: true` gönderin. Bu kural [firma oluştur](/rest-api/companies/create), [görüşme kaydet](/rest-api/activities/log) ve gelen kancalarda (form, veri eşitleme) aynıdır. ## Otomatik açılan firmalar E-posta eşitlemesi, form başvuruları, Segment ve veri eşitleme kancaları yeni firma açabilir. E-postadan açılan firmalar alan adından tanınır (`auto_domain`) ve firma kartında "otomatik açıldı" bandıyla görünür; kullanıcı yanlışsa tek tıkla geri alır. ## İlgili uçlar :::cards - [Firmaları listele](/rest-api/companies/list) list | Süzme, sıralama, sayfalama. - [Firma oluştur](/rest-api/companies/create) plus | Mükerrer korumalı açma, ilk kişiyle birlikte. - [Firmayı güncelle](/rest-api/companies/update) pencil | Kısmi güncelleme. - [Ziyareti ertele](/rest-api/companies/postpone) clock | Ziyaret terminini ileri al. ::: --- # Kişiler > Firmalardaki muhataplar — görüşmeler, e-postalar ve teklifler kişilere bağlanır. Kişi (API'de `contact`) her zaman bir firmaya aittir. Bir firmanın bir **ana kişisi** olur: firma listesinde ve mobil uygulamadaki arama / WhatsApp düğmelerinde o görünür. Firmanın ilk kişisi kendiliğinden ana kişi olur. ## Alanlar | Alan | Tür | Açıklama | |---|---|---| | `id` | integer | Kimlik. | | `rid` | string | Kalıcı kayıt kimliği (`003…`). | | `customer_id`, `customer` | integer, string | Bağlı olduğu firma. | | `name` | string | Ad soyad. | | `title`, `department` | string | Unvan ve departman. | | `phone`, `email` | string | İletişim. | | `wa` | string | Telefonun yalnız rakamları — `https://wa.me/` bağlantısı için. | | `is_main` | boolean | Firmanın ana kişisi. | | `is_former` | boolean | Firmadan ayrıldı. Ayrılan kişiler silinmez; listelerde gizlenir, geçmiş görüşmelerde adı kalır. | | `note` | string | Kısa not (250 karakter). | ## Kişiler nasıl oluşur? - [Kişi ekle](/rest-api/people/create) ucu ya da firma kartı. - [Firma oluştururken](/rest-api/companies/create) `contact_*` alanları. - [Görüşme kaydederken](/rest-api/activities/log) firmada olmayan bir ad yazıldığında. - E-posta eşitlemesi: firmanın alan adından gelen e-postaların göndericileri (otomatik kişiler `auto` olarak işaretlenir ve uygulamalara `contact_created` olayı göndermez). - Form başvuruları, Segment `identify` olayları ve veri eşitleme kancası. ## E-posta ile eşleşme Gelen e-postalar ve kancalar kişiyi önce **e-posta adresiyle**, bulamazsa firma + adla eşler. Aynı e-posta adresini iki firmada kullanmaktan kaçının; eşleşme ilk bulunan kişiye yapılır. ## İlgili uçlar :::cards - [Kişileri listele](/rest-api/people/list) list | Tüm firmaların kişileri, aramayla. - [Kişi ekle](/rest-api/people/create) plus | Firmaya kişi ekle. - [Kişiyi güncelle](/rest-api/people/update) pencil | Ana kişi yap, ayrıldı işaretle. ::: --- # Fırsatlar > Satış projeleri — aşama, değer, sağlık, BANT ve kontrol listesi adımlarıyla. Fırsat (API'de `opportunity`, kısaca `opp`) bir firmayla yürütülen satış projesidir. Arayüzde **Proje** ya da **Fırsat** olarak geçer. Her fırsat bir firmaya bağlıdır, bir aşamadadır ve kapanana kadar aşama kapılarından geçerek ilerler. ## Aşamalar Aşamalar kuruluma göre değişir (**Ayarlar → Aşamalar**). Yeni kurulumlar iki açık aşamayla (Aday · Devam ediyor) başlar; Makro gibi sanayi kurulumları beş açık aşama ve aşama kapıları kullanır: `Qualify` → `Viable` → `Present Solution` → `Negotiation` → `Expect to Close` → `Win` / `Lost` / `Cancel` Kapanış anahtarları her kurulumda `Win`, `Lost`, `Cancel`'dır; görünen adları değişebilir. Kurulumun aşama anahtarlarını ve adlarını [`GET /api/v1/me`](/rest-api/auth/me) yanıtındaki `constants.stages`, `constants.open_stages` ve `constants.stage_labels` verir. Ayrıntı: [Aşamalar ve kapılar](/concepts/pipeline). ## Müşteri takibi kapları Kazanılmış müşterilerle süren ilişki `Account` aşamasındaki **Müşteri Takibi** kaplarında tutulur: bu kaplar satış hunisinde sayılmaz, ziyaret ve siparişler onlara bağlanır. Listede `stage=account` ile alınır, `is_account: true` taşırlar. ## Alanlar | Alan | Tür | Açıklama | |---|---|---| | `id`, `rid` | integer, string | Kimlik ve kalıcı kayıt kimliği (`006…`). | | `name` | string | Görünen ad (ad yoksa ürün ve türden üretilir). | | `customer_id`, `customer` | integer, string | Firma. | | `stage`, `stage_label` | string | Aşama anahtarı ve görünen adı. | | `is_open`, `is_account` | boolean | Açık fırsat · müşteri takibi kabı. | | `potential_kg`, `offer_price`, `payment_days` | number | Aylık miktar, birim fiyat, vade. | | `unit`, `pu`, `cur_sym` | string | Ürünün birimi, fiyat birimi (ör. `€/kg`) ve döviz simgesi. | | `value`, `value_short` | number, string | Yıllık değer = miktar × fiyat × 12 (yoksa elle girilen değer). | | `value_base`, `base_currency` | number, string | Değerin kurulumun ana dövizindeki karşılığı. | | `probability`, `prob`, `fc` | number, string | Olasılık (%) ve öngörü kategorisi. | | `health`, `health_color` | object, string | Sağlık puanı ve rengi (`g` yeşil, `y` sarı, `r` kırmızı). | | `health_manual`, `health_note` | boolean, string | Kullanıcı rengi elle belirlediyse ve notu. | | `sla_days`, `sla_left`, `days_in_stage` | integer | Aşama süresi hedefi, kalan gün, aşamada geçen gün. | | `lifetime_left` | integer | Projenin yaşam süresinden kalan gün. | | `next_step`, `close_date` | string, date | Sıradaki adım ve beklenen kapanış. | | `bant` | integer | Sağlanan BANT ölçütü sayısı (0–4). | | `owner` | user | Sorumlu. | Tekil uç ayrıca `steps` (kontrol listesi), `gate_ok`, `gate_problems`, `next_stage`, `bant_b/a/n/t`, `offers`, `visits`, `demos`, `orders`, `tasks`, `logs` (aşama geçmişi), `notes`, `tags`, `threads` ve kapanmışsa `close_category`, `close_reason`, `closed_at` döndürür. ## Değer nasıl hesaplanır? Ürün, aylık miktar ve birim fiyat girildiyse yıllık değer **miktar × fiyat × 12**'dir ve elle değiştirilemez. Bunlar yoksa `value` alanındaki elle girilen değer kullanılır. Panolar ve toplamlar her zaman ana dövizdeki `value_base` ile hesaplanır. ## Sağlık Sağlık puanı (0–100) kendiliğinden hesaplanır: aşama SLA'sı ve proje yaşam süresi aşımı, son aktiviteden geçen süre, sıradaki adımın tarihi ve ertelemeler, geçmişte kalan kapanış tarihi, aşama kapısı sorunları ve pazarlıkta teklif fiyatının olmaması puanı düşürür; son 7 gündeki aktivite ve olumlu deneme yükseltir. 75 ve üstü yeşil, 50–74 sarı, altı kırmızıdır. Kullanıcı rengi elle seçip kısa bir not yazabilir; elle seçilen renk hesaplananı ezer (`health_manual: true`). `sort=health` listeyi önce kırmızıları gösterecek şekilde sıralar. ## İlgili uçlar :::cards - [Fırsatları listele](/rest-api/deals/list) list | Aşama, sıralama, sayfalama. - [Aşamayı değiştir](/rest-api/deals/stage) arrow | Kapılarla birlikte. - [Fırsatı güncelle](/rest-api/deals/update) pencil | Miktar, fiyat, kapanış, öngörü. - [Adım ekle](/rest-api/deals/steps-create) check | Kontrol listesi. ::: --- # Aksiyonlar ve görevler > Görevler, ziyaret ve denemelerin sonraki aksiyonları ve tarihli proje adımları tek bir iş listesinde. Satışçının yapacağı işler dört farklı kayıttan gelir. CRM bunları **Aksiyonlar** sayfasında tek listede gösterir; API'de de [`GET /api/v1/actions`](/rest-api/actions/list) aynı listeyi verir. | Tür (`kind`) | Kaynağı | Örnek | |---|---|---| | `task` | Görev — elle, iş akışından, formdan ya da kancadan açılır. | "Fiyat listesini gönder" | | `visit` | Bir ziyaretin / telefon görüşmesinin **sonraki aksiyonu**. | Görüşmede "Teklif gönder · 5 Ekim" yazıldı | | `demo` | Bir denemenin (ürün denemesi) sonraki aksiyonu. | "Deneme sonucunu al" | | `step` | Fırsatın tarihli kontrol listesi adımı. | "Hat denemesi planla" | Her satırın `key` alanı `tür:kimlik` biçimindedir (ör. `task:26`). Tamamlama, erteleme ve kapatma uçları `/api/v1/actions/{kind}/{rid}/…` yolunu kullanır; böylece hangi türden geldiğine bakmadan tek kodla işlenir. ## Durum | `state` | Anlamı | |---|---| | `overdue` | Termini geçti. | | `soon` | Termine 7 gün ya da daha az var. | | `open` | Termini ileride. | | `done` | Tamamlandı. | ## Zincir Bir aksiyon kapatılırken sonraki aksiyon açılabilir ([Kapat ve sonrakini aç](/rest-api/actions/close)). Böylece bir firmayla ilgili işler zincir oluşturur; tekil uç zinciri `chain_before` ve `chain_after` alanlarında döndürür. Görüşme kaydı ([Görüşme kaydet](/rest-api/activities/log)) aynı firmadaki uygun açık aksiyonları kendiliğinden kapatır. ## Görev alanları | Alan | Tür | Açıklama | |---|---|---| | `id`, `rid` | integer, string | Kimlik ve kalıcı kayıt kimliği (`00T…`). | | `title`, `detail` | string | Başlık ve ayrıntı. | | `due_date`, `due_time` | date, string | Termin ve isteğe bağlı saat (`HH:MM`). Saatli görevler takvimde saatinde görünür. | | `status` | string | `Açık`, `Devam Ediyor`, `Tamamlandı`. | | `priority` | string | `Düşük`, `Normal`, `Yüksek`, `Acil`. | | `assignee` | user | Atanan kullanıcı. | | `customer_id`, `opp_id` | integer | Bağlı firma ve proje (ikisi de isteğe bağlı). | | `result`, `completed_at` | string, datetime | Sonuç notu ve tamamlanma zamanı. | | `source_key` | string | Görevi açan kaynak (ör. `form:3`, iş akışı). | ## Tamamlanma günü Tamamlama uçları `done_on` ile geçmiş bir günü kabul eder: dün yapılan bir işi bugün işaretleyen kullanıcı raporlarda doğru güne yazılır. Gönderilmezse şu an kullanılır. ## Bildirimler Atanan kullanıcıya uygulama içi bildirim gider; tercihine göre e-posta da alır. Termini geçen işler için her sabah özet e-posta gönderilir; kullanıcı hangi olayda e-posta ya da uygulama bildirimi alacağını **Ayarlar → Bildirimler**'den seçer. --- # Fuar leadleri > Fuar standında toplanan potansiyel müşteriler — hızlı kayıt, sorumlu, sonraki aksiyon ve firmaya dönüştürme. Fuar leadi (API'de `lead`) bir fuarda tanışılan firmanın ilk kaydıdır. Lead henüz bir firma değildir: değerlendirilip **Aktarıldı** durumuna geçtiğinde firma, kişi ve gerekirse fırsat açılır (web arayüzünden). Fuarlar (`fair`) leadleri gruplar; her fuarın adı, şehri, yeri ve tarihleri vardır. ## Durum | `status` | Anlamı | |---|---| | `Yeni` | Standda kaydedildi. | | `İletişime geçildi` | Fuar sonrası ilk temas kuruldu. | | `Teklif verildi` | Teklif gönderildi. | | `Aktarıldı` | Firmaya dönüştürüldü (`customer_id`, `opp_id` dolar). | | `Vazgeçildi` | Takip edilmeyecek. | `status_eff` görünen durumu verir: bağlı proje kazanıldıysa `Kazanıldı`, kaybedildi ya da iptal edildiyse `Kaybedildi`, değilse saklanan durum. ## Alanlar | Alan | Tür | Açıklama | |---|---|---| | `id`, `rid` | integer, string | Kimlik ve kalıcı kayıt kimliği (`00Q…`). | | `fair_id`, `fair` | integer, string | Fuar. | | `company`, `contact_name`, `title`, `phone`, `email` | string | Firma ve kişi bilgileri. | | `city`, `sector`, `website` | string | Firma ayrıntıları. | | `visitor_type` | string | `Potansiyel müşteri`, `Mevcut müşteri`, `Tedarikçi`, `Rakip`, `Diğer`. | | `interest` | string | İlgi: `Sıcak`, `Ilık`, `Soğuk`. | | `products`, `products_text` | string | İlgilenilen ürün kimlikleri (virgüllü) ve serbest metin. | | `potential_kg`, `unit` | number, string | Aylık potansiyel ve birim. | | `timing` | string | `Hemen`, `3 ay içinde`, `6 ay içinde`, `1 yıl+`, `Belirsiz`. | | `next_action`, `next_date` | string, date | Sonraki aksiyon ve tarihi — sorumluya görev açar. | | `owner` | user | Leadi takip edecek satışçı. | | `customer_id`, `opp_id`, `task_id` | integer | Aktarıldıysa firma ve fırsat; takip görevi. | ## Sorumlu ve takip görevi Lead'i kaydeden kişi ile takip edecek kişi farklı olabilir (`owner_id`). Sonraki aksiyon ve tarih girildiğinde sorumluya bir görev açılır; lead güncellendiğinde görev de güncellenir. Satışçılar varsayılan olarak yalnız kendi leadlerini görür (`owner=me`). ## İlgili uçlar :::cards - [Fuarları listele](/rest-api/fairs/list) flag | Fuarlar ve lead sayıları. - [Lead ekle](/rest-api/fairs/leads-create) plus | Standda hızlı kayıt. - [Leadi güncelle](/rest-api/fairs/leads-update) pencil | Durum ve alanlar. ::: --- # E-posta yazışmaları > Kullanıcıların bağlı posta kutularından eşitlenen yazışmalar ve kayıtlara bağlanışları. Kullanıcılar Gmail / Google Workspace, Microsoft 365 ya da şirket postası (IMAP/SMTP) hesaplarını bağladığında e-postaları CRM'e eşitlenir. Aynı konudaki iletiler **yazışma** (`thread`) olarak gruplanır; yazışma bir firmaya ve isteğe bağlı olarak bir projeye bağlanır ve ilgili kayıtların kartlarında E-postalar bölümünde görünür. ## Bağlanma kuralları 1. Karşı tarafın e-posta adresi bir kişininkiyle eşleşirse o kişinin firmasına bağlanır; eşleşmezse alan adından firma bulunur ya da (kuruluş kuralına göre) aday firma ve kişi kendiliğinden açılır. 2. Proje, sırasıyla şu kaynaklardan seçilir: yanıt zinciri, CRM'den gönderilmiş ilk ileti, firmanın tek açık projesi, konudaki teklif / proje numarası, firmanın son projesi (öneri olarak). Seçimin nedeni `opp_how` alanındadır. 3. Kullanıcı yazışmayı başka bir projeye taşıyabilir, ziyaret / deneme / görev / teklif / talep / sözleşmeye bağlayabilir ya da öneriyi yok sayabilir ([Yazışmayı kayda bağla](/rest-api/email/threads-action)). Bülten, bildirim ve otomatik yanıt gibi gürültü iletileri eşitlenmez; yönetici, **E-posta hesabı** sayfasındaki **Kuruluş e-posta kuralları** kartıyla belirli alan adlarını hiç almamayı ya da onlardan kişi açmamayı seçebilir. ## Gizlilik Posta kutusu sahibi paylaşımı seçer: **tam** (ekip ileti içeriğini görür) ya da **yalnız üst bilgi** (ekip konu ve kişileri görür, içerik gizli). İçeriği görme izni olmayan kullanıcıya iletiler `body: null`, `hidden: true` ile gelir. Bu kural API ve MCP için de geçerlidir. ## Yazışma alanları | Alan | Tür | Açıklama | |---|---|---| | `id` | integer | Yazışma kimliği. | | `subject` | string | Konu. | | `customer_id`, `customer` | integer, string | Bağlı firma. | | `opp_id`, `opp`, `opp_how` | integer, string | Bağlı proje ve seçilme nedeni (`chain`, `compose`, `single`, `number`, `recent`, `manual`…). | | `suggested` | boolean | Proje yalnız öneri (`recent`). | | `n`, `last_at`, `last_dir` | integer, datetime, string | İleti sayısı, son ileti zamanı ve yönü (`in` gelen, `out` giden). | | `waiting` | boolean | Son ileti gelen — yanıt bekliyor. | | `peer` | string | Son yazışan dış kişi. | Tekil uç ayrıca `messages`, `links`, `companies`, `suggestions`, `linkables`, `may_edit` ve yanıt için hazır `reply` (alıcı, `Re:` konusu, `in_reply_to`) döndürür. ## Gönderim [E-posta gönder](/rest-api/email/send) ucu iletiyi kullanıcının **kendi hesabından** gönderir; ileti Gönderilenler klasörüne de düşer ve yazışmaya bağlanır. Toplu gönderim ve e-posta dizileri yalnız web arayüzündedir (E-posta Otomasyonu modülü). --- # Kayıt kimlikleri > Sayısal kimlik (id) ile 15 karakterlik kalıcı kayıt kimliği (rid) arasındaki fark ve hangisinin nerede kullanılacağı. Her kaydın iki kimliği vardır: | | `id` | `rid` | |---|---|---| | Biçim | Tamsayı (`42`) | 15 karakter (`001aB3xY7…`) | | Kapsam | Kayıt türü içinde benzersiz | Kurulum içinde benzersiz, türü de söyler | | Kullanım | API yolları ve parametreleri | Dış sistemlerde saklama, arama, Excel eşleştirmesi | | Değişir mi? | Hayır | Hayır | API uçları her zaman `id` alır (`/api/v1/customers/42`). `rid` ise başka bir sistemde CRM kaydına referans saklamak, kullanıcıya göstermek ya da Excel'den içe aktarırken eşleştirmek içindir: tek başına kaydın türünü de taşır. ## Önekler `rid`'in ilk üç karakteri kayıt türünü verir: | Önek | Tür | Önek | Tür | |---|---|---|---| | `001` | Firma | `00T` | Görev | | `003` | Kişi | `00Q` | Fuar leadi | | `006` | Fırsat | `0Q0` | Teklif | | `00U` | Ziyaret / görüşme | `801` | Sipariş | | `a0D` | Deneme | `800` | Sözleşme | | `01t` | Ürün | `500` | Destek talebi | Kalan 12 karakter büyük-küçük harf duyarlı rastgele harf ve rakamlardır. ## rid ile kayıt bulmak [Arama ucuna](/rest-api/workspace/search) 15 karakterlik bir `rid` verirseniz yanıtın `record` alanı o kaydı çözer: ```json { "ok": true, "record": { "kind": "Customer", "url": "/customers/42", "customer_id": 42 }, "customers": [], "contacts": [], "opps": [], "leads": [] } ``` Web arayüzünde de üst çubuktaki arama ve komut paleti (Ctrl/⌘ + K) `rid` kabul eder. ## Dış kimlikler Başka bir sistemdeki kimliği CRM'de saklamak isterseniz [veri eşitleme kancası](/guides/inbound#sync) `external_id` alanını kayda bağlar ve sonraki gönderimlerde aynı kaydı günceller. Uygulamaların açtığı dış kayıtlar (Notion sayfası, Asana görevi…) da dış kimlik ve bağlantısıyla kayda yazılır; kayıt sayfasındaki **Uygulamalar** menüsünden açılır. --- # Aşamalar ve kapılar > Satış aşamaları kuruluma göre tanımlanır; aşama kapıları açıksa fırsat ancak koşulları sağladığında ilerler. Her kurulumun satış hattı **Ayarlar → Aşamalar** sayfasında tanımlanır: açık aşamaların anahtarı, adı ve rengi, kazanıldı / kaybedildi / iptal adları, İptal aşamasının açık olup olmadığı ve **aşama kuralları**. ## Aşama anahtarları API her zaman **anahtarı** kullanır; ad kullanıcıya gösterilir. Kapanış anahtarları sabittir: | Anahtar | Anlamı | |---|---| | `Win` | Kazanıldı | | `Lost` | Kaybedildi | | `Cancel` | İptal (kurulumda kapalı olabilir) | | `Account` | Müşteri takibi kabı (satış hunisinde sayılmaz) | Açık aşama anahtarları kuruluma özeldir. Örneğin Makro hattı `Qualify`, `Viable`, `Present Solution`, `Negotiation`, `Expect to Close`; yeni bir kurulumun varsayılanı `Lead`, `In Progress` kullanır. Anahtarları sabit kodlamayın — [`GET /api/v1/me`](/rest-api/auth/me) yanıtından okuyun: ```json "constants": { "stages": ["Qualify", "Viable", "Present Solution", "Negotiation", "Expect to Close", "Win", "Lost", "Cancel"], "open_stages": ["Qualify", "Viable", "Present Solution", "Negotiation", "Expect to Close"], "stage_labels": { "Qualify": "Qualify", "Win": "Win", "Lost": "Lost", "…": "…" }, "stage_sla": { "Qualify": 30, "Viable": 60, "Present Solution": 60, "Negotiation": 30, "Expect to Close": 15 }, "stage_rules": true } ``` ## Aşama kuralları (kapılar) `stage_rules: true` olan kurulumlarda: - Açık aşamalar **sırayla** geçilir; bir aşama atlanamaz. - Bir sonraki aşamaya geçmek için bulunulan aşamanın kapısı sağlanmalıdır. - `Win` yalnız son iki açık aşamadan (Makro'da `Negotiation`, `Expect to Close`) verilir. - Kapanmış bir fırsat yeniden açılmaz; [Fırsatı canlandır](/rest-api/deals/revive) yeni bir kayıt açar. Makro hattının kapıları: | Aşama | Sonrakine geçmek için | |---|---| | `Qualify` | BANT 4/4 (bütçe, yetki, ihtiyaç, zamanlama) | | `Viable` | Proje başladıktan sonra en az bir yüz yüze (F2F) ziyaret ve fırsata ürün seçilmiş olmalı | | `Present Solution` | Son denemenin sonucu Başarılı ya da Kısmen Olumlu | | `Negotiation` | Teklif fiyatı (ya da tahmini değer) ve vade girilmiş | Kapı sağlanmazsa [aşama ucu](/rest-api/deals/stage) `409 gate` döner: ```json { "ok": false, "error": "gate", "message": "Viable'a geçmek için: BANT kriterleri 4/4 sağlanmalı (Budget · Authority · Need · Time)", "gate_problems": ["BANT kriterleri 4/4 sağlanmalı (Budget · Authority · Need · Time)"] } ``` Fırsatın tekil ucu her zaman `gate_ok`, `gate_problems` ve `next_stage` alanlarını taşır; aşama değiştirmeden önce bunlara bakarak kullanıcıya eksikleri gösterebilirsiniz. `stage_rules: false` olan kurulumlarda kapı yoktur: fırsat herhangi bir açık aşamaya, kazanıldı ya da kaybedildiye taşınabilir. ## Süreler (SLA) Her açık aşamanın gün hedefi (`stage_sla`) ve fırsatın toplam yaşam süresi (`opp_lifetime_days`, varsayılan 180 gün) vardır. Aşmalar fırsatın `sla_left` ve `lifetime_left` alanlarında eksi değer olarak görünür ve sağlık puanını düşürür. Değerler **Ayarlar → Aşama kurulumu** kartından kuruluma göre değişir. ## Kazanma ve kaybetme - `Win`: firma `AC` (aktif müşteri) olur, kabul edilen teklifin kalemleri firmaya anlaşmalı fiyat olarak yazılır, Sözleşmeler modülü açıksa taslak sözleşme açılır ve uygulamalara `opp_won` olayı gider. - `Lost` / `Cancel`: `reason` alanı kayıp nedeni olarak saklanır (`constants.loss_reasons`; listede yoksa `Diğer`). `Lost`'ta uygulamalara `opp_lost` olayı gider. - Her aşama değişikliğinde ayrıca `opp_stage` olayı (eski ve yeni aşamayla) gönderilir. --- # Birim ve döviz > Miktarlar ürünün biriminde, fiyatlar kaydın dövizinde tutulur; toplamlar kurulumun ana dövizine çevrilir. Solk sanayi satışları için tasarlandı: bir fırsatın değeri çoğu zaman **aylık miktar × birim fiyat** ile hesaplanır ve farklı ürünler farklı birimlerle (kg, ton, litre, adet, m²…) satılır. ## Birimler - Kurulumun **ana birimi** firma ayarıdır (ör. `kg`); firma potansiyeli ve panolar bu birimdedir. `GET /api/v1/me` → `app.unit_default`, açık birimler `app.units`. - Her ürünün kendi birimi olabilir. Fırsat yanıtlarında `unit` (ör. `lt`), `unit_code` ve fiyat birimi `pu` (ör. `€/lt`) ürünün birimini gösterir. - Alan adlarındaki `_kg` eki tarihseldir: `potential_kg`, `commit_kg` gibi alanlar **ürünün ya da kurulumun biriminde** miktar taşır, her zaman kilogram değildir. ## Dövizler - Kurulumun **ana dövizi** vardır (`app.currency_base`, ör. `EUR`) ve açık dövizler listesi (`app.currencies`). - Her firmanın varsayılan işlem dövizi olabilir (`currency_default`); fırsat ve teklifler kendi dövizlerini taşır (`currency`, `cur_sym`). - Toplamlar ve panolar ana dövize çevrilmiş değerle hesaplanır: fırsatlarda `value_base`, listelerde `value_open` ve `base_currency`. ## Kurlar Kurlar her iş günü Türkiye Cumhuriyet Merkez Bankası'ndan alınır (22 döviz). Yönetici döviz **alış** ya da **satış** kurunu seçer; bir gün için elle kur girip sabitleyebilir. Çevrim, kaydın tarihindeki değil **güncel** kurla yapılır — geçmiş raporlar kur değiştikçe ana dövizde değişebilir. ## API'de sayılar - Miktar ve tutarlar JSON sayısıdır (`12000`, `2.35`); binlik ayırıcı ya da para simgesi yoktur. Gösterim için `value_short` (ör. `"338K"`, `"2,46M"`) ve `cur_sym` kullanın. - İstek gövdelerinde sayıları JSON sayısı olarak gönderin. Metin olarak gönderilen `"2,35"` ve `"1.250,50"` de doğru çözülür (Türkçe yazım: nokta binlik, virgül ondalık). --- # Pasifleştirme ve silme > Solk'ta kayıtlar çoğunlukla silinmez; kapatılır, pasife alınır ya da ayrıldı olarak işaretlenir. Geçmiş raporlar bozulmaz. Satış geçmişi raporların, öngörünün ve müşteri ilişkisinin temelidir. Bu yüzden Solk silmek yerine kaydı **etkisizleştirir**; kalıcı silme yalnız yöneticinin web arayüzünden, otomatik yedek alındıktan sonra yaptığı bir işlemdir. API'de kalıcı silme ucu yoktur. | Kayıt | Silmek yerine | API'de | |---|---|---| | Firma | **Pasife al** — listelerden ve hatırlatıcılardan çıkar, geçmişi kalır. Pasif firmayı yönetici ikinci kez silerse yedek alınıp kalıcı silinir. | Pasifler `?active=0` ile listelenir; durum API'den değiştirilmez. | | Kişi | **Ayrıldı** işaretle (`is_former`) — geçmiş görüşmelerde adı kalır. | [Kişiyi güncelle](/rest-api/people/update) `is_former: true` | | Fırsat | **Kaybedildi** ya da **İptal** aşamasına al; gerekirse canlandır. | [Aşamayı değiştir](/rest-api/deals/stage), [Canlandır](/rest-api/deals/revive) | | Görev / aksiyon | **Tamamla** — sonuç notuyla. | [Aksiyonu tamamla](/rest-api/actions/complete) | | Fuar leadi | **Vazgeçildi** durumu. | [Leadi güncelle](/rest-api/fairs/leads-update) | | Takvim etkinliği | Uygulamada eklenen etkinlik silinebilir. | [Etkinliği sil](/rest-api/calendar/delete) | | Kullanıcı | **Kapat** — giriş ve belirteçler kapanır, kayıtları kalır. | [SCIM](/rest-api/scim) `active: false` | ## Örnek veri Yönetici, sunum ya da deneme için **Ayarlar** sayfasındaki **Örnek veri** kartıyla kuruluma örnek firmalar, fırsatlar ve aksiyonlar yükleyebilir; tek tıkla hepsi geri alınır. Örnek kayıtlar uygulamalara olay göndermez. Entegrasyonunuzu canlı kuruluma bağlamadan önce bir deneme kurulumunda örnek veriyle sınamanızı öneririz. ## Denetim günlüğü Oluşturma, düzenleme, aşama değişikliği, erteleme, silme, izin reddi ve MCP araç çağrıları kimin yaptığıyla denetim günlüğüne yazılır; mobil uygulama ve API'den gelenler "mobil" notunu taşır. Yönetici **Ayarlar → Denetim günlüğü**'nden görür. --- # Uygulamalar > Ayarlar → Uygulamalar'daki 40'tan fazla hazır bağlantı — ne yaptıkları, nasıl kurulduklarını ve hangi verinin nereye gittiğini. **Ayarlar → Uygulamalar** sayfası hazır bağlantıların kataloğudur. Her kartın bir **detay sayfası** vardır (genel bakış, kurulum adımları, izinler, belgeler bağlantısı); **Bağla** düğmesi bağlantının ayarlarını açar. Bağlı uygulamalar **Bağlı** sekmesinde toplanır ve son gönderimin sonucunu gösterir. Bağlantılar dört biçimde çalışır: | Biçim | Ne olur | Örnek | |---|---|---| | **Kanal** | CRM olayları mesaj olarak bir kanala düşer. | Slack, Teams, Telegram | | **Olaydan kayıt** | CRM olayı başka uygulamada kayıt açar. | Notion sayfası, Asana görevi, Sheets satırı | | **Kayıt eylemi** | Kayıt sayfasındaki **Uygulamalar** menüsünden tek tıkla işlem. | Hunter ile e-posta bul, Aircall ile ara, PandaDoc belgesi aç | | **Gelen veri** | Dış sistem CRM'e veri gönderir. | Site formu, Stripe, Segment | Olay gönderen bağlantılarda hangi olayların gideceğini siz seçersiniz; olay listesi ve gövde biçimi [Web kancaları](/guides/webhooks) sayfasında. ## Katalog | Uygulama | Biçim | Ne yapar | |---|---|---| | Slack | Kanal · komut | Seçilen olaylar kanala düşer; `/crm` komutuyla Slack'ten firma, kişi, fırsat aranır. | | Microsoft Teams | Kanal | Olaylar Teams kanalına kart olarak düşer. | | Google Chat | Kanal | Olaylar Google Chat alanına mesaj olarak düşer. | | Discord | Kanal | Olaylar Discord kanalına düşer. | | Telegram | Kanal | Bir Telegram botu olayları gruba ya da kanala yazar. | | Web kancası | Olay | İmzalı JSON herhangi bir adrese. | | Zapier · Make · n8n · Pipedream | Olay + API | Olaylar akışınızı başlatır; akıştan CRM'e API anahtarıyla yazılır. | | Notion | Olaydan kayıt | Olaylar Notion veritabanına sayfa olarak eklenir. | | Airtable | Olaydan kayıt | Olaylar Airtable tablosuna satır olarak eklenir. | | Google Sheets | Olaydan kayıt | Olaylar tabloya satır olarak eklenir (tarih · olay · başlık · açıklama · bağlantı). | | Asana · ClickUp | Olay + eylem | Olaylardan ve kayıtlardan görev açılır. | | Linear | Eylem | Destek talebi ya da nottan Linear konusu açılır. | | Productboard | Eylem | Müşteri görüşü (not, talep) içgörü olarak gönderilir. | | PandaDoc | Eylem | Tekliften PandaDoc belgesi açılır ve imzaya gönderilir. | | Stripe | Eylem + gelen | Firmanın Stripe müşterisi ve faturaları; ödemeler zaman tüneline düşer. | | Aircall | Eylem + gelen | Kişiden tek tıkla arama; aramalar görüşme olarak, cevapsızlar geri arama görevi olarak düşer. | | RingCentral | Eylem + gelen | RingCentral uygulamasıyla arama; kayıtlar Zapier üzerinden görüşme olur. | | Calendly / Cal.com | Gelen | Randevu takvime, kişiye ve nota düşer; iptal toplantıyı iptal eder. | | Toplantı notları | Gelen | Yapay zekâ notları firmaya not, eylem maddeleri göreve. | | Segment | Gelen | `identify` / `group` / `track` olaylarıyla kişi, firma ve not. | | Veri eşitleme | Gelen | Veri ambarından toplu firma / kişi açma ve güncelleme. | | Site formu · Typeform · Tally | Gelen | Başvuru → aday firma + kişi + sorumluya görev. | | Apollo | Eylem | Firmayı alan adından, kişiyi e-postasından zenginleştirir. | | Hunter | Eylem | E-posta bulur / doğrular; alan adındaki kişileri getirir. | | Mailchimp | Olay + eylem | Kişileri kitleye ekler; isterseniz yeni kişiler otomatik eklenir. | | lemlist | Eylem + gelen | Kişiyi kampanyaya ekler; yanıt ve ilgi olayları not ve görev olur. | | Mixmax | Eylem | Kişiyi Mixmax dizisine ekler. | | Resend | Sistem | Bildirim ve davet e-postaları Resend'den gider; kişiler Resend kitlesine eklenir. | | Okta / Entra ID (SCIM) | Gelen | Kullanıcılar kimlik sağlayıcıdan açılır, güncellenir, kapatılır. [SCIM](/rest-api/scim) | | Tarayıcıdan ekle | Eylem | Yer imi düğmesi: bulunduğunuz sayfadaki firmayı (site, LinkedIn, Gmail) aday olarak ekler. | | Google Drive · OneDrive · Dropbox · Box | Depolama | Kayıt dosyaları buluttaki CRM klasörüne. [Depolama](/guides/storage) | | Bulut yedeği | Depolama | Günlük veritabanı yedeği bağlı bulut hesabına da gider. | | Claude | Yapay zekâ | CRM'i Claude'a bağlar (MCP). [MCP](/mcp/overview) | ## Kayıt sayfasında uygulamalar Firma, kişi, fırsat ve destek talebi sayfalarında **Uygulamalar** menüsü bağlı uygulamaların eylemlerini gösterir: Aircall ile ara, Hunter ile e-posta bul, Apollo ile zenginleştir, Asana görevi aç, PandaDoc belgesi oluştur… Uygulamada açılan dış kayıt (görev, sayfa, belge) bağlantısıyla birlikte CRM kaydına yazılır ve aynı menüden açılır. Arama uygulaması bağlıysa kişi ve firma telefonlarının yanında **Ara** düğmesi çıkar; arama sonrası görüşme kaydı kendiliğinden düşer. ## Uygulama isteği Katalogda olmayan bir uygulama mı lazım? Uygulamalar sayfasının altındaki **Uygulama isteyin** bağlantısı isteği Solk ekibine iletir; en çok istenenler önceliklendirilir. O zamana kadar [web kancaları](/guides/webhooks), [gelen kancalar](/guides/inbound) ve [REST API](/rest-api/overview) ile hemen her aracı bağlayabilirsiniz. ## Güvenlik - Uygulama anahtarları yalnız kurulumun kendi veritabanında tutulur ve ekranda maskelenir; depolama hesaplarının OAuth belirteçleri ve e-posta parolaları **şifreli** saklanır. Veritabanı yedeklerinizi buna göre koruyun. - Bağlantıları yalnız yöneticiler kurar (depolama hesapları ve Claude hariç — onları her kullanıcı kendisi için bağlar). - Her bağlantının son gönderimi, hatası ve sayacı kartta görünür; **Bağlantıyı dene** düğmesi kimlik bilgisini doğrular. --- # Web kancaları > CRM'de bir şey olduğunda (fırsat kazanıldı, yeni firma, form başvurusu…) seçtiğiniz adrese imzalı JSON gönderin. Zapier, Make, n8n ve Pipedream aynı mekanizmayı kullanır. Web kancası (giden kanca), CRM'deki olayları sizin sisteminize **itme** yöntemidir: sürekli API'yi yoklamak yerine olay olduğunda bir HTTP `POST` alırsınız. Kurulum **Ayarlar → Uygulamalar** sayfasından yapılır; kod yazmak gerekmez. ## Kurulum :::steps ### Bağlantıyı açın **Ayarlar → Uygulamalar**'da **Web kancası**'nı (ya da Zapier, Make, n8n, Pipedream kartını) seçin ve **Bağla**'ya basın. ### Adresi girin Olayların gönderileceği `https://` adresini yapıştırın. Zapier'de "Webhooks by Zapier → Catch Hook", Make'te "Webhooks → Custom webhook", n8n'de "Webhook" düğümü, Pipedream'de "HTTP / Webhook" tetikleyicisi size bu adresi verir. ### Olayları seçin Hangi olaylarda gönderim yapılacağını işaretleyin. Bağlantı kartı bir **imza anahtarı** gösterir; alıcı tarafta isteğin gerçekten CRM'den geldiğini doğrulamak için saklayın. ### Deneyin **Bağlantıyı dene** alıcınıza `event: "test"` olan bir deneme gönderir. Son gönderimin sonucu (HTTP durum kodu ya da hata) kartta görünür. ::: ## Olaylar | Olay | Ne zaman | `data` alanları | |---|---|---| | `opp_created` | Yeni fırsat açıldı | `id`, `rid`, `name`, `customer`, `customer_id`, `stage`, `stage_label`, `value`, `currency`, `owner` | | `opp_stage` | Fırsatın aşaması değişti (başlık eski → yeni aşamayı yazar) | fırsat alanları | | `opp_won` | Fırsat kazanıldı (`Win`) | fırsat alanları | | `opp_lost` | Fırsat kaybedildi (`Lost`) | fırsat alanları | | `customer_created` | Yeni firma | `id`, `rid`, `name`, `status`, `source`, `city` | | `contact_created` | Yeni kişi (e-postadan otomatik açılanlar hariç) | `id`, `name`, `email`, `phone`, `title`, `customer`, `customer_id` | | `lead_created` | Yeni fuar leadi | `id`, `company`, `contact`, `email`, `interest` | | `visit_created` | Görüşme / ziyaret kaydı | `id`, `customer`, `type`, `date`, `next_action`, `note` | | `ticket_created` | Yeni destek talebi | `id`, `subject`, `priority`, `who` | | `form_submitted` | Site formu / Typeform / Tally başvurusu | `form`, `company`, `name`, `email`, `phone`, `customer_id`, `task_id`, `new_customer` | Olaylar web arayüzünden, mobil uygulamadan, API'den, MCP'den, iş akışlarından ve gelen kancalardan doğan değişikliklerin hepsinde gönderilir. İki istisna: - **Örnek veri** kayıtları olay göndermez. - Tek bir işlemde 25'ten fazla olay doğarsa (ör. Excel'den toplu içe aktarma) olaylar gönderilmez; denetim günlüğüne not düşülür. ## Gövde ```http POST /sizin/adresiniz HTTP/1.1 Content-Type: application/json X-Solk-Event: opp_won X-Solk-Signature: sha256=5d1c0a3e9b… ``` ```json { "event": "opp_won", "title": "Fırsat kazanıldı", "text": "Kuzey Plastik Sanayi · Streç film tedariki · 338.400 € · Sorumlu: Deniz Aksoy", "url": "https://ornek.solk.app/opportunities/53", "data": { "id": 53, "rid": "006Xq3LmT0aZb9K", "name": "Streç film tedariki", "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "stage": "Win", "stage_label": "Win", "value": 338400.0, "currency": "EUR", "owner": "Deniz Aksoy" }, "workspace": "Örnek Kimya", "app": "Solk CRM", "sent_at": "2026-10-02T14:05:11" } ``` | Alan | Açıklama | |---|---| | `event` | Olay adı (yukarıdaki tablo; denemede `test`). | | `title`, `text` | İnsan okuyacak başlık ve özet — bildirim kanallarına doğrudan basılabilir. | | `url` | Kaydın CRM'deki adresi. | | `data` | Olaya özgü yapısal alanlar. Kaydın tamamı gerekiyorsa `data.id` ile [REST API](/rest-api/overview)'den isteyin. | | `workspace`, `app` | Kurulumun firma adı ve uygulama adı. | | `sent_at` | Gönderim zamanı (kurulumun yerel saati). | ## İmzayı doğrulama `X-Solk-Signature`, istek gövdesinin (ham bayt olarak) imza anahtarıyla HMAC-SHA256 özetidir. Gövdeyi JSON olarak ayrıştırmadan **önce** doğrulayın: :::code ```python Python import hmac, hashlib def dogrula(govde_bytes: bytes, imza: str, anahtar: str) -> bool: beklenen = "sha256=" + hmac.new(anahtar.encode(), govde_bytes, hashlib.sha256).hexdigest() return hmac.compare_digest(beklenen, imza or "") # Flask @app.post("/solk") def solk(): if not dogrula(request.get_data(), request.headers.get("X-Solk-Signature"), ANAHTAR): abort(401) olay = request.get_json() ... ``` ```javascript Node.js import crypto from "node:crypto"; import express from "express"; const app = express(); app.post("/solk", express.raw({ type: "application/json" }), (req, res) => { const beklenen = "sha256=" + crypto.createHmac("sha256", process.env.SOLK_SIGNING_KEY) .update(req.body).digest("hex"); const imza = req.get("X-Solk-Signature") || ""; if (imza.length !== beklenen.length || !crypto.timingSafeEqual(Buffer.from(imza), Buffer.from(beklenen))) { return res.sendStatus(401); } const olay = JSON.parse(req.body); res.sendStatus(204); }); ``` ```php PHP $govde = file_get_contents('php://input'); $beklenen = 'sha256=' . hash_hmac('sha256', $govde, getenv('SOLK_SIGNING_KEY')); if (!hash_equals($beklenen, $_SERVER['HTTP_X_SOLK_SIGNATURE'] ?? '')) { http_response_code(401); exit; } $olay = json_decode($govde, true); ``` ::: ## Teslim - Gönderim istekten bağımsız, arka planda yapılır; CRM kullanıcısı beklemez. - Zaman aşımı **8 saniyedir**. Alıcınız `2xx` döndürmelidir; uzun işleri kuyruğa alıp hemen yanıt verin. - **Yeniden deneme yoktur.** Başarısız gönderim bağlantı kartında son durum olarak görünür. Olay kaçırmamanız gereken durumlarda periyodik olarak API'den eşitleyin (ör. `GET /api/v1/opportunities?sort=update`). - Sıra garanti değildir; aynı kayıt için gelen olayları `sent_at` ile sıralayın. ## Kanallar Slack, Microsoft Teams, Google Chat, Discord ve Telegram bağlantıları aynı olayları kanalın kendi mesaj biçiminde (Slack metni, Teams uyarlanabilir kartı…) gönderir; imza başlığı yalnız web kancası, Zapier, Make, n8n ve Pipedream'de vardır. Notion, Airtable, Google Sheets, Asana, ClickUp, Linear ve Mailchimp bağlantıları ise olaydan o uygulamada kayıt açar. Ayrıntı: [Uygulamalar](/guides/apps). ## CRM'e geri yazmak Akışınız CRM'de kayıt açacak ya da güncelleyecekse (ör. Zapier'de "fatura ödendi → görevi tamamla") **Ayarlar → Geliştiriciler**'den bir API anahtarı açıp Zapier / Make / n8n'in HTTP adımında `Authorization: Bearer sk_…` başlığıyla [REST API](/rest-api/overview)'yi çağırın. Dışarıdan veri itmenin kodsuz yolu ise [gelen kancalardır](/guides/inbound). --- # Gelen kancalar > Formlardan, telefon santralinden, ödeme sisteminden, veri ambarından ve randevu araçlarından CRM'e gizli bir adres üzerinden veri gönderin. Gelen kanca, dış bir sistemin CRM'e **kimlik bilgisi olmadan** veri göndermesini sağlayan gizli bir adrestir. Her bağlantının kendi adresi vardır; adresteki rastgele anahtar hem kimliği hem de yetkiyi taşır. Adresi **Ayarlar → Uygulamalar**'da ilgili kartı bağlayınca alırsınız. ```text https://ornek.solk.app/in// (Aircall, Stripe, Segment, veri eşitleme…) https://ornek.solk.app/in/form/ (site formu, Typeform, Tally) ``` :::warning Gizli anahtarı içeren adres bir parola gibidir. Yalnız gönderen sisteme girin; sızdıysa bağlantı kartından **Adresi yenile** ile yenisini alın — eski adres anında çalışmaz. ::: ## Ortak kurallar - Yöntem `POST`, gövde JSON (form gönderimlerinde `application/x-www-form-urlencoded` de olur). Aynı adrese `GET` isteği bağlantının çalıştığını doğrulayan kısa bir JSON döndürür. - Kayıtlar bağlantının **sorumlusu** adına açılır (bağlantıyı kuran kullanıcı ya da kartta seçilen kişi); olayın kendi kullanıcısı (ör. aramayı yapan temsilcinin e-postası) eşleşirse o kullanıcı kullanılır. - Aynı olay iki kez gelirse (dış kimlik aynıysa) ikinci kez işlenmez: `{"ok": true, "skipped": "zaten işlendi"}`. - Hız sınırı **saat başına** bağlantı başınadır: Segment 3000, veri eşitleme / Aircall / RingCentral / Stripe 600, diğerleri 120 istek. Aşılırsa `429`. - Kapatılmış ya da silinmiş bağlantının adresi `404` döner. ## Site formu {#form} Kendi web sitenizdeki iletişim / teklif formunu doğrudan CRM'e bağlayın. Her başvuru: 1. Firma adına göre mevcut firmayı bulur, yoksa **aday firma** açar (kaynak: Web formu), 2. Kişiyi (e-posta yoksa ekleyerek) firmaya bağlar, 3. Bağlantı sorumlusuna **bugün terminli, yüksek öncelikli** bir görev açar ve bildirim gönderir, 4. `form_submitted` olayını uygulamalara iletir. Alanlar adlarından tanınır; Türkçe ya da İngilizce olabilir: | CRM alanı | Tanınan alan adları | |---|---| | Firma | `firma`, `şirket`, `company`, `kurum`, `organization` | | Ad soyad | `ad soyad`, `adınız`, `isim`, `name`, `full name`, `yetkili` | | E-posta | `e-posta`, `eposta`, `email`, `mail` (değer `@` içermeli) | | Telefon | `telefon`, `phone`, `tel`, `gsm`, `cep` | | Şehir | `şehir`, `il`, `city` | | Unvan | `unvan`, `görev`, `title`, `pozisyon` | | Mesaj | `mesaj`, `message`, `not`, `açıklama`, `talep`, `konu` | Tanınmayan alanlar da kaybolmaz: tüm alanlar görevin ayrıntısına "etiket: değer" satırları olarak yazılır. :::code ```html HTML formu
``` ```bash cURL (JSON) curl https://ornek.solk.app/in/form/GIZLI_ANAHTAR \ -H "Content-Type: application/json" \ -d '{"company": "Anadolu Gıda", "name": "Murat Er", "email": "murat@anadolugida.com.tr", "message": "Aylık 5 ton streç film"}' ``` ::: Yanıt `{"ok": true, "customer_id": 42, "task_id": 118}`. HTML formunda `_next` verilmişse kullanıcı o sayfaya yönlendirilir. **Typeform** ve **Tally** aynı adres yapısını kullanır; formun webhook ayarına adresi yapıştırın. Kartta imza gizli anahtarı girerseniz `Typeform-Signature` / `Tally-Signature` başlıkları doğrulanır ve imzasız istekler `401` alır. ## Veri eşitleme {#sync} Veri ambarınızdaki, ERP'nizdeki ya da başka bir CRM'deki firma ve kişileri toplu olarak gönderip **açın ya da güncelleyin**. Census, Hightouch, Airbyte, kendi betiğiniz ya da Zapier ile kullanılabilir. ```bash curl "https://ornek.solk.app/in/sync/GIZLI_ANAHTAR?obj=customer" \ -H "Content-Type: application/json" \ -d '{"rows": [ {"external_id": "ERP-1042", "name": "Kuzey Plastik Sanayi", "website": "kuzeyplastik.com.tr", "city": "Bursa", "industry": "Plastik", "status": "AC"}, {"external_id": "ERP-1043", "name": "Anadolu Gıda Ambalaj", "phone": "+90 332 555 10 20"} ]}' ``` ```json { "ok": true, "created": 1, "updated": 1, "errors": [] } ``` - Nesne `?obj=customer` / `?obj=contact` ya da gövdede `"object"` ile seçilir; verilmezse satırda `email` olan ve `industry` olmayanlar kişi sayılır. - Gövde bir dizi, `rows` / `records` / `batch` / `data` alanı ya da tek bir nesne olabilir. Tek istekte en çok **500 satır**. - Eşleşme sırası — firma: `external_id` (daha önce gönderildiyse) → ad → web sitesinin alan adı. Kişi: `external_id` → e-posta. - Firma alanları: `name`/`company`, `website`/`domain`, `phone`, `city`, `industry`/`sector`, `address`, `status` (`AC`/`Prospect`). Kişi alanları: `name` (ya da `first_name` + `last_name`), `email`, `phone`/`mobile`, `title`/`job_title`, `department`, firma için `company` ya da `company_external_id`. - Boş değerler mevcut veriyi silmez; yalnız dolu alanlar yazılır. ## Segment {#segment} Segment'te **Webhooks (Actions)** hedefi ekleyip adresi girin. - `identify` → kişi (ve `traits.company` varsa firma) açılır ya da güncellenir; `userId` dış kimlik olarak saklanır. - `group` → firma açılır ya da güncellenir (`groupId`, `traits.name`, `website`, `industry`). - `track` → kartta seçtiğiniz olay adları (ör. `Demo Requested, Trial Started`) kişinin firmasına not olarak düşer. Toplu gönderimde (`batch`) istek başına en çok 100 olay işlenir. ## Telefon: Aircall ve RingCentral {#calls} **Aircall** webhook'unda `call.ended` ve `call.voicemail_left` olaylarını seçin. **RingCentral** ve diğer santraller için Zapier / Make üzerinden şu düz gövdeyi gönderin: ```json { "id": "rc-88231", "direction": "outbound", "number": "+90 224 555 01 02", "result": "answered", "duration": 312, "started_at": "2026-10-02T10:41:00+03:00", "user_email": "deniz@ornekkimya.com.tr", "recording_url": "https://…", "notes": "Fiyat görüşüldü" } ``` - Numara bir kişi ya da firmayla eşleşirse **telefon görüşmesi** kaydı açılır (süre, yön, kayıt bağlantısı, not). - Cevapsız gelen aramada sorumluya **"Geri ara"** görevi açılır (yüksek öncelik, sesli mesaj bağlantısıyla). - Numara bilinmiyorsa "kaydı firmaya bağlayın" görevi açılır. ## Stripe {#stripe} Stripe panelinde webhook uç noktası olarak adresi ekleyin ve `invoice.paid`, `invoice.payment_failed`, `customer.subscription.deleted`, `checkout.session.completed` olaylarını seçin. Kartta **imza gizli anahtarını** (`whsec_…`) girerseniz `Stripe-Signature` doğrulanır. Ödemeler firmaya not düşer; başarısız ödeme ve sona eren abonelik için sorumluya görev açılır. Firma, daha önce bağlanmış Stripe müşteri kimliğinden ya da müşterinin e-postasından bulunur; `checkout.session.completed` olayında firma yoksa aday olarak açılır. ## Randevu: Calendly ve Cal.com {#booking} Randevu alındığında takvime toplantı, firmaya kişi ve not düşer; randevu iptal edilince toplantı da iptal olur. Calendly'de `invitee.created` / `invitee.canceled`, Cal.com'da `BOOKING_CREATED`, `BOOKING_RESCHEDULED`, `BOOKING_CANCELLED` olaylarını seçin. ## Toplantı notları {#meeting-notes} Yapay zekâ toplantı notu araçlarından (Fireflies, Fathom, tl;dv, Otter… doğrudan ya da Zapier ile) gelen özet, katılımcıların firmasına not olarak; eylem maddeleri iki gün terminli görev olarak düşer. ```json { "id": "mtg_5521", "title": "Kuzey Plastik · haftalık", "summary": "Numune sonuçları olumlu, fiyat revizyonu istendi.", "attendees": [{ "email": "emre.yildiz@kuzeyplastik.com.tr", "name": "Emre Yıldız" }], "action_items": ["Revize fiyatı gönder", "Hat denemesi tarihini netleştir"], "url": "https://…" } ``` ## lemlist {#lemlist} Kampanya olayları (`emailsReplied`, `linkedinReplied`, `emailsInterested`, `meetingBooked`, `emailsNotInterested`, `emailsBounced`, `emailsUnsubscribed`, `emailsClicked`) kişinin firmasına not düşer; yanıt, ilgi ve toplantı olaylarında sorumluya "dönüş yapın" görevi açılır. ## Slack komutu {#slack} Slack uygulamanızda bir **Slash Command** (ör. `/crm`) tanımlayıp istek adresi olarak bağlantının adresini, kartta da Slack'in **Signing Secret**'ını girin. `/crm kuzey` yazan kullanıcı eşleşen firma, kişi ve fırsatları yalnız kendisinin göreceği bir yanıtla alır. --- # Depolama hesapları > Kayıt dosyalarını Google Drive, OneDrive, Dropbox ya da Box'a kaydedin; buluttaki dosyaları kayıtlara bağlayın; günlük yedeği buluta gönderin. Firma, fırsat, teklif, sözleşme ve destek talebi kayıtlarına eklenen dosyalar CRM sunucusunda saklanır. Depolama hesabı bağlayan kullanıcı bu dosyaları **kendi bulut hesabına** da kaydedebilir ve bulutta zaten duran dosyaları kayda bağlantı olarak ekleyebilir. ## Desteklenen sağlayıcılar | Sağlayıcı | İzin | Dosyaların yeri | |---|---|---| | Google Drive | `drive.file` — yalnız CRM'in açtığı dosya ve klasörler | `CRM//` | | Microsoft OneDrive | `Files.ReadWrite` (kişisel OneDrive ya da OneDrive for Business) | `CRM//` | | Dropbox | Uygulama klasörü | `Uygulamalar//CRM//` | | Box | Hesap | `CRM//` | Klasör adı kurulumda değiştirilebilir (`STORAGE_ROOT`, varsayılan `CRM`). Firmaya bağlı olmayan kayıtların dosyaları `CRM/Genel/` klasörüne gider. ## Bağlamak :::steps ### Hesabı seçin **Ayarlar → Depolama hesapları** sayfasında (ya da Uygulamalar'daki sağlayıcı kartından) sağlayıcıyı seçin. Sağlayıcının izin ekranında hesabınızla giriş yapıp onay verin. ### Seçenekleri belirleyin Bağlı hesap kartında: - **Eklediğim dosyaları buraya da kaydet** — açıksa kayıtlara yüklediğiniz her dosya arka planda buluta da gönderilir. - **Günlük veritabanı yedeği** (yalnız yönetici) — her gece alınan yedek sıkıştırılıp bu hesaba da yüklenir. ### Deneyin Herhangi bir kaydın **Dosyalar** kartında dosyanın yanındaki **Buluta kaydet**'i seçin. Dosya bulut klasöründe açılır ve dosya satırında sağlayıcının simgesiyle görünür. ::: Her kullanıcı kendi hesabını bağlar; bir kullanıcının bulut hesabı diğer kullanıcılara açılmaz. Aynı kullanıcı birden fazla sağlayıcı bağlayabilir. ## Bulut bağlantısı eklemek Dosya zaten Drive, OneDrive, Dropbox ya da Box'taysa kayda yeniden yüklemek yerine **Dosyalar** kartındaki **Bağlantı** düğmesiyle paylaşım adresini (https://…) yapıştırın. Bağlantı dosya listesinde sağlayıcısının simgesiyle durur ve tıklanınca bulutta açılır; CRM dosyanın içeriğini indirmez. ## Davranış - Buluta kaydedilen dosya CRM'de de **kalır**; bulut kopyası ek bir kopyadır. CRM'den silmek bulut kopyasını silmez. - Aynı dosya ikinci kez kaydedilmez ("zaten bulutta"). - Yükleme başarısızsa (ör. izin geri alınmış) hesap kartı son hatayı gösterir; dosya CRM'de durmaya devam eder. - Hesabın bağlantısını kesmek buluttaki dosyaları silmez. ## Kendi sunucunuzda kurulum Solk'un yönettiği kurulumlarda sağlayıcı uygulamaları hazırdır. Kendi sunucunuzda çalışan bir kurulumda `.env` dosyasına sağlayıcı anahtarları girilmelidir: `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` (Drive API açık, izin ekranında `drive.file`), `MS_CLIENT_ID` / `MS_CLIENT_SECRET` (Graph `Files.ReadWrite`, `User.Read`), `DROPBOX_APP_KEY` / `DROPBOX_APP_SECRET`, `BOX_CLIENT_ID` / `BOX_CLIENT_SECRET`. Yönlendirme adresi, e-posta bağlantısıyla aynı geri dönüş adresidir. --- # REST API > Solk CRM'in JSON API'si — mobil uygulamanın kullandığı uçların aynısı, aynı yetki kuralları ve aynı iş kurallarıyla. Solk REST API'si kurulumunuzdaki firmaları, kişileri, fırsatları, aksiyonları, takvimi, fuar leadlerini ve e-posta yazışmalarını okumanızı ve değiştirmenizi sağlar. Solk'un iOS / Android uygulaması da bu API'yi kullanır; bu yüzden uçlar gerçek bir satışçının ekranlarına göre şekillenmiştir: bir istekte bir ekranın ihtiyaç duyduğu her şey gelir. ## Temeller | | | |---|---| | Taban adres | `https://.solk.app/api/v1` | | Biçim | JSON (`Content-Type: application/json`); çoğu yazma ucu form gövdesi de kabul eder | | Kimlik | `Authorization: Bearer ` — [API anahtarı, OAuth ya da mobil oturum](/rest-api/authentication) | | Karakter seti | UTF-8; Türkçe karakterler olduğu gibi döner | | Tarih | `YYYY-MM-DD`; tarih-saat `YYYY-MM-DDTHH:MM:SS` (kurulumun yerel saati) | | Hız sınırı | Belirteç başına dakikada 300 istek — [Hız sınırları](/rest-api/rate-limits) | | Tanım | [`/openapi.json`](/openapi.json) (OpenAPI 3.1) | ## Yanıt zarfı Her yanıt bir JSON nesnesidir ve `ok` alanı taşır: ```json { "ok": true, "customer": { "id": 31, "name": "Kuzey Plastik Sanayi", "…": "…" } } ``` Hata yanıtlarında HTTP durum kodu, makinece okunur `error` kodu ve kullanıcıya gösterilebilecek Türkçe `message` gelir; bazı hatalar ek alan taşır (`similar`, `gate_problems`…): ```json { "ok": false, "error": "gate", "message": "Viable'a geçmek için: BANT kriterleri 4/4 sağlanmalı …", "gate_problems": ["…"] } ``` Tüm kodlar: [Hatalar](/rest-api/errors). ## Kaynaklar :::cards - [Kimlik doğrulama](/rest-api/auth/me) key | Kim olduğunuz, açık modüller, kurulum sabitleri. - [Çalışma alanı](/rest-api/workspace/today) layout | Bugün, pano, arama, bildirimler. - [Firmalar](/rest-api/companies/list) building | Listele, getir, oluştur, güncelle, ertele. - [Kişiler](/rest-api/people/list) user | Firmalardaki muhataplar. - [Fırsatlar](/rest-api/deals/list) target | Aşama, BANT, adımlar, canlandırma. - [Görüşmeler](/rest-api/activities/log) phone | Telefon ve ziyaret kaydı (Hızlı Giriş). - [Aksiyonlar ve görevler](/rest-api/actions/list) check | Tamamla, ertele, zincirle. - [Takvim](/rest-api/calendar/list) calendar | Toplantılar ve türetilen girişler. - [Fuarlar ve leadler](/rest-api/fairs/list) flag | Stant kayıtları. - [E-posta](/rest-api/email/threads-list) mail | Yazışmalar, bağlama, gönderim. - [Sor](/rest-api/ask/ask) spark | CRM verisine doğal dille soru. - [SCIM 2.0](/rest-api/scim) shield | Kullanıcı yönetimi. ::: ## Sürümleme API yolu `/api/v1` sabittir. Yeni alanlar ve uçlar **eklenerek** gelir; mevcut alanlar kaldırılmaz ve anlamları değişmez. İstemciniz tanımadığı alanları yok saymalıdır. Kırıcı bir değişiklik gerektiğinde yeni bir yol (`/api/v2`) açılır ve önceden [sürüm notlarında](/changelog) duyurulur. ## Yazma uçlarında yöntemler Güncelleme uçları `PATCH`, `PUT` ve `POST`'u aynı şekilde kabul eder (eski HTTP istemcileri ve mobil uygulama için). Güncellemeler **kısmidir**: yalnız gönderdiğiniz alanlar değişir (tek istisna: [fuar leadi](/rest-api/fairs/leads-update) `company` gönderildiğinde tüm form alanlarını yeniden yazar). Bir metin alanını temizlemek için boş metin (`""`) gönderin; `null` gönderilen alan yok sayılır (değişmez). Boolean alanlar JSON `true` / `false` olarak gönderilir. Form gövdesinde (`application/x-www-form-urlencoded`) doğru için `1` gönderin, yanlış için alanı boş gönderin. --- # Kimlik doğrulama > API anahtarı, OAuth erişim belirteci ve mobil oturum belirteci — hangisini ne zaman kullanmalı, nasıl iptal edilir. Her istek `Authorization` başlığında bir **Bearer** belirteci taşır: ```http GET /api/v1/me HTTP/1.1 Host: ornek.solk.app Authorization: Bearer sk_9fQ… ``` Belirteç yoksa, geçersizse, süresi dolmuşsa ya da iptal edildiyse yanıt `401 unauthorized` olur. Belirteci asla adres (query string) içinde göndermeyin. ## Belirteç türleri | Tür | Önek | Kim açar | Süre | Yetki | Ne zaman | |---|---|---|---|---|---| | **API anahtarı** | `sk_` | Yönetici, **Ayarlar → Geliştiriciler** | İptal edilene kadar | Açan yöneticinin tüm yetkileri | Sunucudan sunucuya entegrasyon, Zapier / Make / n8n, betikler | | **OAuth erişim belirteci** | `mcp_` | Kullanıcının onayıyla OAuth akışı | 1 saat (yenileme belirteci 60 gün) | Onaylayan kullanıcı + kapsam (`crm.read`, `crm.write`) | Kullanıcı adına çalışan uygulamalar, MCP istemcileri | | **Mobil oturum belirteci** | `xk_` | [`POST /api/v1/login`](/rest-api/auth/login) | 90 gün (kurulum ayarı) | Giriş yapan kullanıcı | Solk mobil uygulaması; kendi mobil istemciniz | ## API anahtarı 1. **Ayarlar → Geliştiriciler**'i açın (yalnız yöneticiler görür). 2. Anahtara ad verin ve **Anahtar oluştur**'a basın. Anahtar yalnız bir kez gösterilir. 3. Anahtarı ortam değişkeninde ya da gizli anahtar kasasında saklayın. Sayfa her anahtarın adını, oluşturulma ve son kullanım zamanını gösterir; **İptal et** anahtarı hemen geçersiz kılar. Anahtar onu açan yöneticinin kimliğiyle çalıştığından, o yönetici kapatılırsa anahtar da düşer — kurumsal entegrasyonlar için ayrı bir "entegrasyon" yönetici kullanıcısı açmanızı öneririz. :::note API anahtarları ve OAuth belirteçleri iki adımlı doğrulama sormaz; IP kısıtı ise geçerlidir. Anahtarla yapılan değişiklikler denetim günlüğüne anahtar sahibinin adıyla yazılır. ::: ## OAuth Uygulamanız birden fazla kullanıcının kendi hesabıyla çalışacaksa (ör. bir masaüstü eklentisi ya da çok kiracılı bir SaaS), her kullanıcıdan OAuth ile izin alın. Kullanıcı bir onay ekranında uygulamanızı ve istenen kapsamları görür; izin verdiği anda erişim belirteci alırsınız. Kullanıcı izni **Ayarlar → Uygulama bağlantıları**'ndan istediği an kaldırır. | Kapsam | Verdiği yetki | |---|---| | `crm.read` | Firma, kişi, fırsat, aksiyon, e-posta yazışması, takvim ve panoları okuma | | `crm.write` | Firma, kişi, fırsat, görev, not ve görüşme kaydı açma; görev tamamlama; aşama değiştirme | `crm.write` olmadan yapılan `GET` dışı istekler `403 insufficient_scope` alır. Akışın tamamı: [OAuth uygulaması](/rest-api/oauth). ## Mobil oturum Kendi mobil istemcinizi yazıyorsanız kullanıcı adı ve şifreyle [oturum açın](/rest-api/auth/login). İki adımlı doğrulama açık kullanıcılarda ilk istek `401 otp_required` döner; kullanıcıdan 6 haneli kodu alıp aynı isteği `otp` alanıyla tekrarlayın. Aynı IP'den 5 hatalı denemeden sonra giriş 60 saniye kilitlenir (`429 locked`). Çıkışta [`POST /api/v1/logout`](/rest-api/auth/logout) belirteci iptal eder. ## Güvenlik önerileri - Belirteçleri kaynak koduna, tarayıcıda çalışan JavaScript'e ya da herkese açık depolara koymayın. - Her entegrasyona ayrı anahtar açın; birini iptal etmek diğerlerini etkilemesin. - İstekleri yalnız `https://` üzerinden gönderin; Solk kurulumları HTTP'yi HTTPS'e yönlendirir. - Belirteç sızdıysa hemen iptal edin. Kullanıcı kapatılırsa (ya da SCIM ile pasifleşirse) tüm belirteçleri kendiliğinden düşer. --- # OAuth uygulaması > Kullanıcıların kendi hesaplarıyla izin verdiği bir uygulama yazın — istemci kaydı, PKCE'li yetkilendirme, belirteç ve yenileme. Solk her kurulumda bir **OAuth 2.1 yetkilendirme sunucusu** çalıştırır. Aynı sunucu MCP istemcilerine (Claude) ve REST API'yi kullanıcı adına çağıran uygulamalara belirteç verir. Desteklenen akış **yetki kodu + PKCE (S256)** ve **yenileme belirtecidir**; parola akışı ve istemci kimlik bilgisi akışı yoktur. | | Adres | |---|---| | Keşif | `https://ornek.solk.app/.well-known/oauth-authorization-server` | | İstemci kaydı | `POST https://ornek.solk.app/oauth/register` | | Yetkilendirme | `GET https://ornek.solk.app/oauth/authorize` | | Belirteç | `POST https://ornek.solk.app/oauth/token` | | İptal | `POST https://ornek.solk.app/oauth/revoke` | Her kurulumun kendi yetkilendirme sunucusu vardır: kullanıcının kurulum adresini (ör. `ornek.solk.app`) ondan alın ve keşif belgesini o adresten okuyun. :::steps ### İstemciyi kaydedin Uygulamanızı bir kez kaydedin ([Dinamik istemci kaydı](/rest-api/oauth/register), RFC 7591). Sunucu tarafında gizli anahtar saklayabilen uygulamalar `client_secret_post` seçer; masaüstü ve mobil uygulamalar `none` (genel istemci) kullanır. ```bash curl https://ornek.solk.app/oauth/register \ -H "Content-Type: application/json" \ -d '{ "client_name": "Örnek Entegrasyon", "redirect_uris": ["https://uygulamaniz.com/oauth/callback"], "token_endpoint_auth_method": "none" }' ``` ```json { "client_id": "crm_Jq8w…", "client_id_issued_at": 1790939471, "client_name": "Örnek Entegrasyon", "redirect_uris": ["https://uygulamaniz.com/oauth/callback"], "token_endpoint_auth_method": "none", "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"] } ``` Dönüş adresleri `https://` ya da yerel geri döngü (`http://localhost`, `http://127.0.0.1`, `http://[::1]`) olmalıdır. Yerel geri döngü adreslerinde kapı (port) numarası eşleşmede yok sayılır. İstemci kaydı yerine **CIMD** de kullanılabilir: `client_id` olarak istemci bilgi belgenizin `https://` adresini verin. ### PKCE değerlerini üretin Her yetkilendirme için rastgele bir `code_verifier` (43–128 karakter) ve onun SHA-256 özetinin base64url hâli olan `code_challenge` üretin: ```python import secrets, hashlib, base64 verifier = secrets.token_urlsafe(48) challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).decode().rstrip("=") ``` ### Kullanıcıyı yönlendirin ```text https://ornek.solk.app/oauth/authorize ?response_type=code &client_id=crm_Jq8w… &redirect_uri=https%3A%2F%2Fuygulamaniz.com%2Foauth%2Fcallback &scope=crm.read%20crm.write &state=xyz123 &code_challenge=E9Melhoa2Owv… &code_challenge_method=S256 ``` Kullanıcı oturum açmamışsa önce giriş ekranı gelir. Onay ekranı uygulamanızın adını, dönüş adresinin alan adını ve istenen kapsamları gösterir. Kullanıcı **İzin ver** derse tarayıcı şuraya döner: ```text https://uygulamaniz.com/oauth/callback?code=Zx8…&state=xyz123 ``` Reddederse `?error=access_denied&state=xyz123`. `state` değerinin sizin gönderdiğinizle aynı olduğunu doğrulayın. ### Kodu belirteçle değiştirin Kod 5 dakika geçerlidir ve **bir kez** kullanılır. Belirteç ucu form gövdesi bekler: ```bash curl https://ornek.solk.app/oauth/token \ -d grant_type=authorization_code \ -d code=Zx8… \ -d redirect_uri=https://uygulamaniz.com/oauth/callback \ -d client_id=crm_Jq8w… \ -d code_verifier=$VERIFIER ``` ```json { "access_token": "mcp_uWrPBo…", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "mcpr_Nu5ue…", "scope": "crm.read crm.write" } ``` ### API'yi çağırın ```bash curl https://ornek.solk.app/api/v1/me -H "Authorization: Bearer mcp_uWrPBo…" ``` Erişim belirteci REST API'de ve [MCP](/mcp/overview) ucunda geçerlidir. `crm.write` kapsamı yoksa yazma istekleri `403 insufficient_scope` döner. ### Belirteci yenileyin Erişim belirteci 1 saat sonra düşer. Yenileme belirteciyle yeni bir çift alın — her yenilemede **yeni** yenileme belirteci gelir ve eskisi geçersiz olur: ```bash curl https://ornek.solk.app/oauth/token \ -d grant_type=refresh_token \ -d refresh_token=mcpr_Nu5ue… \ -d client_id=crm_Jq8w… ``` Eski yenileme belirteciyle tekrar denemek `400 invalid_grant` döner. Yenileme belirteci 60 gün kullanılmazsa düşer; kullanıcı yeniden izin vermelidir. ::: ## İzinler ve geri alma - Bir kullanıcı aynı uygulamaya ikinci kez izin verdiğinde mevcut izin güncellenir (kapsam yeni onaydakiyle değişir); uygulama başına tek izin vardır. - Kullanıcı **Ayarlar → Uygulama bağlantıları**'nda uygulamanızı, son kullanım zamanını ve çağrı sayısını görür; **Erişimi kaldır** o izinden verilmiş tüm belirteçleri anında düşürür. - Kullanıcı kapatılırsa belirteçleri çalışmaz. - Uygulamanız belirteci kendisi iptal etmek için [`POST /oauth/revoke`](/rest-api/oauth/revoke) çağırabilir. ## Hatalar | Durum | Yanıt | |---|---| | Kod süresi doldu, kullanıldı, yanlış istemci ya da `redirect_uri` eşleşmiyor | `400 {"error": "invalid_grant"}` | | PKCE doğrulanamadı | `400 {"error": "invalid_grant", "error_description": "PKCE doğrulanamadı."}` | | Aynı kod ikinci kez kullanıldı | `400 invalid_grant` **ve** o izinden verilmiş tüm belirteçler iptal edilir | | Desteklenmeyen akış | `400 {"error": "unsupported_grant_type"}` | | Çok fazla istek | `429 {"error": "slow_down"}` | --- # Hız sınırları > Belirteç başına dakikalık istek sınırı, 429 yanıtı ve Retry-After başlığı. REST API, MCP ve SCIM uçları **belirteç başına dakikada 300 istek** kabul eder. Sayaç her takvim dakikasının başında sıfırlanır. Sınır aşıldığında yanıt `429` olur ve `Retry-After` başlığı yeni dakikanın başlamasına kalan saniyeyi verir: ```http HTTP/1.1 429 Too Many Requests Retry-After: 9 Content-Type: application/json ``` ```json { "ok": false, "error": "rate_limited", "message": "Dakikalık istek sınırı aşıldı (300 istek / dk). Retry-After saniye sonra yeniden deneyin." } ``` ## Diğer sınırlar | Ne | Sınır | |---|---| | Mobil oturum açma | Aynı IP'den 5 hatalı denemeden sonra 60 sn kilit (`429 locked`) | | OAuth istemci kaydı | IP başına saatte 60 kayıt (`429 slow_down`) | | OAuth belirteç ucu | IP başına saatte 600 istek (`429 slow_down`) | | Sor (yapay zekâ) | Kullanıcı başına saatte 40 soru (`429 limit`) | | Gelen kancalar | Bağlantı başına saatte 120 istek (Segment 3000; veri eşitleme, Aircall, RingCentral, Stripe 600) | | Liste uçları | Sayfa başına en çok 100 satır; kişi, fuar leadi ve yazışma listeleri en çok 300 / 100 satır | | E-posta gönderimi | İleti başına en çok 10 alıcı (to + cc); toplu gönderim yalnız web'de | Kurulum yöneticisi dakikalık sınırı sunucuda `API_RATE_PER_MIN` ortam değişkeniyle değiştirebilir. ## Önerilen davranış - `429` aldığınızda `Retry-After` kadar bekleyip yeniden deneyin; beklemeden tekrar denemek sayacı doldurmaya devam eder. - Toplu okuma yerine listeleri `per=100` ile sayfalayın ve değişenleri `sort=update` ile alın. - Olayları sürekli yoklamak yerine [web kancalarıyla](/guides/webhooks) bildirim alın. - Çok sayıda kaydı içeri almak için tek tek `POST` yerine [veri eşitleme kancasını](/guides/inbound#sync) kullanın (istek başına 500 satır). --- # Sayfalama ve süzme > Liste uçlarında sayfa, kapsam, süzgeç ve sıralama parametreleri. ## Sayfalama Firma ve fırsat listeleri sayfalanır: | Parametre | Varsayılan | Açıklama | |---|---|---| | `page` | `1` | Sayfa numarası. | | `per` | `40` | Sayfa başına satır (10–100). | Yanıt `rows`, `total` (süzülmüş toplam), `page` ve `pages` taşır: ```json { "ok": true, "rows": [ … ], "total": 21, "page": 1, "pages": 3 } ``` Tüm kayıtları almak için `page` değerini `pages`'e kadar artırın: ```python def tum_firmalar(oturum): sayfa = 1 while True: j = oturum.get("https://ornek.solk.app/api/v1/customers", params={"scope": "all", "per": 100, "page": sayfa}).json() yield from j["rows"] if sayfa >= j["pages"]: break sayfa += 1 ``` Kişi, aksiyon, fuar leadi ve e-posta yazışması listeleri sayfalanmaz; sırasıyla en çok 300, tüm açık aksiyonlar, 300 ve 100 kayıt döner. Daha dar sonuç için süzgeç kullanın. ## Kapsam Çoğu liste `scope` alır: | Değer | Sonuç | |---|---| | `mine` | Sorumlusu benim olan kayıtlar (satış rolünün varsayılanı) | | `all` | Görme yetkim olan tüm kayıtlar (yönetici rolünün varsayılanı) | | `` | O kullanıcının kayıtları | Kapsam görünürlüğü **genişletmez**: takım görünürlüğü açık bir kurulumda `scope=all` yalnız kendi takımınızın kayıtlarını getirir. Bkz. [Kullanıcılar ve roller](/users-and-roles). ## Süzgeçler ve sıralama | Uç | Süzgeçler | Sıralama (`sort`) | |---|---|---| | [Firmalar](/rest-api/companies/list) | `q`, `status`, `city`, `visit`, `risk`, `active` | `name`, `visit`, `priority` | | [Fırsatlar](/rest-api/deals/list) | `q`, `stage` (`open`, `closed`, `account`, aşama anahtarı) | `update`, `health`, `value`, `sla` | | [Kişiler](/rest-api/people/list) | `q`, `former` | ad | | [Aksiyonlar](/rest-api/actions/list) | `state` (`today`, `overdue`, `soon`, `open`, `week`), `customer_id` | termin | | [Takvim](/rest-api/calendar/list) | `from`, `to`, `done` | başlangıç | | [Fuar leadleri](/rest-api/fairs/leads-list) | `q`, `interest`, `owner` | yeniden eskiye | | [Yazışmalar](/rest-api/email/threads-list) | `kind` + `id` ya da `filter`, `q` | son iletiye göre | `q` metin araması büyük-küçük harf duyarsızdır ve alanın herhangi bir yerinde geçeni bulur. ## Değişenleri almak Ayrı bir "değişenler" ucu yoktur. Eşitleme için: - Fırsatları `sort=update` ile alıp `last_update` alanına göre durun. - Yeni ve değişen kayıtlardan anında haberdar olmak için [web kancalarını](/guides/webhooks) kullanın. --- # Hatalar > HTTP durum kodları, makinece okunur hata kodları ve hatalara eşlik eden ek alanlar. Başarısız bir istekte yanıt gövdesi şu biçimdedir: ```json { "ok": false, "error": "similar", "message": "Benzer adlı firma var — mevcut kaydı seçin ya da confirm_new=1 ile yeni açın.", "similar": [ { "id": 7, "name": "Alize Paketleme San. ve Tic. A.Ş.", "…": "…" } ] } ``` - **`error`** makinece okunur, değişmez bir koddur — kodunuzda buna göre dallanın. - **`message`** kullanıcıya gösterilebilecek Türkçe açıklamadır; metni değişebilir, ayrıştırmayın. - Bazı hatalar ek alan taşır: `similar`, `customer`, `gate_problems`, `module`, `state`, `otp`. ## Hata kodları | Kod | HTTP | Anlamı | |---|---|---| | `unauthorized` | 401 | Belirteç yok, geçersiz, süresi dolmuş ya da iptal edilmiş. | | `credentials` | 401 | Kullanıcı adı veya şifre hatalı. | | `otp_required` | 401 | İki adımlı doğrulama kodu gerekli (`otp`). | | `otp_invalid` | 401 | Doğrulama kodu hatalı ya da süresi geçmiş. | | `forbidden` | 403 | Kaydı görme / düzenleme yetkiniz yok. | | `insufficient_scope` | 403 | OAuth belirtecinde `crm.write` yok. | | `module` | 403 | Modül lisansta ya da kullanıcının izinlerinde kapalı. | | `license` | 403 | Lisans süresi doldu — yalnız yönetici erişebilir. | | `ip` | 403 | Bu ağdan erişim izni yok (IP kısıtı). | | `not_found` | 404 | Kayıt bulunamadı. | | `invalid` | 400 | Eksik ya da geçersiz alan (`message` ayrıntıyı verir). | | `send` | 400 | E-posta gönderilemedi (hesap bağlı değil, alıcı geçersiz…). | | `exists` | 409 | Bu adla firma var (`customer` alanında döner). | | `similar` | 409 | Benzer adlı firmalar var (`similar`); `confirm_new: true` ile yine açılır. | | `gate` | 409 | Aşama kapısı sağlanmadı (`gate_problems`). | | `locked` | 429 | Çok fazla hatalı giriş — 60 sn bekleyin. | | `rate_limited` | 429 | Dakikalık istek sınırı aşıldı (`Retry-After`). | | `limit` | 429 | Kullanıcı başına saatlik soru sınırı (40) doldu. | ## HTTP durum kodları | Kod | Anlamı | |---|---| | `200` | Başarılı. | | `201` | Oluşturuldu (OAuth istemci kaydı, SCIM kullanıcı açma). | | `204` | İçeriksiz başarı (SCIM kullanıcı kapatma). | | `400` | Eksik / geçersiz alan. | | `401` | Kimlik doğrulanamadı — belirteci yenileyin ya da yeniden giriş yapın. | | `403` | Yetki yok — rol, modül, kapsam, lisans ya da IP. | | `404` | Kayıt yok ya da görme yetkiniz dışında. | | `405` | Yöntem desteklenmiyor (ör. MCP ucuna `GET`). | | `409` | İş kuralı çakışması — mükerrer firma, aşama kapısı. | | `429` | Hız sınırı — `Retry-After` kadar bekleyin. | | `500` | Sunucu hatası. Tekrarlanıyorsa isteği ve zamanı `info@solk.app`'e iletin. | ## Örnek: mükerrer firma ```python r = s.post(f"{BASE}/api/v1/customers", json={"name": "Alize Ambalaj"}) j = r.json() if r.status_code == 409 and j["error"] == "exists": firma = j["customer"] # zaten var — onu kullan elif r.status_code == 409 and j["error"] == "similar": adaylar = j["similar"] # kullanıcıya seçtir ya da: j = s.post(f"{BASE}/api/v1/customers", json={"name": "Alize Ambalaj", "confirm_new": True}).json() ``` ## Örnek: aşama kapısı ```python r = s.post(f"{BASE}/api/v1/opportunities/{oid}/stage", json={"stage": "Viable"}) if r.status_code == 409 and r.json()["error"] == "gate": for sorun in r.json()["gate_problems"]: print("Eksik:", sorun) ``` --- # SCIM 2.0 > Okta, Microsoft Entra ID, OneLogin ya da JumpCloud'dan kullanıcıları otomatik açın, güncelleyin ve kapatın. SCIM (System for Cross-domain Identity Management), kimlik sağlayıcınızdaki kullanıcı değişikliklerini CRM'e otomatik taşır: işe başlayan kişi CRM kullanıcısı olur, ayrılan kişinin erişimi kapanır. | | | |---|---| | Taban adres | `https://.solk.app/api/scim/v2` | | Kimlik | `Authorization: Bearer ` | | Kaynaklar | `Users` (gruplar eşitlenmez) | | Süzgeç | `userName eq "…"`, `id eq "…"` | | İçerik türü | `application/scim+json` | ## Kurulum :::steps ### SCIM anahtarı alın CRM'de **Ayarlar → Uygulamalar → Okta / Entra ID (SCIM)** kartını bağlayın. Kart taban adresi ve SCIM anahtarını gösterir. Anahtar yalnız SCIM uçlarında geçerlidir; REST API'de kullanılamaz. ### Kimlik sağlayıcıyı ayarlayın **Okta:** Applications → uygulamanız → Provisioning → Integration → *SCIM connector base URL* = taban adres, *Unique identifier field* = `userName`, *Authentication Mode* = HTTP Header, *Authorization* = anahtar. **To App** bölümünde *Create Users*, *Update User Attributes*, *Deactivate Users*'ı açın. **Microsoft Entra ID:** Enterprise applications → uygulamanız → Provisioning → Automatic → *Tenant URL* = taban adres, *Secret Token* = anahtar → **Test Connection**. ### Kullanıcıları atayın Kimlik sağlayıcıda uygulamaya atadığınız kullanıcılar CRM'de açılır. ::: ## Davranış | Kimlik sağlayıcıda | CRM'de | |---|---| | Kullanıcı atandı | Kullanıcı **satış** rolüyle açılır, e-postasına şifre belirleme daveti gider. | | Ad, e-posta, departman, telefon değişti | Kullanıcı güncellenir. | | Kullanıcı askıya alındı / atama kaldırıldı (`active: false`) | Kullanıcı kapatılır: giriş yapamaz, mobil oturumları ve API anahtarları düşer. Kayıtları kalır. | | Kullanıcı silindi (`DELETE`) | Kapatılır (silinmez). | | Yeniden etkinleştirildi | Kullanıcı açılır (lisansta boş koltuk gerekir). | - `userName` CRM'de e-posta adresi olarak kullanılır; aynı e-postayla ikinci kullanıcı açılmaz (`409 uniqueness`). - Rol (yönetici / satış) ve modül izinleri CRM'de atanır; SCIM bunları değiştirmez. - Son etkin yönetici SCIM ile kapatılamaz. - Departman, SCIM kurumsal uzantısındaki `department` alanından gelir ve [takım görünürlüğünde](/users-and-roles#visibility) kullanılır. ## Örnek: kullanıcı açma ```bash curl https://ornek.solk.app/api/scim/v2/Users \ -H "Authorization: Bearer $SCIM_TOKEN" \ -H "Content-Type: application/scim+json" \ -d '{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "userName": "ayse.demir@ornekkimya.com.tr", "name": { "givenName": "Ayşe", "familyName": "Demir" }, "emails": [{ "value": "ayse.demir@ornekkimya.com.tr", "primary": true }], "active": true, "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { "department": "Satış" } }' ``` Tüm SCIM uçları ve gerçek yanıtları başvuru bölümünde: [Kullanıcıları listele](/rest-api/scim/users-list), [Kullanıcı aç](/rest-api/scim/users-create), [Kullanıcıyı güncelle](/rest-api/scim/users-update), [Kullanıcıyı kapat](/rest-api/scim/users-delete). ## Hatalar SCIM hataları SCIM biçimindedir: ```json { "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"], "status": "401", "detail": "Geçersiz ya da eksik SCIM anahtarı." } ``` --- # Kimliği doğrula `GET /api/v1/me` Belirtecin hangi kullanıcıyla çalıştığını, kullanıcının açık modüllerini, kurulumun sabitlerini (aşama anahtarları ve adları, görev öncelikleri, kayıp nedenleri…), ürün listesini ve lisans durumunu döndürür. Entegrasyonunuzu başlatırken ilk çağrı budur; aşama anahtarları kuruluma göre değiştiği için `constants.stages` ve `constants.stage_labels` buradan okunmalıdır. ## Yanıt ```json 200 { "ai": false, "app": { "accent": "#0f3a44", "accent_dark": "#0b2b33", "company": "Örnek Kimya", "currencies": [ "EUR" ], "currency_base": "EUR", "currency_symbol": "€", "logo": "", "name": "Solk CRM", "opp_lifetime_days": 180, "site_url": "https://ornek.solk.app", "stage_sla": { "Expect to Close": 15, "Negotiation": 30, "Present Solution": 60, "Qualify": 30, "Viable": 60 }, "ticket_sla_hours": { "Acil": 4, "Düşük": 168, "Normal": 72, "Yüksek": 24 }, "unit_default": "kg", "units": [ "kg" ], "version": "v22" }, "constants": { "action_soon_days": 7, "barriers": [ "Fiyat" ], "customer_statuses": [ "AC" ], "demo_results": [ "Başarılı" ], "demo_statuses": [ "Henüz Deneme Aşamasına Gelmedik" ], "forecast_cats": [ "Pipeline" ], "lead_interest": [ "Sıcak" ], "lead_next_actions": [ "Teklif gönder" ], "lead_statuses": [ "Yeni" ], "lead_timings": [ "Hemen" ], "lead_visitor_types": [ "Potansiyel müşteri" ], "loss_reasons": [ "Fiyat" ], "makro_stages": [ "Takip" ], "next_actions": [ "Numune Gönder" ], "open_stages": [ "Qualify" ], "opp_line_label": "", "opp_status_labels": false, "opp_types": [ "Yeni Müşteri" ], "order_kinds": [ "İlk Sipariş" ], "stage_colors": { "Cancel": "#62767a", "Expect to Close": "#2d9d78", "Lost": "#c23934", "Negotiation": "#e0a228", "Present Solution": "#6b5bd6", "Qualify": "#8a9ba0", "Viable": "#2a78d6", "Win": "#24733f" }, "stage_gate": { "Expect to Close": "kapanış bekleniyor", "Negotiation": "fiyat / vade pazarlığı", "Present Solution": "aktif deneme, sonucu net", "Qualify": "BANT 4/4", "Viable": "f2f ziyaret + doğru ürün" }, "stage_labels": { "Cancel": "Cancel", "Expect to Close": "Expect to Close", "Lost": "Lost", "Negotiation": "Negotiation", "Present Solution": "Present Solution", "Qualify": "Qualify", "Viable": "Viable", "Win": "Win" }, "stage_rules": true, "stage_sla": { "Expect to Close": 15, "Negotiation": 30, "Present Solution": 60, "Qualify": 30, "Viable": 60 }, "stages": [ "Qualify" ], "task_priorities": [ "Düşük" ], "task_statuses": [ "Açık" ], "visit_period": { "AC": 90, "Prospect": 180 }, "visit_topics": [ "Yeni Ürünler" ], "visit_types": [ "F2F" ] }, "license": { "blocked": false, "customer": "Örnek Kimya", "days_left": null, "end": null, "label": "Deneme modu", "modules": [ "calendar" ], "no": null, "state": "trial" }, "mailbox": { "connected": true, "email": "deniz@ornekkimya.com.tr", "error": "", "signature": false, "status": "ok" }, "ok": true, "products": [ { "code": "HS30", "id": 5, "list_price": 6.4, "name": "HS 30 Isıl Yapışma Laki" }, { "code": "SB450", "id": 3, "list_price": 3.8, "name": "PU-SB 450 Solvent Bazlı Yapıştırıcı" } ], "user": { "email": "deniz@ornekkimya.com.tr", "full_name": "Deniz Aksoy", "id": 1, "modules": [ "calendar" ], "role": "admin", "role_label": "Yönetici", "username": "admin" }, "users": [ { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" } ], "weak_password": true } ``` --- # Kullanıcı adıyla oturum aç `POST /api/v1/login` Mobil uygulamanın kullandığı oturum açma ucu: kullanıcı adı + şifre (+ iki adımlı doğrulama kodu) karşılığında `xk_` ile başlayan bir oturum belirteci verir. Belirteç `API_TOKEN_DAYS` gün (varsayılan 90) geçerlidir. Sunucudan sunucuya entegrasyonlarda bunun yerine Geliştiriciler sayfasından açılan **API anahtarını** ya da **OAuth**'u kullanın. ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `username` (zorunlu) | string | Kullanıcı adı. | | `password` (zorunlu) | string | Şifre. | | `otp` | string | İki adımlı doğrulama açıksa 6 haneli kod (ilk istekte `otp_required` döner). | | `device` | string | Cihaz adı; Ayarlar → Güvenlik'teki oturum listesinde görünür. | ## Yanıt ```json 200 { "app": { "accent": "#0f3a44", "accent_dark": "#0b2b33", "company": "Örnek Kimya", "currencies": [ "EUR" ], "currency_base": "EUR", "currency_symbol": "€", "logo": "", "name": "Solk CRM", "opp_lifetime_days": 180, "site_url": "https://ornek.solk.app", "stage_sla": { "Expect to Close": 15, "Negotiation": 30, "Present Solution": 60, "Qualify": 30, "Viable": 60 }, "ticket_sla_hours": { "Acil": 4, "Düşük": 168, "Normal": 72, "Yüksek": 24 }, "unit_default": "kg", "units": [ "kg" ], "version": "v22" }, "license": { "blocked": false, "customer": "Örnek Kimya", "days_left": null, "end": null, "label": "Deneme modu", "modules": [ "calendar" ], "no": null, "state": "trial" }, "ok": true, "token": "xk_1dcb806508e33cee8d728d9baafc6eee23b6e2cb16da8afd", "user": { "email": "deniz@ornekkimya.com.tr", "full_name": "Deniz Aksoy", "id": 1, "modules": [ "calendar" ], "role": "admin", "role_label": "Yönetici", "username": "admin" }, "weak_password": true } ``` --- # Oturumu kapat `POST /api/v1/logout` İstekteki belirteci iptal eder. Mobil oturum belirteçleri (`xk_`) için kullanılır; API anahtarları Geliştiriciler sayfasından iptal edilir. ## Yanıt ```json 200 { "ok": true } ``` --- # Bugün `GET /api/v1/today` Kullanıcının günü: bugünün toplantıları ve aksiyonları, gecikenler, son açılan kayıtlar (firma, fırsat, kişi, lead) ve bu haftanın ziyaret / görev hedefleri. Mobil uygulamanın ana ekranı bu yanıttan çizilir. ## Yanıt ```json 200 { "cards": { "contacts": { "mine": [ { "customer": "Orkide Medikal Ambalaj Ltd. Şti.", "customer_id": 30, "department": "Üretim", "email": "ugur.ozkan@orkidemedikal.example", "id": 57, "is_former": false, "is_main": true, "name": "Uğur Özkan", "note": "", "phone": "0 (262) 000 91 77", "rid": "003LlalbBDATepx", "title": "Üretim Müdürü", "wa": "902620009177" } ], "n": { "mine": 57, "recent": 0, "team": 57 }, "recent": [], "team": [ { "customer": "Orkide Medikal Ambalaj Ltd. Şti.", "customer_id": 30, "department": "Üretim", "email": "ugur.ozkan@orkidemedikal.example", "id": 57, "is_former": false, "is_main": true, "name": "Uğur Özkan", "note": "", "phone": "0 (262) 000 91 77", "rid": "003LlalbBDATepx", "title": "Üretim Müdürü", "wa": "902620009177" } ] }, "customers": { "mine": [ { "active": true, "at_risk": false, "city": "Bursa", "currency": "EUR", "currency_default": "", "id": 25, "initials": "AE", "last_visit": "2026-07-24", "main_contact": { "email": "kaan.ozkan@akasyaesnek.example", "name": "Kaan Özkan", "phone": "0 (224) 000 17 41", "wa": "902240001741" }, "name": "Akasya Esnek Ambalaj San. ve Tic. A.Ş.", "next_visit_due": "2027-01-20", "open_opps": 1, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "pending_state": "soon", "period": 180, "phone": "0 (224) 000 35 36", "potential_kg": 40000.0, "rid": "001Yk1AKJ1EIEQw", "sector": "Esnek ambalaj", "status": "Prospect", "status_label": "Prospect", "unit": "kg", "visit_overdue_days": -110, "visit_state": "ok" } ], "n": { "mine": 30, "recent": 0, "team": 30 }, "recent": [], "team": [ { "active": true, "at_risk": false, "city": "Bursa", "currency": "EUR", "currency_default": "", "id": 25, "initials": "AE", "last_visit": "2026-07-24", "main_contact": { "email": "kaan.ozkan@akasyaesnek.example", "name": "Kaan Özkan", "phone": "0 (224) 000 17 41", "wa": "902240001741" }, "name": "Akasya Esnek Ambalaj San. ve Tic. A.Ş.", "next_visit_due": "2027-01-20", "open_opps": 1, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "pending_state": "soon", "period": 180, "phone": "0 (224) 000 35 36", "potential_kg": 40000.0, "rid": "001Yk1AKJ1EIEQw", "sector": "Esnek ambalaj", "status": "Prospect", "status_label": "Prospect", "unit": "kg", "visit_overdue_days": -110, "visit_state": "ok" } ] }, "leads": { "mine": [ { "can_edit": true, "city": "Bursa", "company": "Işıltı Ambalaj San. Tic. Ltd. Şti.", "contact_name": "Mehmet Arslan", "created_at": "2026-08-28T14:10:00", "customer_id": null, "email": "mehmet@isiltiambalajsanti.example", "fair": "Ambalaj Fuarı 2026 (örnek)", "fair_id": 1, "id": 4, "initials": "IA", "interest": "Soğuk", "line": "", "next_action": "Numune gönder", "next_date": "2026-10-07", "note": "Stantta görüşüldü.", "opp_id": null, "opp_type": "Mevcut Müşteri — Değişim", "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "phone": "0 (224) 000 96 45", "potential_kg": 6000.0, "products": "4", "products_text": "", "project_note": "", "rid": "00QCwrpUC4bpt3E", "sector": "Gıda ambalajı", "status": "Teklif verildi", "status_eff": "Teklif verildi", "supplier": "Mevcut tedarikçi (Asya)", "task_id": null, "timing": "6 ay içinde", "title": "Kalite Kontrol Müdürü", "unit": "kg", "visitor_type": "Potansiyel müşteri", "wa": "902240009645", "website": "" } ], "n": { "mine": 9, "recent": 0, "team": 3 }, "recent": [], "team": [ { "can_edit": true, "city": "Konya", "company": "Alize Ambalaj A.Ş.", "contact_name": "Barış Kaya", "created_at": "2026-08-25T15:10:00", "customer_id": null, "email": "baris@alizeambalajas.example", "fair": "Ambalaj Fuarı 2026 (örnek)", "fair_id": 1, "id": 5, "initials": "AA", "interest": "Sıcak", "line": "", "next_action": "Teklif gönder", "next_date": "2026-10-03", "note": "Stantta görüşüldü.", "opp_id": null, "opp_type": "Yeni Müşteri", "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "phone": "0 (332) 000 44 97", "potential_kg": 4000.0, "products": "5", "products_text": "", "project_note": "", "rid": "00QiUPO55IJhtmL", "sector": "Etiket", "status": "Yeni", "status_eff": "Yeni", "supplier": "Mevcut tedarikçi (Asya)", "task_id": null, "timing": "3 ay içinde", "title": "Genel Müdür", "unit": "kg", "visitor_type": "Potansiyel müşteri", "wa": "903320004497", "website": "" } ] }, "opps": { "mine": [ { "bant": 4, "base_currency": "EUR", "close_date": "2026-11-17", "cur_sym": "€", "currency": "EUR", "customer": "Begonya Esnek Ambalaj A.Ş.", "customer_city": "Kocaeli", "customer_id": 16, "days_in_stage": 21, "detail_status": "", "fc": "Commit", "health": { "grade": "success", "label": "sağlıklı", "score": 100 }, "health_color": "g", "health_manual": false, "health_note": "", "id": 16, "initials": "BE", "is_account": false, "is_open": true, "last_update": "2026-10-02", "lifetime_left": 105, "line": "", "name": "Mevcut Müşteri — Değişim · 2026", "next_step": "Vade ve teslim koşullarını görüş", "offer_price": 4.08, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": 60, "potential_kg": 4000.0, "prob": 0.75, "probability": null, "product": "PU-SL 200 Solventsiz Laminasyon Yapıştırıcısı", "product_id": 1, "pu": "€/kg", "rid": "006aJYGE9HUIaM2", "sla_days": 30, "sla_left": 9, "source": "Bayi / distribütör", "stage": "Negotiation", "stage_label": "Negotiation", "start_date": "2026-07-19", "trend": "", "trend_label": "Otomatik", "type": "Mevcut Müşteri — Değişim", "unit": "kg", "unit_code": "kg", "value": 195840.0, "value_base": 195840.0, "value_short": "196K" } ], "n": { "mine": 28, "recent": 0, "team": 28 }, "recent": [], "team": [ { "bant": 4, "base_currency": "EUR", "close_date": "2026-11-17", "cur_sym": "€", "currency": "EUR", "customer": "Begonya Esnek Ambalaj A.Ş.", "customer_city": "Kocaeli", "customer_id": 16, "days_in_stage": 21, "detail_status": "", "fc": "Commit", "health": { "grade": "success", "label": "sağlıklı", "score": 100 }, "health_color": "g", "health_manual": false, "health_note": "", "id": 16, "initials": "BE", "is_account": false, "is_open": true, "last_update": "2026-10-02", "lifetime_left": 105, "line": "", "name": "Mevcut Müşteri — Değişim · 2026", "next_step": "Vade ve teslim koşullarını görüş", "offer_price": 4.08, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": 60, "potential_kg": 4000.0, "prob": 0.75, "probability": null, "product": "PU-SL 200 Solventsiz Laminasyon Yapıştırıcısı", "product_id": 1, "pu": "€/kg", "rid": "006aJYGE9HUIaM2", "sla_days": 30, "sla_left": 9, "source": "Bayi / distribütör", "stage": "Negotiation", "stage_label": "Negotiation", "start_date": "2026-07-19", "trend": "", "trend_label": "Otomatik", "type": "Mevcut Müşteri — Değişim", "unit": "kg", "unit_code": "kg", "value": 195840.0, "value_base": 195840.0, "value_short": "196K" } ] } }, "day_label": "Cuma, 2 Ekim 2026", "events": [ { "act": { "addr": "Kayseri Organize Sanayi Bölgesi, 1. Cadde No: 46, Kayseri, Kayseri", "c": 4, "mail": "nazli.aydin@ihlamurmatbaacilik.example", "n": "Ihlamur Matbaacılık A.Ş.", "tel": "0 (352) 000 57 99", "url": "/customers/4", "wa": "903520005799", "who": "Nazlı Aydın" }, "customer_id": 4, "date": "2026-10-02", "kind": "opp", "sub": "Mevcut Müşteri — Ek Satış · 2026 · Qualify", "time": null, "title": "Ihlamur Matbaacılık A.Ş. · sonraki adım", "url": "/opportunities/34" }, { "act": { "addr": "Sakarya Organize Sanayi Bölgesi, 4. Cadde No: 54, Sakarya, Sakarya", "c": 10, "mail": "murat.cetin@reyhanpaketleme.example", "n": "Reyhan Paketleme Ltd. Şti.", "tel": "0 (264) 000 63 67", "url": "/customers/10", "wa": "902640006367", "who": "Murat Çetin" }, "customer_id": 10, "date": "2026-10-02", "kind": "opp", "sub": "Mevcut Müşteri — Ek Satış · 2026 · Expect to Close", "time": null, "title": "Reyhan Paketleme Ltd. Şti. · sonraki adım", "url": "/opportunities/10" } ], "focus": [ { "cls": "bad", "text": "9 gecikmiş aksiyon" }, { "cls": "warn", "text": "3 aksiyon bugün" } ], "greeting": "İyi akşamlar", "ok": true, "today": "2026-10-02", "todos": { "all": [ { "customer": "Lotus Medikal Ambalaj San. Tic. Ltd. Şti.", "customer_id": 3, "date": "2026-09-26", "icon": "📋", "id": 1, "key": "task:1", "kind": "task", "label": "Numune sonucu için ara", "opp": "Yeni Müşteri · 2026", "opp_id": 33, "src": "Görev", "state": "overdue", "url": "/action/task/1" } ], "overdue": [ { "customer": "Lotus Medikal Ambalaj San. Tic. Ltd. Şti.", "customer_id": 3, "date": "2026-09-26", "icon": "📋", "id": 1, "key": "task:1", "kind": "task", "label": "Numune sonucu için ara", "opp": "Yeni Müşteri · 2026", "opp_id": 33, "src": "Görev", "state": "overdue", "url": "/action/task/1" } ], "today": [ { "customer": "Işıltı Film A.Ş.", "customer_id": 8, "date": "2026-10-02", "icon": "📋", "id": 2, "key": "task:2", "kind": "task", "label": "Teklif revizyonunu gönder", "opp": "", "opp_id": null, "src": "Görev", "state": "soon", "url": "/action/task/2" } ] }, "unread": 0, "week": { "demos": 1, "f2f": 3, "f2f_target": 6, "label": "28 Eyl – 4 Eki", "sub_fresh": false, "sub_week": null, "tasks_done": 2, "tasks_target": 12, "visit_target": 10, "visits": 7 } } ``` --- # Satış panosu `GET /api/v1/dashboard` Aşamalara göre fırsat sayısı ve değeri (ana dövizde), SLA aşımları, ziyareti gelen firmalar, sıradaki adımlar, yaklaşan işler ve haftalık hedefler. ## Sorgu parametreleri | Alan | Tür | Açıklama | |---|---|---| | `scope` | string | Boş = rolüme göre varsayılan, `team` = tüm ekip (yönetici). | ## Yanıt ```json 200 { "k": { "ac": 9, "at_risk": 0, "demo_all": 28, "demo_ok": 22, "demo_open": 6, "ok_visits": 29, "open_actions": 13, "opp_value": 11243660.0, "opp_weighted": 5169831.0, "opps": 28, "overdue_actions": 9, "overdue_visits": 1, "pot_kg": 331000.0, "prospect": 21, "prospect_pot": 21, "soon_actions": 30, "soon_visits": 0 }, "last_demos": [ { "action_date": "2026-10-06", "action_done": false, "action_state": "soon", "customer_id": 19, "date": "2026-10-01", "id": 19, "line": "", "next_action": "Takip", "opp_id": 19, "product": "HS 30 Isıl Yapışma Laki", "product_id": 5, "result": "Beklemede", "rid": "a0DuuEJElBgUsAw", "status": "Deneme Gönderildi, Yapılması Bekleniyor", "tech_note": "Numune hatta; sonuç bekleniyor.", "user": "Deniz Aksoy" }, { "action_date": "2026-10-03", "action_done": false, "action_state": "soon", "customer_id": 20, "date": "2026-09-23", "id": 20, "line": "", "next_action": "Takip", "opp_id": 20, "product": "PU-SB 450 Solvent Bazlı Yapıştırıcı", "product_id": 3, "result": "Beklemede", "rid": "a0DLsCXfV1D035V", "status": "Deneme Gönderildi, Yapılması Bekleniyor", "tech_note": "Numune hatta; sonuç bekleniyor.", "user": "Deniz Aksoy" } ], "mine": false, "n_actions": 52, "never_visited": 0, "next_steps": [ { "bant": 4, "base_currency": "EUR", "close_date": "2027-01-16", "cur_sym": "€", "currency": "EUR", "customer": "Hanımeli Etiket San. Tic. Ltd. Şti.", "customer_city": "Adana", "customer_id": 23, "days_in_stage": 74, "detail_status": "", "fc": "Best Case", "health": { "grade": "warning", "label": "dikkat", "score": 60 }, "health_color": "g", "health_manual": true, "health_note": "", "id": 23, "initials": "HE", "is_account": false, "is_open": true, "last_update": "2026-09-30", "lifetime_left": 69, "line": "", "name": "Mevcut Müşteri — Yenileme · 2026", "next_step": "Deneme tarihini planla", "offer_price": null, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": null, "potential_kg": 12000.0, "prob": 0.5, "probability": null, "product": "PU-SL 350 Yüksek Performans (retort)", "product_id": 2, "pu": "€/kg", "rid": "006LsST7gv514nh", "sla_days": 60, "sla_left": -14, "source": "Web formu", "stage": "Present Solution", "stage_label": "Present Solution", "start_date": "2026-06-13", "trend": "up", "trend_label": "Yeşil", "type": "Mevcut Müşteri — Yenileme", "unit": "kg", "unit_code": "kg", "value": 806000.0, "value_base": 806000.0, "value_short": "806K" }, { "bant": 4, "base_currency": "EUR", "close_date": "2026-12-20", "cur_sym": "€", "currency": "EUR", "customer": "Akasya Esnek Ambalaj San. ve Tic. A.Ş.", "customer_city": "Bursa", "customer_id": 25, "days_in_stage": 68, "detail_status": "", "fc": "Pipeline", "health": { "grade": "warning", "label": "dikkat", "score": 66 }, "health_color": "y", "health_manual": true, "health_note": "", "id": 25, "initials": "AE", "is_account": false, "is_open": true, "last_update": "2026-09-22", "lifetime_left": 99, "line": "", "name": "Yeni Müşteri · 2026", "next_step": "Numune gönder", "offer_price": null, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": null, "potential_kg": 8000.0, "prob": 0.25, "probability": null, "product": "PU-SB 450 Solvent Bazlı Yapıştırıcı", "product_id": 3, "pu": "€/kg", "rid": "006Hyi3NMdZrXvO", "sla_days": 60, "sla_left": -8, "source": "Mevcut müşteri", "stage": "Viable", "stage_label": "Viable", "start_date": "2026-07-13", "trend": "flat", "trend_label": "Otomatik", "type": "Yeni Müşteri", "unit": "kg", "unit_code": "kg", "value": 365000.0, "value_base": 365000.0, "value_short": "365K" } ], "ok": true, "sla_over": 4, "stage_counts": { "Expect to Close": 4, "Negotiation": 5, "Present Solution": 6, "Qualify": 7, "Viable": 6 }, "stage_value": { "Expect to Close": 1416240.0, "Negotiation": 2424420.0, "Present Solution": 2287000.0, "Qualify": 2304000.0, "Viable": 2812000.0 }, "upcoming": [ { "date": "2026-10-02", "head": "Reyhan Paketleme Ltd. Şti.", "kind": "opp", "sub": "Proje #10 · Expect to Close · sonraki adım", "url": "/opportunities/10" }, { "date": "2026-10-02", "head": "Yıldızlı Paketleme San. Tic. Ltd. Şti.", "kind": "opp", "sub": "Proje #19 · Present Solution · sonraki adım", "url": "/opportunities/19" } ], "visit_due": [ { "active": true, "at_risk": false, "city": "Adana", "currency": "EUR", "currency_default": "", "id": 9, "initials": "KP", "last_visit": "2026-07-02", "main_contact": { "email": "ayse.aydin@kumsalplastik.example", "name": "Ayşe Aydın", "phone": "0 (322) 000 80 24", "wa": "903220008024" }, "name": "Kumsal Plastik Ambalaj San. ve Tic. A.Ş.", "next_visit_due": "2026-09-30", "open_opps": 0, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "pending_state": null, "period": 90, "phone": "0 (322) 000 53 49", "potential_kg": 12000.0, "rid": "001t29x5kWbHOxg", "sector": "Esnek ambalaj", "status": "AC", "status_label": "Aktif Müşteri", "unit": "kg", "visit_overdue_days": 2, "visit_state": "overdue" } ], "week": { "demos": 1, "f2f": 3, "f2f_target": 6, "label": "28 Eyl – 4 Eki", "n_sales": 1, "sub_fresh": false, "tasks_done": 2, "tasks_due": 10, "tasks_target": 12, "visit_target": 10, "visits": 7, "we": "2026-10-04", "ws": "2026-09-28" } } ``` --- # Ara `GET /api/v1/search` Firma (ad, şehir), kişi (ad, telefon, e-posta), fırsat ve fuar leadlerinde arar; her türden en çok 10–15 sonuç. 15 karakterlik bir kayıt kimliği (`001…`, `006…`) verilirse `record` alanında o kayıt döner. ## Sorgu parametreleri | Alan | Tür | Açıklama | |---|---|---| | `q` (zorunlu) | string | Aranan metin (en az 2 karakter). | ## Yanıt ```json 200 { "contacts": [ { "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_id": 1, "department": "Satın Alma", "email": "selin.dogan@alizepaketleme.example", "id": 1, "is_former": false, "is_main": true, "name": "Selin Doğan", "note": "", "phone": "0 (212) 000 55 24", "rid": "003MaZR2tfJCp1h", "title": "Satın Alma Müdürü", "wa": "902120005524" } ], "customers": [ { "active": true, "at_risk": false, "city": "İstanbul", "currency": "EUR", "currency_default": "", "id": 1, "initials": "AP", "last_visit": "2026-09-30", "main_contact": { "email": "selin.dogan@alizepaketleme.example", "name": "Selin Doğan", "phone": "0 (212) 000 55 24", "wa": "902120005524" }, "name": "Alize Paketleme San. ve Tic. A.Ş.", "next_visit_due": "2026-12-29", "open_opps": 1, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "pending_state": "overdue", "period": 90, "phone": "0 (212) 000 15 65", "potential_kg": 8000.0, "rid": "001RBoHIo80N8nR", "sector": "Gıda ambalajı", "status": "AC", "status_label": "Aktif Müşteri", "unit": "kg", "visit_overdue_days": -88, "visit_state": "ok" } ], "leads": [ { "can_edit": true, "city": "Konya", "company": "Alize Ambalaj A.Ş.", "contact_name": "Barış Kaya", "created_at": "2026-08-25T15:10:00", "customer_id": null, "email": "baris@alizeambalajas.example", "fair": "Ambalaj Fuarı 2026 (örnek)", "fair_id": 1, "id": 5, "initials": "AA", "interest": "Sıcak", "line": "", "next_action": "Teklif gönder", "next_date": "2026-10-03", "note": "Stantta görüşüldü.", "opp_id": null, "opp_type": "Yeni Müşteri", "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "phone": "0 (332) 000 44 97", "potential_kg": 4000.0, "products": "5", "products_text": "", "project_note": "", "rid": "00QiUPO55IJhtmL", "sector": "Etiket", "status": "Yeni", "status_eff": "Yeni", "supplier": "Mevcut tedarikçi (Asya)", "task_id": null, "timing": "3 ay içinde", "title": "Genel Müdür", "unit": "kg", "visitor_type": "Potansiyel müşteri", "wa": "903320004497", "website": "" } ], "ok": true, "opps": [ { "bant": 0, "base_currency": "EUR", "close_date": "2027-03-31", "cur_sym": "€", "currency": "EUR", "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_city": "İstanbul", "customer_id": 1, "days_in_stage": 0, "detail_status": "", "fc": "Omitted", "health": null, "health_color": null, "health_manual": false, "health_note": "", "id": 44, "initials": "AP", "is_account": true, "is_open": false, "last_update": "2026-10-02", "lifetime_left": 180, "line": "", "name": "Müşteri Takibi", "next_step": null, "offer_price": null, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": null, "potential_kg": null, "prob": 0.0, "probability": null, "product": "", "product_id": null, "pu": "€/kg", "rid": "006w4ETEfW9soDW", "sla_days": null, "sla_left": null, "source": "", "stage": "Account", "stage_label": "Müşteri Takibi", "start_date": "2026-10-02", "trend": "", "trend_label": "Otomatik", "type": "Müşteri Takibi", "unit": "kg", "unit_code": "kg", "value": 0, "value_base": 0, "value_short": "0" }, { "bant": 4, "base_currency": "EUR", "close_date": "2026-10-01", "cur_sym": "€", "currency": "EUR", "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_city": "İstanbul", "customer_id": 1, "days_in_stage": 1, "detail_status": "", "fc": "Closed", "health": null, "health_color": null, "health_manual": false, "health_note": "", "id": 1, "initials": "AP", "is_account": false, "is_open": false, "last_update": "2026-10-01", "lifetime_left": 115, "line": "", "name": "Yeni Müşteri · 2026", "next_step": null, "offer_price": 3.92, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": 120, "potential_kg": 1500.0, "prob": 1.0, "probability": null, "product": "PU-SL 200 Solventsiz Laminasyon Yapıştırıcısı", "product_id": 1, "pu": "€/kg", "rid": "006kmnffbRT8muo", "sla_days": null, "sla_left": null, "source": "Bayi / distribütör", "stage": "Win", "stage_label": "Win", "start_date": "2026-07-29", "trend": "", "trend_label": "Otomatik", "type": "Yeni Müşteri", "unit": "kg", "unit_code": "kg", "value": 70560.0, "value_base": 70560.0, "value_short": "71K" } ], "record": null } ``` --- # Bildirimleri listele `GET /api/v1/notifications` Kullanıcının son 50 bildirimi ve okunmamış sayısı. ## Yanıt ```json 200 { "ok": true, "rows": [], "unread": 0 } ``` --- # Bildirimleri okundu say `POST /api/v1/notifications/read` Verilen bildirimleri (ya da `ids` yoksa tümünü) okundu yapar. ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `ids` | array | Bildirim kimlikleri. | ## Yanıt ```json 200 { "marked": 0, "ok": true } ``` --- # Son açılanlara ekle `POST /api/v1/recent` Bir kaydı kullanıcının ‘son açılanlar’ listesine ekler (Bugün ekranındaki kartlar). ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `kind` (zorunlu) | string | Kayıt türü. Değerler: `customer`, `opp`, `contact`, `lead` | | `id` (zorunlu) | integer | Kayıt kimliği. | ## Yanıt ```json 200 { "ok": true } ``` --- # Firmaları listele `GET /api/v1/customers` Aktif firmaları süzer, sıralar ve sayfalar. `counts` süzülmüş listedeki müşteri / aday sayılarını ve ziyareti geciken firmaları verir. ## Sorgu parametreleri | Alan | Tür | Açıklama | |---|---|---| | `scope` | string | `mine` = sorumlusu olduğum kayıtlar (satışçının varsayılanı), `all` = görebildiğim tüm kayıtlar (yöneticinin varsayılanı) ya da bir kullanıcı kimliği. | | `status` | string | `AC` = aktif müşteri, `Prospect` = aday. Değerler: `AC`, `Prospect` | | `q` | string | Ad, şehir ya da sektör içerir. | | `city` | string | Şehir (tam eşleşme). | | `visit` | string | Ziyaret takvimi durumu. Değerler: `overdue`, `soon`, `ok`, `none` | | `risk` | string | `1` = yalnız risk altındaki müşteriler. | | `active` | string | `0` = pasif (arşivlenmiş) firmalar. | | `sort` | string | Sıralama. Değerler: `name`, `visit`, `priority` | | `page` | integer | Sayfa numarası (1'den başlar). | | `per` | integer | Sayfa başına satır (10–100). | ## Yanıt ```json 200 { "counts": { "ac": 9, "overdue": 1, "prospect": 21, "soon": 0 }, "ok": true, "page": 1, "pages": 3, "rows": [ { "active": true, "at_risk": false, "city": "Adana", "currency": "EUR", "currency_default": "", "id": 9, "initials": "KP", "last_visit": "2026-07-02", "main_contact": { "email": "ayse.aydin@kumsalplastik.example", "name": "Ayşe Aydın", "phone": "0 (322) 000 80 24", "wa": "903220008024" }, "name": "Kumsal Plastik Ambalaj San. ve Tic. A.Ş.", "next_visit_due": "2026-09-30", "open_opps": 0, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "pending_state": null, "period": 90, "phone": "0 (322) 000 53 49", "potential_kg": 12000.0, "rid": "001t29x5kWbHOxg", "sector": "Esnek ambalaj", "status": "AC", "status_label": "Aktif Müşteri", "unit": "kg", "visit_overdue_days": 2, "visit_state": "overdue" }, { "active": true, "at_risk": false, "city": "Konya", "currency": "EUR", "currency_default": "", "id": 8, "initials": "IF", "last_visit": "2026-08-25", "main_contact": { "email": "gizem.arslan@isiltifilm.example", "name": "Gizem Arslan", "phone": "0 (332) 000 94 28", "wa": "903320009428" }, "name": "Işıltı Film A.Ş.", "next_visit_due": "2026-11-23", "open_opps": 0, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "pending_state": null, "period": 90, "phone": "0 (332) 000 99 70", "potential_kg": 40000.0, "rid": "001frrIzBuo8ERp", "sector": "Film üretimi", "status": "AC", "status_label": "Aktif Müşteri", "unit": "kg", "visit_overdue_days": -52, "visit_state": "ok" } ], "total": 30 } ``` --- # Firmayı getir `GET /api/v1/customers/{id}` Firma kartının tamamı: kişiler, fırsatlar, son ziyaretler ve denemeler, açık aksiyonlar, teklifler, siparişler, zaman tüneli, rakipler, fiyatlar ve e-posta yazışmaları (`threads`). ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Firma kimliği. | ## Yanıt ```json 200 { "customer": { "act": { "addr": "İstanbul Organize Sanayi Bölgesi, 3. Cadde No: 25, İstanbul, İstanbul", "c": 1, "mail": "selin.dogan@alizepaketleme.example", "n": "Alize Paketleme San. ve Tic. A.Ş.", "tel": "0 (212) 000 55 24", "url": "/customers/1", "wa": "902120005524", "who": "Selin Doğan" }, "active": true, "address": "İstanbul Organize Sanayi Bölgesi, 3. Cadde No: 25, İstanbul", "at_risk": false, "barrier": "", "can_edit": true, "city": "İstanbul", "commit_kg": 1700.0, "competitors": [], "contacts": [ { "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_id": 1, "department": "Satın Alma", "email": "selin.dogan@alizepaketleme.example", "id": 1, "is_former": false, "is_main": true, "name": "Selin Doğan", "note": "", "phone": "0 (212) 000 55 24", "rid": "003MaZR2tfJCp1h", "title": "Satın Alma Müdürü", "wa": "902120005524" } ], "created_at": "2026-03-25T09:00:00", "currency": "EUR", "currency_default": "", "demos": [ { "action_date": "2026-09-02", "action_done": true, "action_state": "done", "customer_id": 1, "date": "2026-08-28", "id": 1, "line": "", "next_action": "Fiyat Teklifi", "opp_id": 1, "product": "PU-SL 200 Solventsiz Laminasyon Yapıştırıcısı", "product_id": 1, "result": "Başarılı", "rid": "a0DZ6tZKUceyZxW", "status": "Deneme Gerçekleştirilmiştir", "tech_note": "Mürekkep uyumu iyi; şeffaflık beklentiyi karşıladı.", "user": "Deniz Aksoy" } ], "events": [ { "badge": null, "dot": "#94a3b8", "icon": "geo-alt", "kind": "plan", "note": "", "opp_id": null, "planned": true, "state": null, "sub": "periyot 90 gün · son ziyaret 30.09.2026", "title": "Planlanan: ziyaret vadesi", "url": "", "when": "2026-12-29", "who": "" } ], "extra_kg": 800.0, "id": 1, "initials": "AP", "last_visit": "2026-09-30", "main_contact": { "email": "selin.dogan@alizepaketleme.example", "name": "Selin Doğan", "phone": "0 (212) 000 55 24", "wa": "902120005524" }, "mgmt_note": "", "mgmt_support": false, "name": "Alize Paketleme San. ve Tic. A.Ş.", "next_visit_due": "2026-12-29", "note": "", "offers": [ { "currency": "EUR", "date": "2026-09-09", "id": 1, "is_primary": true, "kg": 1500.0, "lines": 1, "locked": true, "monthly_amount": 5880.0, "payment_days": 120, "pdf_url": "/offers/1/pdf", "price": 3.92, "product": "PU-SL 200 Solventsiz Laminasyon Yapıştırıcısı", "pu": "€/kg", "quote_no": "TKL-2026-0001", "state": "presented", "status": "Kabul", "unit": "kg", "valid_until": "2026-10-09" } ], "open_actions": [ { "customer": "", "customer_id": null, "date": "2026-09-30", "icon": "⚡", "id": 74, "key": "visit:74", "kind": "visit", "label": "Ziyaret Planla", "opp": "Mevcut Müşteri — Yenileme · 2026", "opp_id": 31, "src": "Ziyaret", "state": "overdue", "url": "/action/visit/74" } ], "open_opps": 1, "opps": [ { "bant": 0, "base_currency": "EUR", "close_date": "2027-03-31", "cur_sym": "€", "currency": "EUR", "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_city": "İstanbul", "customer_id": 1, "days_in_stage": 0, "detail_status": "", "fc": "Omitted", "health": null, "health_color": null, "health_manual": false, "health_note": "", "id": 44, "initials": "AP", "is_account": true, "is_open": false, "last_update": "2026-10-02", "lifetime_left": 180, "line": "", "name": "Müşteri Takibi", "next_step": null, "offer_price": null, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": null, "potential_kg": null, "prob": 0.0, "probability": null, "product": "", "product_id": null, "pu": "€/kg", "rid": "006w4ETEfW9soDW", "sla_days": null, "sla_left": null, "source": "", "stage": "Account", "stage_label": "Müşteri Takibi", "start_date": "2026-10-02", "trend": "", "trend_label": "Otomatik", "type": "Müşteri Takibi", "unit": "kg", "unit_code": "kg", "value": 0, "value_base": 0, "value_short": "0" } ], "orders": [ { "currency": "EUR", "date": "2026-10-02", "id": 1, "kg": 700.0, "kind": "İlk Sipariş", "note": "", "opp_id": 1, "payment_days": 120, "price": 3.92, "product": "PU-SL 200 Solventsiz Laminasyon Yapıştırıcısı", "pu": "€/kg", "unit": "kg" } ], "our_kg_current": 1600.0, "our_kg_target": 2400.0, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "pending_state": "overdue", "period": 90, "phone": "0 (212) 000 15 65", "potential_kg": 8000.0, "prices": [ { "currency": "EUR", "id": 1, "payment_days": 120, "price": 3.92, "product": "PU-SL 200 Solventsiz Laminasyon Yapıştırıcısı", "product_id": 1, "pu": "€/kg", "source": "TKL-2026-0001 (Win)", "valid_until": "2027-10-01" } ], "priority_score": 50.0, "rid": "001RBoHIo80N8nR", "sector": "Gıda ambalajı", "share_current": 20.0, "share_start": null, "share_target": 30.0, "status": "AC", "status_label": "Aktif Müşteri", "supplier_note": "Distribütör ürünü", "threads": [ { "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_id": 1, "id": 1, "last_at": "2026-10-01T15:06:59", "last_dir": "out", "n": 2, "opp": "Mevcut Müşteri — Yenileme · 2026", "opp_how": "single", "opp_id": 31, "opp_note": "firmanın tek açık projesi", "peer": "selin.dogan@alizepaketleme.example", "subject": "Numune ve fiyat teklifi", "suggested": false, "waiting": false } ], "threads_n": 1, "unit": "kg", "visit_overdue_days": -88, "visit_period_days": null, "visit_postponed_until": null, "visit_state": "ok", "visits": [ { "action_date": "2026-09-30", "action_done": false, "action_state": "overdue", "contact": "Selin Doğan", "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_id": 1, "date": "2026-09-30", "id": 74, "line": "", "next_action": "Ziyaret Planla", "note": "Hat 2'de tünelleşme sorunu konuşuldu; solventsiz sisteme geçiş planlıyorlar.", "opp": "Mevcut Müşteri — Yenileme · 2026", "opp_id": 31, "product": "", "result": "Teklif istendi", "rid": "00U2wBTaDRg4JXL", "topic": "Tanışma", "type": "Call", "user": "Deniz Aksoy" } ], "website": "www.alizepaketleme.example" }, "ok": true } ``` --- # Firma oluştur `POST /api/v1/customers` Yeni firma açar (durum verilmezse `Prospect`, sorumlu isteği yapan kullanıcı). Aynı adda firma varsa `409 exists`, benzer adlar varsa `409 similar` döner ve `similar` listesi gelir — yine de açmak için `confirm_new: true` gönderin. İsteğe bağlı olarak ilk kişi de açılır. ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `name` (zorunlu) | string | Firma adı (en çok 200 karakter). | | `status` | string | Durum. Değerler: `AC`, `Prospect` | | `city` | string | Şehir. | | `sector` | string | Sektör. | | `phone` | string | Telefon. | | `website` | string | Web sitesi. | | `address` | string | Adres. | | `note` | string | Not. | | `owner_id` | integer | Sorumlu kullanıcı (yalnız yönetici). | | `visit_period_days` | integer | Kaç günde bir ziyaret edilmeli. | | `potential_kg` | number | Aylık potansiyel miktar (kurulumun ana biriminde). | | `contact_name` | string | İlk kişinin adı (ana kişi olur). | | `contact_title` | string | İlk kişinin unvanı. | | `contact_email` | string | İlk kişinin e-postası. | | `contact_phone` | string | İlk kişinin telefonu. | | `confirm_new` | boolean | Benzer adlar olsa da aç. | ## Yanıt ```json 200 { "customer": { "act": { "addr": "Bursa", "c": 31, "mail": "selin.kara@kuzeyplastik.com.tr", "n": "Kuzey Plastik Sanayi", "tel": "+90 224 555 01 02", "url": "/customers/31", "wa": "902245550102", "who": "Selin Kara" }, "active": true, "address": "", "at_risk": false, "barrier": "", "can_edit": true, "city": "Bursa", "commit_kg": null, "competitors": [], "contacts": [ { "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "department": "", "email": "selin.kara@kuzeyplastik.com.tr", "id": 58, "is_former": false, "is_main": true, "name": "Selin Kara", "note": "", "phone": "", "rid": "003URRQi11tfUY7", "title": "Satın Alma Müdürü", "wa": "" } ], "created_at": "2026-10-02T20:07:00", "currency": "EUR", "currency_default": "", "demos": [], "events": [ { "badge": null, "dot": "#94a3b8", "icon": "building", "kind": "firma", "note": "", "opp_id": null, "planned": false, "state": null, "sub": "Firma #31 · Deniz Aksoy", "title": "Firma kaydı açıldı", "url": "", "when": "2026-10-02", "who": "" } ], "extra_kg": null, "id": 31, "initials": "KP", "last_visit": null, "main_contact": { "email": "selin.kara@kuzeyplastik.com.tr", "name": "Selin Kara", "phone": "", "wa": "" }, "mgmt_note": "", "mgmt_support": false, "name": "Kuzey Plastik Sanayi", "next_visit_due": null, "note": "", "offers": [], "open_actions": [], "open_opps": 0, "opps": [], "orders": [], "our_kg_current": null, "our_kg_target": null, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "pending_state": null, "period": 180, "phone": "+90 224 555 01 02", "potential_kg": null, "prices": [], "priority_score": 0.0, "rid": "001QJi5Zr1Hk2VH", "sector": "Plastik", "share_current": null, "share_start": null, "share_target": null, "status": "Prospect", "status_label": "Prospect", "supplier_note": "", "threads": [], "threads_n": 0, "unit": "kg", "visit_overdue_days": 0, "visit_period_days": null, "visit_postponed_until": null, "visit_state": "none", "visits": [], "website": "kuzeyplastik.com.tr" }, "ok": true } ``` ```json 409 { "error": "similar", "message": "Benzer adlı firma var — mevcut kaydı seçin ya da confirm_new=1 ile yeni açın.", "ok": false, "similar": [ { "active": true, "at_risk": false, "city": "Kocaeli", "currency": "EUR", "currency_default": "", "id": 2, "initials": "FA", "last_visit": "2026-09-24", "main_contact": { "email": "elif.karaca@filizambalaj.example", "name": "Elif Karaca", "phone": "0 (262) 000 27 12", "wa": "902620002712" }, "name": "Filiz Ambalaj Ltd. Şti.", "next_visit_due": "2026-12-23", "open_opps": 1, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "pending_state": "soon", "period": 90, "phone": "0 (262) 000 83 56", "potential_kg": 40000.0, "rid": "001rutEPiRkEPfD", "sector": "Gıda ambalajı", "status": "AC", "status_label": "Aktif Müşteri", "unit": "kg", "visit_overdue_days": -82, "visit_state": "ok" } ] } ``` --- # Firmayı güncelle `PATCH /api/v1/customers/{id}` Yalnız gönderilen alanlar değişir; boş metin alanı temizler. `PUT` ve `POST` da kabul edilir. Firmayı düzenleme yetkisi gerekir (sorumlu, yönetici ya da görünürlük kuralının izin verdiği kullanıcı). ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Firma kimliği. | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `name` | string | Firma adı. | | `status` | string | Durum. Değerler: `AC`, `Prospect` | | `owner_id` | integer | Sorumlu (yalnız yönetici). | | `visit_period_days` | integer | Ziyaret sıklığı (gün). | | `sector / city / phone / website / address / note` | string | Metin alanları. | | `supplier_note / barrier / mgmt_note` | string | Mevcut tedarikçi, engel ve yönetim notu. | | `potential_kg / commit_kg / our_kg_current / our_kg_target` | number | Miktar alanları (aylık). | | `share_start / share_current / share_target` | number | Pay yüzdeleri. | | `mgmt_support` | boolean | Yönetim desteği var. | ## Yanıt ```json 200 { "customer": { "act": { "addr": "Bursa", "c": 31, "mail": "selin.kara@kuzeyplastik.com.tr", "n": "Kuzey Plastik Sanayi", "tel": "+90 224 555 01 02", "url": "/customers/31", "wa": "902245550102", "who": "Selin Kara" }, "active": true, "address": "", "at_risk": true, "barrier": "", "can_edit": true, "city": "Bursa", "commit_kg": null, "competitors": [], "contacts": [ { "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "department": "", "email": "selin.kara@kuzeyplastik.com.tr", "id": 58, "is_former": false, "is_main": true, "name": "Selin Kara", "note": "", "phone": "", "rid": "003URRQi11tfUY7", "title": "Satın Alma Müdürü", "wa": "" } ], "created_at": "2026-10-02T20:07:00", "currency": "EUR", "currency_default": "", "demos": [], "events": [ { "badge": null, "dot": "#94a3b8", "icon": "building", "kind": "firma", "note": "", "opp_id": null, "planned": false, "state": null, "sub": "Firma #31 · Deniz Aksoy", "title": "Firma kaydı açıldı", "url": "", "when": "2026-10-02", "who": "" } ], "extra_kg": null, "id": 31, "initials": "KP", "last_visit": null, "main_contact": { "email": "selin.kara@kuzeyplastik.com.tr", "name": "Selin Kara", "phone": "", "wa": "" }, "mgmt_note": "", "mgmt_support": false, "name": "Kuzey Plastik Sanayi", "next_visit_due": null, "note": "Yıllık sözleşme görüşülüyor.", "offers": [], "open_actions": [], "open_opps": 0, "opps": [], "orders": [], "our_kg_current": null, "our_kg_target": null, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "pending_state": null, "period": 30, "phone": "+90 224 555 01 02", "potential_kg": null, "prices": [], "priority_score": 0.0, "rid": "001QJi5Zr1Hk2VH", "sector": "Plastik", "share_current": null, "share_start": null, "share_target": null, "status": "AC", "status_label": "Aktif Müşteri", "supplier_note": "", "threads": [], "threads_n": 0, "unit": "kg", "visit_overdue_days": 0, "visit_period_days": 30, "visit_postponed_until": null, "visit_state": "none", "visits": [], "website": "kuzeyplastik.com.tr" }, "ok": true } ``` --- # Ziyareti ertele `POST /api/v1/customers/{id}/postpone` Firmanın ziyaret terminini ileri alır. `date` verilirse o gün, yoksa `days` gün sonrası (varsayılan 7). ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Firma kimliği. | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `date` | string | Yeni tarih (YYYY-MM-DD). | | `days` | integer | Gün sayısı. | | `note` | string | Erteleme notu. | ## Yanıt ```json 200 { "customer": { "active": true, "at_risk": false, "city": "Bursa", "currency": "EUR", "currency_default": "", "id": 31, "initials": "KP", "last_visit": null, "main_contact": { "email": "selin.kara@kuzeyplastik.com.tr", "name": "Selin Kara", "phone": "", "wa": "" }, "name": "Kuzey Plastik Sanayi", "next_visit_due": "2026-10-16", "open_opps": 0, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "pending_state": null, "period": 30, "phone": "+90 224 555 01 02", "potential_kg": null, "rid": "001QJi5Zr1Hk2VH", "sector": "Plastik", "status": "AC", "status_label": "Aktif Müşteri", "unit": "kg", "visit_overdue_days": -14, "visit_state": "ok" }, "ok": true, "until": "2026-10-16" } ``` --- # Görüşme formu seçenekleri `GET /api/v1/customers/{id}/lookup` Görüşme kaydı (hızlı giriş) formunu doldurmak için firmanın açık projeleri, varsayılan proje, kişileri, açık aksiyonları ve son aşama etiketi. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Firma kimliği. | ## Yanıt ```json 200 { "city": "İstanbul", "competitor": "Distribütör ürünü", "contacts": [ { "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_id": 1, "department": "Satın Alma", "email": "selin.dogan@alizepaketleme.example", "id": 1, "is_former": false, "is_main": true, "name": "Selin Doğan", "note": "", "phone": "0 (212) 000 55 24", "rid": "003MaZR2tfJCp1h", "title": "Satın Alma Müdürü", "wa": "902120005524" } ], "default_project": 31, "id": 1, "name": "Alize Paketleme San. ve Tic. A.Ş.", "ok": true, "open_actions": [ { "customer": "", "customer_id": null, "date": "2026-09-30", "icon": "⚡", "id": 74, "key": "visit:74", "kind": "visit", "label": "Ziyaret Planla", "opp": "Mevcut Müşteri — Yenileme · 2026", "opp_id": 31, "src": "Ziyaret", "state": "overdue", "url": "/action/visit/74" } ], "opp_type": "Mevcut Müşteri — Yenileme", "potential_kg": 8000.0, "projects": [ { "id": 31, "name": "Mevcut Müşteri — Yenileme · 2026", "stage": "Qualify" }, { "id": 44, "name": "Müşteri Takibi", "stage": "Müşteri Takibi" } ], "stage": "Qualify", "stage_label": "", "status": "AC" } ``` --- # Kişileri listele `GET /api/v1/contacts` Aktif firmaların kişileri (en çok 300, ada göre). Ayrılmış kişiler varsayılan olarak gelmez. ## Sorgu parametreleri | Alan | Tür | Açıklama | |---|---|---| | `scope` | string | `mine` = sorumlusu olduğum kayıtlar (satışçının varsayılanı), `all` = görebildiğim tüm kayıtlar (yöneticinin varsayılanı) ya da bir kullanıcı kimliği. | | `q` | string | Ad, firma, telefon, e-posta ya da unvan içerir. | | `former` | string | `1` = ayrılmış kişiler de gelsin. | ## Yanıt ```json 200 { "ok": true, "rows": [ { "customer": "Ortanca Matbaacılık Ltd. Şti.", "customer_id": 22, "department": "Satın Alma", "email": "ahmet.aydin@ortancamatbaacilik.example", "id": 42, "is_former": false, "is_main": true, "name": "Ahmet Aydın", "note": "", "phone": "0 (332) 000 81 52", "rid": "003qMFfAArgzPDF", "title": "Satın Alma Müdürü", "wa": "903320008152" }, { "customer": "Kamelya Lamine Film Ltd. Şti.", "customer_id": 6, "department": "Teknik", "email": "ahmet.ozturk@kamelyalamine.example", "id": 11, "is_former": false, "is_main": true, "name": "Ahmet Öztürk", "note": "", "phone": "0 (232) 000 89 83", "rid": "003t3MMhbXHOFjR", "title": "Teknik Müdür", "wa": "902320008983" } ], "total": 58 } ``` --- # Kişi ekle `POST /api/v1/customers/{id}/contacts` Firmaya kişi ekler. Firmanın ilk kişisi ya da `is_main: true` ise ana kişi olur. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Firma kimliği. | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `name` (zorunlu) | string | Ad soyad. | | `title` | string | Unvan. | | `department` | string | Departman. | | `email` | string | E-posta. | | `phone` | string | Telefon. | | `is_main` | boolean | Ana kişi yap. | | `note` | string | Not (250 karakter). | ## Yanıt ```json 200 { "contact": { "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "department": "Üretim", "email": "emre.yildiz@kuzeyplastik.com.tr", "id": 59, "is_former": false, "is_main": false, "name": "Emre Yıldız", "note": "", "phone": "+90 532 555 11 22", "rid": "0037CJrFvjDFvYX", "title": "Üretim Şefi", "wa": "905325551122" }, "ok": true } ``` --- # Kişiyi güncelle `PATCH /api/v1/contacts/{id}` Yalnız gönderilen alanlar değişir. `is_former: true` kişiyi ‘ayrıldı’ yapar (silinmez); `is_main: true` ana kişi yapar. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Kişi kimliği. | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `name / title / department / phone / email / note` | string | Metin alanları. | | `is_main` | boolean | Ana kişi yap. | | `is_former` | boolean | Firmadan ayrıldı. | ## Yanıt ```json 200 { "contact": { "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "department": "Üretim", "email": "emre.yildiz@kuzeyplastik.com.tr", "id": 59, "is_former": false, "is_main": true, "name": "Emre Yıldız", "note": "", "phone": "+90 532 555 11 22", "rid": "0037CJrFvjDFvYX", "title": "Fabrika Müdürü", "wa": "905325551122" }, "ok": true } ``` --- # Fırsatları listele `GET /api/v1/opportunities` Fırsatları (satış projeleri) süzer, sıralar ve sayfalar. Yanıtta açık aşama sayıları, açık fırsatların ana dövizdeki toplamı ve SLA aşımı sayısı da gelir. ## Sorgu parametreleri | Alan | Tür | Açıklama | |---|---|---| | `scope` | string | `mine` = sorumlusu olduğum kayıtlar (satışçının varsayılanı), `all` = görebildiğim tüm kayıtlar (yöneticinin varsayılanı) ya da bir kullanıcı kimliği. | | `stage` | string | `open` (varsayılan), `closed`, `account` (müşteri takibi kapları) ya da bir aşama anahtarı (`/me` → `constants.stages`). | | `q` | string | Fırsat ya da firma adı içerir. | | `sort` | string | Sıralama. Değerler: `update`, `health`, `value`, `sla` | | `page` | integer | Sayfa numarası (1'den başlar). | | `per` | integer | Sayfa başına satır (10–100). | ## Yanıt ```json 200 { "base_currency": "EUR", "ok": true, "page": 1, "pages": 3, "rows": [ { "bant": 4, "base_currency": "EUR", "close_date": "2026-11-12", "cur_sym": "€", "currency": "EUR", "customer": "Sardunya Etiket Ltd. Şti.", "customer_city": "Eskişehir", "customer_id": 14, "days_in_stage": 12, "detail_status": "", "fc": "Best Case", "health": { "grade": "success", "label": "sağlıklı", "score": 100 }, "health_color": "g", "health_manual": true, "health_note": "", "id": 14, "initials": "SE", "is_account": false, "is_open": true, "last_update": "2026-10-01", "lifetime_left": 106, "line": "", "name": "Mevcut Müşteri — Ek Satış · 2026", "next_step": "Vade ve teslim koşullarını görüş", "offer_price": 6.12, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": 120, "potential_kg": 12000.0, "prob": 0.75, "probability": null, "product": "HS 30 Isıl Yapışma Laki", "product_id": 5, "pu": "€/kg", "rid": "006riIxDf4YpLLv", "sla_days": 30, "sla_left": 18, "source": "Soğuk arama", "stage": "Negotiation", "stage_label": "Negotiation", "start_date": "2026-07-20", "trend": "up", "trend_label": "Yeşil", "type": "Mevcut Müşteri — Ek Satış", "unit": "kg", "unit_code": "kg", "value": 881280.0, "value_base": 881280.0, "value_short": "881K" }, { "bant": 4, "base_currency": "EUR", "close_date": "2027-01-16", "cur_sym": "€", "currency": "EUR", "customer": "Hanımeli Etiket San. Tic. Ltd. Şti.", "customer_city": "Adana", "customer_id": 23, "days_in_stage": 74, "detail_status": "", "fc": "Best Case", "health": { "grade": "warning", "label": "dikkat", "score": 60 }, "health_color": "g", "health_manual": true, "health_note": "", "id": 23, "initials": "HE", "is_account": false, "is_open": true, "last_update": "2026-09-30", "lifetime_left": 69, "line": "", "name": "Mevcut Müşteri — Yenileme · 2026", "next_step": "Deneme tarihini planla", "offer_price": null, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": null, "potential_kg": 12000.0, "prob": 0.5, "probability": null, "product": "PU-SL 350 Yüksek Performans (retort)", "product_id": 2, "pu": "€/kg", "rid": "006LsST7gv514nh", "sla_days": 60, "sla_left": -14, "source": "Web formu", "stage": "Present Solution", "stage_label": "Present Solution", "start_date": "2026-06-13", "trend": "up", "trend_label": "Yeşil", "type": "Mevcut Müşteri — Yenileme", "unit": "kg", "unit_code": "kg", "value": 806000.0, "value_base": 806000.0, "value_short": "806K" } ], "sla_over": 4, "stage_counts": { "Expect to Close": 4, "Negotiation": 5, "Present Solution": 6, "Qualify": 7, "Viable": 6 }, "total": 28, "value_open": 11243660.0 } ``` --- # Fırsatı getir `GET /api/v1/opportunities/{id}` Fırsatın tamamı: aşama, değer, olasılık, sağlık, kontrol listesi adımları, BANT, aşama kapısı sorunları (`gate_problems`), ziyaretler, denemeler, teklifler, siparişler, görevler, aşama geçmişi, notlar ve e-posta yazışmaları. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Fırsat kimliği. | ## Yanıt ```json 200 { "ok": true, "opp": { "bant": 3, "bant_a": true, "bant_b": true, "bant_n": false, "bant_t": true, "base_currency": "EUR", "can_edit": true, "can_revive": false, "close_category": "", "close_date": "2027-02-06", "close_reason": "", "closed_at": null, "competitor": "Distribütör ürünü", "cur_sym": "€", "currency": "EUR", "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_city": "İstanbul", "customer_id": 1, "days_in_stage": 2, "demos": [], "detail_status": "", "fc": "Pipeline", "gate_ok": false, "gate_problems": [ "BANT kriterleri 4/4 sağlanmalı (Budget · Authority · Need · Time)" ], "guide": "BANT 4/4", "health": { "grade": "success", "label": "sağlıklı", "score": 92 }, "health_color": "g", "health_manual": true, "health_note": "", "health_why": [ [ "−8" ] ], "id": 31, "initials": "AP", "is_account": false, "is_open": true, "last_update": "2026-09-20", "lifetime_left": 178, "line": "", "logs": [ { "date": "2026-09-30T08:00:00", "from": null, "note": "Proje açıldı", "to": "Qualify", "user": "Deniz Aksoy" } ], "name": "Mevcut Müşteri — Yenileme · 2026", "next_stage": "Viable", "next_step": "İhtiyaç, hat ve aylık miktar potansiyelini netleştir (Need)", "note": "", "notes": [], "offer_price": null, "offers": [], "orders": [], "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": null, "potential_kg": 8000.0, "prob": 0.1, "probability": null, "product": "", "product_id": null, "pu": "€/kg", "rid": "006YV7xczE9DnY2", "sla_days": 30, "sla_left": 28, "source": "Bayi / distribütör", "stage": "Qualify", "stage_label": "Qualify", "start_date": "2026-09-30", "steps": [ { "assignee": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "auto": true, "done": true, "due_date": null, "id": 376, "note": "otomatik: BANT kutusu", "stage": "Qualify", "state": "done", "title": "Karar verici / yetkili kişiyi belirle (Authority)" } ], "tags": [], "tasks": [], "threads": [ { "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_id": 1, "id": 1, "last_at": "2026-10-01T15:06:59", "last_dir": "out", "n": 2, "opp": "Mevcut Müşteri — Yenileme · 2026", "opp_how": "single", "opp_id": 31, "opp_note": "firmanın tek açık projesi", "peer": "selin.dogan@alizepaketleme.example", "subject": "Numune ve fiyat teklifi", "suggested": false, "waiting": false } ], "threads_n": 1, "trend": "up", "trend_label": "Yeşil", "type": "Mevcut Müşteri — Yenileme", "unit": "kg", "unit_code": "kg", "value": 403000.0, "value_base": 403000.0, "value_short": "403K", "visits": [ { "action_date": "2026-09-30", "action_done": false, "action_state": "overdue", "contact": "Selin Doğan", "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_id": 1, "date": "2026-09-30", "id": 74, "line": "", "next_action": "Ziyaret Planla", "note": "Hat 2'de tünelleşme sorunu konuşuldu; solventsiz sisteme geçiş planlıyorlar.", "opp": "Mevcut Müşteri — Yenileme · 2026", "opp_id": 31, "product": "", "result": "Teklif istendi", "rid": "00U2wBTaDRg4JXL", "topic": "Tanışma", "type": "Call", "user": "Deniz Aksoy" } ], "weighted": 40300.0 } } ``` --- # Fırsat oluştur `POST /api/v1/opportunities` Firma için yeni satış projesi açar; ilk açık aşamadan başlar ve aşama adımları kendiliğinden kurulur. Yıllık değer miktar × birim fiyattan hesaplanır; ikisi yoksa `value` kullanılır. ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `customer_id` (zorunlu) | integer | Firma kimliği. | | `name` | string | Proje adı. | | `type` | string | Proje türü (`/me` → `constants.opp_types`). | | `product_id` | integer | Ürün (`/me` → `products`). | | `potential_kg` | number | Aylık miktar (ürünün biriminde). | | `offer_price` | number | Birim fiyat. | | `payment_days` | integer | Vade (gün). | | `value` | number | Yıllık değer (miktar × fiyat yoksa). | | `line` | string | Hat / kullanım alanı. | | `competitor` | string | Rakip ya da mevcut tedarikçi. | | `note` | string | Not. | ## Yanıt ```json 200 { "ok": true, "opp": { "bant": 0, "bant_a": false, "bant_b": false, "bant_n": false, "bant_t": false, "base_currency": "EUR", "can_edit": true, "can_revive": false, "close_category": "", "close_date": "2027-03-31", "close_reason": "", "closed_at": null, "competitor": "Mevcut tedarikçi", "cur_sym": "€", "currency": "EUR", "customer": "Kuzey Plastik Sanayi", "customer_city": "Bursa", "customer_id": 31, "days_in_stage": 0, "demos": [], "detail_status": "", "fc": "Pipeline", "gate_ok": false, "gate_problems": [ "BANT kriterleri 4/4 sağlanmalı (Budget · Authority · Need · Time)" ], "guide": "BANT 4/4", "health": { "grade": "success", "label": "sağlıklı", "score": 85 }, "health_color": "g", "health_manual": false, "health_note": "", "health_why": [ [ "−15" ] ], "id": 53, "initials": "KP", "is_account": false, "is_open": true, "last_update": "2026-10-02", "lifetime_left": 180, "line": "", "logs": [ { "date": "2026-10-02T20:07:00", "from": null, "note": "Proje açıldı (mobil)", "to": "Qualify", "user": "Deniz Aksoy" } ], "name": "Streç film tedariki", "next_stage": "Viable", "next_step": "Karar verici / yetkili kişiyi belirle (Authority)", "note": "", "notes": [], "offer_price": 2.35, "offers": [], "orders": [], "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": 60, "potential_kg": 12000.0, "prob": 0.1, "probability": null, "product": "", "product_id": null, "pu": "€/kg", "rid": "006BMHmVNZ9nFLS", "sla_days": 30, "sla_left": 30, "source": "", "stage": "Qualify", "stage_label": "Qualify", "start_date": "2026-10-02", "steps": [ { "assignee": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "auto": false, "done": false, "due_date": null, "id": 467, "note": "", "stage": "Qualify", "state": "open", "title": "Karar verici / yetkili kişiyi belirle (Authority)" } ], "tags": [], "tasks": [], "threads": [], "threads_n": 0, "trend": "", "trend_label": "Otomatik", "type": "", "unit": "kg", "unit_code": "kg", "value": 338400.0, "value_base": 338400.0, "value_short": "338K", "visits": [], "weighted": 33840.0 } } ``` --- # Fırsatı güncelle `POST /api/v1/opportunities/{id}/fields` Sık değişen alanlar: miktar, birim fiyat (değişince teklif geçmişine yazılır), vade, kapanış tarihi (değişiklik günlüğe yazılır), sonraki adım tarihi, not, ürün, tür, hat, ad, öngörü kategorisi ve aşama etiketi. Aşama değiştirmek için [aşama ucunu](/rest-api/deals/stage) kullanın. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Fırsat kimliği. | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `potential_kg` | number | Aylık miktar. | | `offer_price` | number | Birim fiyat. | | `payment_days` | integer | Vade (gün). | | `close_date` | string | Kapanış tarihi (YYYY-MM-DD). | | `next_step_date` | string | Sonraki adım tarihi. | | `note` | string | Not. | | `product_id` | integer | Ürün. | | `type` | string | Tür. | | `line` | string | Hat. | | `name` | string | Ad. | | `forecast_cat` | string | Öngörü kategorisi. Değerler: `Pipeline`, `Best Case`, `Commit`, `Closed`, `Omitted` | | `detail_status` | string | Aşamaya uyan ayrıntı etiketi (Makro hattı). | ## Yanıt ```json 200 { "ok": true, "opp": { "bant": 0, "bant_a": false, "bant_b": false, "bant_n": false, "bant_t": false, "base_currency": "EUR", "can_edit": true, "can_revive": false, "close_category": "", "close_date": "2026-11-16", "close_reason": "", "closed_at": null, "competitor": "Mevcut tedarikçi", "cur_sym": "€", "currency": "EUR", "customer": "Kuzey Plastik Sanayi", "customer_city": "Bursa", "customer_id": 31, "days_in_stage": 0, "demos": [], "detail_status": "", "fc": "Best Case", "gate_ok": false, "gate_problems": [ "BANT kriterleri 4/4 sağlanmalı (Budget · Authority · Need · Time)" ], "guide": "BANT 4/4", "health": { "grade": "success", "label": "sağlıklı", "score": 85 }, "health_color": "g", "health_manual": false, "health_note": "", "health_why": [ [ "−15" ] ], "id": 53, "initials": "KP", "is_account": false, "is_open": true, "last_update": "2026-10-02", "lifetime_left": 180, "line": "", "logs": [ { "date": "2026-10-02T20:07:00", "from": null, "note": "Proje açıldı (mobil)", "to": "Qualify", "user": "Deniz Aksoy" } ], "name": "Streç film tedariki", "next_stage": "Viable", "next_step": "Karar verici / yetkili kişiyi belirle (Authority)", "note": "Numune onayı bekleniyor.", "notes": [], "offer_price": 2.35, "offers": [], "orders": [], "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": 60, "potential_kg": 12000.0, "prob": 0.1, "probability": null, "product": "", "product_id": null, "pu": "€/kg", "rid": "006BMHmVNZ9nFLS", "sla_days": 30, "sla_left": 30, "source": "", "stage": "Qualify", "stage_label": "Qualify", "start_date": "2026-10-02", "steps": [ { "assignee": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "auto": false, "done": false, "due_date": null, "id": 467, "note": "", "stage": "Qualify", "state": "open", "title": "Karar verici / yetkili kişiyi belirle (Authority)" } ], "tags": [], "tasks": [], "threads": [], "threads_n": 0, "trend": "", "trend_label": "Otomatik", "type": "", "unit": "kg", "unit_code": "kg", "value": 338400.0, "value_base": 338400.0, "value_short": "338K", "visits": [], "weighted": 33840.0 } } ``` --- # Aşamayı değiştir `POST /api/v1/opportunities/{id}/stage` Fırsatı başka bir aşamaya taşır. Aşama kapıları web'deki gibi uygulanır: kural sağlanmıyorsa `409 gate` ve `gate_problems` döner. Kaybedildi / İptal için `reason` (kayıp nedeni) gönderin. Kazanılan fırsat firmayı müşteri yapar ve uygulamalara `opp_won` olayı gider. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Fırsat kimliği. | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `stage` (zorunlu) | string | Hedef aşama anahtarı (`/me` → `constants.stages`; kapanış anahtarları `Win`, `Lost`, `Cancel`). | | `note` | string | Aşama günlüğüne not. | | `reason` | string | Kayıp / iptal nedeni (`/me` → `constants.loss_reasons`). | ## Yanıt ```json 200 { "messages": [ "Aşama güncellendi: Viable" ], "ok": true, "opp": { "bant": 4, "bant_a": true, "bant_b": true, "bant_n": true, "bant_t": true, "base_currency": "EUR", "can_edit": true, "can_revive": false, "close_category": "", "close_date": "2026-11-16", "close_reason": "", "closed_at": null, "competitor": "Mevcut tedarikçi", "cur_sym": "€", "currency": "EUR", "customer": "Kuzey Plastik Sanayi", "customer_city": "Bursa", "customer_id": 31, "days_in_stage": 0, "demos": [], "detail_status": "", "fc": "Best Case", "gate_ok": false, "gate_problems": [ "En az bir F2F ziyaret kaydı gerekir (Hızlı Giriş → tür F2F)" ], "guide": "f2f ziyaret + doğru ürün", "health": { "grade": "success", "label": "sağlıklı", "score": 80 }, "health_color": "g", "health_manual": false, "health_note": "", "health_why": [ [ "−15" ] ], "id": 53, "initials": "KP", "is_account": false, "is_open": true, "last_update": "2026-10-02", "lifetime_left": 180, "line": "", "logs": [ { "date": "2026-10-02T20:07:00", "from": "Qualify", "note": "İhtiyaç ve bütçe doğrulandı.", "to": "Viable", "user": "Deniz Aksoy" } ], "name": "Streç film tedariki", "next_stage": "Present Solution", "next_step": "F2F ziyaret planla ve gerçekleştir", "note": "Numune onayı bekleniyor.", "notes": [], "offer_price": 2.35, "offers": [], "orders": [], "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": 60, "potential_kg": 12000.0, "prob": 0.25, "probability": null, "product": "", "product_id": null, "pu": "€/kg", "rid": "006BMHmVNZ9nFLS", "sla_days": 60, "sla_left": 60, "source": "", "stage": "Viable", "stage_label": "Viable", "start_date": "2026-10-02", "steps": [ { "assignee": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "auto": true, "done": true, "due_date": null, "id": 467, "note": "otomatik: BANT kutusu", "stage": "Qualify", "state": "done", "title": "Karar verici / yetkili kişiyi belirle (Authority)" } ], "tags": [], "tasks": [], "threads": [], "threads_n": 0, "trend": "", "trend_label": "Otomatik", "type": "", "unit": "kg", "unit_code": "kg", "value": 338400.0, "value_base": 338400.0, "value_short": "338K", "visits": [], "weighted": 84600.0 } } ``` ```json 409 { "error": "gate", "gate_problems": [ "BANT kriterleri 4/4 sağlanmalı (Budget · Authority · Need · Time)" ], "message": "Viable'a geçmek için: BANT kriterleri 4/4 sağlanmalı (Budget · Authority · Need · Time)", "ok": false } ``` --- # BANT güncelle `POST /api/v1/opportunities/{id}/bant` Bütçe, yetki, ihtiyaç ve zamanlama ölçütlerini işaretler; ilgili kontrol listesi adımları da güncellenir. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Fırsat kimliği. | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `bant_b` | boolean | Bütçe var. | | `bant_a` | boolean | Karar verici belli. | | `bant_n` | boolean | İhtiyaç doğrulandı. | | `bant_t` | boolean | Zamanlama belli. | ## Yanıt ```json 200 { "message": "BANT güncellendi (4/4).", "ok": true, "opp": { "bant": 4, "bant_a": true, "bant_b": true, "bant_n": true, "bant_t": true, "base_currency": "EUR", "can_edit": true, "can_revive": false, "close_category": "", "close_date": "2026-11-16", "close_reason": "", "closed_at": null, "competitor": "Mevcut tedarikçi", "cur_sym": "€", "currency": "EUR", "customer": "Kuzey Plastik Sanayi", "customer_city": "Bursa", "customer_id": 31, "days_in_stage": 0, "demos": [], "detail_status": "", "fc": "Best Case", "gate_ok": true, "gate_problems": [], "guide": "BANT 4/4", "health": { "grade": "success", "label": "sağlıklı", "score": 90 }, "health_color": "g", "health_manual": false, "health_note": "", "health_why": [ [ "−15" ] ], "id": 53, "initials": "KP", "is_account": false, "is_open": true, "last_update": "2026-10-02", "lifetime_left": 180, "line": "", "logs": [ { "date": "2026-10-02T20:07:00", "from": null, "note": "Proje açıldı (mobil)", "to": "Qualify", "user": "Deniz Aksoy" } ], "name": "Streç film tedariki", "next_stage": "Viable", "next_step": "Ön koşullar tamam → Viable aşamasına geçir", "note": "Numune onayı bekleniyor.", "notes": [], "offer_price": 2.35, "offers": [], "orders": [], "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": 60, "potential_kg": 12000.0, "prob": 0.1, "probability": null, "product": "", "product_id": null, "pu": "€/kg", "rid": "006BMHmVNZ9nFLS", "sla_days": 30, "sla_left": 30, "source": "", "stage": "Qualify", "stage_label": "Qualify", "start_date": "2026-10-02", "steps": [ { "assignee": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "auto": true, "done": true, "due_date": null, "id": 467, "note": "otomatik: BANT kutusu", "stage": "Qualify", "state": "done", "title": "Karar verici / yetkili kişiyi belirle (Authority)" } ], "tags": [], "tasks": [], "threads": [], "threads_n": 0, "trend": "", "trend_label": "Otomatik", "type": "", "unit": "kg", "unit_code": "kg", "value": 338400.0, "value_base": 338400.0, "value_short": "338K", "visits": [], "weighted": 33840.0 } } ``` --- # Adım ekle `POST /api/v1/opportunities/{id}/steps` Fırsatın bulunduğu aşamaya kontrol listesi adımı ekler. Tarihli adımlar Aksiyonlar'da `step` türüyle görünür. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Fırsat kimliği. | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `title` (zorunlu) | string | Adım başlığı. | | `due_date` | string | Termin (YYYY-MM-DD). | | `assigned_to` | integer | Atanan kullanıcı. | ## Yanıt ```json 200 { "ok": true, "opp": { "bant": 4, "bant_a": true, "bant_b": true, "bant_n": true, "bant_t": true, "base_currency": "EUR", "can_edit": true, "can_revive": false, "close_category": "", "close_date": "2026-11-16", "close_reason": "", "closed_at": null, "competitor": "Mevcut tedarikçi", "cur_sym": "€", "currency": "EUR", "customer": "Kuzey Plastik Sanayi", "customer_city": "Bursa", "customer_id": 31, "days_in_stage": 0, "demos": [], "detail_status": "", "fc": "Best Case", "gate_ok": false, "gate_problems": [ "En az bir F2F ziyaret kaydı gerekir (Hızlı Giriş → tür F2F)" ], "guide": "f2f ziyaret + doğru ürün", "health": { "grade": "success", "label": "sağlıklı", "score": 95 }, "health_color": "g", "health_manual": false, "health_note": "", "health_why": [ [ "−10" ] ], "id": 53, "initials": "KP", "is_account": false, "is_open": true, "last_update": "2026-10-02", "lifetime_left": 180, "line": "", "logs": [ { "date": "2026-10-02T20:07:00", "from": "Qualify", "note": "İhtiyaç ve bütçe doğrulandı.", "to": "Viable", "user": "Deniz Aksoy" } ], "name": "Streç film tedariki", "next_stage": "Present Solution", "next_step": "F2F ziyaret planla ve gerçekleştir", "note": "Numune onayı bekleniyor.", "notes": [], "offer_price": 2.35, "offers": [], "orders": [], "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": 60, "potential_kg": 12000.0, "prob": 0.25, "probability": null, "product": "", "product_id": null, "pu": "€/kg", "rid": "006BMHmVNZ9nFLS", "sla_days": 60, "sla_left": 60, "source": "", "stage": "Viable", "stage_label": "Viable", "start_date": "2026-10-02", "steps": [ { "assignee": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "auto": true, "done": true, "due_date": null, "id": 467, "note": "otomatik: BANT kutusu", "stage": "Qualify", "state": "done", "title": "Karar verici / yetkili kişiyi belirle (Authority)" } ], "tags": [], "tasks": [], "threads": [], "threads_n": 0, "trend": "", "trend_label": "Otomatik", "type": "", "unit": "kg", "unit_code": "kg", "value": 338400.0, "value_base": 338400.0, "value_short": "338K", "visits": [], "weighted": 84600.0 }, "step": { "assignee": null, "auto": false, "done": false, "due_date": "2026-10-09", "id": 474, "note": "", "stage": "Viable", "state": "soon", "title": "Hat denemesi planla" } } ``` --- # Adımı işaretle `POST /api/v1/steps/{id}/toggle` Adımı tamamlandı / açık yapar (`done` yoksa tersine çevirir). BANT adımları fırsatın BANT işaretlerini de değiştirir. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Adım kimliği. | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `done` | boolean | Tamamlandı. | | `note` | string | Not (250 karakter). | ## Yanıt ```json 200 { "ok": true, "opp": { "bant": 4, "bant_a": true, "bant_b": true, "bant_n": true, "bant_t": true, "base_currency": "EUR", "can_edit": true, "can_revive": false, "close_category": "", "close_date": "2026-11-16", "close_reason": "", "closed_at": null, "competitor": "Mevcut tedarikçi", "cur_sym": "€", "currency": "EUR", "customer": "Kuzey Plastik Sanayi", "customer_city": "Bursa", "customer_id": 31, "days_in_stage": 0, "demos": [], "detail_status": "", "fc": "Best Case", "gate_ok": false, "gate_problems": [ "En az bir F2F ziyaret kaydı gerekir (Hızlı Giriş → tür F2F)" ], "guide": "f2f ziyaret + doğru ürün", "health": { "grade": "success", "label": "sağlıklı", "score": 80 }, "health_color": "g", "health_manual": false, "health_note": "", "health_why": [ [ "−15" ] ], "id": 53, "initials": "KP", "is_account": false, "is_open": true, "last_update": "2026-10-02", "lifetime_left": 180, "line": "", "logs": [ { "date": "2026-10-02T20:07:00", "from": "Qualify", "note": "İhtiyaç ve bütçe doğrulandı.", "to": "Viable", "user": "Deniz Aksoy" } ], "name": "Streç film tedariki", "next_stage": "Present Solution", "next_step": "F2F ziyaret planla ve gerçekleştir", "note": "Numune onayı bekleniyor.", "notes": [], "offer_price": 2.35, "offers": [], "orders": [], "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": 60, "potential_kg": 12000.0, "prob": 0.25, "probability": null, "product": "", "product_id": null, "pu": "€/kg", "rid": "006BMHmVNZ9nFLS", "sla_days": 60, "sla_left": 60, "source": "", "stage": "Viable", "stage_label": "Viable", "start_date": "2026-10-02", "steps": [ { "assignee": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "auto": true, "done": true, "due_date": null, "id": 467, "note": "otomatik: BANT kutusu", "stage": "Qualify", "state": "done", "title": "Karar verici / yetkili kişiyi belirle (Authority)" } ], "tags": [], "tasks": [], "threads": [], "threads_n": 0, "trend": "", "trend_label": "Otomatik", "type": "", "unit": "kg", "unit_code": "kg", "value": 338400.0, "value_base": 338400.0, "value_short": "338K", "visits": [], "weighted": 84600.0 }, "step": { "assignee": null, "auto": false, "done": true, "due_date": "2026-10-09", "id": 474, "note": "Perşembe 10:00", "stage": "Viable", "state": "done", "title": "Hat denemesi planla" } } ``` --- # Fırsatı canlandır `POST /api/v1/opportunities/{id}/revive` Kaybedilmiş / iptal edilmiş fırsattan yeni bir fırsat açar (ürün, miktar ve hat kopyalanır, eskisine bağlanır). ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Kapanmış fırsatın kimliği. | ## Yanıt ```json 200 { "ok": true, "opp": { "bant": 0, "bant_a": false, "bant_b": false, "bant_n": false, "bant_t": false, "base_currency": "EUR", "can_edit": true, "can_revive": false, "close_category": "", "close_date": "2027-03-31", "close_reason": "", "closed_at": null, "competitor": "", "cur_sym": "€", "currency": "EUR", "customer": "Işıltı Film A.Ş.", "customer_city": "Konya", "customer_id": 8, "days_in_stage": 0, "demos": [], "detail_status": "", "fc": "Pipeline", "gate_ok": false, "gate_problems": [ "BANT kriterleri 4/4 sağlanmalı (Budget · Authority · Need · Time)" ], "guide": "BANT 4/4", "health": { "grade": "success", "label": "sağlıklı", "score": 85 }, "health_color": "g", "health_manual": false, "health_note": "", "health_why": [ [ "−15" ] ], "id": 54, "initials": "IF", "is_account": false, "is_open": true, "last_update": "2026-10-02", "lifetime_left": 180, "line": "", "logs": [ { "date": "2026-10-02T20:07:00", "from": null, "note": "Yeniden canlandırıldı (mobil) ← #38", "to": "Qualify", "user": "Deniz Aksoy" } ], "name": "Mevcut Müşteri — Ek Satış · 2026 (canlandırıldı)", "next_stage": "Viable", "next_step": "Karar verici / yetkili kişiyi belirle (Authority)", "note": "", "notes": [], "offer_price": null, "offers": [], "orders": [], "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": null, "potential_kg": 12000.0, "prob": 0.1, "probability": null, "product": "PU-SL 350 Yüksek Performans (retort)", "product_id": 2, "pu": "€/kg", "rid": "006CSwEIJ0YiYkC", "sla_days": 30, "sla_left": 30, "source": "", "stage": "Qualify", "stage_label": "Qualify", "start_date": "2026-10-02", "steps": [ { "assignee": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "auto": false, "done": false, "due_date": null, "id": 475, "note": "", "stage": "Qualify", "state": "open", "title": "Karar verici / yetkili kişiyi belirle (Authority)" } ], "tags": [], "tasks": [], "threads": [], "threads_n": 0, "trend": "", "trend_label": "Otomatik", "type": "Mevcut Müşteri — Ek Satış", "unit": "kg", "unit_code": "kg", "value": 0, "value_base": 0, "value_short": "0", "visits": [], "weighted": 0.0 } } ``` --- # Görüşme kaydet `POST /api/v1/quick` Hızlı Giriş: bir telefon görüşmesini ya da yüz yüze ziyareti tek istekte kaydeder. Firma adla bulunur (yoksa aday olarak açılır; benzer adlar varsa `409 similar`), kişi yoksa eklenir, uygun açık aksiyonlar kapanır, sonraki aksiyon açılır ve proje web formundaki kurallarla ilerler. Yanıttaki `parts` neyin kaydedildiğini, `warnings` ve `advice` dikkat edilecekleri anlatır. ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `customer` (zorunlu) | string | Firma adı (tam ya da yakın). | | `visit_type` | string | `F2F` yüz yüze, `Call` telefon, `Deneme` deneme ziyareti. Değerler: `F2F`, `Call`, `Deneme` | | `visit_date` | string | Görüşme günü (YYYY-MM-DD, varsayılan bugün). | | `contact` | string | Görüşülen kişi (yoksa eklenir). | | `contact_title / contact_phone / contact_email` | string | Yeni kişinin bilgileri. | | `topic` | string | Konu. | | `note` | string | Görüşme notu. | | `result` | string | Sonuç. | | `next_action` | string | Sonraki aksiyon. | | `action_date` | string | Sonraki aksiyon tarihi. | | `opp_id` | integer | Kaydın bağlanacağı proje. | | `product / competitor / offer_price / payment_days / potential_kg` | mixed | Proje bilgileri (web formundaki alanlar). | | `confirm_new` | boolean | Benzer adlar olsa da yeni firma aç. | ## Yanıt ```json 200 { "advice": null, "closed": [], "created": false, "customer": { "active": true, "at_risk": false, "city": "Bursa", "currency": "EUR", "currency_default": "", "id": 31, "initials": "KP", "last_visit": "2026-10-02", "main_contact": { "email": "emre.yildiz@kuzeyplastik.com.tr", "name": "Emre Yıldız", "phone": "+90 532 555 11 22", "wa": "905325551122" }, "name": "Kuzey Plastik Sanayi", "next_visit_due": "2026-11-01", "open_opps": 1, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "pending_state": "soon", "period": 30, "phone": "+90 224 555 01 02", "potential_kg": null, "rid": "001QJi5Zr1Hk2VH", "sector": "Plastik", "status": "AC", "status_label": "Aktif Müşteri", "unit": "kg", "visit_overdue_days": -30, "visit_state": "ok" }, "message": "Kuzey Plastik Sanayi kaydedildi: ziyaret, proje #53 Streç film tedariki.", "next_step": "F2F ziyaret planla ve gerçekleştir", "ok": true, "opp": { "bant": 4, "base_currency": "EUR", "close_date": "2026-11-16", "cur_sym": "€", "currency": "EUR", "customer": "Kuzey Plastik Sanayi", "customer_city": "Bursa", "customer_id": 31, "days_in_stage": 0, "detail_status": "", "fc": "Best Case", "health": { "grade": "success", "label": "sağlıklı", "score": 95 }, "health_color": "g", "health_manual": false, "health_note": "", "id": 53, "initials": "KP", "is_account": false, "is_open": true, "last_update": "2026-10-02", "lifetime_left": 180, "line": "", "name": "Streç film tedariki", "next_step": "F2F ziyaret planla ve gerçekleştir", "offer_price": 2.35, "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "payment_days": 60, "potential_kg": 12000.0, "prob": 0.25, "probability": null, "product": "", "product_id": null, "pu": "€/kg", "rid": "006BMHmVNZ9nFLS", "sla_days": 60, "sla_left": 60, "source": "", "stage": "Viable", "stage_label": "Viable", "start_date": "2026-10-02", "trend": "", "trend_label": "Otomatik", "type": "", "unit": "kg", "unit_code": "kg", "value": 338400.0, "value_base": 338400.0, "value_short": "338K" }, "opp_new": false, "parts": [ "ziyaret", "proje #53 Streç film tedariki" ], "visit": { "action_date": "2026-10-05", "action_done": false, "action_state": "soon", "contact": "Emre Yıldız", "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "date": "2026-10-02", "id": 106, "line": "", "next_action": "Teklif gönder", "note": "Deneme sonuçlarını konuştuk; kalınlık 23 mikron uygun.", "opp": "Streç film tedariki", "opp_id": 53, "product": "", "result": "Olumlu", "rid": "00UEIXukSoU4vCq", "topic": "Görüşme", "type": "Call", "user": "Deniz Aksoy" }, "warnings": [] } ``` --- # Aksiyonları listele `GET /api/v1/actions` Açık işler tek listede: görevler, ziyaret ve denemelerin sonraki aksiyonları, tarihli proje adımları. Her satırdaki `kind` + `id` ile tamamlama / erteleme uçları çağrılır. ## Sorgu parametreleri | Alan | Tür | Açıklama | |---|---|---| | `scope` | string | `mine` = sorumlusu olduğum kayıtlar (satışçının varsayılanı), `all` = görebildiğim tüm kayıtlar (yöneticinin varsayılanı) ya da bir kullanıcı kimliği. | | `state` | string | Termin durumu. Değerler: `today`, `overdue`, `soon`, `open`, `week` | | `customer_id` | integer | Yalnız bu firma. | ## Yanıt ```json 200 { "counts": { "overdue": 9, "soon": 0, "today": 0 }, "ok": true, "rows": [ { "customer": "Lotus Medikal Ambalaj San. Tic. Ltd. Şti.", "customer_id": 3, "date": "2026-09-26", "icon": "📋", "id": 1, "key": "task:1", "kind": "task", "label": "Numune sonucu için ara", "opp": "Yeni Müşteri · 2026", "opp_id": 33, "src": "Görev", "state": "overdue", "url": "/action/task/1" }, { "customer": "Hanımeli Etiket San. Tic. Ltd. Şti.", "customer_id": 23, "date": "2026-09-26", "icon": "📋", "id": 17, "key": "task:17", "kind": "task", "label": "Fiyat listesini güncelleyip gönder", "opp": "Mevcut Müşteri — Yenileme · 2026", "opp_id": 23, "src": "Görev", "state": "overdue", "url": "/action/task/17" } ], "total": 9 } ``` --- # Aksiyonu getir `GET /api/v1/actions/{kind}/{rid}` Aksiyonun ayrıntısı ve bağlamı: firma, proje, önceki / sonraki zincir (`chain_before`, `chain_after`), firmanın diğer açık işleri ve e-posta yazışmaları. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `kind` (zorunlu) | string | Aksiyon türü: `task` görev, `visit` ziyaret aksiyonu, `demo` deneme aksiyonu, `step` proje adımı. Değerler: `task`, `visit`, `demo`, `step` | | `rid` (zorunlu) | integer | Aksiyonun kimliği (listede `id`). | ## Yanıt ```json 200 { "action": { "act": { "addr": "Bursa", "c": 31, "mail": "emre.yildiz@kuzeyplastik.com.tr", "n": "Kuzey Plastik Sanayi", "tel": "+90 532 555 11 22", "url": "/customers/31", "wa": "905325551122", "who": "Emre Yıldız" }, "chain_after": [], "chain_before": [], "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "date": "2026-10-04", "detail": "2027 listesi, €/kg", "done": false, "done_at": null, "editable": true, "icon": "📋", "id": 23, "key": "task:23", "kind": "task", "label": "Fiyat listesini gönder", "opp": "", "opp_id": null, "others": [ { "customer": "", "customer_id": null, "date": "2026-10-05", "icon": "⚡", "id": 106, "key": "visit:106", "kind": "visit", "label": "Teklif gönder", "opp": "Streç film tedariki", "opp_id": 53, "src": "Ziyaret", "state": "soon", "url": "/action/visit/106" } ], "result": "", "rid": "00TxuMVTniYx44l", "src": "Görev", "state": "soon", "threads": [], "threads_n": 0, "user": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" } }, "ok": true } ``` --- # Aksiyonu tamamla `POST /api/v1/actions/{kind}/{rid}/done` Aksiyonu tamamlandı (ya da `done: false` ile açık) yapar. `done_on` tamamlanma gününü belirler (varsayılan şimdi). ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `kind` (zorunlu) | string | Aksiyon türü: `task` görev, `visit` ziyaret aksiyonu, `demo` deneme aksiyonu, `step` proje adımı. Değerler: `task`, `visit`, `demo`, `step` | | `rid` (zorunlu) | integer | Aksiyonun kimliği (listede `id`). | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `done` | boolean | Tamamlandı; yoksa durum tersine döner. | | `done_on` | string | Tamamlanma günü (YYYY-MM-DD). | | `result` | string | Sonuç notu. | ## Yanıt ```json 200 { "action": { "act": { "addr": "Bursa", "c": 31, "mail": "emre.yildiz@kuzeyplastik.com.tr", "n": "Kuzey Plastik Sanayi", "tel": "+90 532 555 11 22", "url": "/customers/31", "wa": "905325551122", "who": "Emre Yıldız" }, "chain_after": [], "chain_before": [], "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "date": "2026-10-05", "detail": "2027 listesi, €/kg\n[Ertelendi 04.10.2026 → 05.10.2026] Liste onayı bekleniyor", "done": true, "done_at": "2026-10-02T20:07:01", "editable": true, "icon": "📋", "id": 23, "key": "task:23", "kind": "task", "label": "Fiyat listesini gönder", "opp": "", "opp_id": null, "others": [ { "customer": "", "customer_id": null, "date": "2026-10-05", "icon": "⚡", "id": 106, "key": "visit:106", "kind": "visit", "label": "Teklif gönder", "opp": "Streç film tedariki", "opp_id": 53, "src": "Ziyaret", "state": "soon", "url": "/action/visit/106" } ], "result": "Liste e-postayla gönderildi", "rid": "00TxuMVTniYx44l", "src": "Görev", "state": "done", "threads": [], "threads_n": 0, "user": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" } }, "ok": true } ``` --- # Aksiyonu ertele `POST /api/v1/actions/{kind}/{rid}/postpone` Termini ileri alır; not, kaydın açıklamasına tarihli satır olarak eklenir. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `kind` (zorunlu) | string | Aksiyon türü: `task` görev, `visit` ziyaret aksiyonu, `demo` deneme aksiyonu, `step` proje adımı. Değerler: `task`, `visit`, `demo`, `step` | | `rid` (zorunlu) | integer | Aksiyonun kimliği (listede `id`). | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `date` | string | Yeni tarih (YYYY-MM-DD). | | `days` | integer | Gün sayısı (date yoksa). | | `note` | string | Erteleme notu. | ## Yanıt ```json 200 { "action": { "act": { "addr": "Bursa", "c": 31, "mail": "emre.yildiz@kuzeyplastik.com.tr", "n": "Kuzey Plastik Sanayi", "tel": "+90 532 555 11 22", "url": "/customers/31", "wa": "905325551122", "who": "Emre Yıldız" }, "chain_after": [], "chain_before": [], "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "date": "2026-10-05", "detail": "2027 listesi, €/kg\n[Ertelendi 04.10.2026 → 05.10.2026] Liste onayı bekleniyor", "done": false, "done_at": null, "editable": true, "icon": "📋", "id": 23, "key": "task:23", "kind": "task", "label": "Fiyat listesini gönder", "opp": "", "opp_id": null, "others": [ { "customer": "", "customer_id": null, "date": "2026-10-05", "icon": "⚡", "id": 106, "key": "visit:106", "kind": "visit", "label": "Teklif gönder", "opp": "Streç film tedariki", "opp_id": 53, "src": "Ziyaret", "state": "soon", "url": "/action/visit/106" } ], "result": "", "rid": "00TxuMVTniYx44l", "src": "Görev", "state": "soon", "threads": [], "threads_n": 0, "user": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" } }, "ok": true, "until": "2026-10-05" } ``` --- # Kapat ve sonrakini aç `POST /api/v1/actions/{kind}/{rid}/close` Aksiyonu sonucuyla kapatır ve isteğe bağlı olarak zincirdeki sonraki görevi açar (aynı firma ve projeye bağlı). `mode: keep` aksiyonu açık bırakıp yalnız sonraki görevi ekler. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `kind` (zorunlu) | string | Aksiyon türü: `task` görev, `visit` ziyaret aksiyonu, `demo` deneme aksiyonu, `step` proje adımı. Değerler: `task`, `visit`, `demo`, `step` | | `rid` (zorunlu) | integer | Aksiyonun kimliği (listede `id`). | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `result` | string | Sonuç. | | `next_title` | string | Sonraki görevin başlığı. | | `next_date` | string | Sonraki görevin termini. | | `next_assigned` | integer | Sonraki görevin sahibi. | | `next_priority` | string | Öncelik. Değerler: `Düşük`, `Normal`, `Yüksek`, `Acil` | | `mode` | string | `close` kapat, `keep` açık bırak. Değerler: `close`, `keep` | ## Yanıt ```json 200 { "action": { "act": { "addr": "Ankara Organize Sanayi Bölgesi, 8. Cadde No: 35, Ankara, Ankara", "c": 3, "mail": "cem.polat@lotusmedikal.example", "n": "Lotus Medikal Ambalaj San. Tic. Ltd. Şti.", "tel": "0 (312) 000 46 49", "url": "/customers/3", "wa": "903120004649", "who": "Cem Polat" }, "chain_after": [ [ { "date": "2026-10-04", "done": false, "id": 24, "key": "task:24", "kind": "task", "label": "Teklif hazırla", "src": "Görev", "state": "soon" } ] ], "chain_before": [], "customer": "Lotus Medikal Ambalaj San. Tic. Ltd. Şti.", "customer_id": 3, "date": "2026-09-26", "detail": "", "done": true, "done_at": "2026-10-02T20:07:01", "editable": true, "icon": "📋", "id": 1, "key": "task:1", "kind": "task", "label": "Numune sonucu için ara", "opp": "Yeni Müşteri · 2026", "opp_id": 33, "others": [ { "customer": "", "customer_id": null, "date": "2026-10-03", "icon": "📋", "id": 19, "key": "task:19", "kind": "task", "label": "Kargo takip numarasını ilet", "opp": "Yeni Müşteri · 2026", "opp_id": 33, "src": "Görev", "state": "soon", "url": "/action/task/19" } ], "result": "Müşteri aradı, teklif istendi", "rid": "00Tc4zPI4VpXeWp", "src": "Görev", "state": "done", "threads": [], "threads_n": 0, "user": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" } }, "next_task": { "assignee": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "completed_at": null, "customer": "Lotus Medikal Ambalaj San. Tic. Ltd. Şti.", "customer_id": 3, "detail": "↳ Görev #1 (Numune sonucu için ara) sonrası: Müşteri aradı, teklif istendi", "due_date": "2026-10-04", "due_time": "", "id": 24, "opp": "Yeni Müşteri · 2026", "opp_id": 33, "priority": "Normal", "result": "", "rid": "00TfYtMol2EnOoG", "source_key": "task:1", "state": "soon", "status": "Açık", "title": "Teklif hazırla" }, "ok": true } ``` --- # Görev oluştur `POST /api/v1/tasks` Görev açar; `assigned_to` yoksa isteği yapana atanır. Firma bağlamak isteğe bağlıdır (kimlik ya da adla); kendiliğinden proje açılmaz — `opp_id` ile mevcut projeye, `opp_name` ile yeni projeye bağlanır. ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `title` (zorunlu) | string | Görev başlığı. | | `due_date` | string | Termin (YYYY-MM-DD). | | `due_time` | string | Saat (HH:MM). | | `priority` | string | Öncelik. Değerler: `Düşük`, `Normal`, `Yüksek`, `Acil` | | `detail` | string | Ayrıntı. | | `assigned_to` | integer | Atanan kullanıcı. | | `customer_id` | integer | Firma kimliği. | | `customer` | string | Firma adı (kimlik bilinmiyorsa). | | `opp_id` | string | Proje kimliği ya da `__none__` (projesiz). | | `opp_name` | string | Yeni proje adı. | ## Yanıt ```json 200 { "action": { "act": { "addr": "Bursa", "c": 31, "mail": "emre.yildiz@kuzeyplastik.com.tr", "n": "Kuzey Plastik Sanayi", "tel": "+90 532 555 11 22", "url": "/customers/31", "wa": "905325551122", "who": "Emre Yıldız" }, "chain_after": [], "chain_before": [], "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "date": "2026-10-04", "detail": "2027 listesi, €/kg", "done": false, "done_at": null, "editable": true, "icon": "📋", "id": 23, "key": "task:23", "kind": "task", "label": "Fiyat listesini gönder", "opp": "", "opp_id": null, "others": [ { "customer": "", "customer_id": null, "date": "2026-10-05", "icon": "⚡", "id": 106, "key": "visit:106", "kind": "visit", "label": "Teklif gönder", "opp": "Streç film tedariki", "opp_id": 53, "src": "Ziyaret", "state": "soon", "url": "/action/visit/106" } ], "result": "", "rid": "00TxuMVTniYx44l", "src": "Görev", "state": "soon", "threads": [], "threads_n": 0, "user": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" } }, "ok": true, "task": { "assignee": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "completed_at": null, "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "detail": "2027 listesi, €/kg", "due_date": "2026-10-04", "due_time": "10:30", "id": 23, "opp": "", "opp_id": null, "priority": "Yüksek", "result": "", "rid": "00TxuMVTniYx44l", "source_key": "", "state": "soon", "status": "Açık", "title": "Fiyat listesini gönder" } } ``` --- # Görevi güncelle `PATCH /api/v1/tasks/{id}` Yalnız gönderilen alanlar değişir. `status: Tamamlandı` görevi kapatır (`done_on` ile gün seçilir). `PUT` ve `POST` da kabul edilir. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Görev kimliği. | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `title` | string | Başlık. | | `detail` | string | Ayrıntı. | | `due_date` | string | Termin. | | `due_time` | string | Saat (HH:MM). | | `priority` | string | Öncelik. Değerler: `Düşük`, `Normal`, `Yüksek`, `Acil` | | `status` | string | Durum. Değerler: `Açık`, `Devam Ediyor`, `Tamamlandı` | | `done_on` | string | Tamamlanma günü. | | `assigned_to` | integer | Atanan. | ## Yanıt ```json 200 { "ok": true, "task": { "assignee": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "completed_at": null, "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "detail": "2027 listesi, €/kg", "due_date": "2026-10-04", "due_time": "09:00", "id": 23, "opp": "", "opp_id": null, "priority": "Acil", "result": "", "rid": "00TxuMVTniYx44l", "source_key": "", "state": "soon", "status": "Açık", "title": "Fiyat listesini gönder" } } ``` --- # Takvimi listele `GET /api/v1/events` Tarih aralığındaki toplantılar (`events`) ve aksiyonlardan türeyen girişler (`derived`: termini gelen aksiyonlar, ziyaret günleri). İç toplantılar yalnız sahibine görünür. `mode` toplantının çevrim içi mi yüz yüze mi olduğunu, `join_url` katılım bağlantısını verir. ## Sorgu parametreleri | Alan | Tür | Açıklama | |---|---|---| | `from` | string | Başlangıç (YYYY-MM-DD, varsayılan bugün). | | `to` | string | Bitiş (varsayılan +30 gün). | | `done` | string | `1` = tamamlanan aksiyonlar da gelsin. | ## Yanıt ```json 200 { "derived": [ { "date": "2026-10-05", "done": false, "firm": "Kuzey Plastik Sanayi", "icon": "⚡", "kind": "visit", "location": "", "note": "Deneme sonuçlarını konuştuk; kalınlık 23 mikron uygun.", "state": "soon", "title": "⚡ Kuzey Plastik Sanayi · Teklif gönder", "uid": "visit-106@ornek", "url": "/action/visit/106", "what": "Teklif gönder" } ], "events": [], "from": "2026-10-02", "ok": true, "to": "2026-10-16" } ``` --- # Etkinlik oluştur `POST /api/v1/events` Takvime toplantı ekler; CalDAV açıksa telefon takvimine de gider. ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `title` (zorunlu) | string | Başlık. | | `start` (zorunlu) | string | Başlangıç (YYYY-MM-DDTHH:MM). | | `end` | string | Bitiş. | | `all_day` | boolean | Tüm gün. | | `customer_id` | integer | Firma. | | `customer` | string | Firma adı. | | `location` | string | Yer. | | `note` | string | Not. | ## Yanıt ```json 200 { "event": { "all_day": false, "customer": "Kuzey Plastik Sanayi", "customer_id": 31, "editable": true, "end": "2026-10-07T11:30:00", "id": 1, "join_url": "", "location": "Bursa OSB", "mode": "onsite", "note": "", "source": "manual", "start": "2026-10-07T10:00:00", "title": "Kuzey Plastik · hat denemesi" }, "ok": true } ``` --- # Etkinliği sil `DELETE /api/v1/events/{id}` Uygulamada eklenmiş bir etkinliği siler (e-postadan ya da CalDAV'dan gelenler silinmez). `POST` da kabul edilir. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Etkinlik kimliği. | ## Yanıt ```json 200 { "ok": true } ``` --- # Fuarları listele `GET /api/v1/fairs` Fuarlar (aktif olanlar önce) ve lead sayıları. ## Yanıt ```json 200 { "ok": true, "rows": [ { "active": true, "city": "İstanbul", "date_label": "25.08.2026 – 28.08.2026", "end_date": "2026-08-28", "id": 1, "is_current": false, "lead_count": 10, "name": "Ambalaj Fuarı 2026 (örnek)", "start_date": "2026-08-25", "venue": "Fuar merkezi · Salon 4, Stand B12" } ] } ``` --- # Fuar leadlerini listele `GET /api/v1/fairs/{id}/leads` Fuarda toplanan leadler (en çok 300, yeniden eskiye). ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Fuar kimliği. | ## Sorgu parametreleri | Alan | Tür | Açıklama | |---|---|---| | `owner` | string | `me` (satışçının varsayılanı), boş = herkes, ya da kullanıcı kimliği. | | `interest` | string | İlgi düzeyi. Değerler: `Sıcak`, `Ilık`, `Soğuk` | | `q` | string | Firma, kişi ya da telefon içerir. | ## Yanıt ```json 200 { "fair": { "id": 1, "name": "Ambalaj Fuarı 2026 (örnek)" }, "ok": true, "rows": [ { "can_edit": true, "city": "Bursa", "company": "Işıltı Ambalaj San. Tic. Ltd. Şti.", "contact_name": "Mehmet Arslan", "created_at": "2026-08-28T14:10:00", "customer_id": null, "email": "mehmet@isiltiambalajsanti.example", "fair": "Ambalaj Fuarı 2026 (örnek)", "fair_id": 1, "id": 4, "initials": "IA", "interest": "Soğuk", "line": "", "next_action": "Numune gönder", "next_date": "2026-10-07", "note": "Stantta görüşüldü.", "opp_id": null, "opp_type": "Mevcut Müşteri — Değişim", "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "phone": "0 (224) 000 96 45", "potential_kg": 6000.0, "products": "4", "products_text": "", "project_note": "", "rid": "00QCwrpUC4bpt3E", "sector": "Gıda ambalajı", "status": "Teklif verildi", "status_eff": "Teklif verildi", "supplier": "Mevcut tedarikçi (Asya)", "task_id": null, "timing": "6 ay içinde", "title": "Kalite Kontrol Müdürü", "unit": "kg", "visitor_type": "Potansiyel müşteri", "wa": "902240009645", "website": "" }, { "can_edit": true, "city": "Tekirdağ", "company": "Nehirli Ambalaj San. Tic. Ltd. Şti.", "contact_name": "İpek Karaca", "created_at": "2026-08-28T12:10:00", "customer_id": null, "email": "ipek@nehirliambalajsant.example", "fair": "Ambalaj Fuarı 2026 (örnek)", "fair_id": 1, "id": 8, "initials": "NA", "interest": "Soğuk", "line": "", "next_action": "Teklif gönder", "next_date": "2026-10-13", "note": "Stantta görüşüldü.", "opp_id": null, "opp_type": "Mevcut Müşteri — Değişim", "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "phone": "0 (282) 000 76 98", "potential_kg": 2000.0, "products": "3", "products_text": "", "project_note": "", "rid": "00QBIhQqejYyYc3", "sector": "Film üretimi", "status": "Teklif verildi", "status_eff": "Teklif verildi", "supplier": "Mevcut tedarikçi (Asya)", "task_id": null, "timing": "Hemen", "title": "Satın Alma Müdürü", "unit": "kg", "visitor_type": "Potansiyel müşteri", "wa": "902820007698", "website": "" } ], "total": 10 } ``` --- # Lead ekle `POST /api/v1/fairs/{id}/leads` Fuar standında lead kaydeder; sonraki aksiyon verilirse sorumluya görev açılır. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Fuar kimliği. | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `company` (zorunlu) | string | Firma adı. | | `contact_name / title / phone / email` | string | Kişi bilgileri. | | `city / sector / website` | string | Firma bilgileri. | | `visitor_type` | string | Ziyaretçi türü. Değerler: `Potansiyel müşteri`, `Mevcut müşteri`, `Tedarikçi`, `Rakip`, `Diğer` | | `interest` | string | İlgi. Değerler: `Sıcak`, `Ilık`, `Soğuk` | | `products` | array | İlgilendiği ürünler. | | `products_text` | string | Ürünler (serbest metin). | | `potential_kg` | number | Aylık potansiyel. | | `timing` | string | Zamanlama. Değerler: `Hemen`, `3 ay içinde`, `6 ay içinde`, `1 yıl+`, `Belirsiz` | | `next_action` | string | Sonraki aksiyon. Değerler: `Teklif gönder`, `Ziyaret planla`, `Numune gönder`, `Deneme planla`, `Bilgi gönder`, `Ara`, `Aksiyon yok` | | `next_date` | string | Aksiyon tarihi. | | `owner_id` | integer | Sorumlu. | | `note / project_note / supplier / line / opp_type` | string | Diğer alanlar. | ## Yanıt ```json 200 { "lead": { "can_edit": true, "city": "Konya", "company": "Anadolu Gıda Ambalaj", "contact_name": "Murat Er", "created_at": "2026-10-02T20:07:01", "customer_id": null, "email": "", "fair": "Ambalaj Fuarı 2026 (örnek)", "fair_id": 1, "id": 11, "initials": "AG", "interest": "Sıcak", "line": "", "next_action": "Numune gönder", "next_date": "2026-10-07", "note": "", "opp_id": null, "opp_type": "", "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "phone": "+90 533 555 33 44", "potential_kg": null, "products": "", "products_text": "", "project_note": "", "rid": "00QpWFogpnPsXu7", "sector": "", "status": "Yeni", "status_eff": "Yeni", "supplier": "", "task_id": 25, "timing": "", "title": "Genel Müdür", "unit": "kg", "visitor_type": "Potansiyel müşteri", "wa": "905335553344", "website": "" }, "ok": true } ``` --- # Leadi getir `GET /api/v1/leads/{id}` Lead ve ürün listesi (form seçenekleri için). ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Lead kimliği. | ## Yanıt ```json 200 { "lead": { "can_edit": true, "city": "Konya", "company": "Anadolu Gıda Ambalaj", "contact_name": "Murat Er", "created_at": "2026-10-02T20:07:01", "customer_id": null, "email": "", "fair": "Ambalaj Fuarı 2026 (örnek)", "fair_id": 1, "id": 11, "initials": "AG", "interest": "Sıcak", "line": "", "next_action": "Numune gönder", "next_date": "2026-10-07", "note": "", "opp_id": null, "opp_type": "", "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "phone": "+90 533 555 33 44", "potential_kg": null, "products": "", "products_text": "", "project_note": "", "rid": "00QpWFogpnPsXu7", "sector": "", "status": "Yeni", "status_eff": "Yeni", "supplier": "", "task_id": 25, "timing": "", "title": "Genel Müdür", "unit": "kg", "visitor_type": "Potansiyel müşteri", "wa": "905335553344", "website": "" }, "ok": true, "products": [ { "id": 5, "name": "HS 30 Isıl Yapışma Laki" }, { "id": 3, "name": "PU-SB 450 Solvent Bazlı Yapıştırıcı" } ] } ``` --- # Leadi güncelle `PATCH /api/v1/leads/{id}` Durumu değiştirir; `company` gönderilirse tüm form alanları yeniden yazılır (gönderilmeyenler boşalır). ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Lead kimliği. | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `status` | string | Durum. Değerler: `Yeni`, `İletişime geçildi`, `Teklif verildi`, `Aktarıldı`, `Vazgeçildi` | | `company …` | string | Lead ekle ucundaki alanlar. | ## Yanıt ```json 200 { "lead": { "can_edit": true, "city": "Konya", "company": "Anadolu Gıda Ambalaj", "contact_name": "Murat Er", "created_at": "2026-10-02T20:07:01", "customer_id": null, "email": "", "fair": "Ambalaj Fuarı 2026 (örnek)", "fair_id": 1, "id": 11, "initials": "AG", "interest": "Sıcak", "line": "", "next_action": "Numune gönder", "next_date": "2026-10-07", "note": "", "opp_id": null, "opp_type": "", "owner": { "full_name": "Deniz Aksoy", "id": 1, "role": "admin", "role_label": "Yönetici", "username": "admin" }, "phone": "+90 533 555 33 44", "potential_kg": null, "products": "", "products_text": "", "project_note": "", "rid": "00QpWFogpnPsXu7", "sector": "", "status": "İletişime geçildi", "status_eff": "İletişime geçildi", "supplier": "", "task_id": 25, "timing": "", "title": "Genel Müdür", "unit": "kg", "visitor_type": "Potansiyel müşteri", "wa": "905335553344", "website": "" }, "ok": true } ``` --- # Yazışmaları listele `GET /api/v1/threads` E-posta yazışmaları: bir kaydınkiler (`kind` + `id`) ya da gelen kutusu (`filter`, `q`). İçerik kullanıcının görme iznine göre gelir. ## Sorgu parametreleri | Alan | Tür | Açıklama | |---|---|---| | `kind` | string | Kayıt türü. Değerler: `customer`, `opp`, `visit`, `demo`, `task`, `offer`, `ticket`, `contract` | | `id` | integer | Kayıt kimliği. | | `filter` | string | `waiting` yanıt bekleyen, `replied` yanıtlanan, `mine` benim. Değerler: `all`, `waiting`, `replied`, `mine` | | `q` | string | Konu ya da adres içerir. | ## Yanıt ```json 200 { "items": [ { "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_id": 1, "id": 1, "last_at": "2026-10-01T15:06:59", "last_dir": "out", "n": 2, "opp": "Mevcut Müşteri — Yenileme · 2026", "opp_how": "single", "opp_id": 31, "opp_note": "firmanın tek açık projesi", "peer": "selin.dogan@alizepaketleme.example", "subject": "Numune ve fiyat teklifi", "suggested": false, "waiting": false } ], "mailbox": { "connected": true, "email": "deniz@ornekkimya.com.tr", "error": "", "signature": false, "status": "ok" }, "ok": true, "total": 1 } ``` --- # Yazışmayı getir `GET /api/v1/threads/{id}` İletiler (görme izni yoksa `body: null`, `hidden: true`), bağlı kayıtlar, firmalar ve kişiler, yanıt taslağı (`reply`) ve bağlama önerileri. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Yazışma kimliği. | ## Yanıt ```json 200 { "mailbox": { "connected": true, "email": "deniz@ornekkimya.com.tr", "error": "", "signature": false, "status": "ok" }, "ok": true, "thread": { "companies": [ { "contacts": [ { "email": "selin.dogan@alizepaketleme.example", "id": 1, "name": "Selin Doğan" } ], "id": 1, "name": "Alize Paketleme San. ve Tic. A.Ş.", "role": "main" } ], "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_id": 1, "id": 1, "last_at": "2026-10-01T15:06:59", "last_dir": "out", "linkables": [ { "items": [ { "id": 74, "label": "30.09.2026 · Call · Tanışma" } ], "kind": "visit", "klabel": "Ziyaret / görüşme" } ], "links": [], "may_edit": true, "messages": [ { "at": "2026-09-30T17:06:59", "body": "Merhaba Deniz Bey,\n\nNumuneler bize ulaştı, hat denemesini perşembe yapacağız. Aylık 12 ton için güncel fiyatınızı paylaşabilir misiniz?\n\nİyi çalışmalar", "cc": "", "dir": "in", "from_addr": "selin.dogan@alizepaketleme.example", "from_name": "Selin Doğan", "hidden": false, "id": 1, "subject": "Numune ve fiyat teklifi", "to": "deniz@ornekkimya.com.tr" } ], "n": 2, "opp": "Mevcut Müşteri — Yenileme · 2026", "opp_how": "single", "opp_id": 31, "opp_note": "firmanın tek açık projesi", "opps": [ { "id": 44, "name": "Müşteri Takibi", "open": true, "stage_label": "Müşteri Takibi" } ], "peer": "selin.dogan@alizepaketleme.example", "reply": { "contact_id": 1, "customer_id": 1, "in_reply_to": "", "opp_id": 31, "subject": "Re: Numune ve fiyat teklifi", "to": "selin.dogan@alizepaketleme.example" }, "subject": "Numune ve fiyat teklifi", "suggested": false, "suggestions": [ { "date": "2026-09-30", "how": "", "id": 74, "kind": "visit", "klabel": "Ziyaret / görüşme", "label": "Call 30.09.2026", "nav": { "id": 74, "kind": "visit", "screen": "action" }, "url": "/action/visit/74", "why": "yazışmayla aynı günlerde görüşme" } ], "waiting": false } } ``` --- # Yazışmayı kayda bağla `POST /api/v1/threads/{id}/{action}` `opp` projeyi değiştirir (`opp_id`), `link` bir kayda bağlar (`kind`, `obj_id`), `unlink` bağı kaldırır, `dismiss` öneriyi yok sayar. Yalnız yazışmanın sahibi, firmanın sorumlusu ya da yönetici değiştirebilir. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | integer | Yazışma kimliği. | | `action` (zorunlu) | string | Eylem. Değerler: `opp`, `link`, `unlink`, `dismiss` | ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `opp_id` | integer | Proje (`opp`). | | `kind` | string | Kayıt türü (`link`/`unlink`). | | `obj_id` | integer | Kayıt kimliği. | ## Yanıt ```json 200 { "msg": "Yazışma projesi: Mevcut Müşteri — Yenileme · 2026.", "ok": true, "thread": { "companies": [ { "contacts": [ { "email": "selin.dogan@alizepaketleme.example", "id": 1, "name": "Selin Doğan" } ], "id": 1, "name": "Alize Paketleme San. ve Tic. A.Ş.", "role": "main" } ], "customer": "Alize Paketleme San. ve Tic. A.Ş.", "customer_id": 1, "id": 1, "last_at": "2026-10-01T15:06:59", "last_dir": "out", "linkables": [ { "items": [ { "id": 74, "label": "30.09.2026 · Call · Tanışma" } ], "kind": "visit", "klabel": "Ziyaret / görüşme" } ], "links": [], "may_edit": true, "messages": [ { "at": "2026-09-30T17:06:59", "body": "Merhaba Deniz Bey,\n\nNumuneler bize ulaştı, hat denemesini perşembe yapacağız. Aylık 12 ton için güncel fiyatınızı paylaşabilir misiniz?\n\nİyi çalışmalar", "cc": "", "dir": "in", "from_addr": "selin.dogan@alizepaketleme.example", "from_name": "Selin Doğan", "hidden": false, "id": 1, "subject": "Numune ve fiyat teklifi", "to": "deniz@ornekkimya.com.tr" } ], "n": 2, "opp": "Mevcut Müşteri — Yenileme · 2026", "opp_how": "manual", "opp_id": 31, "opp_note": "elle seçildi", "opps": [ { "id": 44, "name": "Müşteri Takibi", "open": true, "stage_label": "Müşteri Takibi" } ], "peer": "selin.dogan@alizepaketleme.example", "reply": { "contact_id": 1, "customer_id": 1, "in_reply_to": "", "opp_id": 31, "subject": "Re: Numune ve fiyat teklifi", "to": "selin.dogan@alizepaketleme.example" }, "subject": "Numune ve fiyat teklifi", "suggested": false, "suggestions": [ { "date": "2026-09-30", "how": "", "id": 74, "kind": "visit", "klabel": "Ziyaret / görüşme", "label": "Call 30.09.2026", "nav": { "id": 74, "kind": "visit", "screen": "action" }, "url": "/action/visit/74", "why": "yazışmayla aynı günlerde görüşme" } ], "waiting": false } } ``` --- # E-posta hazırlığı `GET /api/v1/emails/compose` Yeni e-posta için: hesabın bağlı olup olmadığı, firmanın e-postalı kişileri ve açık projeleri. ## Sorgu parametreleri | Alan | Tür | Açıklama | |---|---|---| | `customer_id` | integer | Firma. | | `opp_id` | integer | Proje. | ## Yanıt ```json 200 { "contacts": [ { "email": "selin.dogan@alizepaketleme.example", "id": 1, "main": true, "name": "Selin Doğan", "title": "Satın Alma Müdürü" } ], "customer": { "id": 1, "name": "Alize Paketleme San. ve Tic. A.Ş." }, "mailbox": { "connected": true, "email": "deniz@ornekkimya.com.tr", "error": "", "signature": false, "status": "ok" }, "ok": true, "opp": null, "opps": [ { "id": 44, "name": "Müşteri Takibi", "stage_label": "Müşteri Takibi" }, { "id": 31, "name": "Mevcut Müşteri — Yenileme · 2026", "stage_label": "Qualify" } ] } ``` --- # E-posta gönder `POST /api/v1/emails/send` Kullanıcının bağlı e-posta hesabından tek e-posta (yanıt dahil) gönderir; ileti yazışmaya ve projeye web'deki kurallarla bağlanır. Hesap bağlı değilse `400 send` döner. Yalnız JSON gövde kabul edilir. ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `to` (zorunlu) | string | Alıcı(lar), virgülle. | | `subject` (zorunlu) | string | Konu. | | `body` (zorunlu) | string | Metin (düz yazı). | | `cc` | string | Bilgi. | | `customer_id / contact_id / opp_id` | integer | Bağlanacak kayıtlar. | | `in_reply_to` | string | Yanıtlanan iletinin Message-ID'si (`reply.in_reply_to`). | ## Yanıt ```json 400 { "error": "invalid", "message": "E-posta metni boş.", "ok": false } ``` --- # Soru sor `POST /api/v1/ask` CRM verisine doğal dilde soru: Yapay Zekâ modülü açıksa model yanıtlar (`mode: ai`), değilse kural tabanlı yanıt gelir (`mode: kural`). `blocks` ekrana çizilecek yapıdır, `refs` yanıtta geçen kayıtlar. Yalnız JSON gövde. ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `q` (zorunlu) | string | Soru. | | `history` | array | Önceki sorular / yanıtlar (sohbet bağlamı). | ## Yanıt ```json 200 { "ai": false, "blocks": [ { "muted": false, "spans": [ { "b": true, "text": "Gecikmiş 2 görev" } ], "t": "p" }, { "muted": false, "spans": [ { "b": false, "nav": { "id": 17, "kind": "task", "screen": "action" }, "text": "Fiyat listesini güncelleyip gönder", "url": "/action/task/17" } ], "t": "li" } ], "mode": "kural", "ok": true, "q": "Bu hafta geciken aksiyonlarım neler?", "refs": [ { "kind": "task", "label": "Fiyat listesini güncelleyip gönder", "nav": { "id": 17, "kind": "task", "screen": "action" }, "url": "/action/task/17" }, { "kind": "task", "label": "Yeni hat devreye alma toplantısı", "nav": { "id": 8, "kind": "task", "screen": "action" }, "url": "/action/task/8" } ], "text": "**Gecikmiş 2 görev** (sizin)\n- [Fiyat listesini güncelleyip gönder](/action/task/17) · 26.09.2026 · Hanımeli Etiket San. Tic. Ltd. Şti.\n- [Yeni hat devreye alma toplantısı](/action/task/8) · 30.09.2026 · Işıltı Film A.Ş." } ``` --- # Son sorular `GET /api/v1/ask/history` Kullanıcının son 12 sorusu ve önerilen sorular. ## Yanıt ```json 200 { "ai": false, "items": [ { "at": "2026-10-02T20:07:01", "mode": "kural", "q": "Bu hafta geciken aksiyonlarım neler?" } ], "ok": true, "suggest": [ "Bu ay kapanması beklenen fırsatlar", "Gecikmiş görevlerim" ] } ``` --- # Sağlayıcı yapılandırması `GET /api/scim/v2/ServiceProviderConfig` Desteklenen SCIM özellikleri (PATCH var; toplu işlem, sıralama, ETag yok). ## Yanıt ```json 200 { "schemas": [ "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig" ], "patch": { "supported": true }, "bulk": { "supported": false }, "filter": { "supported": true, "maxResults": 200 }, "changePassword": { "supported": false }, "sort": { "supported": false }, "etag": { "supported": false }, "authenticationSchemes": [ { "type": "oauthbearertoken", "name": "Bearer", "description": "Ayarlar → Uygulamalar → SCIM anahtarı" } ] } ``` --- # Kaynak türleri `GET /api/scim/v2/ResourceTypes` Yalnız `User` kaynağı. ## Yanıt ```json 200 { "schemas": [ "urn:ietf:params:scim:api:messages:2.0:ListResponse" ], "totalResults": 1, "Resources": [ { "schemas": [ "urn:ietf:params:scim:schemas:core:2.0:ResourceType" ], "id": "User", "name": "User", "endpoint": "/Users", "schema": "urn:ietf:params:scim:schemas:core:2.0:User", "schemaExtensions": [ { "schema": "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", "required": false } ] } ] } ``` --- # Şemalar `GET /api/scim/v2/Schemas` Kullanıcı ve kurumsal uzantı şemaları. ## Yanıt ```json 200 { "schemas": [ "urn:ietf:params:scim:api:messages:2.0:ListResponse" ], "totalResults": 1, "Resources": [ { "id": "urn:ietf:params:scim:schemas:core:2.0:User", "name": "User", "attributes": [ { "name": "userName", "type": "string", "required": true, "uniqueness": "server" } ] } ] } ``` --- # Kullanıcıları listele `GET /api/scim/v2/Users` Kullanıcılar; yalnız `userName eq "…"` (ya da `id eq`) süzgeci desteklenir. Sayfalama `startIndex` + `count` (en çok 200). ## Sorgu parametreleri | Alan | Tür | Açıklama | |---|---|---| | `filter` | string | `userName eq "ad@firma.com"` | | `startIndex` | integer | 1'den başlar. | | `count` | integer | Sayfa boyu. | ## Yanıt ```json 200 { "schemas": [ "urn:ietf:params:scim:api:messages:2.0:ListResponse" ], "totalResults": 1, "startIndex": 1, "itemsPerPage": 1, "Resources": [ { "schemas": [ "urn:ietf:params:scim:schemas:core:2.0:User", "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" ], "id": "2", "userName": "ayse.demir@ornekkimya.com.tr", "displayName": "Ayşe Demir", "name": { "formatted": "Ayşe Demir", "givenName": "Ayşe", "familyName": "Demir" }, "active": true, "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { "department": "Satış" }, "meta": { "resourceType": "User", "location": "https://ornek.solk.app/api/scim/v2/Users/2" }, "emails": [ { "value": "ayse.demir@ornekkimya.com.tr", "type": "work", "primary": true } ] } ] } ``` --- # Kullanıcı aç `POST /api/scim/v2/Users` Kullanıcı açar ve şifre belirleme daveti gönderir (rol: satışçı). Aynı e-posta varsa `409 uniqueness`. ## Yanıt ```json 201 { "schemas": [ "urn:ietf:params:scim:schemas:core:2.0:User", "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" ], "id": "2", "userName": "ayse.demir@ornekkimya.com.tr", "displayName": "Ayşe Demir", "name": { "formatted": "Ayşe Demir", "givenName": "Ayşe", "familyName": "Demir" }, "active": true, "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { "department": "Satış" }, "meta": { "resourceType": "User", "location": "https://ornek.solk.app/api/scim/v2/Users/2" }, "emails": [ { "value": "ayse.demir@ornekkimya.com.tr", "type": "work", "primary": true } ] } ``` --- # Kullanıcıyı getir `GET /api/scim/v2/Users/{id}` Tek kullanıcı. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | string | Kullanıcı kimliği. | ## Yanıt ```json 200 { "schemas": [ "urn:ietf:params:scim:schemas:core:2.0:User", "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" ], "id": "2", "userName": "ayse.demir@ornekkimya.com.tr", "displayName": "Ayşe Demir", "name": { "formatted": "Ayşe Demir", "givenName": "Ayşe", "familyName": "Demir" }, "active": true, "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { "department": "Satış" }, "meta": { "resourceType": "User", "location": "https://ornek.solk.app/api/scim/v2/Users/2" }, "emails": [ { "value": "ayse.demir@ornekkimya.com.tr", "type": "work", "primary": true } ] } ``` --- # Kullanıcıyı değiştir `PUT /api/scim/v2/Users/{id}` Ad, e-posta, departman, telefon ve `active` alanlarını yeniden yazar. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | string | Kullanıcı kimliği. | ## Yanıt ```json 200 { "schemas": [ "urn:ietf:params:scim:schemas:core:2.0:User", "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" ], "id": "2", "userName": "ayse.demir@ornekkimya.com.tr", "displayName": "Ayşe Demir Kaya", "name": { "formatted": "Ayşe Demir Kaya", "givenName": "Ayşe Demir", "familyName": "Kaya" }, "active": true, "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { "department": "" }, "meta": { "resourceType": "User", "location": "https://ornek.solk.app/api/scim/v2/Users/2" }, "emails": [ { "value": "ayse.demir@ornekkimya.com.tr", "type": "work", "primary": true } ] } ``` --- # Kullanıcıyı güncelle `PATCH /api/scim/v2/Users/{id}` `replace`, `add`, `remove` işlemleri; `active: false` kullanıcıyı kapatır (mobil oturumları ve API anahtarları düşer, kayıtları kalır; son etkin yönetici kapatılamaz). `active: true` boş koltuk ister. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | string | Kullanıcı kimliği. | ## Yanıt ```json 200 { "schemas": [ "urn:ietf:params:scim:schemas:core:2.0:User", "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" ], "id": "2", "userName": "ayse.demir@ornekkimya.com.tr", "displayName": "Ayşe Demir", "name": { "formatted": "Ayşe Demir", "givenName": "Ayşe", "familyName": "Demir" }, "active": false, "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { "department": "Satış" }, "meta": { "resourceType": "User", "location": "https://ornek.solk.app/api/scim/v2/Users/2" }, "emails": [ { "value": "ayse.demir@ornekkimya.com.tr", "type": "work", "primary": true } ] } ``` --- # Kullanıcıyı kapat `DELETE /api/scim/v2/Users/{id}` Kullanıcıyı siler gibi kapatır; kayıtlar ve geçmiş korunur. `204` döner. ## Yol parametreleri | Alan | Tür | Açıklama | |---|---|---| | `id` (zorunlu) | string | Kullanıcı kimliği. | ## Yanıt ```json 204 HTTP/1.1 204 No Content ``` --- # Gruplar `GET /api/scim/v2/Groups` Grup eşitleme yok; boş liste döner (roller CRM'de atanır). ## Yanıt ```json 200 { "schemas": [ "urn:ietf:params:scim:api:messages:2.0:ListResponse" ], "totalResults": 0, "startIndex": 1, "itemsPerPage": 0, "Resources": [] } ``` --- # Korunan kaynak bilgisi `GET /.well-known/oauth-protected-resource/mcp` RFC 9728 belgesi: MCP kaynağının adresi, yetkilendirme sunucusu ve kapsamlar. İstemciler `401` yanıtındaki `resource_metadata`'dan buraya gelir. ## Yanıt ```json 200 { "authorization_servers": [ "https://ornek.solk.app" ], "bearer_methods_supported": [ "header" ], "resource": "https://ornek.solk.app/mcp", "resource_documentation": "https://docs.solk.app/mcp/overview", "resource_name": "Solk CRM (Örnek Kimya)", "scopes_supported": [ "crm.read", "crm.write" ] } ``` --- # Yetkilendirme sunucusu bilgisi `GET /.well-known/oauth-authorization-server` RFC 8414 belgesi: uç adresleri, desteklenen akışlar (yalnız `authorization_code` + PKCE S256 ve `refresh_token`), istemci kimlik doğrulama yöntemleri. ## Yanıt ```json 200 { "authorization_endpoint": "https://ornek.solk.app/oauth/authorize", "client_id_metadata_document_supported": true, "code_challenge_methods_supported": [ "S256" ], "grant_types_supported": [ "authorization_code", "refresh_token" ], "issuer": "https://ornek.solk.app", "registration_endpoint": "https://ornek.solk.app/oauth/register", "response_modes_supported": [ "query" ], "response_types_supported": [ "code" ], "revocation_endpoint": "https://ornek.solk.app/oauth/revoke", "revocation_endpoint_auth_methods_supported": [ "none", "client_secret_post", "client_secret_basic" ], "scopes_supported": [ "crm.read", "crm.write", "offline_access" ], "service_documentation": "https://ornek.solk.app/settings/connections", "token_endpoint": "https://ornek.solk.app/oauth/token", "token_endpoint_auth_methods_supported": [ "none", "client_secret_post", "client_secret_basic" ] } ``` --- # İstemci kaydı `POST /oauth/register` Dinamik istemci kaydı (RFC 7591). `redirect_uris` https ya da yerel geri döngü (`http://localhost`, `127.0.0.1`, `[::1]`) olmalı; en çok 10. `token_endpoint_auth_method: none` genel (public) istemci, `client_secret_post` / `client_secret_basic` gizli anahtarlı istemci açar. ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `redirect_uris` (zorunlu) | array | Dönüş adresleri. | | `client_name` | string | Onay ekranında görünen ad. | | `client_uri` | string | Uygulamanın sitesi (https). | | `token_endpoint_auth_method` | string | İstemci kimlik doğrulaması. Değerler: `none`, `client_secret_post`, `client_secret_basic` | | `grant_types` | array | `authorization_code`, `refresh_token`. | ## Yanıt ```json 201 { "client_id": "crm_sN-deD6QaASdSwwlE2np8BWe", "client_id_issued_at": 1790960821, "client_name": "Örnek Entegrasyon", "grant_types": [ "authorization_code", "refresh_token" ], "redirect_uris": [ "https://uygulamaniz.com/oauth/callback" ], "response_types": [ "code" ], "token_endpoint_auth_method": "none" } ``` --- # Yetkilendir `GET /oauth/authorize` Kullanıcıyı onay ekranına götürür (oturum yoksa önce giriş). Onaylanınca `redirect_uri?code=…&state=…` adresine döner; reddedilirse `error=access_denied`. PKCE (S256) zorunludur. ## Sorgu parametreleri | Alan | Tür | Açıklama | |---|---|---| | `response_type` (zorunlu) | string | Yalnız `code`. Değerler: `code` | | `client_id` (zorunlu) | string | Kayıtta alınan kimlik ya da CIMD adresi. | | `redirect_uri` (zorunlu) | string | Kayıtlı dönüş adresi. | | `scope` | string | `crm.read crm.write` (ve isteğe bağlı `offline_access`). Boşsa ikisi birden. | | `state` | string | İstemcinin CSRF değeri (aynen döner). | | `code_challenge` (zorunlu) | string | BASE64URL(SHA256(code_verifier)). | | `code_challenge_method` (zorunlu) | string | Yalnız `S256`. Değerler: `S256` | | `resource` | string | MCP kaynağı (`https:///mcp`) — REST için boş bırakın. | ## Yanıt ```json 302 HTTP/1.1 302 Found Location: https://uygulamaniz.com/oauth/callback?code=Zx8…&state=xyz123 ``` --- # Belirteç al `POST /oauth/token` Form gövdesi (`application/x-www-form-urlencoded`). `authorization_code`: kod + `code_verifier` → 1 saatlik erişim belirteci (`mcp_…`) + 60 günlük yenileme belirteci (`mcpr_…`). `refresh_token`: her yenilemede yeni çift verilir, eskisi düşer. Kod yalnız bir kez kullanılır; ikinci kullanım o izinden verilmiş belirteçleri iptal eder. ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `grant_type` (zorunlu) | string | Akış. Değerler: `authorization_code`, `refresh_token` | | `code` | string | Yetki kodu. | | `redirect_uri` | string | Yetkilendirmedeki adres. | | `code_verifier` | string | PKCE doğrulayıcısı. | | `refresh_token` | string | Yenileme belirteci. | | `client_id` (zorunlu) | string | İstemci kimliği. | | `client_secret` | string | Gizli anahtarlı istemcilerde. | ## Yanıt ```json 200 { "access_token": "mcp_uBprY6…", "expires_in": 3600, "refresh_token": "mcpr_AXksm…", "scope": "crm.read crm.write", "token_type": "Bearer" } ``` --- # Belirteci iptal et `POST /oauth/revoke` Erişim ya da yenileme belirtecini iptal eder (RFC 7009); her zaman `200` döner. Kullanıcı izni tümden Ayarlar → Uygulama bağlantıları'ndan kaldırır. ## Gövde | Alan | Tür | Açıklama | |---|---|---| | `token` (zorunlu) | string | Belirteç. | ## Yanıt ```json 200 HTTP/1.1 200 OK ``` --- # Solk MCP > Claude gibi yapay zekâ asistanlarını CRM'inize bağlayın — firmaları ve fırsatları sorun, görev ve görüşme kaydını sohbetten açın. Solk MCP, her kurulumda çalışan bir **Model Context Protocol** sunucusudur. MCP destekleyen bir yapay zekâ asistanı (Claude, Claude Code, ChatGPT geliştirici modu, Cursor, VS Code…) bu sunucuya bağlandığında CRM'inizi doğal dille sorgulayabilir ve sizin onayınızla kayıt açabilir. ```text https://.solk.app/mcp ``` Bağlayıcı adresinizi CRM'de **Ayarlar → Uygulama bağlantıları** sayfası gösterir ve tek tıkla kopyalar. ## Neler yapabilirsiniz? :::cards - [Firma ve kişi bulma](/mcp/prompts#lookup) search | "Alize Paketleme'de satın almadan kim sorumlu, son ne konuştuk?" - [Günlük iş listesi](/mcp/prompts#today) check | "Bugün ve bu hafta geciken aksiyonlarım neler?" - [Satış hattı](/mcp/prompts#pipeline) target | "Negotiation'daki fırsatları değere göre sırala, SLA'sı aşanları işaretle." - [Kayıt açma](/mcp/prompts#write) plus | "Kuzey Plastik'le az önceki telefon görüşmesini kaydet, perşembeye teklif aksiyonu aç." ::: ## Başlarken **Gereksinimler:** Solk kurulumunuzda bir kullanıcı hesabı ve MCP destekleyen bir istemci. Bağlantı sizin hesabınızla çalışır; ek lisans gerekmez. ### Claude (claude.ai, masaüstü ve mobil) 1. Claude'da **Customize → Connectors → + Add → Add custom connector**'ı açın. 2. Ad olarak `Solk` yazın, adres olarak `https://.solk.app/mcp`'yi yapıştırın ve **Add**'e basın. 3. **Connect**'e basın. Solk giriş sayfası açılır (oturumunuz açıksa doğrudan onay ekranı); izinleri onaylayın. Team ve Enterprise planlarında özel bağlayıcıyı kuruluş sahibi bir kez ekler (**Organization settings → Connectors → Add → Custom → Web**); her üye kendi Solk hesabıyla **Connect**'e basar. Ücretsiz planda tek özel bağlayıcı eklenebilir. ### Claude Code ```bash claude mcp add --transport http solk https://ornek.solk.app/mcp ``` Claude Code içinde `/mcp` yazıp **solk** sunucusunu seçin ve tarayıcıda açılan onay ekranında izin verin. ### Diğer istemciler İstemciye uzak MCP sunucusu (Streamable HTTP) olarak adresi girin; istemci OAuth keşfini kendisi yapar. OAuth desteklemeyen ve sabit başlık isteyen araçlarda yönetici **Ayarlar → Geliştiriciler**'den açtığı API anahtarını kullanabilir: ```json { "mcpServers": { "solk": { "url": "https://ornek.solk.app/mcp", "headers": { "Authorization": "Bearer sk_…" } } } } ``` Ayrıntılı adımlar: [Bağlanma](/mcp/connect). ## Araçlar Sunucu 14 okuma ve 9 yazma aracı sunar: arama, firma / kişi / fırsat / aksiyon / takvim / e-posta okuma, pano ve "Bugün"; firma, kişi, fırsat, görev ve not açma, aşama değiştirme, aksiyon tamamlama ve görüşme kaydı. Tam liste ve parametreler: [Araçlar](/mcp/tools). ## Güvenlik - **OAuth ile kimlik** — parolanız istemciye verilmez; onay ekranında istenen izinleri görürsünüz. - **Hesabınızın yetkileri** — asistan yalnız sizin görebildiğiniz kayıtları görür ve yalnız sizin değiştirebildiklerinizi değiştirir; rol, modül izinleri ve kayıt görünürlüğü aynen geçerlidir. - **Okuma ve yazma ayrı** — `crm.read` ve `crm.write` kapsamları ayrı onaylanır; okuma araçları istemciye "salt okunur" olarak bildirilir. - **Geri alınabilir** — **Ayarlar → Uygulama bağlantıları**'nda bağlı uygulamaları, son kullanımı ve işlem sayısını görür, **Erişimi kaldır** ile anında kesersiniz. - **Denetlenebilir** — her araç çağrısı denetim günlüğüne yazılır. Ayrıntı: [Güvenlik](/mcp/security). ## Sınırlar Belirteç başına dakikada 300 istek. Bir araç yanıtı en çok ~90.000 karakter taşır; daha uzun yanıtlar kısaltılır ve sonuna sayfa ya da süzgeç kullanılması gerektiği notu eklenir. --- # Bağlanma > Claude, Claude Code ve diğer MCP istemcilerini Solk kurulumunuza adım adım bağlayın; OAuth keşfinin nasıl çalıştığını görün. ## Claude :::steps ### Bağlayıcıyı ekleyin Claude'da (web, masaüstü ya da mobil) **Customize → Connectors → + Add → Add custom connector**'ı seçin. Ad: `Solk`, adres: `https://.solk.app/mcp`. Gelişmiş ayarlardaki istemci kimliği alanlarını boş bırakın — Claude kendini otomatik kaydeder. ### Bağlanın Bağlayıcının yanındaki **Connect**'e basın. Solk kurulumunuzun giriş sayfası açılır; giriş yaptıktan sonra onay ekranı Claude'un istediği izinleri gösterir: - **Kayıtları okuma** — firma, kişi, fırsat, aksiyon, e-posta yazışması, takvim ve panolar - **Kayıt oluşturma ve güncelleme** — firma, kişi, fırsat, görev, not ve görüşme kaydı; görev tamamlama; aşama değiştirme **İzin ver**'e basınca Claude'a dönersiniz. ### Kullanın Sohbette Solk'u açık tutun ve sorunuzu yazın. Claude bir aracı ilk kez kullanırken izin ister; okuma araçları için "her zaman izin ver" seçebilirsiniz. ::: **Team / Enterprise:** Bağlayıcıyı kuruluş sahibi **Organization settings → Connectors → Add → Custom → Web** ile ekler. Üyeler bağlayıcıyı kendi listelerinde görür ve **Connect** ile kendi Solk hesaplarıyla bağlanır — her üyenin yetkisi kendi Solk rolüyle sınırlıdır. ## Claude Code ```bash claude mcp add --transport http solk https://ornek.solk.app/mcp ``` Ardından Claude Code'da `/mcp` → **solk** → **Authenticate**. Claude Code yerel bir dönüş adresi (`http://localhost:/callback`) kullanır; Solk yerel geri döngü adreslerinde kapı numarasını eşleşmede yok sayar. Projede herkesin kullanması için kapsamı proje yapabilirsiniz (`--scope project`, `.mcp.json`'a yazılır); her geliştirici yine kendi hesabıyla izin verir. ## ChatGPT, Cursor, VS Code ve diğerleri Uzak MCP sunucusu (Streamable HTTP) destekleyen istemcilere aynı adresi girin. OAuth destekleyen istemciler keşfi kendileri yapar. Desteklemeyenlerde yönetici bir [API anahtarı](/rest-api/authentication) açıp `Authorization: Bearer sk_…` başlığıyla verebilir — bu durumda araçlar anahtarı açan yöneticinin yetkisiyle ve her iki kapsamla çalışır. ## OAuth keşfi nasıl işler? İstemciler için ek yapılandırma gerekmez; akış standartlara göredir (MCP Authorization, OAuth 2.1): 1. İstemci belirteçsiz `POST /mcp` gönderir → `401` ve `WWW-Authenticate: Bearer … resource_metadata="https://ornek.solk.app/.well-known/oauth-protected-resource/mcp"`. 2. İstemci [korunan kaynak bilgisini](/rest-api/oauth/protected-resource) okur (RFC 9728) → yetkilendirme sunucusu `https://ornek.solk.app`. 3. [Yetkilendirme sunucusu bilgisini](/rest-api/oauth/authorization-server) okur (RFC 8414). 4. Kendini [kaydeder](/rest-api/oauth/register) (RFC 7591) ya da istemci bilgi belgesi (CIMD) adresini `client_id` olarak kullanır. 5. Kullanıcıyı PKCE (S256) ile [onay ekranına](/rest-api/oauth/authorize) yönlendirir, kodu [belirteçle](/rest-api/oauth/token) değiştirir. 6. `Authorization: Bearer mcp_…` ile MCP isteklerini gönderir; belirteç süresi dolunca yeniler. ## Protokol ayrıntıları | | | |---|---| | Taşıma | Streamable HTTP, JSON yanıt (SSE akışı yok) | | Protokol sürümleri | `2025-11-25`, `2025-06-18`, `2025-03-26`, `2024-11-05` | | Yöntemler | `initialize`, `tools/list`, `tools/call`, `ping` ve bildirimler | | `GET /mcp` | `405` (sunucudan istemciye akış yok) | | Oturum | Durumsuz; `Mcp-Session-Id` gerekmez | | Yetersiz kapsam | Yazma aracı `crm.write` olmadan çağrılırsa `403` + `WWW-Authenticate: … error="insufficient_scope"` | ```bash curl https://ornek.solk.app/mcp \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` --- # Araçlar > Solk MCP sunucusunun okuma ve yazma araçları, parametreleri ve örnek istemleri. Araçlar REST API'nin üzerinde çalışır: her araç çağrısı ilgili API ucunu sizin hesabınızla çağırır ve aynı kurallar (yetki, görünürlük, aşama kapıları, mükerrer koruması) geçerlidir. İstemciye yalnız sizin kullanabildiğiniz araçlar listelenir: - `crm.read` kapsamı yoksa okuma, `crm.write` yoksa yazma araçları listede görünmez. - Lisansta ya da kullanıcınızda kapalı modüllerin araçları (ör. Fuar leadleri modülü kapalıysa ilgili araçlar) listelenmez. Araçların adları ve açıklamaları modellerin daha iyi anlaması için İngilizcedir; asistanla Türkçe konuşabilirsiniz. ## Okuma araçları Okuma araçları istemciye `readOnlyHint: true` olarak bildirilir — Claude bunlar için "her zaman izin ver" seçeneği sunar. | Araç | Açıklama | Parametreler | Örnek istem | |---|---|---|---| | `search` | Ad, şehir, telefon ya da e-postayla firma, kişi, fırsat ve fuar leadi arar (en az 2 karakter); 15 karakterlik kayıt kimliğini de çözer. | `query`* | *Kumsal Plastik'i bul* | | `list_companies` | Firmaları süzgeçlerle listeler: durum (AC / Prospect), sorumlu, şehir, ziyaret durumu ve sayaçlar. | `page`, `query`, `scope` (mine · all), `sort` (name · visit · priority), `status` (AC · Prospect), `visit` (overdue · soon · ok · none) | *Ziyareti geciken aktif müşterilerimi listele* | | `get_company` | Firma kartının tamamı: kişiler, fırsatlar, son ziyaret ve denemeler, açık aksiyonlar, teklifler, notlar ve e-posta yazışmaları. | `company_id`* | *Alize Paketleme'nin kartını özetle* | | `list_contacts` | Firmalardaki kişileri listeler. | `query`, `scope` (mine · all) | *Satın alma müdürü unvanlı kişileri göster* | | `list_opportunities` | Fırsatları listeler: açık (varsayılan), kapalı, müşteri takibi ya da bir aşama; güncelleme, sağlık, değer ya da SLA'ya göre sıralı. | `page`, `query`, `scope` (mine · all), `sort` (update · health · value · sla), `stage` | *Negotiation'daki fırsatları değere göre sırala* | | `get_opportunity` | Fırsatın tamamı: aşama, değer, olasılık, sağlık, kontrol listesi, BANT, kapı sorunları, ziyaretler, denemeler, teklifler, aşama geçmişi. | `opportunity_id`* | *Streç film fırsatı bir sonraki aşama için neyi bekliyor?* | | `list_actions` | Açık işler: görevler, ziyaret ve denemelerin sonraki aksiyonları, tarihli proje adımları; her satırda tür + kimlik. | `company_id`, `scope` (mine · all), `state` (today · overdue · soon · open · week) | *Bu hafta geciken aksiyonlarım neler?* | | `get_action` | Tek aksiyon ya da görev, bağlamıyla (firma, proje, geçmiş). | `id`*, `kind`* (visit · demo · task · step) | *Fiyat listesi görevinin ayrıntısını aç* | | `list_calendar` | Tarih aralığındaki toplantılar ve aksiyonlardan türeyen takvim girişleri. | `from`, `to` | *Gelecek hafta takvimimde neler var?* | | `list_email_threads` | Kullanıcıların posta kutularından eşitlenen yazışmalar: bir kaydınkiler ya da gelen kutusu. | `filter` (all · waiting · replied · mine), `query`, `record_id`, `record_type` (customer · opp · visit · demo · task · offer · ticket · contract) | *Yanıt bekleyen müşteri e-postalarını göster* | | `get_email_thread` | Bir yazışmanın iletileri (görme izni yoksa gizli) ve bağlı kayıtları. | `thread_id`* | *Kuzey Plastik'le son yazışmayı özetle* | | `get_dashboard` | Pano: aşamalara göre sayı ve değer (ana dövizde), haftalık ziyaret hedefleri, SLA aşımları, sıradaki adımlar. | `scope` | *Satış hattının özetini çıkar* | | `get_today` | Günüm: bugünün toplantıları ve işleri, gecikenler, son açılan kayıtlar ve bu haftanın aktivitesi. | — | *Bugün ne yapmam gerekiyor?* | | `list_team` | Etkin kullanıcılar (kimlik, ad, rol, departman) — atama ve sorumlu alanları için. | — | *Ekipte kimler var?* | `*` zorunlu parametre. ## Yazma araçları Yazma araçları kayıt oluşturur ya da değiştirir; hiçbiri kayıt silmez (`destructiveHint: false`). Claude her yazma çağrısından önce onay ister. | Araç | Açıklama | Parametreler | Örnek istem | |---|---|---|---| | `create_company` | Firma açar (verilmezse Prospect). Aynı ad varsa reddedilir; benzer adlarda confirm_new gerekir. İsteğe bağlı ilk kişi. | `address`, `city`, `confirm_new`, `contact_email`, `contact_name`, `contact_phone`, `contact_title`, `name`*, `note`, `owner_id`, `phone`, `sector`, `status` (AC · Prospect), `website` | *Anadolu Gıda Ambalaj adında Konya'da yeni bir aday firma aç* | | `update_company` | Firma alanlarını günceller (yalnız verilenler değişir). | `address`, `city`, `company_id`*, `name`, `note`, `owner_id`, `phone`, `sector`, `status` (AC · Prospect), `visit_period_days`, `website` | *Kuzey Plastik'i aktif müşteri yap ve 30 günde bir ziyaret ayarla* | | `create_contact` | Firmaya kişi ekler. | `company_id`*, `department`, `email`, `is_main`, `name`*, `note`, `phone`, `title` | *Kuzey Plastik'e Emre Yıldız'ı üretim şefi olarak ekle* | | `create_opportunity` | Firma için yeni satış projesi açar (ilk aşamadan başlar). | `company_id`*, `competitor`, `name`, `note`, `type`, `value` | *Kuzey Plastik için 'Streç film tedariki' fırsatı aç* | | `change_opportunity_stage` | Fırsatı başka aşamaya taşır; aşama kapıları uygulanır, reddedilirse nedenleri döner. | `note`, `opportunity_id`*, `reason`, `stage`* (Qualify · Viable · Present Solution · Negotiation · Expect to Close · Win · Lost · Cancel) | *Streç film fırsatını Viable'a taşı* | | `create_task` | Görev açar (verilmezse bana atanır). Firma bağlamak isteğe bağlı; kendiliğinden proje açılmaz. | `assigned_to`, `company_id`, `company_name`, `detail`, `due_date`, `due_time`, `opportunity_id`, `priority` (Düşük · Normal · Yüksek · Acil), `title`* | *Yarın 10:30'a Kuzey Plastik'i arama görevi oluştur* | | `complete_action` | Aksiyonu tamamlar (görev, ziyaret / deneme aksiyonu, proje adımı); done_on tamamlanma günü. | `done_on`, `id`*, `kind`* (visit · demo · task · step), `result` | *Fiyat listesi görevini tamamlandı yap* | | `log_activity` | Telefon görüşmesi ya da yüz yüze ziyaret kaydeder (Hızlı Giriş): not, sonuç, sonraki aksiyon ve tarihi; uygun açık aksiyonları kapatır. | `action_date`, `company`*, `confirm_new`, `contact`, `next_action`, `note`, `opportunity_id`, `result`, `topic`, `visit_date`, `visit_type`* (F2F · Call) | *Kuzey Plastik'le az önceki telefon görüşmesini kaydet, perşembeye teklif aksiyonu aç* | | `add_note` | Firma, fırsat, destek talebi ya da sözleşmeye not ekler. | `body`*, `record_id`*, `record_type`* (customer · opp · ticket · contract) | *Alize Paketleme'ye 'fiyat revizyonu istendi' notu düş* | `*` zorunlu parametre. ## Yanıt biçimi Araçlar sonucu hem metin (`content[0].text`, JSON) hem yapısal içerik (`structuredContent`) olarak döndürür. İş kuralı hatalarında (ör. aşama kapısı, benzer firma adı) `isError: true` ve nedeni açıklayan metin gelir; asistan bunu kullanıcıya aktarır ya da düzeltip yeniden dener. ```json { "jsonrpc": "2.0", "id": 3, "result": { "content": [{ "type": "text", "text": "{\"counts\": {\"overdue\": 8, \"soon\": 0, \"today\": 0}, \"rows\": [ … ]}" }], "structuredContent": { "counts": { "overdue": 8, "soon": 0, "today": 0 }, "rows": [ "…" ] }, "isError": false } } ``` --- # Güvenlik > MCP bağlantısında kimlik, yetki, onay, denetim ve erişimi geri alma. ## Kimlik Solk MCP yalnız **OAuth 2.1** ile (ya da yöneticinin açtığı API anahtarıyla) çalışır. Parolanız asistana ya da istemciye hiçbir zaman verilmez: istemci sizi Solk kurulumunuzun kendi giriş ve onay sayfasına yönlendirir, siz izin verince kısa ömürlü bir erişim belirteci alır. | | | |---|---| | Erişim belirteci | 1 saat | | Yenileme belirteci | 60 gün; her yenilemede değişir | | PKCE | Zorunlu (S256) | | Dönüş adresleri | Yalnız `https://` ve yerel geri döngü | | Belirteç saklama | Sunucuda yalnız SHA-256 özeti tutulur | ## Yetki Asistan **sizin hesabınızla** çalışır: - Rolünüz (yönetici / satış), modül izinleriniz ve kayıt görünürlüğü aynen geçerlidir. Satışçı olarak bağlandıysanız asistan da yalnız sizin görebildiklerinizi görür. - E-posta içerikleri, posta kutusu sahibinin paylaşım ayarına göre gelir; görme izniniz olmayan iletilerin metni asistana verilmez. - Kapsamlar ayrıdır: yalnız `crm.read` verirseniz asistan hiçbir kaydı değiştiremez. - Hiçbir araç kayıt silmez. ## Onay Claude, araçları ilk kullanışında izin ister. Okuma araçları "salt okunur" olarak işaretli olduğundan bunlar için kalıcı izin verebilirsiniz; yazma araçlarında her çağrıda onay istemesini öneririz. Kuruluş sahipleri Claude'un yönetim ayarlarından hangi araçların kullanılabileceğini sınırlayabilir. ## Denetim - Her araç çağrısı Solk denetim günlüğüne kullanıcı, araç adı ve bağlı uygulama adıyla yazılır. - Yazma araçlarının oluşturduğu kayıtlar, web arayüzünde oluşturulmuş gibi sahibi ve zamanıyla görünür; uygulamalara (Slack, web kancası…) aynı olaylar gider. - **Ayarlar → Uygulama bağlantıları** her uygulamanın bağlanma tarihini, son kullanımını ve işlem sayısını gösterir. ## Erişimi geri alma | Kim | Nasıl | |---|---| | Kullanıcı | **Ayarlar → Uygulama bağlantıları → Erişimi kaldır** — o uygulamanın tüm belirteçleri anında düşer. | | Yönetici | Kullanıcıyı kapatmak tüm bağlantılarını keser. | | Claude tarafında | Connectors listesinden bağlayıcıyı kaldırmak ya da **Disconnect**. | ## Veri işleme MCP sunucusu kurulumunuzun içinde çalışır; ek bir aracı hizmet yoktur. Asistanın okuduğu veri, kullandığınız yapay zekâ hizmetine (ör. Anthropic) o hizmetin koşullarıyla iletilir. Kuruluşunuzun yapay zekâ kullanım politikasına göre hangi kullanıcıların bağlanabileceğine karar verin. --- # Örnek istemler > Solk bağlı bir asistana sorabileceğiniz gerçek iş örnekleri — arama, günlük plan, satış hattı, kayıt açma. Asistan önce `search` ile adları kimliğe çevirir, sonra ayrıntı araçlarını çağırır. Firma ve kişi adlarını yaklaşık yazmanız yeterlidir. ## Firma ve kişi bulma {#lookup} - "Alize Paketleme'nin ana kişisi kim, telefonu ne?" - "Kumsal Plastik'le son üç görüşmede neler konuşuldu?" - "Bursa'daki aktif müşterilerden ziyareti geciken hangileri?" - "001 ile başlayan şu kayıt kimliği hangi firma: 001aB3xY7kLmN2q" ## Günüm ve aksiyonlar {#today} - "Bugün ne yapmam gerekiyor? Toplantılarımı ve geciken işleri ayrı listele." - "Bu hafta termini olan aksiyonlarım neler, firmaya göre grupla." - "Ilgaz Plastik'teki açık işleri göster ve hangisinin en eski olduğunu söyle." ## Satış hattı {#pipeline} - "Negotiation ve Expect to Close aşamasındaki fırsatları değere göre sırala." - "Sağlığı kırmızı olan fırsatlarım hangileri ve neden?" - "Bu çeyrek kapanması beklenen fırsatların toplam değeri ne (ana dövizde)?" - "Streç film tedariki fırsatı bir sonraki aşamaya geçmek için neyi bekliyor?" ## E-posta {#email} - "Yanıt bekleyen müşteri e-postalarını göster." - "Kuzey Plastik'le son yazışmayı özetle; hangi projeye bağlı?" ## Kayıt açma ve güncelleme {#write} - "Kuzey Plastik'le az önce telefonda konuştum: deneme sonucu olumlu, perşembe teklif göndereceğim. Görüşmeyi kaydet ve aksiyonu aç." - "Anadolu Gıda Ambalaj diye yeni bir aday firma aç, şehir Konya; Murat Er'i genel müdür olarak ekle." - "Fiyat listesini gönder görevini tamamlandı yap, sonuç: e-postayla gönderildi." - "Kuzey Plastik için yarın 10:30'a 'numune sonuçlarını sor' görevi oluştur." - "Streç film tedariki fırsatını Viable'a taşı, not: ihtiyaç ve bütçe doğrulandı." :::tip Yazma isteklerinde asistan önce ne yapacağını söyler ve onayınızı alır. Benzer adlı bir firma varsa yeni firma açmadan önce size hangisi olduğunu sorar; aşama kapısı sağlanmıyorsa eksikleri listeler. ::: ## Toplu işler - "Ziyareti 30 günden fazla geciken aktif müşterilerin her biri için sorumlusuna 'ziyaret planla' görevi aç." — asistan önce listeyi gösterir, onaylarsanız görevleri tek tek açar. - "Kaybedilen fırsatların kayıp nedenlerini say ve en sık üç nedeni yaz." --- # Sorun giderme > Bağlanamama, izin, eksik araç ve hız sınırı sorunlarının nedenleri ve çözümleri. | Belirti | Neden | Çözüm | |---|---|---| | Claude "couldn't connect" diyor | Adres yanlış ya da kurulum eski sürümde. | Adresin `https://.solk.app/mcp` olduğunu ve `https://.solk.app/health` → `version` değerinin `v22` ya da üstü olduğunu kontrol edin. | | Onay ekranı "Uygulama tanınmadı" diyor | İstemci kaydı silinmiş ya da başka kurulumdan. | Claude'da bağlayıcıyı kaldırıp yeniden ekleyin. | | "Dönüş adresi bu uygulamaya kayıtlı değil" | İstemci kayıttaki adresten farklı bir dönüş adresi kullanıyor. | İstemciyi yeniden kaydedin (Claude'da bağlayıcıyı kaldırıp ekleyin). | | Bağlantı bir süre sonra kopuyor | Yenileme belirteci 60 gün kullanılmadı ya da erişim kaldırıldı. | **Connect** ile yeniden izin verin. | | Bazı araçlar görünmüyor | Kapsam ya da modül izni yok. | Bağlantıyı kaldırıp her iki izni vererek bağlanın; modül iznini yöneticiden isteyin. | | "Bu işlem için kayıt oluşturma / güncelleme izni gerekli" | Yalnız `crm.read` verilmiş. | Yeniden bağlanırken yazma iznini de onaylayın. | | Asistan bir kaydı bulamıyor | Kayıt görünürlüğünüz dışında ya da firma pasif. | Web arayüzünde aynı kullanıcıyla kaydı görebildiğinizi kontrol edin. | | "Dakikalık istek sınırı aşıldı" | Toplu işte çok sayıda çağrı. | Bir dakika bekleyin; asistandan daha dar süzgeçle çalışmasını isteyin. | | Lisans hatası | Kurulumun lisansı dolmuş. | Bağlantı yalnız yöneticiler için çalışır; lisansı yenileyin. | ## Elle deneme Belirteci ve sunucuyu doğrudan denemek için: ```bash # 1) keşif (belirteçsiz 401 ve resource_metadata dönmeli) curl -i -X POST https://ornek.solk.app/mcp -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"ping"}' # 2) API anahtarıyla araç listesi curl https://ornek.solk.app/mcp -H "Authorization: Bearer sk_…" \ -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' ``` Resmî **MCP Inspector** (`npx @modelcontextprotocol/inspector`) ile de adresi girip OAuth akışını ve araçları adım adım deneyebilirsiniz. --- # Sürüm notları > Solk CRM, REST API ve MCP'deki değişiklikler — en yenisi üstte. Kurulumlar aynı kod tabanından güncellenir. Kurulumunuzun sürümünü `https://.solk.app/health` gösterir. API'yi etkileyen değişiklikler **API** etiketiyle işaretlidir. ## v22 · 2 Ekim 2026 {#v22} **Claude ve MCP** - Her kurulumda MCP sunucusu: `https://.solk.app/mcp` — 14 okuma, 9 yazma aracı. [MCP](/mcp/overview) - OAuth 2.1 yetkilendirme sunucusu: keşif (RFC 9728 / 8414), dinamik istemci kaydı ve CIMD, zorunlu PKCE, yenileme belirteci döndürme. [OAuth uygulaması](/rest-api/oauth) - **Ayarlar → Uygulama bağlantıları**: bağlayıcı adresi, bağlı uygulamalar, son kullanım, erişimi kaldırma. **Uygulamalar** - Uygulama detay sayfaları (genel bakış, kurulum adımları, izinler, belgeler). - Kayıtlarda **Ara** düğmesi (Aircall, RingCentral) ve "Şimdi ara" penceresi. - Katalogda olmayan uygulamalar için **uygulama isteği**. **API** - REST API OAuth erişim belirteçlerini (`mcp_…`) kabul eder; `crm.write` olmadan yazma `403 insufficient_scope`. - Belirteç başına dakikalık hız sınırı (varsayılan 300) — `429 rate_limited` ve `Retry-After`. [Hız sınırları](/rest-api/rate-limits) - JSON gövdede `false` artık açıkça yanlış demektir: `done: false` aksiyonu açık bırakır (önceden durumu tersine çeviriyordu), `is_former: false` ve `bant_b: false` gibi alanlar değeri geri alır. - Bu belge sitesi, `llms.txt` ve OpenAPI 3.1 tanımı yayında. ## v21 · 2 Ekim 2026 {#v21} - 41 kartlık **Uygulamalar** kataloğu: Slack (+ `/crm` komutu), Teams, Google Chat, Discord, Telegram, Zapier, Make, n8n, Pipedream, web kancası; Notion, Asana, ClickUp, Airtable, Google Sheets, Linear, Mailchimp; Hunter, Apollo, lemlist, Mixmax, Productboard, PandaDoc, Stripe, Aircall, RingCentral; site formu, Typeform, Tally, Segment, veri eşitleme, Calendly / Cal.com, toplantı notları; Resend; tarayıcıdan ekle. [Uygulamalar](/guides/apps) - **Depolama hesapları**: Google Drive, OneDrive, Dropbox, Box — buluta kaydet, paylaşım bağlantısı ekle, otomatik kaydet, günlük bulut yedeği. [Depolama](/guides/storage) - **SCIM 2.0** (Okta, Entra ID). [SCIM](/rest-api/scim) — **API** - **Geliştiriciler** sayfası: süresiz API anahtarları (`sk_…`). — **API** - İş akışlarında "Uygulamaya gönder" adımı. ## v20 · 2 Ekim 2026 {#v20} - Daraltılabilir, hareketli sol menü (Ctrl + .). - Satış aşamaları ayardan: ad, sıra, renk, İptal aşaması, aşama kuralları; yeni kurulumlar iki açık aşamayla başlar. [Aşamalar](/concepts/pipeline) — **API**: `constants.stages`, `stage_labels`, `stage_rules` - Fırsat, görev ve firma şablonları. - Bildirim tercihleri (olay × e-posta / uygulama) ve günlük özet. - Slack, Teams, Telegram bildirimleri, web kancası, form → lead. ## v19 · 2 Ekim 2026 {#v19} - Sol menülü, sade arayüz. - Fırsat sağlığı renkli nokta; isteyen kullanıcı rengi elle seçip not yazar. — **API**: `health_color`, `health_manual`, `health_note` - Tamamlarken gün seçimi (Bugün / Dün / tarih). — **API**: `done_on` ## v18 · 1–2 Ekim 2026 {#v18} - **E-posta Otomasyonu** modülü (toplu gönderim, diziler); mobil uygulamada e-posta yazışmaları ve Sor. — **API**: `threads`, `emails/send`, `ask` - Otomatik müşteri kurulumu ve kaldırma (panel → sunucu). - E-posta eşitleme: tüm zamanlar, Gmail arşivi, Türkçe klasör adları; şirket postası bağlantısı; toplu yazışmalarda doğru firma / kişi; alan adından firma unvanı. - Aksiyonlar sayfası yeniden; geciken işler e-postası. - Örnek veri (sunum için, tek tıkla kaldırılır). - E-postadaki toplantı davetleri takvime (çevrim içi / yüz yüze ayrımı). — **API**: `mode`, `join_url` - Görevde proje seçimi ve saat; saat bazlı takvim; iPhone için ortak + kişisel takvim. — **API**: `due_time`, `opp_name` ## v17 · 1 Ekim 2026 {#v17} - E-posta yazışmaları kayıtlara bağlanır (proje, ziyaret, deneme, görev, teklif, talep, sözleşme); bağlama önerileri. - E-postadan otomatik firma ve kişi; alan adı analizi; gürültü filtresi. - Kayıt görünürlüğü: herkes / takım. [Kullanıcılar ve roller](/users-and-roles) ## v16 · 30 Eylül – 1 Ekim 2026 {#v16} - E-posta penceresi: Bcc, ek, değişkenler, zamanlanmış ve toplu gönderim, taslaklar. - E-posta dizileri. - Google / Microsoft ile posta kutusu bağlama (merkezi yönlendirme). - Rapor oluşturucu ve **Sor** (yapay zekâ modülü). ## v15 · 30 Eylül 2026 {#v15} - Komut paleti (Ctrl/⌘ + K), hızlı görev ve not, @ ile bahsetme. - Başlarken listesi ve ekip daveti. - İş akışları (otomasyon). - E-posta hesabı bağlama (IMAP/SMTP). ## v14 · Eylül 2026 {#v14} - Ürün bazında birim, çoklu döviz, TCMB kurları; toplamlar ana dövizde. [Birim ve döviz](/concepts/units-currency) — **API**: `unit`, `pu`, `value_base` - SLA süreleri firmaya göre (aşama günleri, proje yaşam süresi, destek talebi hedefi). - Ürün listesi Excel'den. ## v13 · Eylül 2026 {#v13} - Destek talepleri, sözleşmeler, Kanban, Fırsat Pano, asistan, iki adımlı doğrulama, IP kısıtı. ## v12 · Eylül 2026 {#v12} - Bugün sayfası; iOS / Android uygulaması ve JSON API (`/api/v1`). — **API**