Entegrasyon

Özel kanal — kendi yazılımını bağla

Sitendeki formları, lead araçlarını ya da kendi uygulamandaki sohbeti CRM'e API ile bağla — kanal anahtarı, gönderim, mesaj ve webhook.

Özel kanal, kendi yazılımını CRM'in gelen kutusuna bağlamanın yoludur. Hazır bir entegrasyonu olmayan her şey buradan girer: kendi sitenin teklif formu, bir lead toplama aracı, mobil uygulamanın içindeki sohbet, bir kiosk.

İki türü var; kanalı açarken birini seçersin:

TürNe içinGelen uçYanıt
Formlar ve taleplerSite formları, lead araçları, "sizi arayalım" kutuları…/submissionsMüşteriye e-postayla
İki yönlü sohbetKendi uygulamanın içindeki mesajlaşma…/messagesSenin webhook adresine

Kanalı aç

CRM → Ayarlar → Çalışma alanı → Kanallar → Özel kanal → Bağlantı kur

Türü ve kanalın adını seçersin (sohbette bir de webhook adresi). Karşılığında:

  • Kanal anahtarı (bck_ önekli) — gelen uçları bununla çağırırsın.
  • Gelen uç adresi — çalışma alanının CRM adresinde.
  • Sohbet türünde ayrıca imza sırrı (bcs_ önekli) — webhook isteklerini doğrularsın.

Anahtar ve sır bir kez gösterilir; bizde yalnız özetleri durur. Kaybedersen kanalın detayından Yeni kanal anahtarı üretirsin — eskisi o an çalışmaz olur.

Kanal anahtarı için çekirdek panelden ayrıca API anahtarı almana gerek yoktur. Anahtar yalnız kendi kanalının uçlarına girer: kişi listesini okuyamaz, başka konuşmalara dokunamaz. Bunun yerine conversations.write kapsamlı bir API anahtarı da kullanabilirsin (bkz. Kimlik doğrulama).

[!İPUCU] Anahtarı sunucunda tut, sitenin tarayıcıda çalışan JavaScript'ine gömme. Formu kendi sunucuna gönder, oradan bize ilet. Anahtar sızarsa en kötü ihtimalle o kanala sahte gönderim düşer; panelden yenileyip kapatırsın.

Form gönderimi

bash
curl -X POST 'https://<çalışma-alanın>.crm.blesyum.app/api/crm/public/v1/channels/custom/<kanal>/submissions' \
  -H 'Authorization: Bearer bck_…' \
  -H 'Content-Type: application/json' \
  -d '{
    "id": "lead-1001",
    "form": "Teklif formu",
    "contact": { "name": "Deniz Kaya", "email": "deniz@ornek.com", "phone": "0532 111 22 33" },
    "fields": [
      { "label": "Bütçe", "value": "10.000 TL" },
      { "label": "Modüller", "value": ["CRM", "ERP"] }
    ],
    "message": "Pazartesi arayın.",
    "page_url": "https://ornek.com/teklif",
    "utm": { "utm_source": "google" },
    "deal": {}
  }'

Adresi panelden kopyala: kanal ekranı tam adresi verir (kendi alan adını bağladıysan onu taşır).

AlanZorunluNe olur
idhayırSenin kayıt kimliğin (en çok 190 karakter). Aynı kimlikle tekrar gönderirsen yeni talep açılmaz — ağ hatasında güvenle yeniden dene.
formhayırTalebin konusu (Teklif formu — Deniz Kaya). Boşsa kanalın adı.
contact.name · email · phonehayırKişi e-postayla, e-posta yoksa telefonla bulunur ya da açılır. İkisi de yoksa talep yine açılır, kişi açılmaz.
fields[]message yoksa evetlabel + value (metin, sayı, true/false ya da liste). Talebin gövdesine Etiket: değer satırları olarak yazılır; boş değerler atlanır. En çok 50 alan.
messagefields yoksa evetSerbest metin — alanlardan sonra kendi paragrafında.
page_urlhayırFormun gönderildiği sayfa — talebin künyesinde görünür.
utmhayırKampanya kaynağı (utm_source → source olarak saklanır). En çok 10 anahtar.
dealhayırVarsa satış hattının ilk sütununda fırsat açılır. {&quot;title&quot;: &quot;…&quot;} ile başlık verebilirsin; boşsa talebin konusu.

Gövdedeki alanların adı bir sözleşmedir: tanınmayan bir alan (&quot;email&quot; kökte, contact altında değil) sessizce yok sayılmaz, 400 alır.

Yanıt

Yeni talep 201, aynı id ile tekrar 200 döner:

json
{
  "conversation_id": "0193c4e2-…",
  "duplicate": false,
  "deal_id": null,
  "deal_note": "pipeline_not_set_up"
}
AlanAnlamı
duplicatetrue ise bu id daha önce işlendi; aynı talep döner.
deal_idAçılan (ya da daha önce açılmış) fırsat.
deal_noteFırsat açılamadıysa sebebi. pipeline_not_set_up: çalışma alanında satış hattı yok — CRM → Fırsatlar → Önerilen hattı kur. Hat kurulduktan sonra aynı idyi tekrar göndermek fırsatı açar, ikinci talep açmaz.

