Connectly AI
كيف يعمل المميزات المعمارية التكاملات لماذا نحن الأسعار الأسئلة الشائعة المطورون
تسجيل دخول تسجيل
Developers

Build with Connectly

Add AI-first customer chat - with handoff to your human agents - to your website or mobile app.

On this page
Before you startInstallationShow the chatCustomizeCallbacksConversationsIdentified visitorsBuilt-in behaviorAPI reference
01

Before you start

Every integration talks to a Chatting App - one per website or mobile app. Ask your Connectly workspace admin to create one (Desk → Chatting App) and send you:

كل تكامل يتصل بـ تطبيق محادثة (Chatting App) - واحد لكل موقع أو تطبيق جوال. اطلب من مسؤول مساحة عمل Connectly إنشاء واحد (Desk ← Chatting App) وإرسال ما يلي إليك:

  • Public key (pk_…) - identifies your app. Safe to ship in a web page or app binary.
  • Secret key - only if you sign in your own users (see Identified visitors). Keep it on your server, never in the client.
  • Server URL - https://connectly.wowdigital.sa (used for both the API and the realtime connection).
  • المفتاح العام (pk_…) - يعرّف تطبيقك. آمن لتضمينه في صفحة ويب أو تطبيق.
  • المفتاح السري - فقط إذا كنت تربط المحادثة بمستخدميك المسجّلين (انظر الزوار المعرّفون). احتفظ به على خادمك فقط، وليس في التطبيق أبداً.
  • عنوان الخادم - https://connectly.wowdigital.sa (يُستخدم للواجهة البرمجية والاتصال اللحظي معاً).
