CRM

portal

27 uç · /api/crm/v1/portal

İstek örnekleri ucun GERÇEK metodu ve yolundan üretilir. 27 ucun elle doğrulanmış gövde örneği henüz yok; o uçlarda iskelet gövdesizdir — uydurma bir alan yazmıyoruz.

POST/api/crm/v1/portal/login

POST /api/crm/v1/portal/login {email, password}.

Herkese açık
İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/login" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/login-link

POST /api/crm/v1/portal/login-link — müşteri kendi giriş bağlantısını ister.

Herkese açık

🔴 DAİMA 204 döner. "Bu e-posta kayıtlı değil" demek, bu kiracının müşteri listesini dışarıdan sorgulanabilir yapardı (adres sayımı). Kullanıcıya ekranda söylenen cümle de o yüzden koşulsuzdur: *"adresin kayıtlıysa bağlantı gönderildi"*. 🔴 Hız sınırı ZORUNLU: sınırsız bir uç, birinin kutusuna bağlantı yağdırmanın (posta bombası) ve adres sayımının yoludur.

İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/login-link" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/logout

POST /api/crm/v1/portal/logout — portal oturumunu kapat.

Herkese açık
İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/logout" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
GET/api/crm/v1/portal/me

GET /api/crm/v1/portal/me.

Oturum
İstek
curl -X GET "https://{slug}.crm.blesyum.app/api/crm/v1/portal/me" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
PATCH/api/crm/v1/portal/me

PATCH /api/crm/v1/portal/me.

Oturum
İstek
curl -X PATCH "https://{slug}.crm.blesyum.app/api/crm/v1/portal/me" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
GET/api/crm/v1/portal/notifications

GET /portal/notifications — zil.

Oturum
İstek
curl -X GET "https://{slug}.crm.blesyum.app/api/crm/v1/portal/notifications" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/notifications/read-all

POST /portal/notifications/read-all.

Oturum
İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/notifications/read-all" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
GET/api/crm/v1/portal/notify/status

GET /api/crm/v1/portal/notify/status — yönetici ekranının okuduğu DURUM. Hangi

Oturum

kanalın GERÇEKTEN çalışabileceğini söyler ("gösterilen kutu ÇALIŞIR"): SMS anahtarı kimlik yokken açılabilir ama hiçbir mesaj çıkmaz — ekran bunu buradan bilir.

İstek
curl -X GET "https://{slug}.crm.blesyum.app/api/crm/v1/portal/notify/status" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
GET/api/crm/v1/portal/oauth/google/callback

GET /api/crm/v1/portal/oauth/google/callback — kiracısız, sabit host.

Oturum

Kimlik doğrulaması tek kullanımlık state satırıyla yapılır; kiracı onun ön ekinden çözülür. Korumalı bir router'a konulsaydı istek her seferinde 404/401 alırdı (çıplak host hiçbir kiracıya çözülmez — Meta callback'in aynı gerekçesi).

İstek
curl -X GET "https://{slug}.crm.blesyum.app/api/crm/v1/portal/oauth/google/callback" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
GET/api/crm/v1/portal/oauth/google/start

GET /api/crm/v1/portal/oauth/google/start?next=/hc/... — kiracı host'unda.

Herkese açık
İstek
curl -X GET "https://{slug}.crm.blesyum.app/api/crm/v1/portal/oauth/google/start" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/password

POST /api/crm/v1/portal/password {current_password?, password}.

Oturum
İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/password" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/password/forgot

POST /api/crm/v1/portal/password/forgot {email} — daima 204.

Herkese açık
İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/password/forgot" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/password/reset

POST /api/crm/v1/portal/password/reset {token, password}.

Herkese açık
İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/password/reset" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
GET/api/crm/v1/portal/push/subscriptions

GET /api/crm/v1/portal/push/subscriptions — müşterinin kaç cihazı abone + açık

Oturum

anahtar. Tarayıcı kendi aboneliğini pushManager.getSubscription()tan bilir; bu uç "sunucu da biliyor mu" sorusunu cevaplar (ölü abonelik silinmiş olabilir).

İstek
curl -X GET "https://{slug}.crm.blesyum.app/api/crm/v1/portal/push/subscriptions" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/push/subscriptions

Müşterinin tarayıcı aboneliğini yaz.

Oturum

⚠ Çakışma anahtarı (modul, endpoint) ve endpoint TARAYICI PROFİLİNİNDİR (aynı köken + aynı service worker + aynı VAPID anahtarı → aynı adres). Aynı tarayıcıda önce temsilci sonra müşteri abone olursa satır SON ABONEYE geçer (user_id boşalır, kisi_ref dolar — push_subs_sahip_chk "tam olarak biri"). Bu bilinçli: bir tarayıcı profili aynı anda iki kimliğe bildirim gösteremez ve ekranda o an kim oturum açmışsa bildirim ONA gitmelidir (ajan tarafının push::save kararıyla aynı).

İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/push/subscriptions" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/push/unsubscribe

Müşterinin aboneliğini sil. Silme SAHİPLİKLE sınırlıdır (kisi_ref): gövdeye

Oturum

başka birinin endpoint'ini yazan müşteri onun aboneliğini düşüremez (müşavirin "taşınan tablo, taşınmayan politika" dersi — kapı burada, açık yüklemle).

İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/push/unsubscribe" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/register

POST /api/crm/v1/portal/register — kayıt başlat. Daima 202 {sent_to}.

Herkese açık
İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/register" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/session

POST /api/crm/v1/portal/session — bağlantıyı tüket, portal oturumunu aç.

Herkese açık

Oturum HttpOnly bir çereze yazılır; gövdede token DÖNMEZ. Panelin çekirdek token'ı localStorageda duruyor ve bu bilinen bir açık (self-XSS yüzeyi); yeni bir yüzeyi aynı açıkla doğurmanın sebebi yok.

İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/session" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
GET/api/crm/v1/portal/tickets

GET /api/crm/v1/portal/tickets — müşterinin kendi talepleri (simple-pagination).

Oturum

2026-09-29: satır artık portal_talep::listein BEYAZ LİSTESİdir (ajan profilinin to_jsonb(c) projeksiyonu search_vector/spam_*/ai_state sızdırıyordu) ve bölme (status=open|closed|all), arama (query: konu ya da #numara), okunmamış sayısı ile bölme sayaçlarını (counts) taşır. Sayaçlar listeyle AYNI yüklemden.

İstek
curl -X GET "https://{slug}.crm.blesyum.app/api/crm/v1/portal/tickets" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/tickets

POST /api/crm/v1/portal/tickets — müşteri yeni talep (server-authoritative user_id).

Oturum
İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/tickets" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
GET/api/crm/v1/portal/tickets/{id}

GET /api/crm/v1/portal/tickets/{id} — kendi talep detayı.

OturumYol parametresi: id
İstek
curl -X GET "https://{slug}.crm.blesyum.app/api/crm/v1/portal/tickets/<id>" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
GET/api/crm/v1/portal/tickets/{id}/items

GET /api/crm/v1/portal/tickets/{id}/items — kendi talebinin mesajları (internal not GİZLİ).

OturumYol parametresi: id
İstek
curl -X GET "https://{slug}.crm.blesyum.app/api/crm/v1/portal/tickets/<id>/items" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/tickets/{id}/mark-as-solved

POST /api/crm/v1/portal/tickets/{id}/mark-as-solved — müşteri kendi talebini çözüldü işaretler.

OturumYol parametresi: id
İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/tickets/<id>/mark-as-solved" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/tickets/{id}/read

POST /portal/tickets/{id}/read — okuma izini şimdiye ilerlet. Detay GET okundu

OturumYol parametresi: id

İŞARETLEMEZ: sekmede açık kalan bir talep, müşteri görmeden "okundu" sayılmasın; ön yüz bu ucu mesajlar ekranda göründüğünde çağırır.

İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/tickets/<id>/read" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/tickets/{id}/reply

POST /api/crm/v1/portal/tickets/{id}/reply — kendi talebine yanıt (kilitli→red, reopen).

OturumYol parametresi: id
İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/tickets/<id>/reply" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/uploads

POST /portal/uploads (multipart, tek file) — müşterinin eki.

Oturum

Üç sınır (widget ucunun ikizi): kimlik (müşteri kapısı) · hız (müşteri başına WIDGET_UPLOAD_LIMIT / WIDGET_UPLOAD_WINDOW) · boyut ([crate::portal_dosya::MAX_BYTES]). Tür BAYTLARDAN okunur (portal_dosya::musteri_eki_turu); saklanan MIME imzadan türer. Dosyanın sahibi müşteridir (owner_id) — yanıt ucu yalnız ONUN bağlanmamış dosyalarını kabul eder (portal_dosya::ekleri_dogrula).

İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/uploads" \
  -H "Authorization: Bearer <oturum-token>" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
POST/api/crm/v1/portal/verify

POST /api/crm/v1/portal/verify {token} — kayıt doğrulandı: kişi + hesap + oturum.

Herkese açık
İstek
curl -X POST "https://{slug}.crm.blesyum.app/api/crm/v1/portal/verify" \
  -H "Accept: application/json"
Yanıt
{
  "data": "…",
  "trace_id": "01JC…"
}
Bu sayfa işine yaradı mı?