Ana içeriğe geç

Sohbet widget'ı

Genel sohbet widget'ı, kimliği doğrulanmamış web sitesi ziyaretçilerinin bir çalışma alanıyla konuşma başlatmasını sağlar. Bu sayfa yönetici tarafından yönetilen yapılandırmayı ve kurulum parçacığını kapsar. Ziyaretçiye dönük oturum ve mesajlaşma uçları ayrı belgelenir.

Widget anahtarı​

Her tenant'ın bir widget_key değeri vardır — müşterinin web sitesindeki widget parçacığına gömülen, yüksek entropili, opak bir genel tanımlayıcı (Intercom'un app_id değeriyle benzer). Sunucu tarafından kayıt sırasında üretilir ve gizli bir değer değildir:

  • Yalnızca genel widget yüzeyi için tenant'ı çözer; tek başına hiçbir ayrıcalık vermez.
  • İstemci girdisinden asla yazılamaz (yalnızca sunucu üretir).
  • Ziyaretçi yetkilendirmesi ayrı bir imzalı oturum token'ı ile sağlanır — widget anahtarı tek başına hiçbir konuşmayı okuyamaz veya yazamaz.

Ham tenant id'si istemciden asla kabul edilmez; widget yüzeyi tenant'ı yalnızca widget_key üzerinden çözer (X-Widget-Key başlığı veya widget_key parametresi olarak gönderilir). Eksik veya bilinmeyen anahtar reddedilir (404, fail-closed).

Yönetici yapılandırması​

Yalnızca yönetici (manage-widget-settings yetkisi). Tenant izolasyonu, altta yatan tenant_settings satırındaki Satır Düzeyi Güvenlik (RLS) ile sağlanır.

UçAçıklama
GET /api/widget/settingsEtkin widget yapılandırmasını okur.
PUT /api/widget/settingsKısmi güncelleme (tüm alanlar opsiyonel).
GET /api/widget/installGenel widget_key ve gömme parçacığını okur.

Ayarlar​

AlanTipNotlar
enabledbooleanAna anahtar. Varsayılan kapalı.
namestring | nullBaşlık görünen adı (≤ 120 karakter).
welcome_messagestring | nullAçılışta gösterilen karşılama (≤ 2000 karakter).
primary_colorstringHex renk, örn. #2563eb.
localestringDesteklenen dillerden biri.

Her değer geneldir (kimliği doğrulanmamış ziyaretçilere sunulur); doğrulama kuralları gizlilik için değil, arayüz güvenliği içindir (uzunluk sınırları, katı hex renk, desteklenen dil listesi).

Kurulum parçacığı​

GET /api/widget/install şunu döner:

{
"data": {
"widget_key": "…",
"script_url": "https://app.example.com/widget.js",
"snippet": "<script>…</script>"
}
}

snippet değerini hedef web sitesine yapıştırın. Widget yükleyici betiğini yükler ve widget'ın çalışma alanını çözebilmesi için genel widget_key değerini geçirir.

Yükleyici ayarları: öznitelik veya sorgu dizesi​

Yükleyici anahtarını şu sırayla okur: window.SmaibotWidgetKey, script etiketindeki data-widget-key özniteliği, ardından script URL'sindeki ?key=. İsteğe bağlı locale, name, welcome-message ve manual geçersiz kılmaları da önce data-<ad>, sonra ?<ad>= üzerinden okunur; dolayısıyla şu iki etiket eşdeğerdir:

<script async src="https://app.example.com/widget.js"
data-widget-key="wk_…" data-locale="tr" data-manual="true"></script>

<script async src="https://app.example.com/widget.js?key=wk_…&amp;locale=tr&amp;manual=true"></script>

Sorgu dizesi biçimi etiket yöneticileri için vardır: Google Tag Manager'ın Özel HTML etiketi script öğelerini yeniden oluşturur ve tüm data-* özniteliklerini siler, ama src'yi korur. Sorgu değerleri URL-kodludur (yerelleştirilmiş bir name / welcome-message, URL-kodlu {default, translations} JSON nesnesidir). data-sso-token, data-ephemeral ve data-api-base-url yalnızca öznitelik olarak okunur — URL'deki bir token CDN ve proxy günlüklerine düşer. Hiçbir yerde anahtar yoksa yükleyici hiçbir şey yapmaz.