Talep gelen kutusuna Web formu kanalıyla düşer; temsilcinin yanıtı müşteriye e-postayla gider. Kanal ilk gönderim gelince panelde "Bağlı" görünür.

Sohbet mesajı (iki yönlü)

bash
curl -X POST 'https://<çalışma-alanın>.crm.blesyum.app/api/crm/public/v1/channels/custom/<kanal>/messages' \
  -H 'Authorization: Bearer bck_…' \
  -H 'Content-Type: application/json' \
  -d '{
    "contact": { "external_id": "user-42", "name": "Deniz Kaya", "email": "deniz@ornek.com" },
    "message": { "id": "msg-1", "body": "Merhaba, siparişim nerede?" },
    "attachments": [{ "url": "https://cdn.ornek.com/fatura.pdf", "name": "fatura.pdf" }]
  }'
  • contact.external_id senin sistemindeki kişi kimliğidir; aynı kişi hep aynı konuşmaya düşer (kapalıysa yeniden açılır).
  • message.id mükerrer korumasıdır: aynı kimlik ikinci kez yazılmaz.
  • Ek adresleri herkese açık olmalı (iç ağ adresleri reddedilir); dosyayı biz indirip saklarız (en çok 25 MB, 10 ek).

Yanıt 201:

json
{ "conversation_id": "0193c4e2-…", "message_id": "0193c4e3-…", "new_conversation": true, "duplicate": false }

Yanıtlar senin webhook'una

Temsilci (ya da AI) yanıt verince adresine imzalı bir POST gelir:

http
POST /senin-ucun HTTP/1.1
Content-Type: application/json
X-Blesyum-Event: message.created
X-Blesyum-Signature: t=1790000000,v1=5f3a9c…
json
{
  "event": "message.created",
  "channel_id": "cc_…",
  "conversation_id": "0193c4e2-…",
  "contact": { "external_id": "user-42" },
  "message": {
    "id": "0193c4e4-…",
    "author": { "type": "agent", "name": "Temsilci Can" },
    "text": "Kargoda, yarın elinizde.",
    "html": "<p>Kargoda, <b>yarın</b> elinizde.</p>",
    "attachments": []
  },
  "sent_at": "2026-10-03T12:00:00Z"
}

2xx dönmezsen gönderim temsilciye "iletilemedi" olarak görünür. Yanıt gövdende {&quot;id&quot;: &quot;…&quot;} döndürürsen o kimliği saklarız.

Kapanış, yeniden açılış ve AI'dan insana devir de aynı adrese olay olarak gelir: conversation.closed · conversation.reopened · conversation.handed_off. Bunlar kuyruktan teslim edilir ve tutmazsa üstel beklemeyle birkaç saat boyunca yeniden denenir.

İmzayı doğrula

v1, imza sırrıyla &quot;&lt;t&gt;.&lt;ham gövde&gt;&quot; metninin HMAC-SHA256'sıdır (hex). t beş dakikadan eskiyse reddet — tekrar oynatılan istek budur.

ts
import { createHmac, timingSafeEqual } from "node:crypto";

export function imzaGecerli(hamGovde: string, baslik: string, sir: string): boolean {
  const parca = Object.fromEntries(baslik.split(",").map(p => p.trim().split("=")));
  const t = Number(parca.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const beklenen = createHmac("sha256", sir).update(`${t}.${hamGovde}`).digest("hex");
  const a = Buffer.from(beklenen);
  const b = Buffer.from(String(parca.v1 ?? ""));
  return a.length === b.length && timingSafeEqual(a, b);
}

Müşterinin arayüzünden iki eylem

UçNe yapar
POST …/custom/&lt;kanal&gt;/conversations/&lt;konuşma&gt;/escalate"Bir insanla görüşmek istiyorum" — AI'dan ekibe devreder. Zaten insandaysa {&quot;transferred&quot;: false, &quot;reason&quot;: &quot;already_human&quot;}.
POST …/custom/&lt;kanal&gt;/conversations/&lt;konuşma&gt;/csat{&quot;rating&quot;: 4, &quot;remark&quot;: &quot;…&quot;} — memnuniyet puanı. CSAT kapalıysa ya da zaten puanlandıysa 409.

Hatalar

Gövde her zaman aynı biçimdedir:

json
{ "type": "error.list", "errors": [{ "code": "parameter_invalid", "message": "Send at least one field or a message." }] }
DurumNe zaman
400 parameter_invalidGövde okunamadı, tanınmayan alan, boş gönderim, geçersiz e-posta, tavanı aşan değer (sessizce kırpılmaz).
401 unauthorizedAnahtar yok, yanlış ya da yenilenmiş.
403 forbiddenBaşka bir kanalın anahtarı; kanal anahtarıyla kanal dışı bir uç; kapsamı eksik API anahtarı; çalışma alanının CRM aboneliği kapalı.
404 not_foundKanal yok ya da kaldırılmış.
409 conflictKanal duraklatılmış; form kanalına /messages ya da sohbet kanalına /submissions.
429 rate_limit_exceededKanal başına dakikada 300 istek aşıldı — X-RateLimit-Reset saniye sonra yeniden dene.