Web apps: the Chatting App only accepts visitors from the sites listed in its Allowed Origins (e.g. https://www.your-site.com). Ask for your production and staging addresses to be added.
تطبيقات الويب: لا يقبل تطبيق المحادثة الزوار إلا من المواقع المدرجة في Allowed Origins (مثل https://www.your-site.com). اطلب إضافة عناوين موقعك الفعلي وموقع الاختبار.
This tab is the ready-made chat - a complete chat UI you place on your page and style with a theme and options. Most websites need only this. Want to draw the chat yourself? See the Custom UI tab.
هذا التبويب خاص بـالمحادثة الجاهزة - واجهة محادثة كاملة تضعها في صفحتك وتخصّص مظهرها وخياراتها. معظم المواقع تحتاج هذه فقط. تريد رسم المحادثة بنفسك؟ انظر تبويب واجهة مخصصة.
02

Installation

Add one script tag to your page. No build step or package manager needed.

أضف وسم script واحداً إلى صفحتك. لا حاجة لأدوات بناء أو مدير حزم.

html
<script src="https://connectly.wowdigital.sa/assets/connectly/chatting-sdk/connectly-chat.js"></script>
The SDK loads socket.io-client by itself. If your page already includes socket.io-client v4, that copy is used.
تحمّل المكتبة socket.io-client تلقائياً. وإذا كانت صفحتك تتضمن socket.io-client الإصدار 4 فستُستخدم تلك النسخة.
03

Show the chat

ConnectlyChatWidget is a complete chat: messages, images, files, voice notes, typing indicator and the "talk to a human" flow. Add a wrapper element to your page and mount the chat into it - it fills the element.

ConnectlyChatWidget محادثة كاملة: الرسائل والصور والملفات والرسائل الصوتية ومؤشر الكتابة وخيار "التحدث مع موظف". أضف عنصراً حاوياً إلى صفحتك وضع المحادثة بداخله - وستملأ العنصر بالكامل.

html
<div id="support-chat" style="height: 600px"></div>

<script src="https://connectly.wowdigital.sa/assets/connectly/chatting-sdk/connectly-chat.js"></script>
<script>
  const widget = ConnectlyChatWidget.mount({
    publicKey: "pk_xxx",
    container: "support-chat",   // the wrapper element's id
    title: "Support",
  });
</script>
Give the wrapper a height - the chat takes the element's size (if it has none, the chat is 600px tall). container can also be a CSS selector or the element itself.
حدّد ارتفاعاً للعنصر الحاوي - تأخذ المحادثة حجمه (وإن لم يكن له ارتفاع يكون ارتفاع المحادثة 600px). يمكن أن تكون قيمة container محدِّد CSS أو العنصر نفسه أيضاً.

Floating chat button

Leave out container to get a round chat button in the corner of the page instead. It opens the chat above it (full screen on phones) and shows a badge for unread replies. It connects only when the visitor first opens it.

احذف container لتحصل بدلاً من ذلك على زر محادثة دائري في زاوية الصفحة. يفتح المحادثة فوقه (بملء الشاشة على الجوال) ويعرض عدد الردود غير المقروءة. ولا يتصل إلا عندما يفتحه الزائر أول مرة.

javascript
const widget = ConnectlyChatWidget.mount({
  publicKey: "pk_xxx",
  position: "right",   // or "left"
  startOpen: false,
});

widget.open();      // open the chat from your own "Contact us" button
widget.close();     // minimise it - the conversation continues
widget.destroy();   // remove it and end the conversation
apiBaseUrl and wsUrl default to the server the script is loaded from (https://connectly.wowdigital.sa). Set them only if you host connectly-chat.js yourself.
القيمتان apiBaseUrl وwsUrl تساويان افتراضياً الخادم الذي حُمّل منه السكربت (https://connectly.wowdigital.sa). حدّدهما فقط إذا كنت تستضيف connectly-chat.js بنفسك.
04

Customize

Match your brand with theme, and switch features or change any text with options. Pass only what you want to change. The names are the same as the Flutter SDK's ConnectlyChatTheme and ConnectlyChatOptions; times are in milliseconds.

طابق هوية علامتك عبر theme، وفعّل الميزات أو غيّر أي نص عبر options. مرّر فقط ما تريد تغييره. الأسماء نفسها المستخدمة في ConnectlyChatTheme وConnectlyChatOptions في حزمة Flutter؛ والأوقات بالملي ثانية.

javascript
ConnectlyChatWidget.mount({
  publicKey: "pk_xxx",
  container: "support-chat",
  theme: {
    primaryColor: "#16A34A",
    headerBackgroundColor: "#16A34A",
    visitorBubbleColor: "#16A34A",
    backgroundImage: "https://www.your-site.com/chat-bg.png",
    bubbleBorderRadius: 16,
  },
  options: {
    enableAttachments: true,
    enableVoiceRecording: false,
    enableTalkToHuman: true,
    welcomeMessage: "Hi! How can we help you today?",
    inputPlaceholder: "Type a message...",
  },
});

Options

OptionالخيارDefaultالافتراضيDescriptionالوصف
enableAttachmentstrueAttach files and imagesإرفاق الملفات والصور
enableVoiceRecordingtrueRecord voice messages (shown on HTTPS pages, in browsers that can record)تسجيل رسائل صوتية (يظهر في صفحات HTTPS وفي المتصفحات التي تدعم التسجيل)
enableTalkToHumantrueShow "Talk to a human" in the chatإظهار "التحدث مع موظف" داخل المحادثة
enableTypingIndicatortrueAnimated dots while the other side typesنقاط متحركة أثناء كتابة الطرف الآخر
enableSoundstrueA soft chime when a message arrivesتنبيه صوتي خفيف عند وصول رسالة
allowedFileExtensionspng, jpg, jpeg, gif, webp, pdf, doc, docx, txtPickable file typesأنواع الملفات المسموحة
maxFileSizeBytes10485760 (10 MB)Largest attachmentأكبر حجم للمرفق
maxRecordingDuration120000 (2 min)Longest voice note, in millisecondsأطول رسالة صوتية، بالملي ثانية
messageCharacterLimit2000Longest text message (0 = no limit)أطول رسالة نصية (0 = بلا حد)
typingThrottle2000How often "typing" is sent while the visitor types, in millisecondsمعدل إرسال إشارة "يكتب" أثناء كتابة الزائر، بالملي ثانية
resumeInterruptedChatWithin1800000 (30 min)If the page is reloaded or closed while the chat is open, coming back within this time continues the same chat (0 = off)إذا أُعيد تحميل الصفحة أو أُغلقت أثناء فتح المحادثة، تُستكمل نفس المحادثة عند العودة خلال هذه المدة (0 = إيقاف)
direction"auto""ltr", "rtl", or "auto" to follow the page's <html dir>"ltr" أو "rtl" أو "auto" لاتباع <html dir> في الصفحة
welcomeMessage, inputPlaceholder, …LabelEnglish textنصوص إنجليزيةEvery visible text can be replaced - e.g. with your translations (see below)يمكن استبدال كل نص ظاهر - بترجماتك مثلاً (انظر أدناه)

Theme

PropertyالخاصيةDefaultالافتراضيDescriptionالوصف
primaryColor#4F46E5Send button, chat button, highlightsزر الإرسال وزر المحادثة والعناصر المميزة
backgroundColor#F3F4F6Behind the messagesخلفية منطقة الرسائل
backgroundImagenullImage behind the messages: a URL, or { url, fit, opacity } with fit "cover", "contain" or "repeat"صورة خلف الرسائل: رابط، أو { url, fit, opacity } حيث fit هي "cover" أو "contain" أو "repeat"
visitorBubbleColor / visitorTextColor#4F46E5 / #FFFFFFThe visitor's own messagesرسائل الزائر نفسه
otherBubbleColor / otherTextColor#F1F1F4 / rgba(0,0,0,.87)Messages from the AI and agentsرسائل المساعد الذكي والموظفين
bubbleBorderRadius16Bubble corners: pixels, or any CSS value like "16px 16px 4px 16px"زوايا الفقاعات: بالبكسل، أو أي قيمة CSS مثل "16px 16px 4px 16px"
headerBackgroundColor / headerTextColor#4F46E5 / #FFFFFFThe title barشريط العنوان
messageTextStyle / timestampTextStylenullCSS properties, e.g. { fontSize: "15px" }خصائص CSS، مثل { fontSize: "15px" }
fontFamilySystem fontsخطوط النظامFont of the whole chatخط المحادثة بالكامل
composerBackgroundColor / inputTextColor#FFFFFF / #111827The message box areaمنطقة مربع الكتابة

Texts and Arabic

Every text the chat shows is an option: title, welcomeMessage, inputPlaceholder, talkToHumanLabel, talkToHumanHint, connectingLabel, onlineLabel, waitingForAgentLabel, agentConnectedLabel, agentLeftLabel, connectionLostLabel, connectErrorLabel, retryLabel, rateLimitedLabel, notSentLabel, fileTooLargeLabel, fileTypeNotAllowedLabel, microphonePermissionLabel, recordingLabel, recordingTooShortLabel, loadMessagesFailedLabel, attachmentLabel. {seconds}, {size} and {max} in a text are replaced with the actual value.

كل نص تعرضه المحادثة هو خيار: title, welcomeMessage, inputPlaceholder, talkToHumanLabel, talkToHumanHint, connectingLabel, onlineLabel, waitingForAgentLabel, agentConnectedLabel, agentLeftLabel, connectionLostLabel, connectErrorLabel, retryLabel, rateLimitedLabel, notSentLabel, fileTooLargeLabel, fileTypeNotAllowedLabel, microphonePermissionLabel, recordingLabel, recordingTooShortLabel, loadMessagesFailedLabel, attachmentLabel. وتُستبدل {seconds} و{size} و{max} داخل النص بالقيمة الفعلية.

javascript
ConnectlyChatWidget.mount({
  publicKey: "pk_xxx",
  container: "support-chat",
  title: "تواصل معنا",
  options: {
    direction: "rtl",
    welcomeMessage: "أهلاً بك! كيف نقدر نساعدك؟",
    inputPlaceholder: "اكتب رسالتك...",
    onlineLabel: "متصل",
    connectingLabel: "جارٍ الاتصال...",
    talkToHumanHint: "تحتاج موظفاً؟",
    talkToHumanLabel: "التحدث مع موظف",
    waitingForAgentLabel: "جارٍ تحويلك إلى موظف...",
    agentConnectedLabel: "أنت الآن تتحدث مع موظف",
    agentLeftLabel: "غادر الموظف المحادثة. المساعد الذكي عاد لمساعدتك.",
    notSentLabel: "لم تُرسل. اضغط لإعادة المحاولة",
    rateLimitedLabel: "أنت ترسل بسرعة كبيرة. حاول بعد {seconds} ثانية.",
    retryLabel: "إعادة المحاولة",
  },
});
05

Callbacks

Hook into the chat without building your own UI:

تابع أحداث المحادثة دون بناء واجهتك الخاصة:

javascript
ConnectlyChatWidget.mount({
  publicKey: "pk_xxx",
  container: "support-chat",
  onChatStarted: (chatId) => localStorage.setItem("my_chat_id", chatId), // keep it to resume later
  onMessageReceived: (m) => console.log(m.role, m.content),
  onMessageSend: (m) => analytics.track("chat_send", { type: m.type }),
  onMessageStatusChanged: (m) => console.log(m.id, m.status),            // "sent" or "failed"
  onClose: () => console.log("chat closed"),
});

Messages have the same fields as in Custom UI: id, role, type, content, file, timestamp and status.

للرسائل نفس الحقول الموضحة في واجهة مخصصة: id وrole وtype وcontent وfile وtimestamp وstatus.

06

Conversations

Each time the widget is mounted a new conversation starts. If the visitor reloads the page (or leaves and comes back) while a chat is open, it continues within resumeInterruptedChatWithin. Minimising the floating chat never ends it; destroy() does. To continue a specific conversation, save the id from onChatStarted and pass it back as chatId - its earlier messages load automatically.

في كل مرة تُضاف فيها الواجهة تبدأ محادثة جديدة. وإذا أعاد الزائر تحميل الصفحة (أو غادرها ثم عاد) أثناء فتح المحادثة فإنها تُستكمل خلال مدة resumeInterruptedChatWithin. تصغير المحادثة العائمة لا ينهيها؛ أما destroy() فينهيها. لاستكمال محادثة بعينها، احفظ المعرّف من onChatStarted ومرّره مجدداً في chatId - وتُحمّل رسائلها السابقة تلقائياً.

javascript
// Open the conversation the visitor had before (null = start a new one):
ConnectlyChatWidget.mount({
  publicKey: "pk_xxx",
  container: "support-chat",
  chatId: localStorage.getItem("my_chat_id"),
  onChatStarted: (chatId) => localStorage.setItem("my_chat_id", chatId),
});
07

Identified visitors

Signed-in users can be linked to their account, so agents see who they're talking to and the user's chats follow them across devices. Your backend signs a short-lived token with the app's secret key - never the browser.

يمكن ربط المستخدمين المسجّلين بحساباتهم، فيرى الموظفون من يحادثون وتتبع محادثات المستخدم بين أجهزته. خادمك هو من يوقّع رمزاً قصير الأجل بالمفتاح السري للتطبيق - وليس المتصفح أبداً.

javascript
// In the browser - get a short-lived token from YOUR backend, then pass it in:
const { identify_token } = await fetch("/api/chat-token").then((r) => r.json());

ConnectlyChatWidget.mount({
  publicKey: "pk_xxx",
  container: "support-chat",
  identifyToken: identify_token,
});
ClaimالحقلRequiredإلزاميPurposeالغرض
user_idYesنعمYour user's idمعرّف المستخدم لديك
expYesنعمExpiry (Unix time) - keep it short, e.g. 5 minutesوقت الانتهاء (Unix) - اجعله قصيراً، مثل 5 دقائق
name, email, phoneNoلاShown to agentsتظهر للموظفين

Signed with HS256 using the Chatting App's secret key:

يُوقّع بخوارزمية HS256 باستخدام المفتاح السري لتطبيق المحادثة:

node.js
const jwt = require("jsonwebtoken");

app.get("/api/chat-token", requireLogin, (req, res) => {
  const token = jwt.sign(
    { user_id: req.user.id, name: req.user.name, email: req.user.email },
    process.env.CONNECTLY_SECRET_KEY,
    { algorithm: "HS256", expiresIn: "5m" }
  );
  res.json({ identify_token: token });
});
python
import time, jwt  # PyJWT

@app.get("/api/chat-token")
def chat_token(user=Depends(require_login)):
    token = jwt.encode(
        {"user_id": user.id, "name": user.name, "email": user.email,
         "exp": int(time.time()) + 300},
        CONNECTLY_SECRET_KEY,
        algorithm="HS256",
    )
    return {"identify_token": token}
08

Built-in behavior

  • Instant messages: messages appear right away with 🕓, then ✓ - or "Not sent, tap to retry". Retries are never delivered twice.
  • Files and voice notes: images show in the chat, documents open in a new tab, and voice notes - recorded right in the browser - play inline. While the AI reads an upload, the typing dots show.
  • Silent reconnecting: if the connection drops (laptop sleep, tab in the background, network change), the chat returns to the same conversation and loads anything missed. A Retry bar appears only if that fails.
  • Human handoff: "Talk to a human" sits above the message box, shows a waiting line once asked, and hides while an agent is in the chat.
  • Limits: up to 15 messages per minute per visitor; past that the message shows "Not sent" with how long to wait.
  • Your styles stay yours: the chat is isolated (Shadow DOM) - your page's CSS doesn't change it, and it doesn't change your page.
  • Footer: the chat shows a small "Powered by Connectly" line under the message box.
  • رسائل فورية: تظهر الرسالة فوراً مع 🕓 ثم ✓ - أو "لم تُرسل، اضغط لإعادة المحاولة". لا تُسلّم إعادة المحاولة مرتين أبداً.
  • الملفات والرسائل الصوتية: تظهر الصور داخل المحادثة، وتُفتح المستندات في تبويب جديد، وتُشغَّل الرسائل الصوتية - المسجّلة من المتصفح مباشرة - داخل المحادثة. وأثناء قراءة المساعد للمرفق تظهر نقاط الكتابة.
  • إعادة اتصال صامتة: إذا انقطع الاتصال (سكون الجهاز، تبويب في الخلفية، تغيّر الشبكة) تعود المحادثة لنفس الجلسة وتُحمّل ما فات. ولا يظهر شريط إعادة المحاولة إلا إذا فشل ذلك.
  • التحويل إلى موظف: يظهر "التحدث مع موظف" فوق مربع الكتابة، ثم سطر انتظار بعد الطلب، ويختفي أثناء وجود موظف في المحادثة.
  • الحدود: حتى 15 رسالة في الدقيقة لكل زائر؛ بعدها تظهر "لم تُرسل" مع مدة الانتظار.
  • تنسيقاتك تبقى كما هي: المحادثة معزولة (Shadow DOM) - لا تؤثر تنسيقات CSS في صفحتك عليها، ولا تؤثر هي على صفحتك.
  • التذييل: تعرض المحادثة سطراً صغيراً "Powered by Connectly" أسفل مربع الكتابة.
09

API reference

ConnectlyChatWidget.mount(config)

OptionالخيارDescriptionالوصف
publicKeyRequired. The Chatting App's public keyإلزامي. المفتاح العام لتطبيق المحادثة
containerWrapper element id, CSS selector or element to fill. Omit for a floating chat buttonمعرّف العنصر الحاوي أو محدِّد CSS أو العنصر نفسه. احذفه لزر محادثة عائم
titleHeader title (default "Chat with us")عنوان الشريط العلوي (الافتراضي "Chat with us")
themeColors and look - see Customizeالألوان والمظهر - انظر التخصيص
optionsFeatures, limits and texts - see Customizeالميزات والحدود والنصوص - انظر التخصيص
identifyTokenToken from your backend for a signed-in userرمز من خادمك لمستخدم مسجّل
chatIdResume this conversationاستكمال هذه المحادثة
position, startOpenFloating button only: "right" or "left" corner; open right awayللزر العائم فقط: الزاوية "right" أو "left"؛ والفتح مباشرة
onChatStarted, onMessageReceived, onMessageSend, onMessageStatusChanged, onCloseSee Callbacksانظر الاستدعاءات
apiBaseUrl, wsUrlOnly if you host the script yourself - default to the server it's loaded fromفقط إذا كنت تستضيف السكربت بنفسك - افتراضياً الخادم الذي حُمّل منه
MethodالدالةDescriptionالوصف
open()Opens the floating chatيفتح المحادثة العائمة
close()Minimises the floating chat (the conversation continues)يصغّر المحادثة العائمة (تستمر المحادثة)
toggle()Opens or closes itيفتحها أو يغلقها
destroy()Removes the chat and ends the conversationيزيل المحادثة وينهيها
clientThe ConnectlyChat it runs on - for listening to its eventsكائن ConnectlyChat الذي تعمل عليه - للاستماع إلى أحداثه
On this page
Before you startInstallationQuick startShowing messagesSending & delivery statusFiles & voice notesTalk to a humanConnection & reconnectingConversationsIdentified visitorsAPI reference
01

Before you start

Every integration talks to a Chatting App - one per website or mobile app. Ask your Connectly workspace admin to create one (Desk → Chatting App) and send you:

كل تكامل يتصل بـ تطبيق محادثة (Chatting App) - واحد لكل موقع أو تطبيق جوال. اطلب من مسؤول مساحة عمل Connectly إنشاء واحد (Desk ← Chatting App) وإرسال ما يلي إليك:

  • Public key (pk_…) - identifies your app. Safe to ship in a web page or app binary.
  • Secret key - only if you sign in your own users (see Identified visitors). Keep it on your server, never in the client.
  • Server URL - https://connectly.wowdigital.sa (used for both the API and the realtime connection).
  • المفتاح العام (pk_…) - يعرّف تطبيقك. آمن لتضمينه في صفحة ويب أو تطبيق.
  • المفتاح السري - فقط إذا كنت تربط المحادثة بمستخدميك المسجّلين (انظر الزوار المعرّفون). احتفظ به على خادمك فقط، وليس في التطبيق أبداً.
  • عنوان الخادم - https://connectly.wowdigital.sa (يُستخدم للواجهة البرمجية والاتصال اللحظي معاً).
Web apps: the Chatting App only accepts visitors from the sites listed in its Allowed Origins (e.g. https://www.your-site.com). Ask for your production and staging addresses to be added.
تطبيقات الويب: لا يقبل تطبيق المحادثة الزوار إلا من المواقع المدرجة في Allowed Origins (مثل https://www.your-site.com). اطلب إضافة عناوين موقعك الفعلي وموقع الاختبار.
This tab is for your own UI: ConnectlyChat is the engine without any UI - the same one the ready-made chat runs on. Just want a chat on your page? See the Ready-made chat tab.
هذا التبويب خاص بـواجهتك الخاصة: ConnectlyChat هو المحرك بدون واجهة - نفس المحرك الذي تعمل عليه المحادثة الجاهزة. تريد محادثة جاهزة في صفحتك فقط؟ انظر تبويب محادثة جاهزة.
02

Installation

Add one script tag to your page. No build step or package manager needed.

أضف وسم script واحداً إلى صفحتك. لا حاجة لأدوات بناء أو مدير حزم.

html
<script src="https://connectly.wowdigital.sa/assets/connectly/chatting-sdk/connectly-chat.js"></script>
The SDK loads socket.io-client by itself. If your page already includes socket.io-client v4, that copy is used.
تحمّل المكتبة socket.io-client تلقائياً. وإذا كانت صفحتك تتضمن socket.io-client الإصدار 4 فستُستخدم تلك النسخة.
03

Quick start

ConnectlyChat handles the connection, the conversation and reconnecting. You render the messages in your own UI, so it fits any design.

يتولى ConnectlyChat الاتصال والمحادثة وإعادة الاتصال، وأنت تعرض الرسائل بواجهتك الخاصة، لذا يناسب أي تصميم.

javascript
const chat = new ConnectlyChat({
  publicKey: "pk_xxx",
  apiBaseUrl: "https://connectly.wowdigital.sa",
  wsUrl: "https://connectly.wowdigital.sa",
});

// The whole conversation - on first load, after a page reload and after every reconnect.
chat.on("history", (messages) => {
  clearMessages();
  messages.forEach(renderMessage);
});

// New messages from the AI assistant or a human agent.
chat.on("message", (message) => renderMessage(message));

chat.on("typing", () => showTypingIndicator());

await chat.connect();
04

Showing messages

Each message has id, role, type (text, image, audio, document), content, file, timestamp and, for the visitor's own messages, status. History and live messages have the same shape.

تحتوي كل رسالة على id وrole وtype (text أو image أو audio أو document) وcontent وfile وtimestamp، وعلى status لرسائل الزائر نفسه. رسائل السجل والرسائل اللحظية لها نفس الشكل.

javascript
function renderMessage(m) {
  // Only "is it the visitor?" decides the side. Other roles are free-form:
  // "assistant" for the AI, an agent's user name for a human agent.
  const mine = m.role === "user";
  addBubble({
    side: mine ? "right" : "left",
    type: m.type,                 // "text" | "image" | "audio" | "document"
    text: m.content,              // the caption for files (may be empty)
    file: m.file,                 // absolute URL for images, voice notes, documents
    time: new Date(m.timestamp),  // when it was sent
    status: m.status,             // "sent" on the visitor's stored messages
  });
}
Style bubbles by role === "user" only. Human agents' messages carry the agent's user name as their role (for example Administrator), so never use the role itself as a CSS class.
حدّد شكل الفقاعة بالشرط role === "user" فقط. رسائل الموظفين تحمل اسم المستخدم كـ role (مثل Administrator)، فلا تستخدم قيمة role نفسها كاسم class.
05

Sending & delivery status

Show the visitor's message immediately, then confirm it. sendMessage() resolves once the server has the message, and waits through a brief reconnect instead of failing.

اعرض رسالة الزائر فوراً ثم أكّدها. تكتمل sendMessage() عندما يستلم الخادم الرسالة، وتنتظر خلال انقطاع قصير بدلاً من أن تفشل.

javascript
async function send(text) {
  const clientId = crypto.randomUUID();     // keep it to resend the same message
  const bubble = addBubble({ side: "right", text, status: "sending" }); // show it right away

  try {
    await chat.sendMessage(text, { clientId });
    bubble.setStatus("sent");              // ✓ - the server has it
  } catch (err) {
    bubble.setStatus("failed");            // show "Not sent" + a Resend button
    if (err.code === "rate_limited") {
      showNotice(`You're sending messages too fast. Try again in ${err.retryAfter}s.`);
    }
  }
}
// Resend = call sendMessage again with the SAME clientId - never delivered twice.
Each visitor can send up to 15 messages per minute. Past that, sendMessage() rejects with err.code === "rate_limited" and err.retryAfter (seconds); the message wasn't processed and can be resent.
يمكن لكل زائر إرسال 15 رسالة في الدقيقة كحد أقصى. بعدها ترفض sendMessage() بالخطأ err.code === "rate_limited" مع err.retryAfter (بالثواني)؛ لم تُعالج الرسالة ويمكن إعادة إرسالها.
06

Files & voice notes

sendAttachment() uploads a File or Blob - an image, a document, or a voice note recorded with MediaRecorder. The server makes it an image, audio or document message from its type. It resolves once the file is stored; the AI's reply arrives as a message event.

ترفع sendAttachment() ملفاً من نوع File أو Blob - صورة أو مستنداً أو رسالة صوتية مسجّلة عبر MediaRecorder. يحدد الخادم نوع الرسالة (صورة أو صوت أو مستند) من نوع الملف. تكتمل عند حفظ الملف، ويصل رد المساعد كحدث message.

javascript
fileInput.onchange = async () => {
  const file = fileInput.files[0];
  const clientId = crypto.randomUUID();     // same idea as sendMessage - retries are safe
  const bubble = addBubble({ side: "right", file: URL.createObjectURL(file), status: "sending" });

  try {
    const { file: url, type } = await chat.sendAttachment(file, { clientId, caption: "" });
    bubble.setStatus("sent");               // url: the stored file; type: image | audio | document
  } catch (err) {
    bubble.setStatus("failed");             // same rate limit as sendMessage (err.code, err.retryAfter)
  }
};

// A voice note: pass the recorded Blob with a file name.
await chat.sendAttachment(blob, { fileName: "voice.webm" });
07

Talk to a human

Conversations start with the AI assistant. A visitor can ask for a person at any time; the AI stops answering and the chat appears in your agents' live-chat inbox. Hide the button while chat.isLive is true - a person already owns the chat.

تبدأ المحادثات مع المساعد الذكي. يمكن للزائر طلب موظف في أي وقت؛ فيتوقف المساعد عن الرد وتظهر المحادثة في صندوق المحادثات المباشرة لدى موظفيك. أخفِ الزر ما دامت chat.isLive صحيحة - فالمحادثة لدى موظف بالفعل.

javascript
chat.on("ready", ({ is_live, handoff_status }) => updateHandoffUI());
chat.on("handoff_requested", () => showWaiting("Connecting you with a human agent..."));
chat.on("handoff_accepted",  () => showNotice("A human agent has joined the chat."));
chat.on("handoff_resolved",  () => showNotice("The agent has left. Our assistant is back."));
chat.on("live_changed", ({ is_live }) => updateHandoffUI());

function updateHandoffUI() {
  // Offer the button only while the AI is answering.
  talkToHumanButton.hidden = chat.isLive;
}

talkToHumanButton.onclick = () => chat.requestHuman("Customer asked for an agent");
08

Connection & reconnecting

Reconnecting is automatic and silent. If the connection drops - flaky network, laptop sleep, the tab in the background - the SDK returns to the same conversation and emits history with everything, including replies sent while offline. When the tab becomes visible again or the network comes back, it checks the connection right away.

إعادة الاتصال تلقائية وصامتة. إذا انقطع الاتصال - شبكة ضعيفة، سكون الجهاز، تبويب في الخلفية - تعود المكتبة إلى نفس المحادثة وتُطلق history بكل الرسائل، بما فيها الردود التي وصلت أثناء الانقطاع. وعند عودة التبويب للظهور أو عودة الشبكة تتحقق من الاتصال فوراً.

javascript
chat.on("connection", ({ state }) => {
  // "connecting" | "connected" | "reconnecting" | "failed"
  retryBar.hidden = state !== "failed";    // only shown after automatic retries gave up
});

retryButton.onclick = () => chat.retry();
09

Conversations

With ConnectlyChat, a returning visitor continues their previous conversation by default (the visitor is remembered in localStorage).

مع ConnectlyChat يكمل الزائر العائد محادثته السابقة افتراضياً (يُحفظ الزائر في localStorage).

javascript
// Default: the visitor returns to their previous conversation.
new ConnectlyChat({ publicKey, apiBaseUrl, wsUrl });

// Always start a fresh conversation:
new ConnectlyChat({ publicKey, apiBaseUrl, wsUrl, newChat: true });

// Resume a specific conversation (an id you saved from the "ready" event):
chat.on("ready", ({ chat_id }) => localStorage.setItem("my_chat_id", chat_id));
await chat.connect({ chatId: localStorage.getItem("my_chat_id") });
10

Identified visitors

Signed-in users can be linked to their account, so agents see who they're talking to and the user's chats follow them across devices. Your backend signs a short-lived token with the app's secret key - never the browser.

يمكن ربط المستخدمين المسجّلين بحساباتهم، فيرى الموظفون من يحادثون وتتبع محادثات المستخدم بين أجهزته. خادمك هو من يوقّع رمزاً قصير الأجل بالمفتاح السري للتطبيق - وليس المتصفح أبداً.

javascript
// In the browser - get a short-lived token from YOUR backend, then pass it in:
const { identify_token } = await fetch("/api/chat-token").then((r) => r.json());

await chat.connect({ identifyToken: identify_token });
ClaimالحقلRequiredإلزاميPurposeالغرض
user_idYesنعمYour user's idمعرّف المستخدم لديك
expYesنعمExpiry (Unix time) - keep it short, e.g. 5 minutesوقت الانتهاء (Unix) - اجعله قصيراً، مثل 5 دقائق
name, email, phoneNoلاShown to agentsتظهر للموظفين

Signed with HS256 using the Chatting App's secret key:

يُوقّع بخوارزمية HS256 باستخدام المفتاح السري لتطبيق المحادثة:

node.js
const jwt = require("jsonwebtoken");

app.get("/api/chat-token", requireLogin, (req, res) => {
  const token = jwt.sign(
    { user_id: req.user.id, name: req.user.name, email: req.user.email },
    process.env.CONNECTLY_SECRET_KEY,
    { algorithm: "HS256", expiresIn: "5m" }
  );
  res.json({ identify_token: token });
});
python
import time, jwt  # PyJWT

@app.get("/api/chat-token")
def chat_token(user=Depends(require_login)):
    token = jwt.encode(
        {"user_id": user.id, "name": user.name, "email": user.email,
         "exp": int(time.time()) + 300},
        CONNECTLY_SECRET_KEY,
        algorithm="HS256",
    )
    return {"identify_token": token}
11

API reference

new ConnectlyChat(options)

OptionالخيارDescriptionالوصف
publicKeyRequired. The Chatting App's public keyإلزامي. المفتاح العام لتطبيق المحادثة
apiBaseUrlRequired. Server URLإلزامي. عنوان الخادم
wsUrlRequired. Realtime URL - usually the same as apiBaseUrlإلزامي. عنوان الاتصال اللحظي - عادةً نفس apiBaseUrl
newChatStart a fresh conversation instead of resuming the visitor's last one (default false)بدء محادثة جديدة بدلاً من استكمال الأخيرة (الافتراضي false)
chatIdResume this conversationاستكمال هذه المحادثة
MethodالدالةDescriptionالوصف
connect(options?)Opens the chat. Options: identifyToken, chatId, newChatيفتح المحادثة. الخيارات: identifyToken وchatId وnewChat
sendMessage(text, { clientId })Sends text; resolves when the server has itيرسل نصاً؛ يكتمل عند استلام الخادم له
sendAttachment(file, { clientId, caption, fileName })Uploads a file or voice note; resolves with { id, file, type }يرفع ملفاً أو رسالة صوتية؛ يكتمل بـ { id, file, type }
sendTyping()Tells the other side the visitor is typing (throttle to ~every 2s)يُعلم الطرف الآخر أن الزائر يكتب (مرة كل ثانيتين تقريباً)
requestHuman(reason?)Asks for a human agentيطلب موظفاً
getHistory({ page, pageSize })The conversation so far, oldest firstالمحادثة حتى الآن، من الأقدم
on(event, fn) / off(event, fn)Listen to an event / stop listeningالاستماع إلى حدث / إيقاف الاستماع
retry()Reconnect after the connection state is "failed"إعادة الاتصال بعد حالة "failed"
ensureConnected()Checks the connection now and reconnects if it's dead (done automatically when the tab returns)يتحقق من الاتصال الآن ويعيده إن كان منقطعاً (يحدث تلقائياً عند عودة التبويب)
disconnect()Closes the connectionيغلق الاتصال
EventالحدثPayloadالبياناتWhenمتى
ready{ chat_id, is_live, handoff_status }Connected to the conversation (also after reconnects)عند الاتصال بالمحادثة (وبعد كل إعادة اتصال)
history[message]The whole conversation - render itالمحادثة كاملة - اعرضها
message{ role, type, content, file, timestamp }A new message from the AI or an agentرسالة جديدة من المساعد أو موظف
typing-The other side is typingالطرف الآخر يكتب
handoff_requested, handoff_accepted, handoff_resolved{ handoff }Human handoff progressمراحل التحويل إلى موظف
live_changed{ is_live }An agent took over / handed backتولّى موظف المحادثة / أعادها
connection{ state }connecting, connected, reconnecting, failedconnecting أو connected أو reconnecting أو failed
error{ stage, message }For logging - failures that need the user show as connection "failed"للتسجيل - الأعطال التي تهم المستخدم تظهر كحالة failed
On this page
Before you startInstallationPlatform setupConfigureShow the chatCustomizeCallbacksConversationsIdentified visitorsBuilt-in behaviorCustom UI (client API)
01

Before you start

Every integration talks to a Chatting App - one per website or mobile app. Ask your Connectly workspace admin to create one (Desk → Chatting App) and send you:

كل تكامل يتصل بـ تطبيق محادثة (Chatting App) - واحد لكل موقع أو تطبيق جوال. اطلب من مسؤول مساحة عمل Connectly إنشاء واحد (Desk ← Chatting App) وإرسال ما يلي إليك:

  • Public key (pk_…) - identifies your app. Safe to ship in a web page or app binary.
  • Secret key - only if you sign in your own users (see Identified visitors). Keep it on your server, never in the client.
  • Server URL - https://connectly.wowdigital.sa (used for both the API and the realtime connection).
  • المفتاح العام (pk_…) - يعرّف تطبيقك. آمن لتضمينه في صفحة ويب أو تطبيق.
  • المفتاح السري - فقط إذا كنت تربط المحادثة بمستخدميك المسجّلين (انظر الزوار المعرّفون). احتفظ به على خادمك فقط، وليس في التطبيق أبداً.
  • عنوان الخادم - https://connectly.wowdigital.sa (يُستخدم للواجهة البرمجية والاتصال اللحظي معاً).
Web apps: the Chatting App only accepts visitors from the sites listed in its Allowed Origins (e.g. https://www.your-site.com). Ask for your production and staging addresses to be added.
تطبيقات الويب: لا يقبل تطبيق المحادثة الزوار إلا من المواقع المدرجة في Allowed Origins (مثل https://www.your-site.com). اطلب إضافة عناوين موقعك الفعلي وموقع الاختبار.
02

Installation

Coming soon to pub.dev as connectly_chat. Source: github.com/WOW-DIT/connectly_chat_flutter
قريباً على pub.dev باسم connectly_chat. الشيفرة المصدرية: github.com/WOW-DIT/connectly_chat_flutter
shell
flutter pub add connectly_chat

or add it to pubspec.yaml:

أو أضفها إلى pubspec.yaml:

yaml
dependencies:
  connectly_chat: ^1.0.0

Until it's on pub.dev, install it straight from GitHub (requires access to the repository):

إلى أن تُنشر على pub.dev، ثبّتها مباشرة من GitHub (يتطلب صلاحية الوصول إلى المستودع):

yaml · github
dependencies:
  connectly_chat:
    git:
      url: https://github.com/WOW-DIT/connectly_chat_flutter.git
03

Platform setup

Flutter packages can't change your app's manifest, so add these yourself:

لا تستطيع حزم Flutter تعديل ملف manifest لتطبيقك، لذا أضف ما يلي بنفسك:

xml · android
<!-- android/app/src/main/AndroidManifest.xml, inside <manifest> -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- only if voice messages are enabled -->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>

<!-- lets the chat open documents (Android 11+), inside <queries> -->
<queries>
  <intent>
    <action android:name="android.intent.action.VIEW"/>
    <data android:scheme="https"/>
  </intent>
</queries>
xml · ios
<!-- ios/Runner/Info.plist - only if voice messages are enabled -->
<key>NSMicrophoneUsageDescription</key>
<string>Used to record voice messages in chat.</string>
04

Configure

Configure once, before runApp(). The connection details belong in the app build - never on a settings screen.

اضبط الإعدادات مرة واحدة قبل runApp(). بيانات الاتصال جزء من بناء التطبيق - وليست شاشة إعدادات أبداً.

dart
import 'package:connectly_chat/connectly_chat.dart';
import 'package:flutter/material.dart';

void main() {
  ConnectlyChat.configure(const ConnectlyChatConfig(
    publicKey: 'pk_xxx',
    apiBaseUrl: 'https://connectly.wowdigital.sa',
    wsUrl: 'https://connectly.wowdigital.sa',
  ));
  runApp(const MyApp());
}

Or keep them out of the source and pass them at build time:

أو أبقها خارج الشيفرة ومرّرها عند البناء:

dart · shell
// main.dart
ConnectlyChat.configure(ConnectlyChatConfig.fromEnvironment());

// build / run with:
flutter run \
  --dart-define=CONNECTLY_PUBLIC_KEY=pk_xxx \
  --dart-define=CONNECTLY_API_BASE_URL=https://connectly.wowdigital.sa \
  --dart-define=CONNECTLY_WS_URL=https://connectly.wowdigital.sa
Running from the IDE? Add the three --dart-define flags to its launch configuration - without them the app stops at startup with a message naming the missing value.
تشغّل من بيئة التطوير؟ أضف الأعلام الثلاثة --dart-define إلى إعدادات التشغيل - بدونها يتوقف التطبيق عند البدء برسالة توضح القيمة الناقصة.
05

Show the chat

ConnectlyChatWidget is a complete chat screen: messages, images, files, voice notes, typing indicator and the "talk to a human" flow. Push it as a page or embed it anywhere.

ConnectlyChatWidget شاشة محادثة كاملة: الرسائل والصور والملفات والرسائل الصوتية ومؤشر الكتابة وخيار "التحدث مع موظف". افتحها كصفحة أو ضمّنها في أي مكان.

dart
Navigator.push(context, MaterialPageRoute(
  builder: (_) => Scaffold(
    body: SafeArea(
      child: ConnectlyChatWidget(
        title: 'Support',
        onClose: () => Navigator.pop(context),
      ),
    ),
  ),
));
06

Customize

Match your brand with ConnectlyChatTheme, and switch features or change any text with ConnectlyChatOptions.

طابق هوية علامتك عبر ConnectlyChatTheme، وفعّل الميزات أو غيّر أي نص عبر ConnectlyChatOptions.

dart
ConnectlyChatWidget(
  theme: const ConnectlyChatTheme(
    primaryColor: Color(0xFF16A34A),
    headerBackgroundColor: Color(0xFF16A34A),
    visitorBubbleColor: Color(0xFF16A34A),
  ),
  options: const ConnectlyChatOptions(
    enableAttachments: true,
    enableVoiceRecording: false,
    enableTalkToHuman: true,
    welcomeMessage: 'Hi! How can we help you today?',
    inputPlaceholder: 'Type a message...',
  ),
)

Options

OptionالخيارDefaultالافتراضيDescriptionالوصف
enableAttachmentstrueAttach files and imagesإرفاق الملفات والصور
enableVoiceRecordingtrueRecord voice messagesتسجيل رسائل صوتية
enableTalkToHumantrueShow "Talk to a human" in the chatإظهار "التحدث مع موظف" داخل المحادثة
enableTypingIndicatortrueAnimated dots while the other side typesنقاط متحركة أثناء كتابة الطرف الآخر
allowedFileExtensionspng, jpg, jpeg, gif, webp, pdf, doc, docx, txtPickable file typesأنواع الملفات المسموحة
maxFileSizeBytes10 MBLargest attachmentأكبر حجم للمرفق
maxRecordingDuration2 minLongest voice noteأطول رسالة صوتية
messageCharacterLimit2000Longest text message (0 = no limit)أطول رسالة نصية (0 = بلا حد)
resumeInterruptedChatWithin30 minIf the OS kills the app while the chat is open, reopening it within this time continues the same chatإذا أغلق النظام التطبيق أثناء فتح المحادثة، تُستكمل نفس المحادثة عند فتحها خلال هذه المدة
welcomeMessage`, `inputPlaceholder`, `…LabelEnglish textنصوص إنجليزيةEvery visible text can be replaced - e.g. with your translationsيمكن استبدال كل نص ظاهر - بترجماتك مثلاً

Theme

primaryColor, backgroundColor, backgroundImage, visitorBubbleColor, otherBubbleColor, visitorTextColor, otherTextColor, bubbleBorderRadius, headerBackgroundColor, headerTextColor, messageTextStyle, timestampTextStyle.

primaryColor وbackgroundColor وbackgroundImage وvisitorBubbleColor وotherBubbleColor وvisitorTextColor وotherTextColor وbubbleBorderRadius وheaderBackgroundColor وheaderTextColor وmessageTextStyle وtimestampTextStyle.

07

Callbacks

Hook into the chat without building your own UI:

تابع أحداث المحادثة دون بناء واجهتك الخاصة:

dart
ConnectlyChatWidget(
  onChatStarted: (chatId) => saveChatId(chatId),          // keep it to resume later
  onMessageReceived: (m) => debugPrint('${m.role}: ${m.content}'),
  onMessageSend: (m) => analytics.log('chat_send', m.type.name),
  onMessageStatusChanged: (m) => debugPrint('${m.id} -> ${m.status}'), // sent / failed
)
08

Conversations

Each time the chat screen opens, a new conversation starts. To continue one, save the id from onChatStarted and pass it back as chatId - its earlier messages load automatically.

في كل مرة تُفتح شاشة المحادثة تبدأ محادثة جديدة. لاستكمال محادثة، احفظ المعرّف من onChatStarted ومرّره مجدداً في chatId - وتُحمّل رسائلها السابقة تلقائياً.

dart
// Open the conversation the user had before (null = start a new one):
ConnectlyChatWidget(
  chatId: await loadSavedChatId(),
  onChatStarted: saveChatId,
)
09

Identified visitors

Pass a token minted by your backend (same token and examples as in the Web tab). Fetch a fresh one each time the chat opens.

مرّر رمزاً يصدره خادمك (نفس الرمز والأمثلة في تبويب الويب). اطلب رمزاً جديداً في كل مرة تُفتح المحادثة.

dart
final token = await myApi.getChatToken(); // minted by YOUR backend (see the Web tab for examples)
ConnectlyChatWidget(identifyToken: token)
10

Built-in behavior

  • Instant messages: messages appear right away with 🕓, then ✓ - or "Not sent, tap to retry". Retries are never delivered twice.
  • Silent reconnecting: if Android or iOS drops the connection (screen off, file picker, camera, switching apps), the chat reconnects to the same conversation when the app returns and loads anything missed. A Retry bar appears only if that fails.
  • Human handoff: "Talk to a human" sits above the message box, shows a waiting line once asked, and hides while an agent is in the chat.
  • Limits: up to 15 messages per minute per visitor; past that the message shows "Not sent" with how long to wait.
  • رسائل فورية: تظهر الرسالة فوراً مع 🕓 ثم ✓ - أو "لم تُرسل، اضغط لإعادة المحاولة". لا تُسلّم إعادة المحاولة مرتين أبداً.
  • إعادة اتصال صامتة: إذا قطع النظام الاتصال (إطفاء الشاشة، منتقي الملفات، الكاميرا، التنقل بين التطبيقات) تعود المحادثة لنفس الجلسة عند عودة التطبيق وتُحمّل ما فات. ولا يظهر زر إعادة المحاولة إلا إذا فشل ذلك.
  • التحويل إلى موظف: يظهر "التحدث مع موظف" فوق مربع الكتابة، ثم سطر انتظار بعد الطلب، ويختفي أثناء وجود موظف في المحادثة.
  • الحدود: حتى 15 رسالة في الدقيقة لكل زائر؛ بعدها تظهر "لم تُرسل" مع مدة الانتظار.
11

Custom UI (client API)

Building your own chat screen? Use ConnectlyChatClient directly - the same engine the widget runs on.

تبني شاشة محادثة خاصة؟ استخدم ConnectlyChatClient مباشرة - نفس المحرك الذي تعمل عليه الواجهة.

dart
final client = ConnectlyChatClient(ConnectlyChat.config);

client.messages.listen((m) => setState(() => messages.add(m)));
client.connectionChanges.listen((state) => setState(() => connection = state));

await client.connect();                       // or connect(chatId: saved)
final past = await client.fetchHistory();     // the conversation so far
await client.sendMessage('Hi', clientId: id); // throws ConnectlyRateLimitException past 15/min
await client.sendAttachment(File(path));
client.requestHuman();

// Back in the foreground (screen on, picker closed): check the connection.
await client.ensureConnected();

client.dispose();
© 2025 Connectly. All rights reserved.
FAQ Privacy Policy Back to Home