Widget varlıklarını sunma (CORS)​

Yükleyici (widget.js) klasik bir betiktir ve cross-origin olarak özel bir yapılandırma gerektirmeden yüklenir. Ardından uygulama bundle'ını (widget-app.js) bir ES modülü olarak import eder ve tarayıcılar modül betiklerini CORS modunda getirir. Bu yüzden widget bir müşteri sitesine (varlığın sunulduğu origin'den farklı) gömüldüğünde, bundle'ı sunan host — CDN'iniz veya uygulama origin'iniz — widget.js, widget-app.js ve assets/ altındaki hash'li parçalarda Access-Control-Allow-Origin: * göndermelidir. Aksi halde modül import'u engellenir ve widget hiç eklenmez.

Bu, aşağıdaki widget API CORS'undan bağımsızdır (onu uygulamanın kendisi yönetir). Yükleyici varsayılan olarak API origin'ini kendi betik URL'sinden türetir; API'yi varlıklardan farklı bir host'a yöneltmek için parçacıkta data-api-base-url geçirin.

Ziyaretçi oturumu (genel yüzey)​

Anonim ziyaretçiler personel kimlik bilgileri yerine imzalı bir oturum token'ı ile kimlik doğrular. Oturum şununla kurulur:

UçYetkiAçıklama
POST /api/widget/sessionwidget_key (+ opsiyonel token)Yeni ziyaretçi oturumu başlat veya devam ettir.

Çerez veya sunucu tarafı oturum satırı yoktur — token kendinden taşıyıcı, durumsuz bir kimlik bilgisidir.

Başlatma ve devam ettirme​

  • Yeni ziyaretçi — widget_key (başlık X-Widget-Key veya gövde) gönderir, token göndermez. Sunucu yeni bir ziyaretçi (visitor) kişisi ve açık sohbet (open chat) konuşması oluşturur, ardından bir oturum token'ı döner. Yanıt durumu 201.
  • Geri dönen ziyaretçi — ek olarak önceden verilen token'ı Authorization: Bearer <token> (veya X-Widget-Session) ile sunar. Sunucu token'ı doğrular ve aynı konuşmaya devam eder. Yanıt durumu 200.
  • Geçersiz, kurcalanmış, süresi dolmuş veya başka tenant'a ait token token yokmuş gibi ele alınır — ziyaretçi şeffaf biçimde yeni bir oturum başlatır (asla hata değil, asla başkasının konuşmasına devam değil).

İstek gövdesi opsiyoneldir ve yalnızca düşük güvenli görüntüleme alanları taşır:

{ "visitor": { "name": "Ada Lovelace", "email": "ada@example.com" } }

İstemciden asla tenant / kişi / konuşma id'si kabul edilmez. Widget tenant için etkin olmalıdır, aksi halde uç 403 döner.

Oturum token'ı​

Token şifrelenmiş + kimlik doğrulamalıdır (Laravel Crypt) ve ziyaretçiyi tam olarak bir {tenant, kişi, konuşma} kapsamına bağlar:

  • Kurcalama veya sahtecilik bütünlük kontrolünü geçemez ve reddedilir.
  • Bir verilme zamanı (issued-at) taşır ve config('widget.session_ttl_days') (varsayılan 30 gün) sonra geçersiz olur.
  • Tek konuşmasının ötesinde hiçbir erişim vermez; tenant'lar veya konuşmalar arası kullanım yapısal olarak imkânsızdır.
  • Token'ı istemcide gizli bir kimlik bilgisi olarak ele alın.

Yanıt​

{
"data": {
"token": "…",
"is_new": true,
"config": { "name": "…", "welcome_message": "…", "primary_color": "#2563eb", "locale": "en", "agents_online": true },
"conversation": { "id": "…", "status": "open", "created_at": "…" },
"messages": [
{ "id": "…", "author": "visitor", "sender_type": "contact", "body": "…", "created_at": "…" }
]
}
}

Ziyaretçi verisi bilinçli olarak dardır: personel kimliği, AI meta verisi (meta), kişi PII / özel öznitelikleri veya başka tenant'ın verisini asla içermez. Her mesaj author: "visitor" | "staff" olarak normalize edilir; bir personel/bot sender_id değeri asla sızdırılmaz (yalnız ziyaretçinin kendi mesajları için kendi kişi id'si yansıtılır).

config.agents_online, çıplak bir boolean'dır — tenant'ın en az bir personeli şu anda Çevrimiçi presence'e ayarlıyken true'dur (AgentAvailability::anyOnline()). Widget'ın hangi ajanın (veya kaç ajanın) müsait olduğunu asla açığa çıkarmadan bir "çevrimiçi" ipucu göstermesini sağlar.

config.ai_available yalnızca AI asistanı bir widget sohbetini gerçekten yanıtlayacaksa true'dur: widget'ın AI anahtarı açık ve AiManager::isEnabled() (planda AI özelliği, tenant'ın AI anahtarı, kullanılabilir bir anahtar). false iken widget "AI asistan" etiketi ve "temsilciye bağlan" düğmesi göstermez; başlıkta agents_online'dan ekibin durumunu gösterir.

config.commerce.enabled yalnızca planda katalog ve satış varsa, çalışma alanı widget mağazasını açtıysa (widget ayarı shop_enabled, varsayılanı false; Ayarlar → Widget → Genel altındaki "Mağazayı widget'ta göster") ve en az bir ürün widget üzerinden sipariş edilebiliyorsa (product türünde, aktif, fiyatlı, para birimi var, widget'tan gizlenmemiş; hizmetler ve paketler burada satılmaz) true'dur. Aynı tanım GET /api/widget/catalogue'un da arkasındadır; planda katalog ya da satış yoksa veya shop_enabled kapalıysa bu uç (ve POST /api/widget/orders) 404 döner. GET /api/widget/orders (ziyaretçinin kendi siparişleri) bu anahtara bağlı değildir: mağazayı kapatmak girişi gizler, hiçbir şeyi silmez.

AI araçları​

Bir çalışma alanı widget'ın içinde AI aracı profilleri sunabilir (doc 97 §13). Oturum yapılandırması bunları ai_tools içinde listeler — yalnızca çalışma alanının widget için açtığı profiller, sayfanın alan adı (Origin) için, ziyaretçinin dilinde: etiket, açıklama, simge, hazır formats (anahtar + etiket, asla istem değil), per_day, max_upload_mb ve version'ıyla (metnin sha256'sı) birlikte consent metni.

Uç noktaKimlikAçıklama
POST /api/widget/tools/runsoturum belirteciMultipart: module, profile, photo, consent_version, isteğe bağlı format, locale. Çalıştırmayla 201 (state pending, ya da profilin message'ıyla refused).
GET /api/widget/tools/runs/{run}oturum belirteciZiyaretçinin KENDİ çalıştırması; delivered olunca result.url kısa ömürlü imzalı bir bağlantıdır.

Çalıştırma belirteçteki kişiye aittir. Güncel onay sürümü olmadan başlatma 409 consent_required döner ve hiçbir şey saklanmaz; sunulmayan (ya da bu alan adında sunulmayan) profil 404 surface_off. Başlatma oturum ve IP başına saatlik, widget_key başına dakikalık sınırlıdır (tool_modules.direct.widget_rate_limits); profilin kişi başına günlük limiti de geçerlidir. Profilde widget.post_to_conversation açık değilse konuşmaya hiçbir şey yazılmaz; açıksa (kayıt posts_to_conversation: true der, onay metni de bunu söyler) biten sonuç ziyaretçinin konuşmasına görselli bir bot mesajı olarak da gönderilir ve widget onu diğer ekler gibi gösterir.

Mesajlaşma​

Oturum kurulduktan sonra ziyaretçi, oturum token'ını Authorization: Bearer <token> başlığında (veya X-Widget-Session) ve widget_key ile birlikte sunarak mesaj gönderir ve okur.

UçYetkiAçıklama
POST /api/widget/messagesoturum token'ıZiyaretçinin konuşmasına mesaj gönderir.
GET /api/widget/messagesoturum token'ıZiyaretçinin konuşma geçmişini sayfalar.

Konuşma her zaman oturum token'ından alınır, istekten asla — ziyaretçi yalnızca kendi konuşmasına erişebilir. Gönderilen her mesaj sunucu tarafında sender_type: contact ve chat kanalı olarak yazılır; sender_type, sender_id, channel ve konuşma id'si istemciden gelirse yok sayılır, böylece ziyaretçi bir personel/bot mesajını taklit edemez veya başka bir konuşmayı hedefleyemez.

Ziyaretçi mesajı bir contact mesajı olduğu için, herhangi bir kanal gibi aynı gelen-mesaj hattından geçer — çalışma alanında AI etkinse AI otomatik yanıtı dahil.

Blok kısıtlamaları​

Ziyaretçi mesajı boş olmayan body metni ve/veya bir blocks dizisi taşımalıdır. Ziyaretçiler yalnızca kısıtlı blok türlerini gönderebilir — text, image ve file. Personele özel etkileşimli bloklar (buttons, card, slider, translation) 422 ile reddedilir. Gövde uzunluğu ve blok sayısı sınırlıdır (config('widget.message_max_chars') / message_max_blocks).

{ "body": "Merhaba, yardıma ihtiyacım var" }

Gerçek zamanlı​

Widget, atanan ajanın izlediği aynı özel konuşma kanalı üzerinden canlı mesaj alır — private-tenant.{tenantId}.conversation.{conversationId} — böylece ziyaretçi ve ajan tek bir ortak akış görür.

Ziyaretçinin personel kimlik bilgileri olmadığından, Pusher/Echo istemcisi authEndpoint değerini özel, genel bir broadcasting-auth ucuna yönlendirir:

UçYetkiAçıklama
POST /api/widget/broadcasting/authoturum token'ıZiyaretçinin kendi konuşma kanalını imzalar.

Bu uç yalnızca ziyaretçinin kendi konuşma kanalını — token'da adı geçeni — yetkilendirir. Başka herhangi bir kanal (başka konuşma, ajan presence kanalı presence-tenant.{t}.agents, bir kullanıcının kişisel kanalı, farklı bir tenant) reddedilir (403). Konuşma id'si yalnızca token'dan geldiği için çapraz-konuşma / çapraz-tenant abonelik yapısal olarak imkansızdır.

CORS​

Widget herhangi bir müşteri web sitesine gömülür, bu nedenle widget uçları (api/widget/*) özel, kimlik-bilgisiz bir CORS politikası kullanır: Access-Control-Allow-Origin: * ve Access-Control-Allow-Credentials yok. Bu, tam olarak widget çerezsiz olduğu için güvenlidir — oturum token'ı bir istek başlığında taşınır, dolayısıyla CSRF yüzeyi yoktur ve joker origin kimlik bilgileriyle birleştirilemez.

Bu, sıkı açık-origin izin listesini kimlik bilgileriyle koruyan personel/SPA CORS'undan (config/cors.php) ayrıdır. İkisi asla örtüşmez: genel CORS işleyicisi widget yolları için atlanır ve bunlar yerine WidgetCors ara yazılımıyla sunulur.

Hız sınırlama​

Genel widget yüzeyi katmanlı olarak hız-sınırlıdır (dakika başına, config('widget.rate_limits') içinde yapılandırılır):

  • POST /session — widget_key + istemci IP'siyle anahtarlanır (oturum oluşturma spam'ı).
  • Mesajlaşma + broadcast auth — iki yığılı tavan: ziyaretçi oturumu başına (tek bir gevezelik eden ziyaretçi taşamasın) ve daha kaba widget_key başına (gürültülü-komşu: bir çalışma alanı yüzeyi diğerleri için tüketemesin).

Bir sınır aşıldığında Retry-After ve X-RateLimit-* başlıklarıyla 429 döner.

Yetkili istek/yanıt şekilleri için OpenAPI spesifikasyonuna bakın (apps/api/openapi/openapi.yaml, etiket Widget).